import Foundation private enum SelfTestFailure: Error { case failed(String) } private func expect(_ condition: @autoclosure () -> Bool, _ message: String) throws { if !condition() { throw SelfTestFailure.failed(message) } } func runDesktopControlSelfTests() { do { let status = try JSONDecoder().decode(RecordingStatus.self, from: Data(#"{"isRecording":false,"state":2,"launchProfile":null}"#.utf8)) try expect(String(describing: status.state).lowercased() == "summarizing", "numeric status 2 must show summarizing") print("PASS numeric recording status") let requests = try decodeDesktopRequests(Data(requestFixture.utf8)) try expect(requests.count == 1 && requests[0].id == "approval-1", "pending request must decode") try expect(requests[0].actions == [DesktopUserAction(id: "yes", label: "Yes"), DesktopUserAction(id: "no", label: "No")], "offered actions must be preserved") try expect(requests[0].expiresAt.timeIntervalSince1970 == 1_791_022_260, "offset and fractional timestamps must decode") print("PASS pending request wire payload") var state = DesktopUserRequestState() let now = Date(timeIntervalSince1970: 1_791_022_220) _ = state.reconcile(requests, now: now) try expect(state.notificationsToAnnounce().map(\.id) == ["approval-1"], "new pending request must be announced") state.markAnnounced("approval-1") _ = state.reconcile(requests, now: now) try expect(state.notificationsToAnnounce().isEmpty && state.pending.count == 1, "repeated polling must not redeliver or hide menu requests") try expect(state.reconcile([], now: now) == ["approval-1"] && state.pending.isEmpty, "withdrawn request must remove native presentation") print("PASS notification reconciliation and menu fallback") _ = state.reconcile(requests, now: now) try expect(state.beginResponse(baseUrl: "http://127.0.0.1:5090", requestId: "approval-1", action: "not-offered", now: now) == nil, "invalid action must keep request pending") guard let response = state.beginResponse(baseUrl: "http://127.0.0.1:5090/", requestId: "approval-1", action: "yes", now: now) else { throw SelfTestFailure.failed("offered action must produce a response") } try expect(response.url?.absoluteString == "http://127.0.0.1:5090/desktop/requests/approval-1/respond" && response.httpMethod == "POST", "notification and menu response endpoint") let body = try JSONDecoder().decode([String: String].self, from: response.httpBody ?? Data()) try expect(body == ["action": "yes"], "chosen action must use shared JSON contract") try expect(state.beginResponse(baseUrl: "http://127.0.0.1:5090", requestId: "approval-1", action: "yes", now: now) == nil, "same pending action must not submit twice") state.completeResponse("approval-1", accepted: false) try expect(!state.isResponding("approval-1") && state.pending.count == 1, "failed transport must leave menu fallback actionable") try expect(state.beginResponse(baseUrl: "http://127.0.0.1:5090", requestId: "approval-1", action: "yes", now: requests[0].expiresAt) == nil, "expired response must not submit") try expect(state.reconcile(requests, now: requests[0].expiresAt) == ["approval-1"] && state.pending.isEmpty, "expired request must be removed locally") try expect(state.beginResponse(baseUrl: "http://127.0.0.1:5090", requestId: "approval-1", action: "yes", now: now) == nil, "stale notification must not reconstruct request") print("PASS allowed once-only response and stale action rejection") _ = state.reconcile(requests, now: now) state.completeResponse("approval-1", accepted: true) _ = state.reconcile(requests, now: now) try expect(state.pending.isEmpty && state.notificationsToAnnounce().isEmpty, "old HTTP snapshot must not resurrect accepted request") print("PASS acknowledged response survives stale polling") let category = requests[0].notificationCategory() let notification = requests[0].notificationRequest() try expect(category.actions.map(\.identifier) == ["yes", "no"], "native notification must offer same actions as menu") try expect(category.actions.map(\.title) == ["Yes", "No"], "native action labels") try expect(notification.content.title == "Associate project?" && notification.content.body == "Add project Delta to this meeting?", "native notification must show pending request text") try expect(notification.content.categoryIdentifier == category.identifier && notification.content.userInfo["requestId"] as? String == "approval-1", "native actions must route original request id") try expect(category.options.contains(.customDismissAction), "notification dismissal must leave menu decision pending") print("PASS actionable native notification payload") let windows = try JSONDecoder().decode([DesktopAgentWindow].self, from: Data(#"[{"id":"workflow-1","url":"http://127.0.0.1:5090/desktop/agent-sessions/workflow-1"},{"id":"workflow-2","url":"http://127.0.0.1:5090/desktop/agent-sessions/workflow-2"}]"#.utf8)) var windowState = DesktopAgentWindowState() try expect(windowState.takeUnopened(windows).map(\.id) == ["workflow-1", "workflow-2"], "two approved workflows must request independent windows") try expect(windowState.takeUnopened(windows).isEmpty, "polling must not reopen independent windows") print("PASS independent agent window wire payload and opening") } catch { fputs("FAIL desktop controls self-test: \(error)\n", stderr) exit(1) } } private let requestFixture = #""" [{"id":"approval-1","kind":"approval","title":"Associate project?","message":"Add project Delta to this meeting?","createdAt":"2026-10-03T12:10:00.1234567+02:00","expiresAt":"2026-10-03T12:11:00+02:00","actions":[{"id":"yes","label":"Yes"},{"id":"no","label":"No"}]}] """#