Files
lanework/KanbanTests/WindowRefsTests.swift
T
rzen fccdf56cf4 Stand up the window architecture — welcome, board, card
Four scenes (welcome, restore bootstrap, board group, card group) with
system restoration disabled in favor of the registry's open-now flags:
set when a window actually opens, cleared only on user close, so quit —
and crash — leave exactly the restoration set behind. AppModel joins
windows to sessions (shared store, registry record, card refs, held
security scope); CloseFlushCoordinator pins 02's strict close order as
a seam-injected machine (card sessions end, windows drain, store
flushes, record stamps, teardown) with named slots where m6/m7 flushes
land. HostedWindowController proxies — never replaces — SwiftUI's
window delegate to intercept windowShouldClose for the flush, report
frames, and place saved frames onto live screens. Card windows are
(board path, case-folded card id) values: reopen focuses, and a
snapshot-pure fate function dismisses on delete, tombstone, tombstoned
lane, or cross-board move.

Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
2026-07-27 07:47:11 -04:00

83 lines
3.7 KiB
Swift

import Foundation
import Testing
@testable import Kanban
/// A window ref is two things at once — the value `WindowGroup(for:)` keys its windows on, and the
/// argument the host is built from — so it has exactly two promises: it survives the round trip
/// through a scene's archive, and two refs naming one thing are *equal*. Both are pinned here,
/// because a failure of either is invisible until the app opens a second window for something that
/// already has one.
@Suite("Window refs")
struct WindowRefsTests {
// MARK: Codable
@Test("Both refs round-trip through JSON unchanged")
func refsRoundTripThroughCoding() throws {
let board = BoardWindowRef(path: "/Users/x/Boards/Work.kanban")
let card = CardWindowRef(boardPath: board.path, cardID: "55555555-5555-4555-8555-555555555555")
let encoder = JSONEncoder()
let decoder = JSONDecoder()
#expect(try decoder.decode(BoardWindowRef.self, from: encoder.encode(board)) == board)
let decodedCard = try decoder.decode(CardWindowRef.self, from: encoder.encode(card))
#expect(decodedCard == card)
#expect(decodedCard.cardID == card.cardID, "the folder's exact spelling survives, not just its value")
#expect(decodedCard.boardPath == board.path)
}
@Test("A board ref and a URL agree in both directions")
func boardRefAndURLAgree() {
let url = URL(fileURLWithPath: "/Users/x/Boards/Work.kanban", isDirectory: true)
let ref = BoardWindowRef(url: url)
#expect(ref.path == url.path)
#expect(ref.url.path == url.path)
}
// MARK: Identity
@Test("A card ref's id compares by UUID value, not by spelling")
func cardRefFoldsTheCaseOfItsCardID() {
let board = "/Users/x/Boards/Work.kanban"
let lower = CardWindowRef(boardPath: board, cardID: "55555555-5555-4555-8555-555555555555")
let upper = CardWindowRef(boardPath: board, cardID: "55555555-5555-4555-8555-555555555555".uppercased())
// The consequence that matters: `WindowGroup(for:)` keys on this equality, so a card whose
// folder is spelled in caps focuses the window the lowercase spelling already opened instead
// of opening a second one for the same card.
#expect(lower == upper)
#expect(lower.hashValue == upper.hashValue)
#expect(Set([lower, upper]).count == 1)
// And `rawValue` is untouched by any of that — it is what builds URLs.
#expect(upper.cardID == "55555555-5555-4555-8555-555555555555".uppercased())
#expect(upper.cardIdentity == lower.cardIdentity)
}
@Test("The board half of a card ref is compared verbatim")
func cardRefsOnDifferentBoardsAreDifferent() {
let card = "55555555-5555-4555-8555-555555555555"
let here = CardWindowRef(boardPath: "/Users/x/Boards/Work.kanban", cardID: card)
let there = CardWindowRef(boardPath: "/Users/x/Boards/Home.kanban", cardID: card)
// The cross-board move rule in one assertion: the card's UUID travels with it, but the key
// that named its window does not — so the window dismisses exactly like a delete.
#expect(here != there)
#expect(here.board == BoardWindowRef(path: "/Users/x/Boards/Work.kanban"))
#expect(here.boardURL.path == "/Users/x/Boards/Work.kanban")
}
@Test("A card ref built from an ItemID keeps the id's exact spelling")
func cardRefFromItemIDKeepsRawValue() {
let board = BoardWindowRef(path: "/Users/x/Boards/Work.kanban")
let id = ItemID(rawValue: "AAAAAAAA-AAAA-4AAA-8AAA-AAAAAAAAAAAA")
let ref = CardWindowRef(board: board, cardID: id)
#expect(ref.cardID == id.rawValue)
#expect(ref == CardWindowRef(boardPath: board.path, cardID: id.rawValue.lowercased()))
}
}