import Foundation @main struct MatchedPPTSelectionTests { static func match(_ id: String, category: String) -> MatchGroup { MatchGroup(id: id, invoices: [material(id + "-invoice", type: "invoice")], payments: [material(id + "-payment", type: "payment"), material(id + "-payment-2", type: "payment")], category: category, matchType: "manual", score: 100, reasons: []) } static func material(_ id: String, type: String) -> Material { Material(id: id, name: id + ".png", path: "/tmp/" + id + ".png", type: type, size: 1, matched: true, previewPath: "", ocr: OCRData(), displayAmount: 10, sortDate: "2026-09-16", issueDate: "2026-09-16") } static func main() throws { let meal = match("meal", category: "餐饮") let travel = match("travel", category: "交通") let hotel = match("hotel", category: "住宿") var workspace = Workspace() workspace.matches = [meal, travel, hotel] workspace.invoices = workspace.matches.flatMap(\.invoices) workspace.payments = workspace.matches.flatMap(\.payments) var selection = MatchedPPTSelection() precondition(!selection.containsAll([])) precondition(!selection.containsAll(workspace.matches)) precondition((try? selection.exportWorkspace(from: workspace)) == nil) selection.setVisible([meal], selected: true) selection.setVisible([travel], selected: true) precondition(selection.ids == ["meal", "travel"]) precondition(selection.containsAll([meal])) precondition(!selection.containsAll(workspace.matches)) let exported = try selection.exportWorkspace(from: workspace) precondition(exported.matches.map(\.id) == ["meal", "travel"]) precondition(exported.invoices.map(\.id) == ["meal-invoice", "travel-invoice"]) precondition(exported.payments.count == 4) precondition(exported.matches[0].payments.count == 2) let payload = try JSONDecoder().decode(Workspace.self, from: JSONEncoder().encode(exported)) precondition(payload.matches.map(\.id) == ["meal", "travel"]) precondition(!payload.invoices.contains { $0.id.hasPrefix("hotel") }) precondition(workspace.matches.count == 3) precondition(workspace.invoices.count == 3) selection.setVisible([meal], selected: false) precondition(selection.ids == ["travel"]) let single = try selection.exportWorkspace(from: workspace) precondition(single.matches.map(\.id) == ["travel"]) selection.setVisible(workspace.matches, selected: true) precondition(selection.containsAll(workspace.matches)) let all = try selection.exportWorkspace(from: workspace) precondition(all.matches.map(\.id) == workspace.matches.map(\.id)) selection.setVisible(workspace.matches, selected: false) precondition(selection.ids.isEmpty) selection.ids = ["removed", "meal"] precondition((try? selection.exportWorkspace(from: workspace)) == nil) selection.retain(workspace.matches.map(\.id)) precondition(selection.ids == ["meal"]) selection.retain([]) precondition(selection.ids.isEmpty) print("Matched PPT selection tests passed") } }