This commit is contained in:
csj
2026-09-17 14:13:31 +08:00
parent ced5db5473
commit 10697d3dfa
18 changed files with 2097 additions and 26 deletions
+74
View File
@@ -0,0 +1,74 @@
import Foundation
@main
struct ExpenseTemplateTests {
static func rejects(_ action: () throws -> Void) {
do {
try action()
preconditionFailure("Unexpected success")
} catch {
precondition(!error.localizedDescription.isEmpty)
}
}
static func main() throws {
let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
try FileManager.default.createDirectory(at: root, withIntermediateDirectories: true)
defer { try? FileManager.default.removeItem(at: root) }
let source = root.appendingPathComponent("原模板.xlsx")
let data = Data("Synthetic template fixture".utf8)
try data.write(to: source)
let storage = ExpenseTemplateStorage(root: root)
let initial = try storage.load()
precondition(initial == nil)
let mapping = ExpenseTemplateMapping(sheetName: "组 A", startRow: 5, endRow: 10,
columns: ["purpose": "B", "amount": "F"], cells: ["recipient": "B12"])
let validation = ExpenseTemplateValidation(templateVersion: "mapped-xlsx-v1",
fingerprint: ExpenseTemplateStorage.fingerprint(data),
mapping: mapping, mappingDigest: "test-mapping-digest")
let saved = try storage.save(name: source.lastPathComponent, source: source, validation: validation)
let reloadedStorage = ExpenseTemplateStorage(root: root)
let reloaded = try reloadedStorage.load()!
precondition(reloaded.mapping == mapping)
precondition(reloaded.name == "原模板.xlsx")
let reloadedData = try Data(contentsOf: reloadedStorage.file(for: reloaded))
precondition(reloadedData == data)
let workspace = root.appendingPathComponent("workspace.json")
try Data("current expense data".utf8).write(to: workspace)
try Data("{}".utf8).write(to: workspace, options: .atomic)
let afterWorkspaceClear = try reloadedStorage.load()
precondition(afterWorkspaceClear?.fingerprint == saved.fingerprint)
let next = try storage.save(name: "新模板.xlsx", source: source, validation: validation)
precondition(!FileManager.default.fileExists(atPath: storage.directory.appendingPathComponent(saved.fileName).path))
precondition(next.fileName != saved.fileName)
var bad = validation
bad.fingerprint = "wrong"
rejects { _ = try storage.save(name: "invalid", source: source, validation: bad) }
let afterFailedSave = try storage.load()
precondition(afterFailedSave?.fileName == next.fileName)
var traversal = next
traversal.fileName = "../原模板.xlsx"
rejects { _ = try storage.file(for: traversal) }
let result = try JSONSerialization.data(withJSONObject: [
"templateVersion": "mapped-xlsx-v1", "templateFingerprint": saved.fingerprint,
"templateMappingDigest": saved.mappingDigest
])
try ExpenseTemplateStorage.validateExport(result, template: saved)
rejects { try ExpenseTemplateStorage.validateExport(Data("{}".utf8), template: saved) }
var differentMapping = saved
differentMapping.mappingDigest = "different"
rejects { try ExpenseTemplateStorage.validateExport(result, template: differentMapping) }
let storedFile = try storage.file(for: next)
try Data("changed".utf8).write(to: storedFile)
rejects { _ = try storage.file(for: next) }
try storage.remove()
let afterRemoval = try storage.load()
precondition(afterRemoval == nil)
precondition(!FileManager.default.fileExists(atPath: storage.directory.path))
let originalData = try Data(contentsOf: source)
precondition(originalData == data)
precondition(FileManager.default.fileExists(atPath: workspace.path))
try storage.remove()
print("Template persistence, replacement, reset isolation and export fingerprint tests passed")
}
}