import Foundation import Testing @testable import Kanban /// What a paste actually writes (04-interactions.md ▸ Clipboard) — the materialization rules, the /// armed cut's move, and the anchors applied end to end. /// /// Like every other write suite here these drive a **real store over a real temp board** and then /// read back through the loader or the raw bytes, never through a snapshot the store handed out: the /// interesting claims are about the files — which folder arrived, which UUID was minted, which /// `deleted:` was stripped, which attachment travelled. `WriterFixture`, `Ident` and `Item` come from /// `WriterTestSupport.swift`; `FakePasteboard`, `ClipboardHarness` and the board fixture come from /// `ClipboardTests.swift`. // MARK: - Helpers /// The board as the loader sees it — never the store's snapshot, which a paste deliberately does not /// touch (the one-way flow: the write lands, the watcher reloads). private func pasted(_ fixture: WriterFixture) throws -> BoardModel { try BoardLoader.load(boardRoot: fixture.root).model } private func lane(_ id: ItemID, in fixture: WriterFixture) throws -> Lane? { try pasted(fixture).lanes.first { $0.id == id } } /// A lane's rendered card titles, in display order. private func pastedTitles(_ id: ItemID, in fixture: WriterFixture) throws -> [String] { try lane(id, in: fixture)?.cards.filter { !$0.isDeleted }.compactMap(\.title.value) ?? [] } /// A lane's rendered card folder names, in display order — identity, where titles cannot tell an /// original from its copy. private func pastedIDs(_ id: ItemID, in fixture: WriterFixture) throws -> [String] { try lane(id, in: fixture)?.cards.filter { !$0.isDeleted }.map(\.id.rawValue) ?? [] } /// A fresh, empty destination board — one lane holding one card, so an arrival has neighbours. @MainActor private func makeDestination() throws -> WriterFixture { let fixture = try WriterFixture() try fixture.item("", Item.board) try fixture.item(Ident.lane4, Item.rich(order: "1024", title: "Inbox")) try fixture.item("\(Ident.lane4)/\(Ident.indexless)", Item.rich(order: "1024", title: "Resident")) return fixture } private let destinationLane = ItemID(rawValue: Ident.lane4) // MARK: - Copy materialization @MainActor @Suite("Paste ▸ copy from staging") struct PasteFromStagingTests { @Test("A pasted card is byte-perfect from the snapshot, attachments and all, under a fresh GUID") func bytePerfectCopy() async throws { let harness = try makeClipboardHarness() defer { harness.tearDown() } let destination = try makeDestination() defer { destination.tearDown() } let target = try BoardStore(rootURL: destination.root) harness.store.select([clipboardCard1], in: .board) harness.clipboard.copy(from: harness.store) target.select([destinationLane], in: .board) await harness.clipboard.paste(into: target)?.value let arrived = try #require(try pastedIDs(destinationLane, in: destination).last) // Fresh identity — "copies mint fresh ones" (01-storage-format.md). #expect(arrived != Ident.card1) #expect(try pastedTitles(destinationLane, in: destination) == ["Resident", "First"]) // The attachments came with it, byte for byte. #expect(try destination.data("\(Ident.lane4)/\(arrived)/attachments/photo.png") == Data("png bytes".utf8)) #expect(try destination.data("\(Ident.lane4)/\(arrived)/attachments/notes.txt") == Data("notes".utf8)) } @Test("A copy keeps `created` and takes a fresh `modified` — a duplicate is a fork") func forkStamps() async throws { let harness = try makeClipboardHarness() defer { harness.tearDown() } let destination = try makeDestination() defer { destination.tearDown() } let target = try BoardStore(rootURL: destination.root) harness.store.select([clipboardCard1], in: .board) harness.clipboard.copy(from: harness.store) target.select([destinationLane], in: .board) await harness.clipboard.paste(into: target)?.value let arrived = try #require(try pastedIDs(destinationLane, in: destination).last) let document = try FrontmatterDocument.parse(destination.indexText("\(Ident.lane4)/\(arrived)")) #expect(document.created.value == ISO8601DateFormatter().date(from: "2026-01-01T09:00:00Z")) #expect(document.modified.value != ISO8601DateFormatter().date(from: "2026-02-02T09:00:00Z")) // The unknown keys and the body rode along untouched. #expect(document.value(for: "project") != nil) #expect(document.body.contains("First body")) } @Test("The originals stay exactly where they were") func originalsUntouched() async throws { let harness = try makeClipboardHarness() defer { harness.tearDown() } let destination = try makeDestination() defer { destination.tearDown() } let target = try BoardStore(rootURL: destination.root) harness.store.select([clipboardCard1], in: .board) harness.clipboard.copy(from: harness.store) target.select([destinationLane], in: .board) await harness.clipboard.paste(into: target)?.value #expect(try pastedTitles(clipboardLane1, in: harness.fixture) == ["First", "Second"]) } @Test("A second paste materializes a second copy") func secondPasteCopiesAgain() async throws { let harness = try makeClipboardHarness() defer { harness.tearDown() } let destination = try makeDestination() defer { destination.tearDown() } let target = try BoardStore(rootURL: destination.root) harness.store.select([clipboardCard1], in: .board) harness.clipboard.copy(from: harness.store) target.select([destinationLane], in: .board) await harness.clipboard.paste(into: target)?.value target.handleWatcherEvent(.treeChanged(.appMediated)) await target.awaitQuiescence() target.select([destinationLane], in: .board) await harness.clipboard.paste(into: target)?.value let ids = try pastedIDs(destinationLane, in: destination) #expect(ids.count == 3) #expect(Set(ids).count == 3) } @Test("Pasting into the source board is the within-board duplicate") func pasteIntoSource() async throws { let harness = try makeClipboardHarness() defer { harness.tearDown() } harness.store.select([clipboardCard1], in: .board) harness.clipboard.copy(from: harness.store) await harness.clipboard.paste(into: harness.store)?.value // The copy landed immediately after its own original, which is the anchor rule. #expect(try pastedTitles(clipboardLane1, in: harness.fixture) == ["First", "First", "Second"]) let ids = try pastedIDs(clipboardLane1, in: harness.fixture) #expect(Set(ids).count == 3) } } // MARK: - Lane pastes @MainActor @Suite("Paste ▸ lanes") struct PasteLaneTests { @Test("A pasted lane copy takes fresh GUIDs throughout and carries exactly its cards") func laneCopyRemintsThroughout() async throws { let harness = try makeClipboardHarness() defer { harness.tearDown() } let destination = try makeDestination() defer { destination.tearDown() } let target = try BoardStore(rootURL: destination.root) harness.store.select([clipboardLane1], in: .board) harness.clipboard.copy(from: harness.store) await harness.clipboard.paste(into: target)?.value let model = try pasted(destination) #expect(model.lanes.count == 2) let arrived = try #require(model.lanes.last) #expect(arrived.id.rawValue != Ident.lane1) #expect(arrived.title.value == "Todo") // "A lane carries exactly its cards — the trash is board-level, so there is nothing // lane-nested to strip or carry" (04 ▸ Drag and drop, resettled 2026-07-28). #expect(arrived.cards.count == 2) #expect(arrived.cards.map(\.id.rawValue).allSatisfy { $0 != Ident.card1 && $0 != Ident.card2 }) #expect(try pasted(harness.fixture).trash.count == 1, "and the source's trash is untouched") } @Test("A lane paste with nothing selected lands at the board's right end") func laneLandsAtTheRightEnd() async throws { let harness = try makeClipboardHarness() defer { harness.tearDown() } let destination = try makeDestination() defer { destination.tearDown() } let target = try BoardStore(rootURL: destination.root) harness.store.select([clipboardLane1], in: .board) harness.clipboard.copy(from: harness.store) await harness.clipboard.paste(into: target)?.value #expect(try pasted(destination).lanes.compactMap(\.title.value) == ["Inbox", "Todo"]) } @Test("A lane pastes onto a board with no lanes at all") func laneOntoZeroLaneBoard() async throws { let harness = try makeClipboardHarness() defer { harness.tearDown() } let empty = try WriterFixture() defer { empty.tearDown() } try empty.item("", Item.board) let target = try BoardStore(rootURL: empty.root) harness.store.select([clipboardLane1], in: .board) harness.clipboard.copy(from: harness.store) await harness.clipboard.paste(into: target)?.value #expect(try pasted(empty).lanes.compactMap(\.title.value) == ["Todo"]) } @Test("A lane cut-move carries its cards whole, identity and all") func laneCutMoveCarriesItsCards() async throws { let harness = try makeClipboardHarness() defer { harness.tearDown() } let destination = try makeDestination() defer { destination.tearDown() } let target = try BoardStore(rootURL: destination.root) harness.store.select([clipboardLane1], in: .board) harness.clipboard.cut(from: harness.store) await harness.clipboard.paste(into: target)?.value let model = try pasted(destination) let arrived = try #require(model.lanes.first { $0.id == clipboardLane1 }) // Identity travelled, and every card came with it — nothing to strip. #expect(arrived.cards.count == 2) #expect(model.trash.isEmpty, "the source board's trash is board-level and stays there") // The lane left the source board entirely. #expect(try pasted(harness.fixture).lanes.map(\.id) == [clipboardLane2]) } @Test("The within-board lane duplicate — paste into the source board") func withinBoardLaneDuplicate() async throws { let harness = try makeClipboardHarness() defer { harness.tearDown() } harness.store.select([clipboardLane1], in: .board) harness.clipboard.copy(from: harness.store) await harness.clipboard.paste(into: harness.store)?.value let model = try pasted(harness.fixture) #expect(model.lanes.compactMap(\.title.value) == ["Todo", "Todo", "Doing"]) let duplicate = try #require(model.lanes.dropFirst().first) #expect(duplicate.id != clipboardLane1) #expect(duplicate.cards.count == 2) } } // MARK: - The trash's copy-out @MainActor @Suite("Paste ▸ from the trash") struct PasteFromTrashTests { @Test("A card copied out of the trash is an ordinary copy of an ordinary card") func cardCopiesOutOrdinarily() async throws { let harness = try makeClipboardHarness() defer { harness.tearDown() } let destination = try makeDestination() defer { destination.tearDown() } let target = try BoardStore(rootURL: destination.root) harness.store.transient.isTrashVisible = true harness.store.select([clipboardCard3], in: .trash) harness.clipboard.copy(from: harness.store) target.select([destinationLane], in: .board) await harness.clipboard.paste(into: target)?.value #expect(try pastedTitles(destinationLane, in: destination) == ["Resident", "Trashed"]) // The original stays in the source trash — a copy is a copy (04 ▸ The trash: "⌘C copies a // trash card; a live copy lands wherever pasted, like copying out of Finder's Trash"). #expect(try pasted(harness.fixture).trash.map(\.id) == [clipboardCard3]) // And nothing had to be stripped on arrival: a trashed card carries no key at all. let landed = try #require(try lane(destinationLane, in: destination)?.cards.last) #expect(landed.deleted.isMissing) } /// **Cut in the trash, paste into a lane, is the keyboard-native restore** (04-interactions.md /// ▸ The trash, resettled 2026-07-28) — "an ordinary folder move", which is exactly what the /// armed cut already does. @Test("Cut in the trash and paste into a lane is the keyboard restore — the folder moves") func cutFromTheTrashIsTheKeyboardRestore() async throws { let harness = try makeClipboardHarness() defer { harness.tearDown() } harness.store.transient.isTrashVisible = true harness.store.select([clipboardCard3], in: .trash) harness.clipboard.cut(from: harness.store) harness.store.select([clipboardLane2], in: .board) await harness.clipboard.paste(into: harness.store)?.value let model = try pasted(harness.fixture) #expect(model.trash.isEmpty, "the folder left the trash") let lane = try #require(model.lanes.first { $0.id == clipboardLane2 }) #expect(lane.cards.map(\.id).contains(clipboardCard3), "identity travels — it is a move") #expect(harness.store.transient.pendingCut.isEmpty, "the cut was consumed") } } // MARK: - The destination's search, and the stale pasteboard /// Two rules that meet at the same guard. /// /// **A paste is a user-initiated creation, so it clears the destination's query** /// (04-interactions.md § Search, stated by mechanism: "⌘N, Return-creation, the header button, /// empty-space double-click, paste, and Finder file drops alike") — cards and lanes alike, since the /// clipboard holds one or the other and both mint items on arrival. /// /// **The pasteboard is re-read lazily, and a stale paste no-ops** (04 ▸ Clipboard, settled): /// "changeCount is checked on activation, on menu validation, and before paste — no timers … the /// paste itself re-validates and no-ops — nothing stale ever lands, which is the guarantee that /// matters". Both checks are pinned here, and the clear rides behind the second of them: a paste that /// lands nothing clears nothing. @MainActor @Suite("Paste ▸ the destination's search and the stale pasteboard") struct PasteSearchAndStalenessTests { @Test("A card paste clears the destination board's search") func aCardPasteClearsTheSearch() async throws { let harness = try makeClipboardHarness() defer { harness.tearDown() } let destination = try makeDestination() defer { destination.tearDown() } let target = try BoardStore(rootURL: destination.root) harness.store.select([clipboardCard1], in: .board) harness.clipboard.copy(from: harness.store) target.select([destinationLane], in: .board) // "First" would be hidden by this query — exactly the card that must not arrive invisible. target.searchQuery = "resident" await harness.clipboard.paste(into: target)?.value #expect(target.searchQuery.isEmpty) #expect(try pastedTitles(destinationLane, in: destination) == ["Resident", "First"]) } @Test("A lane paste clears it too — the rule is creation, not the payload's kind") func aLanePasteClearsTheSearch() async throws { let harness = try makeClipboardHarness() defer { harness.tearDown() } let destination = try makeDestination() defer { destination.tearDown() } let target = try BoardStore(rootURL: destination.root) harness.store.select([clipboardLane2], in: .board) harness.clipboard.copy(from: harness.store) target.searchQuery = "resident" await harness.clipboard.paste(into: target)?.value #expect(target.searchQuery.isEmpty) #expect(try pasted(destination).lanes.count == 2) } @Test("Another app taking the pasteboard before ⌘V: the paste is refused outright") func aTakeoverBeforeThePasteRefuses() async throws { let harness = try makeClipboardHarness() defer { harness.tearDown() } let destination = try makeDestination() defer { destination.tearDown() } let target = try BoardStore(rootURL: destination.root) harness.store.select([clipboardCard1], in: .board) harness.clipboard.copy(from: harness.store) harness.pasteboard.takeOver() target.select([destinationLane], in: .board) target.searchQuery = "resident" // `refresh()` at the front of the paste sees the moved changeCount, so there is no payload // and no task at all — the same condition the menu item's enablement reads. #expect(harness.clipboard.paste(into: target) == nil) #expect(harness.clipboard.canPaste(into: target) == false) #expect(try pastedTitles(destinationLane, in: destination) == ["Resident"]) #expect(target.searchQuery == "resident") } @Test("Another app taking it while the staging chain settles: the paste re-validates and lands nothing") func aTakeoverMidPasteLandsNothing() async throws { let harness = try makeClipboardHarness() defer { harness.tearDown() } let destination = try makeDestination() defer { destination.tearDown() } let target = try BoardStore(rootURL: destination.root) harness.store.select([clipboardCard1], in: .board) harness.clipboard.copy(from: harness.store) target.select([destinationLane], in: .board) target.searchQuery = "resident" // The gesture passed validation; the takeover lands while the task is still waiting on the // staging chain, which is the window 04 calls the brief lie. Synchronous, so the task cannot // have run yet: it can only resume where this test suspends. let paste = harness.clipboard.paste(into: target) harness.pasteboard.takeOver() await paste?.value #expect(try pastedTitles(destinationLane, in: destination) == ["Resident"]) // Nothing landed, so nothing was created — and the query the user was running stands. #expect(target.searchQuery == "resident") } } // MARK: - The deferred cut @MainActor @Suite("Paste ▸ the deferred cut") struct PasteCutTests { @Test("The first armed paste moves the originals and clears the cut") func armedPasteMoves() async throws { let harness = try makeClipboardHarness() defer { harness.tearDown() } let destination = try makeDestination() defer { destination.tearDown() } let target = try BoardStore(rootURL: destination.root) harness.store.select([clipboardCard1], in: .board) harness.clipboard.cut(from: harness.store) target.select([destinationLane], in: .board) await harness.clipboard.paste(into: target)?.value // Identity travelled. #expect(try pastedIDs(destinationLane, in: destination) == [Ident.indexless, Ident.card1]) // The attachments came with the folder. #expect(try destination.data("\(Ident.lane4)/\(Ident.card1)/attachments/photo.png") == Data("png bytes".utf8)) // The original left the source board. #expect(try pastedTitles(clipboardLane1, in: harness.fixture) == ["Second"]) #expect(harness.store.transient.pendingCut.isEmpty) } @Test("A second paste after an armed cut materializes a copy from staging") func secondPasteAfterACutCopies() async throws { let harness = try makeClipboardHarness() defer { harness.tearDown() } let destination = try makeDestination() defer { destination.tearDown() } let target = try BoardStore(rootURL: destination.root) harness.store.select([clipboardCard1], in: .board) harness.clipboard.cut(from: harness.store) target.select([destinationLane], in: .board) await harness.clipboard.paste(into: target)?.value target.handleWatcherEvent(.treeChanged(.appMediated)) await target.awaitQuiescence() target.select([destinationLane], in: .board) await harness.clipboard.paste(into: target)?.value let ids = try pastedIDs(destinationLane, in: destination) #expect(ids.count == 3) #expect(ids.contains(Ident.card1)) // The second arrival is a fresh identity, not the moved one seen twice. #expect(Set(ids).count == 3) } @Test("A voided cut downgrades to a copy: the originals stay") func voidedCutCopies() async throws { let harness = try makeClipboardHarness() defer { harness.tearDown() } let destination = try makeDestination() defer { destination.tearDown() } let target = try BoardStore(rootURL: destination.root) harness.store.select([clipboardCard1], in: .board) harness.clipboard.cut(from: harness.store) // The source board closes — its store goes, and with it the cut's arming. harness.store.transient.pendingCut = .empty target.select([destinationLane], in: .board) await harness.clipboard.paste(into: target)?.value // A copy: a fresh identity at the destination, and the original still at home. let arrived = try #require(try pastedIDs(destinationLane, in: destination).last) #expect(arrived != Ident.card1) #expect(try pastedTitles(clipboardLane1, in: harness.fixture) == ["First", "Second"]) } @Test("Per-item voiding: the paste moves only the survivors") func survivorsOnly() async throws { let harness = try makeClipboardHarness() defer { harness.tearDown() } let destination = try makeDestination() defer { destination.tearDown() } let target = try BoardStore(rootURL: destination.root) harness.store.select([clipboardCard1, clipboardCard2], in: .board) harness.clipboard.cut(from: harness.store) // One of the two is deleted before the paste: the reload ejects it from the pending cut. harness.store.delete([clipboardCard1]) harness.store.handleWatcherEvent(.treeChanged(.appMediated)) await harness.store.awaitQuiescence() #expect(harness.store.transient.pendingCut.ids == [clipboardCard2]) target.select([destinationLane], in: .board) await harness.clipboard.paste(into: target)?.value #expect(try pastedIDs(destinationLane, in: destination) == [Ident.indexless, Ident.card2]) // The deleted one stayed behind, in the source board's trash. #expect(try pasted(harness.fixture).trash.map(\.id).contains(clipboardCard1)) } @Test("A cut emptied down to nothing is simply void — a paste copies instead") func emptiedCutIsVoid() async throws { let harness = try makeClipboardHarness() defer { harness.tearDown() } let destination = try makeDestination() defer { destination.tearDown() } let target = try BoardStore(rootURL: destination.root) harness.store.select([clipboardCard1], in: .board) harness.clipboard.cut(from: harness.store) harness.store.delete([clipboardCard1]) harness.store.handleWatcherEvent(.treeChanged(.appMediated)) await harness.store.awaitQuiescence() #expect(harness.store.transient.pendingCut.isEmpty) target.select([destinationLane], in: .board) await harness.clipboard.paste(into: target)?.value // The staged snapshot is still there, so the paste is a copy — content intact. #expect(try pastedTitles(destinationLane, in: destination) == ["Resident", "First"]) } } // MARK: - The staging-less fallback @MainActor @Suite("Paste ▸ the staging-less fallback") struct PasteFallbackTests { @Test("A missing snapshot falls back to the embedded index.md, byte-faithfully") func fallbackWritesTheSourceBytes() async throws { let harness = try makeClipboardHarness() defer { harness.tearDown() } let destination = try makeDestination() defer { destination.tearDown() } let target = try BoardStore(rootURL: destination.root) harness.store.select([clipboardCard1], in: .board) harness.clipboard.copy(from: harness.store) await harness.clipboard.stagingSettled() // The snapshot goes — a swept tree, a full disk, an unreadable container. let copyID = try #require(harness.clipboard.payload?.copyID) try FileManager.default.removeItem( at: harness.staging.appendingPathComponent(copyID, isDirectory: true) ) target.select([destinationLane], in: .board) await harness.clipboard.paste(into: target)?.value let arrived = try #require(try pastedIDs(destinationLane, in: destination).last) let document = try FrontmatterDocument.parse(destination.indexText("\(Ident.lane4)/\(arrived)")) #expect(document.title.value == "First") // Content intact: unknown keys, the comment's key, and the body all survived. #expect(document.value(for: "project") != nil) #expect(document.value(for: "labels") != nil) #expect(document.body.contains("First body — with *markdown*")) // `created` kept, fresh `order`. #expect(document.created.value == ISO8601DateFormatter().date(from: "2026-01-01T09:00:00Z")) #expect(document.order.value != 1024) // Attachments absent — which is exactly what the banner is about to say. #expect(!destination.exists("\(Ident.lane4)/\(arrived)/attachments")) } @Test("A degraded paste banners, naming exactly what was lost") func fallbackBanners() async throws { let harness = try makeClipboardHarness() defer { harness.tearDown() } let destination = try makeDestination() defer { destination.tearDown() } let target = try BoardStore(rootURL: destination.root) harness.store.select([clipboardCard1], in: .board) harness.clipboard.copy(from: harness.store) await harness.clipboard.stagingSettled() let copyID = try #require(harness.clipboard.payload?.copyID) try FileManager.default.removeItem( at: harness.staging.appendingPathComponent(copyID, isDirectory: true) ) target.select([destinationLane], in: .board) await harness.clipboard.paste(into: target)?.value #expect(target.banners.losses.map(\.message) == ["Pasted 'First' without its 2 attachments"]) } @Test("A fallback that lost nothing says nothing") func fallbackWithoutAttachmentsIsSilent() async throws { let harness = try makeClipboardHarness() defer { harness.tearDown() } let destination = try makeDestination() defer { destination.tearDown() } let target = try BoardStore(rootURL: destination.root) // `card2` has no attachments, so a fallback loses nothing at all. harness.store.select([clipboardCard2], in: .board) harness.clipboard.copy(from: harness.store) await harness.clipboard.stagingSettled() let copyID = try #require(harness.clipboard.payload?.copyID) try FileManager.default.removeItem( at: harness.staging.appendingPathComponent(copyID, isDirectory: true) ) target.select([destinationLane], in: .board) await harness.clipboard.paste(into: target)?.value #expect(try pastedTitles(destinationLane, in: destination) == ["Resident", "Second"]) #expect(target.banners.losses.isEmpty) } @Test("A lane's fallback materializes its embedded cards") func laneFallbackCarriesItsCards() async throws { let harness = try makeClipboardHarness() defer { harness.tearDown() } let destination = try makeDestination() defer { destination.tearDown() } let target = try BoardStore(rootURL: destination.root) harness.store.select([clipboardLane1], in: .board) harness.clipboard.copy(from: harness.store) await harness.clipboard.stagingSettled() let copyID = try #require(harness.clipboard.payload?.copyID) try FileManager.default.removeItem( at: harness.staging.appendingPathComponent(copyID, isDirectory: true) ) await harness.clipboard.paste(into: target)?.value let arrived = try #require(try pasted(destination).lanes.last) #expect(arrived.title.value == "Todo") // Both of the lane's cards. #expect(arrived.cards.count == 2) #expect(Set(arrived.cards.compactMap(\.title.value)) == ["First", "Second"]) #expect(target.banners.losses.map(\.message) == ["Pasted 'Todo' without its 2 attachments"]) } @Test("A trash-sourced fallback materializes an ordinary card — there is no key to strip") func trashedFallbackIsOrdinary() async throws { let harness = try makeClipboardHarness() defer { harness.tearDown() } let destination = try makeDestination() defer { destination.tearDown() } let target = try BoardStore(rootURL: destination.root) harness.store.transient.isTrashVisible = true harness.store.select([clipboardCard3], in: .trash) harness.clipboard.copy(from: harness.store) await harness.clipboard.stagingSettled() let copyID = try #require(harness.clipboard.payload?.copyID) try FileManager.default.removeItem( at: harness.staging.appendingPathComponent(copyID, isDirectory: true) ) target.select([destinationLane], in: .board) await harness.clipboard.paste(into: target)?.value #expect(try pastedTitles(destinationLane, in: destination) == ["Resident", "Trashed"]) } } // MARK: - BoardWriter.materializeItem @Suite("BoardWriter ▸ materializeItem") struct MaterializeItemTests { @Test("The supplied bytes land verbatim but for the rewritten order and stamps") func writesTheSuppliedBytes() throws { let fixture = try WriterFixture() defer { fixture.tearDown() } try fixture.item("", Item.board) try fixture.item(Ident.lane1, Item.rich(order: "1024", title: "Todo")) let id = try BoardWriter.materializeItem( inParent: fixture.url(Ident.lane1), indexText: Item.rich(order: "9999", title: "Pasted"), order: 512 ) let document = try FrontmatterDocument.parse(fixture.indexText("\(Ident.lane1)/\(id.rawValue)")) #expect(document.title.value == "Pasted") #expect(document.order.value == 512) #expect(document.value(for: "project") != nil) #expect(document.value(for: "labels") != nil) // The app-write stamps: `modified` set, `modified-by` cleared. #expect(document.modified.value != ISO8601DateFormatter().date(from: "2026-02-02T09:00:00Z")) #expect(document.modifiedBy.isMissing) // `created` untouched — a paste is a fork. #expect(document.created.value == ISO8601DateFormatter().date(from: "2026-01-01T09:00:00Z")) } @Test("Children are materialized under fresh identities and never rewritten") func childrenAreVerbatim() throws { let fixture = try WriterFixture() defer { fixture.tearDown() } try fixture.item("", Item.board) let id = try BoardWriter.materializeItem( inParent: fixture.root, indexText: Item.rich(order: "1024", title: "Lane"), children: [Item.rich(order: "1024", title: "One"), Item.uneditable], order: 1024 ) let lane = try #require(try BoardLoader.load(boardRoot: fixture.root).model.lanes.first) #expect(lane.id == id) #expect(lane.cards.count == 2) // An uneditable child arrives exactly as it was — the leniency `copyItem` extends below its // root, applied here. let names = try FileManager.default.contentsOfDirectory(atPath: fixture.url(id.rawValue).path) .filter { $0 != "index.md" } let odd = try #require(names.first { name in (try? fixture.indexText("\(id.rawValue)/\(name)")) == Item.uneditable }) #expect(try fixture.indexText("\(id.rawValue)/\(odd)") == Item.uneditable) } @Test("An unparseable root refuses and leaves nothing behind") func unparseableRootLeavesNoResidue() throws { let fixture = try WriterFixture() defer { fixture.tearDown() } try fixture.item("", Item.board) let error = writeFailure { _ = try BoardWriter.materializeItem( inParent: fixture.root, indexText: "no frontmatter here at all\n", order: 1024 ) } #expect(error != nil) #expect(try fixture.entryNames("") == ["index.md"]) } }