oficina / calendar Public

Clone
git clone https://amadan.net/oficina/calendar

Plain git — no account needed to clone.

Download

Download this file

1import XCTest
2import RastrilloApple
3@testable import CalendarKit
4
5final class SessionLifecycleTests: XCTestCase {
6 // Independent reviewer mutation, 2026-09-13: remove saveQuick's guard after
7 // the POST response. Four assertions fail (cancellation/date/notice/result);
8 // restored source passes all five Swift tests.
9 @MainActor func testLateSavesCannotReloadClearedSession() async throws {
10 for quick in [false, true] {
11 let process = Process()
12 process.executableURL = URL(fileURLWithPath: "/usr/bin/python3")
13 process.arguments = ["-u", "-c", """
14import http.server, threading, json
15started=threading.Event(); release=threading.Event(); reads=0
16class Handler(http.server.BaseHTTPRequestHandler):
17 def log_message(self,*args): pass
18 def reply(self,value):
19 data=json.dumps(value).encode(); self.send_response(200); self.send_header('Content-Length',str(len(data))); self.end_headers(); self.wfile.write(data)
20 def do_POST(self):
21 self.rfile.read(int(self.headers.get('Content-Length','0'))); started.set(); release.wait(10); self.reply({'id':1,'warning':'Event saved. Reminder unavailable.'})
22 def do_GET(self):
23 global reads
24 if self.path=='/wait': started.wait(10); self.reply({}); return
25 if self.path=='/release': release.set(); self.reply({}); return
26 if self.path=='/counts': self.reply({'reads':reads}); return
27 reads+=1; self.reply({})
28server=http.server.ThreadingHTTPServer(('127.0.0.1',0),Handler)
29print(server.server_port,flush=True); server.serve_forever()
30"""]
31 let output = Pipe(); process.standardOutput = output; process.standardError = FileHandle.nullDevice
32 try process.run()
33 defer { process.terminate() }
34 let data = output.fileHandleForReading.availableData
35 let port = try XCTUnwrap(String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines))
36 let client = NativeHTTP(origin: URL(string: "http://127.0.0.1:\(port)")!)
37 let model = CalendarModel(zone: "Europe/Dublin")
38 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: [])
39 let preview = QuickPreview(draft: draft, when: "Fixture time", repeat_label: "", calendar_name: "Personal", assumptions: [])
40 let task = Task {
41 if quick { try await model.saveQuick(preview, client: client) }
42 else { try await model.save(CalendarEvent(), client: client) }
43 }
44 _ = try await client.response("/wait")
45 model.clear()
46 let clearedDate = model.date
47 _ = try await client.response("/release")
48 do { try await task.value; XCTFail("Old save completed against cleared session") }
49 catch is CancellationError { }
50 await model.load(client) // Even queued refresh callbacks cannot revive this model.
51 struct Counts: Decodable { let reads: Int }
52 let counts: Counts = try await client.request("/counts")
53 XCTAssertEqual(counts.reads, 0)
54 XCTAssertEqual(model.date, clearedDate)
55 XCTAssertTrue(model.events.isEmpty)
56 XCTAssertTrue(model.calendars.isEmpty)
57 XCTAssertTrue(model.notice.isEmpty)
58 XCTAssertTrue(model.quickSavedNotice.isEmpty)
59 XCTAssertFalse(model.quickSaving)
60 }
61 }
62 @MainActor func testSystemTimeZoneDefaultAndOverride() {
63 let key = "calendar.timeZoneOverride"
64 let prior = UserDefaults.standard.object(forKey: key)
65 defer { if let prior { UserDefaults.standard.set(prior, forKey: key) } else { UserDefaults.standard.removeObject(forKey: key) } }
66 UserDefaults.standard.removeObject(forKey: key)
67 XCTAssertEqual(CalendarModel().zone, TimeZone.current.identifier)
68 UserDefaults.standard.set("Pacific/Kiritimati", forKey: key)
69 XCTAssertEqual(CalendarModel().newEvent().tz, "Pacific/Kiritimati")
70 UserDefaults.standard.set("", forKey: key)
71 XCTAssertEqual(CalendarModel().zone, TimeZone.current.identifier)
72 }
73}
74