Skip to content

Commit b925739

Browse files
authored
Swift Testing and Swift 6 (#139)
* Bump to Swift 6 with Swift 5 language mode * Migrate tests to Swift Testing * Don't add migrations with no Fluent * Fix formatting * Fix formatting for DB confog * Update Dockerfile for Swift 6 * Update CI
1 parent e71aef7 commit b925739

File tree

5 files changed

+63
-52
lines changed

5 files changed

+63
-52
lines changed

.github/workflows/test-template.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ on:
77
cache_key:
88
value: ${{ jobs.cache-toolbox.outputs.cache_key }}
99
env:
10-
SWIFT_IMAGE: 'swift:5.10-noble'
10+
SWIFT_IMAGE: 'swift:6.0-noble'
1111

1212
jobs:
1313

@@ -57,7 +57,7 @@ jobs:
5757
fail-fast: false
5858
matrix:
5959
swift-image:
60-
- 'swift:5.10-noble'
60+
- 'swift:6.0-noble'
6161
fluentflags:
6262
- '--no-fluent'
6363
#- '--fluent.db mysql' # The MySQL image can't be configured usably via GH Actions at this time

Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# ================================
22
# Build image
33
# ================================
4-
FROM swift:5.10-noble AS build
4+
FROM swift:6.0-noble AS build
55

66
# Install OS updates
77
RUN export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true \

Package.swift

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// swift-tools-version:5.10
1+
// swift-tools-version:6.0
22
import PackageDescription
33

44
let package = Package(
@@ -39,7 +39,8 @@ let package = Package(
3939
],
4040
swiftSettings: swiftSettings
4141
)
42-
]
42+
],
43+
swiftLanguageModes: [.v5]
4344
)
4445

