Files
lanework/KanbanTests/BoardInfoTabTests.swift
T
rzen 5779da2b6c The Info tab fills in — the board's vital statistics in two honest registers
Model facts off the live snapshot (Lanes, Cards with the trash's freight as a
quiet tail, Attachments — the welcome-count live-only rule) and disk facts off
one background whole-folder walk (File, Files, Size, Created, Modified — .git
and .trash included, so the rows agree with Finder's Get Info), with a Reveal
in Finder link as the door to the folder the rows describe.

Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
2026-08-07 13:58:15 -04:00

153 lines
6.3 KiB
Swift

import Foundation
import Testing
@testable import Kanban
/// **The Info tab's two seams** (03-board-ui.md § Board popover ▸ Info tab, settled 2026-08-07),
/// pinned the way the popover's other pure seams are: `BoardInfoMetrics` against a loaded fixture
/// board, `BoardDiskFootprint` against real bytes on disk. The rows' rendering — labels, the em-dash
/// placeholder, the Reveal button — is SwiftUI and deliberately untested.
@Suite("Board popover ▸ Info tab metrics")
struct BoardInfoTabTests {
/// Two live lanes holding three cards (one carrying two attachments), a loose trashed card, and
/// a trashed lane holding two more — the shape that exercises every counting rule at once.
private func makeFixture() throws -> WriterFixture {
let fixture = try WriterFixture()
try fixture.item("", Item.board)
try fixture.item(Ident.lane1, Item.rich(order: "1024", title: "Doing"))
try fixture.item("\(Ident.lane1)/\(Ident.card1)", Item.rich(order: "1024", title: "One"))
try fixture.item("\(Ident.lane1)/\(Ident.card2)", Item.rich(order: "2048", title: "Two"))
try fixture.file("\(Ident.lane1)/\(Ident.card1)/attachments/sketch.png", Data([0xFF, 0xD8]))
try fixture.file("\(Ident.lane1)/\(Ident.card1)/attachments/notes.txt", Data("notes".utf8))
try fixture.item(Ident.lane2, Item.rich(order: "2048", title: "Done"))
try fixture.item("\(Ident.lane2)/\(Ident.card3)", Item.rich(order: "1024", title: "Three"))
try fixture.item(".trash/\(Ident.card4)", Item.rich(order: "1024", title: "Tossed"))
try fixture.item(
".trash/\(Ident.lane3)",
"---\nschema: 1\ntitle: Shipped\norder: 3072\nkind: lane\n---\n"
)
try fixture.item(
".trash/\(Ident.lane3)/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
Item.rich(order: "1024", title: "Held one")
)
try fixture.item(
".trash/\(Ident.lane3)/bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
Item.rich(order: "2048", title: "Held two")
)
return fixture
}
@Test("Lanes and cards count the live board only — the welcome-count rule")
func lanesAndCardsAreLiveOnly() throws {
let fixture = try makeFixture()
defer { fixture.tearDown() }
let metrics = BoardInfoMetrics(snapshot: try BoardLoader.load(boardRoot: fixture.root).model)
#expect(metrics.lanes == 2)
#expect(metrics.cards == 3)
}
@Test("The trash tail counts freight — loose cards plus every trashed lane's held cards")
func trashedCardsCountFreight() throws {
let fixture = try makeFixture()
defer { fixture.tearDown() }
let metrics = BoardInfoMetrics(snapshot: try BoardLoader.load(boardRoot: fixture.root).model)
// One loose trashed card, two held by the trashed lane — the purge-confirm counting rule.
#expect(metrics.trashedCards == 3)
}
@Test("Attachments count the live cards' files")
func attachmentsCountLiveCards() throws {
let fixture = try makeFixture()
defer { fixture.tearDown() }
let metrics = BoardInfoMetrics(snapshot: try BoardLoader.load(boardRoot: fixture.root).model)
#expect(metrics.attachments == 2)
}
@Test("An empty board is all zeros, not a crash or a lie")
func emptyBoardIsZeros() throws {
let fixture = try WriterFixture()
defer { fixture.tearDown() }
try fixture.item("", Item.board)
let metrics = BoardInfoMetrics(snapshot: try BoardLoader.load(boardRoot: fixture.root).model)
#expect(metrics.lanes == 0)
#expect(metrics.cards == 0)
#expect(metrics.trashedCards == 0)
#expect(metrics.attachments == 0)
}
}
/// **The whole-folder walk** — the disk rows exist to agree with Finder's Get Info, so the walk
/// must count hidden entries (`.git`, `.trash/`) exactly as it counts the visible board.
@Suite("Board popover ▸ Info tab disk footprint")
struct BoardDiskFootprintTests {
@Test("Files counts every regular file in the tree, hidden entries included")
func filesCountsTheWholeFolder() throws {
let fixture = try WriterFixture()
defer { fixture.tearDown() }
try fixture.item("", Item.board)
try fixture.item(Ident.lane1, Item.rich(order: "1024", title: "Doing"))
try fixture.item("\(Ident.lane1)/\(Ident.card1)", Item.rich(order: "1024", title: "One"))
try fixture.file("\(Ident.lane1)/\(Ident.card1)/attachments/sketch.png", Data([0xFF, 0xD8]))
try fixture.file(".git/HEAD", Data("ref: refs/heads/main\n".utf8))
try fixture.item(".trash/\(Ident.card2)", Item.rich(order: "1024", title: "Tossed"))
let footprint = BoardDiskFootprint.measure(at: fixture.root)
// Three index.md files live (board, lane, card), one attachment, one .git file, one trashed
// card's index.md — six regular files; directories are containers, not files.
#expect(footprint.files == 6)
}
@Test("Size on disk is at least the logical bytes — allocation rounds up, never down to zero")
func bytesCoverTheLogicalSize() throws {
let fixture = try WriterFixture()
defer { fixture.tearDown() }
try fixture.item("", Item.board)
let payload = Data(repeating: 0x2A, count: 4096)
try fixture.file("stray.bin", payload)
let footprint = BoardDiskFootprint.measure(at: fixture.root)
let logical = Int64(payload.count + Data(Item.board.utf8).count)
#expect(footprint.bytes >= logical)
}
@Test("Created and Modified come back from a real folder, and never out of order")
func datesAreReadAndOrdered() throws {
let fixture = try WriterFixture()
defer { fixture.tearDown() }
try fixture.item("", Item.board)
let footprint = BoardDiskFootprint.measure(at: fixture.root)
let created = try #require(footprint.created)
let modified = try #require(footprint.modified)
#expect(modified >= created)
}
@Test("A vanished folder answers empty rather than trapping — the walk has no user to ask")
func vanishedFolderAnswersEmpty() {
let gone = URL(fileURLWithPath: "/tmp/lanework-info-tab-never-existed-\(UUID().uuidString)")
let footprint = BoardDiskFootprint.measure(at: gone)
#expect(footprint.files == 0)
#expect(footprint.bytes == 0)
}
}