CalendrierOuvreTest.swift 117 lignes · 5588 octets
import XCTest
@testable import Card2vcf

/// Port Swift de `Server/tests/calendrier.rs` et de `CalendrierOuvreTest.kt` :
/// **les mêmes cas, les mêmes valeurs attendues**. Si les trois suites
/// divergent, le téléphone affichera une date de fin différente de celle du
/// Gantt web.
///
/// Repères : 2026-01-05 est un lundi, 2026-01-09 un vendredi, 2026-01-10 un
/// samedi et 2026-01-11 un dimanche.
final class CalendrierOuvreTest: XCTestCase {
    private func j(_ s: String) -> DateCivile {
        guard let d = DateCivile(iso: s) else {
            XCTFail("date de test invalide : \(s)")
            return DateCivile(iso: "1970-01-01")!
        }
        return d
    }

    func testEstOuvreDependDuRythme() {
        let samedi = j("2026-01-10")
        let dimanche = j("2026-01-11")
        let lundi = j("2026-01-05")

        for rythme in [5, 6, 7] {
            XCTAssertTrue(CalendrierOuvre(joursOuvres: rythme).estOuvre(lundi))
        }
        XCTAssertFalse(CalendrierOuvre(joursOuvres: 5).estOuvre(samedi))
        XCTAssertTrue(CalendrierOuvre(joursOuvres: 6).estOuvre(samedi))
        XCTAssertTrue(CalendrierOuvre(joursOuvres: 7).estOuvre(samedi))
        XCTAssertFalse(CalendrierOuvre(joursOuvres: 5).estOuvre(dimanche))
        XCTAssertFalse(CalendrierOuvre(joursOuvres: 6).estOuvre(dimanche))
        XCTAssertTrue(CalendrierOuvre(joursOuvres: 7).estOuvre(dimanche))
    }

    func testCalerAvanceAuProchainJourOuvre() {
        let samedi = j("2026-01-10")
        XCTAssertEqual(j("2026-01-12"), CalendrierOuvre(joursOuvres: 5).caler(samedi))
        XCTAssertEqual(samedi, CalendrierOuvre(joursOuvres: 6).caler(samedi))
        XCTAssertEqual(samedi, CalendrierOuvre(joursOuvres: 7).caler(samedi))
        let mardi = j("2026-01-06")
        XCTAssertEqual(mardi, CalendrierOuvre(joursOuvres: 5).caler(mardi), "jour déjà ouvré")
    }

    func testFinCompteDesJoursOuvres() {
        let vendredi = j("2026-01-09")
        XCTAssertEqual(j("2026-01-13"), CalendrierOuvre(joursOuvres: 5).fin(vendredi, 3))
        XCTAssertEqual(j("2026-01-12"), CalendrierOuvre(joursOuvres: 6).fin(vendredi, 3))
        XCTAssertEqual(j("2026-01-11"), CalendrierOuvre(joursOuvres: 7).fin(vendredi, 3))
    }

    func testFinDUneTacheDUnJourEstSonDebut() {
        let vendredi = j("2026-01-09")
        XCTAssertEqual(vendredi, CalendrierOuvre(joursOuvres: 5).fin(vendredi, 1))
        XCTAssertEqual(vendredi, CalendrierOuvre(joursOuvres: 5).fin(vendredi, 0), "durée nulle = un jour")
    }

    func testFinCaleUnDebutTombantUnJourChome() {
        let samedi = j("2026-01-10")
        XCTAssertEqual(j("2026-01-13"), CalendrierOuvre(joursOuvres: 5).fin(samedi, 2))
    }

    func testAvancerSauteLesJoursChomes() {
        let vendredi = j("2026-01-09")
        XCTAssertEqual(vendredi, CalendrierOuvre(joursOuvres: 5).avancer(vendredi, 0))
        XCTAssertEqual(j("2026-01-12"), CalendrierOuvre(joursOuvres: 5).avancer(vendredi, 1))
        XCTAssertEqual(j("2026-01-10"), CalendrierOuvre(joursOuvres: 6).avancer(vendredi, 1))
        XCTAssertEqual(j("2026-01-10"), CalendrierOuvre(joursOuvres: 7).avancer(vendredi, 1))
    }

    func testCompterLesJoursOuvresDUnIntervalle() {
        let lundi = j("2026-01-05")
        let dimanche = j("2026-01-11")
        XCTAssertEqual(5, CalendrierOuvre(joursOuvres: 5).compter(lundi, dimanche))
        XCTAssertEqual(6, CalendrierOuvre(joursOuvres: 6).compter(lundi, dimanche))
        XCTAssertEqual(7, CalendrierOuvre(joursOuvres: 7).compter(lundi, dimanche))
        XCTAssertEqual(10, CalendrierOuvre(joursOuvres: 5).compter(lundi, j("2026-01-18")))
        XCTAssertEqual(1, CalendrierOuvre(joursOuvres: 5).compter(lundi, lundi))
        XCTAssertEqual(0, CalendrierOuvre(joursOuvres: 5).compter(dimanche, lundi), "intervalle inversé")
    }

    func testUnRythmeAberrantRetombeSurSeptJours() {
        XCTAssertTrue(CalendrierOuvre(joursOuvres: 0).estOuvre(j("2026-01-11")))
        XCTAssertTrue(CalendrierOuvre(joursOuvres: 99).estOuvre(j("2026-01-11")))
    }

    func testPeriodeAffichee() {
        XCTAssertEqual("09/01 → 13/01", CalendrierOuvre(joursOuvres: 5).periodeAffichee(j("2026-01-09"), 3))
        XCTAssertEqual("09/01", CalendrierOuvre(joursOuvres: 5).periodeAffichee(j("2026-01-09"), 1))
    }

    // MARK: - `DateCivile`, sans équivalent Kotlin (`LocalDate` y est fourni)

    func testDateCivileRejetteLesChainesInvalides() {
        XCTAssertNil(DateCivile(iso: "2026-02-31"), "jour inexistant")
        XCTAssertNil(DateCivile(iso: "2026-13-01"), "mois inexistant")
        XCTAssertNil(DateCivile(iso: "26-01-05"), "année sur deux chiffres")
        XCTAssertNil(DateCivile(iso: "2026-1-5"), "champs non remplis à deux chiffres")
        XCTAssertNil(DateCivile(iso: ""))
        XCTAssertNil(DateCivile(iso: "pas une date"))
    }

    func testDateCivileEstIndependanteDuFuseau() {
        // Un `Date` interprété dans un fuseau à l'ouest basculerait la veille.
        let lundi = j("2026-01-05")
        XCTAssertEqual(1, lundi.jourDeSemaine, "2026-01-05 est un lundi")
        XCTAssertEqual(7, j("2026-01-11").jourDeSemaine, "2026-01-11 est un dimanche")
        XCTAssertEqual("2026-01-05", lundi.iso)
        XCTAssertEqual("05/01", lundi.jourMois)
    }

    func testDateCivileFranchitLesBornesDeMoisEtDAnnee() {
        XCTAssertEqual(j("2026-03-01"), j("2026-02-28").plusJours(1))
        XCTAssertEqual(j("2027-01-01"), j("2026-12-31").plusJours(1))
        XCTAssertEqual(j("2024-02-29"), j("2024-02-28").plusJours(1), "2024 est bissextile")
    }
}