4546
var swiftSettings: [SwiftSetting] { [

Sources/App/configure.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@ public func configure(_ app: Application) async throws {
1616
password: Environment.get("DATABASE_PASSWORD") ?? "vapor_password",
1717
database: Environment.get("DATABASE_NAME") ?? "vapor_database",
1818
tls: .prefer(try .init(configuration: .clientDefault)))
19-
), as: .psql){{/fluent.db.is_postgres}}{{#fluent.db.is_mysql}}app.databases.use(DatabaseConfigurationFactory.mysql(
19+
), as: .psql){{/fluent.db.is_postgres}}{{#fluent.db.is_mysql}} app.databases.use(DatabaseConfigurationFactory.mysql(
2020
hostname: Environment.get("DATABASE_HOST") ?? "localhost",
2121
port: Environment.get("DATABASE_PORT").flatMap(Int.init(_:)) ?? MySQLConfiguration.ianaPortNumber,
2222
username: Environment.get("DATABASE_USERNAME") ?? "vapor_username",
2323
password: Environment.get("DATABASE_PASSWORD") ?? "vapor_password",
2424
database: Environment.get("DATABASE_NAME") ?? "vapor_database"
25-
), as: .mysql){{/fluent.db.is_mysql}}{{#fluent.db.is_mongo}}try app.databases.use(DatabaseConfigurationFactory.mongo(
25+
), as: .mysql){{/fluent.db.is_mysql}}{{#fluent.db.is_mongo}} try app.databases.use(DatabaseConfigurationFactory.mongo(
2626
connectionString: Environment.get("DATABASE_URL") ?? "mongodb://localhost:27017/vapor_database"
27-
), as: .mongo){{/fluent.db.is_mongo}}{{#fluent.db.is_sqlite}}app.databases.use(DatabaseConfigurationFactory.sqlite(.file("db.sqlite")), as: .sqlite){{/fluent.db.is_sqlite}}
27+
), as: .mongo){{/fluent.db.is_mongo}}{{#fluent.db.is_sqlite}} app.databases.use(DatabaseConfigurationFactory.sqlite(.file("db.sqlite")), as: .sqlite){{/fluent.db.is_sqlite}}
2828

2929
app.migrations.add(CreateTodo()){{/fluent}}{{#leaf}}
3030

Tests/AppTests/AppTests.swift

+54-44
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,74 @@
11
@testable import App
22
import XCTVapor
3+
import Testing
34
{{#fluent}}import Fluent
45
{{/fluent}}
56

6-
final class AppTests: XCTestCase {
7-
var app: Application!
8-
9-
override func setUp() async throws {
10-
self.app = try await Application.make(.testing)
11-
try await configure(app){{#fluent}}
12-
try await app.autoMigrate(){{/fluent}}
13-
}
14-
15-
override func tearDown() async throws { {{#fluent}}
16-
try await app.autoRevert(){{/fluent}}
17-
try await self.app.asyncShutdown()
18-
self.app = nil
7+
{{#fluent}}@Suite("App Tests with DB", .serialized)
8+
{{/fluent}}{{^fluent}}@Suite("App Tests")
9+
{{/fluent}}
10+
struct AppTests {
11+
private func withApp(_ test: (Application) async throws -> ()) async throws {
12+
let app = try await Application.make(.testing)
13+
try await configure(app)
14+
{{#fluent}}try await app.autoMigrate()
15+
{{/fluent}} try await test(app)
16+
{{#fluent}}try await app.autoRevert()
17+
{{/fluent}} try await app.asyncShutdown()
1918
}
2019

21-
func testHelloWorld() async throws {
22-
try await self.app.test(.GET, "hello", afterResponse: { res async in
23-
XCTAssertEqual(res.status, .ok)
24-
XCTAssertEqual(res.body.string, "Hello, world!")
25-
})
20+
@Test("Test Hello World Route")
21+
func helloWorld() async throws {
22+
try await withApp { app in
23+
try await app.test(.GET, "hello", afterResponse: { res async in
24+
#expect(res.status == .ok)
25+
#expect(res.body.string == "Hello, world!")
26+
})
27+
}
2628
}{{#fluent}}
2729

28-
func testTodoIndex() async throws {
29-
let sampleTodos = [Todo(title: "sample1"), Todo(title: "sample2")]
30-
try await sampleTodos.create(on: self.app.db)
31-
32-
try await self.app.test(.GET, "todos", afterResponse: { res async throws in
33-
XCTAssertEqual(res.status, .ok)
34-
XCTAssertEqual(
35-
try res.content.decode([TodoDTO].self).sorted(by: { $0.title ?? "" < $1.title ?? "" }),
36-
sampleTodos.map { $0.toDTO() }.sorted(by: { $0.title ?? "" < $1.title ?? "" })
37-
)
38-
})
30+
@Test("Getting all the Todos")
31+
func getAllTodos() async throws {
32+
try await withApp { app in
33+
let sampleTodos = [Todo(title: "sample1"), Todo(title: "sample2")]
34+
try await sampleTodos.create(on: app.db)
35+
36+
try await app.test(.GET, "todos", afterResponse: { res async throws in
37+
#expect(res.status == .ok)
38+
#expect(try res.content.decode([TodoDTO].self) == sampleTodos.map { $0.toDTO()} )
39+
})
40+
}
3941
}
4042

41-
func testTodoCreate() async throws {
43+
@Test("Creating a Todo")
44+
func createTodo() async throws {
4245
let newDTO = TodoDTO(id: nil, title: "test")
4346

44-
try await self.app.test(.POST, "todos", beforeRequest: { req in
45-
try req.content.encode(newDTO)
46-
}, afterResponse: { res async throws in
47-
XCTAssertEqual(res.status, .ok)
48-
let models = try await Todo.query(on: self.app.db).all()
49-
XCTAssertEqual(models.map { $0.toDTO().title }, [newDTO.title])
50-
})
47+
try await withApp { app in
48+
try await app.test(.POST, "todos", beforeRequest: { req in
49+
try req.content.encode(newDTO)
50+
}, afterResponse: { res async throws in
51+
#expect(res.status == .ok)
52+
let models = try await Todo.query(on: app.db).all()
53+
#expect(models.map({ $0.toDTO().title }) == [newDTO.title])
54+
XCTAssertEqual(models.map { $0.toDTO() }, [newDTO])
55+
})
56+
}
5157
}
5258

53-
func testTodoDelete() async throws {
59+
@Test("Deleting a Todo")
60+
func deleteTodo() async throws {
5461
let testTodos = [Todo(title: "test1"), Todo(title: "test2")]
55-
try await testTodos.create(on: app.db)
5662

57-
try await self.app.test(.DELETE, "todos/\(testTodos[0].requireID())", afterResponse: { res async throws in
58-
XCTAssertEqual(res.status, .noContent)
59-
let model = try await Todo.find(testTodos[0].id, on: self.app.db)
60-
XCTAssertNil(model)
61-
})
63+
try await withApp { app in
64+
try await testTodos.create(on: app.db)
65+
66+
try await app.test(.DELETE, "todos/\(testTodos[0].requireID())", afterResponse: { res async throws in
67+
#expect(res.status == .noContent)
68+
let model = try await Todo.find(testTodos[0].id, on: app.db)
69+
#expect(model == nil)
70+
})
71+
}
6272
}{{/fluent}}
6373
}
6474
{{#fluent}}

0 commit comments

Comments
 (0)