-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMigrationRunnerTests.swift
More file actions
156 lines (130 loc) · 4.99 KB
/
MigrationRunnerTests.swift
File metadata and controls
156 lines (130 loc) · 4.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
//
// MigrationRunnerTests.swift
// PureSQL
//
// Created by Wes Wickwire on 6/24/25.
//
import Testing
@testable import PureSQL
@Suite
struct MigrationRunnerTests: ~Copyable {
let connection = try! SQLiteConnection(path: ":memory:")
@Test func migrationTableIsCreated() async throws {
try MigrationRunner.execute(migrations: [], connection: connection)
#expect(try runMigrations().isEmpty)
let tableNames = try tableNames()
#expect(tableNames.contains(MigrationRunner.migrationTableName))
}
@Test func userTableIsCreated() async throws {
try MigrationRunner.execute(migrations: ["CREATE TABLE foo (bar INTEGER)"], connection: connection)
#expect(try runMigrations() == [0])
let tableNames = try tableNames()
#expect(tableNames.contains("foo"))
}
@Test func migrationFailsWithErrorIfMigrationHasAnError() async throws {
#expect(throws: SQLError.self) {
try MigrationRunner.execute(migrations: [
"CREATE TABLE foo (bar INTEGER)",
"CREATE TABLE foo (bar INTEGER)"
], connection: connection)
}
}
@Test func migrationsAreRunInOrder() async throws {
try MigrationRunner.execute(
migrations: [
"CREATE TABLE migrationOrder (value TEXT)",
"INSERT INTO migrationOrder (value) VALUES ('first')",
"UPDATE migrationOrder SET value = value || ', second'",
"UPDATE migrationOrder SET value = value || ', third'",
],
connection: connection
)
let value: String? = try query("SELECT * FROM migrationOrder") { try $0.fetchOne() }
#expect(value == "first, second, third")
}
@Test func migrationsAreRunIncrementally() async throws {
var migrations = ["CREATE TABLE foo (value TEXT)"]
try MigrationRunner.execute(
migrations: migrations,
connection: connection
)
migrations.append("CREATE TABLE bar (value TEXT)")
try MigrationRunner.execute(
migrations: migrations,
connection: connection
)
// Run again with no new migrations
try MigrationRunner.execute(
migrations: migrations,
connection: connection
)
let _: String? = try query("SELECT * FROM foo") { try $0.fetchOne() }
let _: String? = try query("SELECT * FROM bar") { try $0.fetchOne() }
}
@Test func failedMigrationRollsbackChanges() async throws {
#expect(throws: SQLError.self) {
try MigrationRunner.execute(
migrations: [
"""
CREATE TABLE foo (bar INTEGER);
CREATE TABLE foo (bar INTEGER); -- Fails
"""
],
connection: connection
)
}
let tables = try tableNames()
#expect(!tables.contains("foo"))
}
@Test func migrationsBeforeAFailureAreCommited() async throws {
#expect(throws: SQLError.self) {
try MigrationRunner.execute(
migrations: [
"CREATE TABLE foo (bar INTEGER);",
"CREATE TABLE foo (bar INTEGER); -- Fails"
],
connection: connection
)
}
let tables = try tableNames()
#expect(tables.contains("foo"))
}
@Test func canRunMigrationsUpToCertainNumber() async throws {
let migrations = [
"CREATE TABLE foo (bar INTEGER);",
"CREATE TABLE bar (baz TEXT);",
]
try MigrationRunner.execute(
migrations: migrations,
connection: connection,
upTo: 0 // Dont run the last migration
)
let onlyFirstMigration = try tableNames()
#expect(onlyFirstMigration.contains("foo"))
#expect(!onlyFirstMigration.contains("bar"))
try MigrationRunner.execute(
migrations: migrations,
connection: connection,
upTo: nil // Now run all migrations
)
let allMigrations = try tableNames()
#expect(allMigrations.contains("foo"))
#expect(allMigrations.contains("bar"))
}
private func runMigrations() throws -> [Int] {
return try query("SELECT * FROM \(MigrationRunner.migrationTableName) ORDER BY number ASC") { try $0.fetchAll() }
}
private func tableNames() throws -> Set<String> {
return try query("SELECT name FROM sqlite_master") { try Set($0.fetchAll()) }
}
private func query<Output>(
_ stmt: String,
execute: (consuming Statement) throws -> Output
) throws -> Output {
let tx = try Transaction(connection: connection, kind: .read)
let statement = try Statement(in: tx) { stmt }
let result = try execute(statement)
try tx.commit()
return result
}
}