import Foundation import MacSyncShared import Testing @testable import INoteSync final class FakeNoteApplier: NoteApplying, @unchecked Sendable { enum Call: Equatable { case create(NoteSendPayload) case update(NoteSendPayload) case delete(NoteSendPayload) } private(set) var calls: [Call] = [] var nextResult: SendQueueApplyResult = .sent func create(_ payload: NoteSendPayload) async -> SendQueueApplyResult { calls.append(.create(payload)) return nextResult } func update(_ payload: NoteSendPayload) async -> SendQueueApplyResult { calls.append(.update(payload)) return nextResult } func delete(_ payload: NoteSendPayload) async -> SendQueueApplyResult { calls.append(.delete(payload)) return nextResult } } @Suite("NoteSender dispatch") @MainActor struct SenderDispatchTests { private func item(_ action: String, _ payload: NoteSendPayload = NoteSendPayload()) -> PendingNoteSend { PendingNoteSend(id: "i-1", action: action, payload: payload, createdAt: "2026-05-13T00:00:00Z") } @Test("create_note dispatches to create") func dispatchCreate() async { let fake = FakeNoteApplier() let sender = NoteSender(applier: fake) let p = NoteSendPayload(folder: "Notes", name: "Hi", body: "
x
") let result = await sender.apply(item("create_note", p)) guard case .sent = result else { Issue.record("expected .sent"); return } #expect(fake.calls == [.create(p)]) } @Test("update_note dispatches to update") func dispatchUpdate() async { let fake = FakeNoteApplier() let sender = NoteSender(applier: fake) let p = NoteSendPayload(noteIdentifier: "id-1", body: "new
") _ = await sender.apply(item("update_note", p)) #expect(fake.calls == [.update(p)]) } @Test("delete_note dispatches to delete") func dispatchDelete() async { let fake = FakeNoteApplier() let sender = NoteSender(applier: fake) let p = NoteSendPayload(noteIdentifier: "id-1") _ = await sender.apply(item("delete_note", p)) #expect(fake.calls == [.delete(p)]) } @Test("unknown action returns .failed without invoking applier") func unknownAction() async { let fake = FakeNoteApplier() let sender = NoteSender(applier: fake) let result = await sender.apply(item("frobnicate")) guard case .failed(let reason) = result else { Issue.record("expected .failed") return } #expect(reason.contains("unknown action")) #expect(fake.calls.isEmpty) } @Test("propagates .failed from applier") func propagatesFailure() async { let fake = FakeNoteApplier() fake.nextResult = .failed(reason: "boom") let sender = NoteSender(applier: fake) let result = await sender.apply(item("create_note", NoteSendPayload(name: "n"))) guard case .failed(let reason) = result else { Issue.record("expected .failed") return } #expect(reason == "boom") } } @Suite("AppleScriptNoteApplier missing-field guards") struct AppleScriptNoteApplierGuards { @Test("create without name returns .failed") func createMissingName() async { let applier = AppleScriptNoteApplier() let result = await applier.create(NoteSendPayload(body: "x")) guard case .failed(let reason) = result else { Issue.record("expected .failed"); return } #expect(reason.contains("name")) } @Test("update without noteIdentifier returns .failed") func updateMissingId() async { let applier = AppleScriptNoteApplier() let result = await applier.update(NoteSendPayload(body: "x")) guard case .failed(let reason) = result else { Issue.record("expected .failed"); return } #expect(reason.contains("noteIdentifier")) } @Test("update with id but no fields returns .failed") func updateNoFields() async { let applier = AppleScriptNoteApplier() let result = await applier.update(NoteSendPayload(noteIdentifier: "id-1")) guard case .failed(let reason) = result else { Issue.record("expected .failed"); return } #expect(reason.contains("no fields")) } @Test("delete without noteIdentifier returns .failed") func deleteMissingId() async { let applier = AppleScriptNoteApplier() let result = await applier.delete(NoteSendPayload()) guard case .failed(let reason) = result else { Issue.record("expected .failed"); return } #expect(reason.contains("noteIdentifier")) } }