ContratSyncFixturesTest.swift
111 lignes · 5281 octets
import XCTest @testable import Card2vcf /// Décode les fixtures de contrat de synchronisation avec les vrais DTO Swift. /// /// Si un champ serveur est renommé, ce test devient rouge immédiatement. /// Preuve de rougeur (vérifiée) : en renommant `transcription_erreur` → `transcription_error` /// dans `interaction_note_vocale.json`, l'assertion suivante échoue : /// XCTAssertEqual failed: ("Optional("Modèle ASR indisponible")") is not equal to ("nil") final class ContratSyncFixturesTest: XCTestCase { private func fixture(_ nom: String) throws -> Data { let bundle = Bundle(for: ContratSyncFixturesTest.self) guard let url = bundle.url(forResource: nom, withExtension: nil) else { XCTFail("Fixture introuvable dans le bundle de test : \(nom)") throw NSError(domain: "fixture", code: 0) } return try Data(contentsOf: url) } // ---- sync_pull.json ---- func testSyncPull_decodeSyncPullResponse() throws { let data = try fixture("sync_pull.json") let pull = try SyncJson.decoder().decode(SyncPullResponse.self, from: data) XCTAssertFalse(pull.contacts.isEmpty, "contacts doit contenir au moins un élément") XCTAssertFalse(pull.interactions.isEmpty, "interactions doit contenir au moins un élément") XCTAssertFalse(pull.notes.isEmpty, "notes doit contenir au moins un élément") } func testSyncPull_contactChampsCritiques() throws { let data = try fixture("sync_pull.json") let pull = try SyncJson.decoder().decode(SyncPullResponse.self, from: data) let contact = try XCTUnwrap(pull.contacts.first) XCTAssertFalse(contact.id.isEmpty, "id contact ne doit pas être vide") XCTAssertFalse(contact.creeLe.isEmpty, "creeLe contact ne doit pas être vide") } /// Contrat de planification : si le serveur renomme un de ces champs, le /// téléphone cesserait silencieusement d'afficher dates, étiquettes ou /// sous-tâches. Ce test rend la rupture immédiate. /// /// Preuve de rougeur (vérifiée) : en renommant `duree_jours` → `duree` dans /// `sync_pull.json`, ce test **et** `testSyncPull_periodeCalculeeCommeLeServeur` /// échouent — /// XCTAssertEqual failed: ("Optional(3)") is not equal to ("nil") func testSyncPull_planificationDesTaches() throws { let data = try fixture("sync_pull.json") let pull = try SyncJson.decoder().decode(SyncPullResponse.self, from: data) let projet = try XCTUnwrap(pull.projets.first) XCTAssertTrue(projet.planification, "planification doit être transportée") XCTAssertEqual(5, projet.joursOuvres) XCTAssertEqual("2026-09-30", projet.echeance) let tache = try XCTUnwrap(pull.taches.first) XCTAssertEqual("2026-09-21", tache.debut) XCTAssertEqual(3, tache.dureeJours) XCTAssertEqual("contrat", tache.etiquette) XCTAssertEqual(2, tache.sousTaches.count) let premiere = try XCTUnwrap(tache.sousTaches.first) XCTAssertTrue(premiere.fait, "l'état coché doit survivre au transport") } /// La fin affichée sur le téléphone doit être celle du Gantt web. func testSyncPull_periodeCalculeeCommeLeServeur() throws { let data = try fixture("sync_pull.json") let pull = try SyncJson.decoder().decode(SyncPullResponse.self, from: data) let projet = try XCTUnwrap(pull.projets.first) let tache = try XCTUnwrap(pull.taches.first) // Lundi 2026-09-21 + 3 jours ouvrés en rythme 5 : mercredi 23. let debutIso = try XCTUnwrap(tache.debut) let debut = try XCTUnwrap(DateCivile(iso: debutIso)) let duree = try XCTUnwrap(tache.dureeJours) let periode = CalendrierOuvre(joursOuvres: projet.joursOuvres) .periodeAffichee(debut, duree) XCTAssertEqual("21/09 → 23/09", periode) } // ---- interaction_note_vocale.json ---- func testInteraction_decodeInteractionDto() throws { let data = try fixture("interaction_note_vocale.json") let dto = try SyncJson.decoder().decode(InteractionDto.self, from: data) XCTAssertEqual("note_vocale", dto.typeInteraction) XCTAssertEqual("vocale_contrat_1.wav", dto.pieceJointe) XCTAssertEqual("echec", dto.transcription) // Champ critique : si transcription_erreur est renommé côté serveur, // cette assertion devient rouge : // XCTAssertEqual failed: ("Optional("Modèle ASR indisponible")") is not equal to ("nil") XCTAssertEqual("Modèle ASR indisponible", dto.transcriptionErreur) } // ---- note_projet.json ---- func testNoteProjet_decodeNoteProjetDto() throws { let data = try fixture("note_projet.json") let dto = try SyncJson.decoder().decode(NoteProjetDto.self, from: data) XCTAssertEqual("projet-contrat-1", dto.projetId) XCTAssertEqual("note_contrat_1.wav", dto.audio) XCTAssertEqual("terminee", dto.transcription) // transcription_erreur null pour une transcription réussie XCTAssertNil(dto.transcriptionErreur, "transcriptionErreur doit être nil pour une transcription réussie") } }
GitRust