import Foundation import Testing @testable import ClientTLS // B3 · The silent rotation scheduler decides — from the installed identity's // rotation timing — whether to renew before expiry, then drives an injected // mTLS renew. Pure and transport-free: fully unit-testable with fakes. private let t0 = ISO8601DateFormatter().date(from: "2026-09-05T00:00:00Z")! private let renewAfter = ISO8601DateFormatter().date(from: "2026-09-05T00:00:00Z")! private let notAfter = ISO8601DateFormatter().date(from: "2026-10-06T00:00:00Z")! private let before = ISO8601DateFormatter().date(from: "2026-09-04T00:00:00Z")! private let after = ISO8601DateFormatter().date(from: "2026-09-06T00:00:00Z")! private func state(renewAfter: Date? = renewAfter) -> DeviceRenewalState { DeviceRenewalState(deviceId: "dev-1", notAfter: notAfter, renewAfter: renewAfter) } /// Records how many times the renew operation ran. `@unchecked Sendable`: guarded /// by a lock. private final class RenewSpy: @unchecked Sendable { private let lock = NSLock() private var _count = 0 var count: Int { lock.withLock { _count } } func bump() { lock.withLock { _count += 1 } } } @Test("no enrolled identity → notEnrolled, and renew never runs") func schedulerNotEnrolled() async { let spy = RenewSpy() let scheduler = CertificateRotationScheduler( renewalState: { nil }, performRenew: { spy.bump(); return nil }, now: { after } ) let outcome = await scheduler.runIfDue() #expect(outcome == .notEnrolled) #expect(spy.count == 0) } @Test("cert not yet due → notDue, and renew never runs") func schedulerNotDue() async { let spy = RenewSpy() let scheduler = CertificateRotationScheduler( renewalState: { state() }, performRenew: { spy.bump(); return nil }, now: { before } ) let outcome = await scheduler.runIfDue() #expect(outcome == .notDue(renewAfter: renewAfter)) #expect(spy.count == 0) } @Test("past renewAfter → renews exactly once") func schedulerRenews() async { let spy = RenewSpy() let scheduler = CertificateRotationScheduler( renewalState: { state() }, performRenew: { spy.bump(); return nil }, now: { after } ) let outcome = await scheduler.runIfDue() #expect(outcome == .renewed) #expect(spy.count == 1) } @Test("due exactly at renewAfter (>= boundary) renews") func schedulerRenewsAtBoundary() async { let scheduler = CertificateRotationScheduler( renewalState: { state() }, performRenew: { nil }, now: { renewAfter } // now == renewAfter ) #expect(await scheduler.runIfDue() == .renewed) } @Test("a missing renewAfter never triggers (fail-safe — TLS stack is the gate)") func schedulerMissingRenewAfterNeverDue() async { let scheduler = CertificateRotationScheduler( renewalState: { state(renewAfter: nil) }, performRenew: { nil }, now: { after } ) #expect(await scheduler.runIfDue() == .notDue(renewAfter: nil)) } @Test("a renew that throws is surfaced as .failed (never crashes, cert still valid)") func schedulerRenewFailure() async { struct Boom: Error {} let scheduler = CertificateRotationScheduler( renewalState: { state() }, performRenew: { throw Boom() }, now: { after } ) #expect(await scheduler.runIfDue() == .failed) } @Test("a keychain read fault is surfaced as .failed, not a crash") func schedulerReadFailure() async { struct Boom: Error {} let scheduler = CertificateRotationScheduler( renewalState: { throw Boom() }, performRenew: { nil }, now: { after } ) #expect(await scheduler.runIfDue() == .failed) }