import XCTest import RastrilloApple @testable import CalendarKit final class SessionLifecycleTests: XCTestCase { // Independent reviewer mutation, 2026-09-13: remove saveQuick's guard after // the POST response. Four assertions fail (cancellation/date/notice/result); // restored source passes all five Swift tests. @MainActor func testLateSavesCannotReloadClearedSession() async throws { for quick in [false, true] { let process = Process() process.executableURL = URL(fileURLWithPath: "/usr/bin/python3") process.arguments = ["-u", "-c", """ import http.server, threading, json started=threading.Event(); release=threading.Event(); reads=0 class Handler(http.server.BaseHTTPRequestHandler): def log_message(self,*args): pass def reply(self,value): data=json.dumps(value).encode(); self.send_response(200); self.send_header('Content-Length',str(len(data))); self.end_headers(); self.wfile.write(data) def do_POST(self): self.rfile.read(int(self.headers.get('Content-Length','0'))); started.set(); release.wait(10); self.reply({'id':1,'warning':'Event saved. Reminder unavailable.'}) def do_GET(self): global reads if self.path=='/wait': started.wait(10); self.reply({}); return if self.path=='/release': release.set(); self.reply({}); return if self.path=='/counts': self.reply({'reads':reads}); return reads+=1; self.reply({}) server=http.server.ThreadingHTTPServer(('127.0.0.1',0),Handler) print(server.server_port,flush=True); server.serve_forever() """] let output = Pipe(); process.standardOutput = output; process.standardError = FileHandle.nullDevice try process.run() defer { process.terminate() } let data = output.fileHandleForReading.availableData let port = try XCTUnwrap(String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines)) let client = NativeHTTP(origin: URL(string: "http://127.0.0.1:\(port)")!) let model = CalendarModel(zone: "Europe/Dublin") let draft = QuickDraft(calendar_id: 1, title: "Fixture", location: "", start_local: "2030-10-25T09:00", end_local: "2030-10-25T10:00", tz: "Europe/Dublin", all_day: false, repeat: "none", attendees: []) let preview = QuickPreview(draft: draft, when: "Fixture time", repeat_label: "", calendar_name: "Personal", assumptions: []) let task = Task { if quick { try await model.saveQuick(preview, client: client) } else { try await model.save(CalendarEvent(), client: client) } } _ = try await client.response("/wait") model.clear() let clearedDate = model.date _ = try await client.response("/release") do { try await task.value; XCTFail("Old save completed against cleared session") } catch is CancellationError { } await model.load(client) // Even queued refresh callbacks cannot revive this model. struct Counts: Decodable { let reads: Int } let counts: Counts = try await client.request("/counts") XCTAssertEqual(counts.reads, 0) XCTAssertEqual(model.date, clearedDate) XCTAssertTrue(model.events.isEmpty) XCTAssertTrue(model.calendars.isEmpty) XCTAssertTrue(model.notice.isEmpty) XCTAssertTrue(model.quickSavedNotice.isEmpty) XCTAssertFalse(model.quickSaving) } } @MainActor func testSystemTimeZoneDefaultAndOverride() { let key = "calendar.timeZoneOverride" let prior = UserDefaults.standard.object(forKey: key) defer { if let prior { UserDefaults.standard.set(prior, forKey: key) } else { UserDefaults.standard.removeObject(forKey: key) } } UserDefaults.standard.removeObject(forKey: key) XCTAssertEqual(CalendarModel().zone, TimeZone.current.identifier) UserDefaults.standard.set("Pacific/Kiritimati", forKey: key) XCTAssertEqual(CalendarModel().newEvent().tz, "Pacific/Kiritimati") UserDefaults.standard.set("", forKey: key) XCTAssertEqual(CalendarModel().zone, TimeZone.current.identifier) } }