carlos / todomvc-bakeoff Public

Clone
git clone https://amadan.net/carlos/todomvc-bakeoff

Plain git — no account needed to clone.

Download

Download this file

1import Foundation
2import Testing
3@testable import TodoApp
4
5/// A store on a fresh database file, removed afterwards.
6func withStore<T>(_ body: (Store, String) async throws -> T) async throws -> T {
7 let dir = FileManager.default.temporaryDirectory.appendingPathComponent("todo-swift-test-\(UUID().uuidString)")
8 try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
9 defer { try? FileManager.default.removeItem(at: dir) }
10 let path = dir.appendingPathComponent("app.db").path
11 let store = try await Store.open(path: path)
12 do {
13 let result = try await body(store, path)
14 try await store.close()
15 return result
16 } catch {
17 try? await store.close()
18 throw error
19 }
20}
21
22@Suite struct StoreTests {
23 @Test func usesWALAndRecordsTheSchemaVersion() async throws {
24 try await withStore { store, path in
25 #expect(try await store.db.query("PRAGMA journal_mode").first?.column("journal_mode")?.string == "wal")
26 let version = try await store.db.query("PRAGMA user_version").first?.column("user_version")?.integer
27 #expect(version == Store.migrations.count)
28 // Opening again runs nothing twice and keeps the data.
29 let list = try await store.createList(token: Visitors.newToken())
30 try await store.add(list: list, title: "kept")
31 let again = try await Store.open(path: path)
32 #expect(try await again.todos(list: list).map(\.title) == ["kept"])
33 try await again.close()
34 }
35 }
36
37 @Test func toggleAllCompletesEverythingThenReopensEverything() async throws {
38 try await withStore { store, _ in
39 let list = try await store.createList(token: Visitors.newToken())
40 for title in ["a", "b", "c"] { try await store.add(list: list, title: title) }
41 let first = try await store.todos(list: list)[0].id
42 #expect(try await store.toggle(list: list, id: first))
43
44 try await store.toggleAll(list: list)
45 #expect(try await store.todos(list: list).allSatisfy(\.completed))
46 try await store.toggleAll(list: list)
47 #expect(try await store.todos(list: list).allSatisfy { !$0.completed })
48 }
49 }
50
51 @Test func listsAreSeparate() async throws {
52 try await withStore { store, _ in
53 let mine = try await store.createList(token: Visitors.newToken())
54 let theirs = try await store.createList(token: Visitors.newToken())
55 try await store.add(list: mine, title: "mine")
56 let id = try await store.todos(list: mine)[0].id
57
58 #expect(try await store.toggle(list: theirs, id: id) == false)
59 #expect(try await store.rename(list: theirs, id: id, title: "x") == false)
60 #expect(try await store.destroy(list: theirs, id: id) == false)
61 try await store.toggleAll(list: theirs)
62 try await store.clearCompleted(list: theirs)
63 #expect(try await store.todos(list: mine) == [Todo(id: id, title: "mine", completed: false)])
64 #expect(try await store.todos(list: theirs).isEmpty)
65 }
66 }
67
68 @Test func clearCompletedKeepsActiveItems() async throws {
69 try await withStore { store, _ in
70 let list = try await store.createList(token: Visitors.newToken())
71 for title in ["a", "b", "c"] { try await store.add(list: list, title: title) }
72 let b = try await store.todos(list: list)[1].id
73 _ = try await store.toggle(list: list, id: b)
74 try await store.clearCompleted(list: list)
75 #expect(try await store.todos(list: list).map(\.title) == ["a", "c"])
76 }
77 }
78}
79
80@Suite struct OptionsTests {
81 @Test func socketMode() throws {
82 let o = try Options(arguments: ["--socket", "/run/a.sock", "--db", "/d/app.db"], environment: ["CARLOS_HOST": "todo-swift.bfj.oncarlos.com"])
83 #expect(o.address == .socket("/run/a.sock"))
84 #expect(o.origin == "https://todo-swift.bfj.oncarlos.com")
85 #expect(o.servedOverHTTPS)
86 }
87
88 @Test func listenMode() throws {
89 let o = try Options(arguments: ["--listen", "127.0.0.1:8080", "--db", "x.db"], environment: [:])
90 #expect(o.address == .listen(host: "127.0.0.1", port: 8080))
91 #expect(o.origin == "http://127.0.0.1:8080")
92 #expect(!o.servedOverHTTPS)
93 }
94
95 @Test(arguments: [
96 ["--listen", "0.0.0.0:8080", "--db", "x.db"],
97 ["--listen", ":8080", "--db", "x.db"],
98 ["--listen", "127.0.0.1:8080"],
99 ["--socket", "a.sock", "--listen", "127.0.0.1:1", "--db", "x.db"],
100 ["--db"],
101 ])
102 func refusesBadArguments(_ arguments: [String]) {
103 #expect(throws: Options.UsageError.self) { try Options(arguments: arguments, environment: [:]) }
104 }
105}
106
107@Suite struct AssetTests {
108 static let appDir = URL(fileURLWithPath: #filePath).deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent()
109
110 @Test(arguments: [
111 ("../../shared/base.css", Assets.baseCSS),
112 ("../../shared/index.css", Assets.indexCSS),
113 ("static/app.css", Assets.appCSS),
114 ("static/app.js", Assets.appJS),
115 ])
116 func embeddedFilesMatchTheirSources(_ path: String, _ embedded: String) throws {
117 let source = try Data(contentsOf: Self.appDir.appendingPathComponent(path))
118 #expect(Data(embedded.utf8) == source, "run tools/embed.sh")
119 }
120}
121