Skip to content

Commit

Permalink
Swift Testing and Swift 6 (#139)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
0xTim authored Oct 11, 2024
1 parent e71aef7 commit b925739
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 52 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/test-template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
cache_key:
value: ${{ jobs.cache-toolbox.outputs.cache_key }}
env:
SWIFT_IMAGE: 'swift:5.10-noble'
SWIFT_IMAGE: 'swift:6.0-noble'

jobs:

Expand Down Expand Up @@ -57,7 +57,7 @@ jobs:
fail-fast: false
matrix:
swift-image:
- 'swift:5.10-noble'
- 'swift:6.0-noble'
fluentflags:
- '--no-fluent'
#- '--fluent.db mysql' # The MySQL image can't be configured usably via GH Actions at this time
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# ================================
# Build image
# ================================
FROM swift:5.10-noble AS build
FROM swift:6.0-noble AS build

# Install OS updates
RUN export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true \
Expand Down
5 changes: 3 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:5.10
// swift-tools-version:6.0
import PackageDescription

let package = Package(
Expand Down Expand Up @@ -39,7 +39,8 @@ let package = Package(
],
swiftSettings: swiftSettings
)
]
],
swiftLanguageModes: [.v5]
)

var swiftSettings: [SwiftSetting] { [
Expand Down
6 changes: 3 additions & 3 deletions Sources/App/configure.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ public func configure(_ app: Application) async throws {
password: Environment.get("DATABASE_PASSWORD") ?? "vapor_password",
database: Environment.get("DATABASE_NAME") ?? "vapor_database",
tls: .prefer(try .init(configuration: .clientDefault)))
), as: .psql){{/fluent.db.is_postgres}}{{#fluent.db.is_mysql}}app.databases.use(DatabaseConfigurationFactory.mysql(
), as: .psql){{/fluent.db.is_postgres}}{{#fluent.db.is_mysql}} app.databases.use(DatabaseConfigurationFactory.mysql(
hostname: Environment.get("DATABASE_HOST") ?? "localhost",
port: Environment.get("DATABASE_PORT").flatMap(Int.init(_:)) ?? MySQLConfiguration.ianaPortNumber,
username: Environment.get("DATABASE_USERNAME") ?? "vapor_username",
password: Environment.get("DATABASE_PASSWORD") ?? "vapor_password",
database: Environment.get("DATABASE_NAME") ?? "vapor_database"
), as: .mysql){{/fluent.db.is_mysql}}{{#fluent.db.is_mongo}}try app.databases.use(DatabaseConfigurationFactory.mongo(
), as: .mysql){{/fluent.db.is_mysql}}{{#fluent.db.is_mongo}} try app.databases.use(DatabaseConfigurationFactory.mongo(
connectionString: Environment.get("DATABASE_URL") ?? "mongodb://localhost:27017/vapor_database"
), as: .mongo){{/fluent.db.is_mongo}}{{#fluent.db.is_sqlite}}app.databases.use(DatabaseConfigurationFactory.sqlite(.file("db.sqlite")), as: .sqlite){{/fluent.db.is_sqlite}}
), as: .mongo){{/fluent.db.is_mongo}}{{#fluent.db.is_sqlite}} app.databases.use(DatabaseConfigurationFactory.sqlite(.file("db.sqlite")), as: .sqlite){{/fluent.db.is_sqlite}}

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

Expand Down
98 changes: 54 additions & 44 deletions Tests/AppTests/AppTests.swift
Original file line number Diff line number Diff line change
@@ -1,64 +1,74 @@
@testable import App
import XCTVapor
import Testing
{{#fluent}}import Fluent
{{/fluent}}

final class AppTests: XCTestCase {
var app: Application!

override func setUp() async throws {
self.app = try await Application.make(.testing)
try await configure(app){{#fluent}}
try await app.autoMigrate(){{/fluent}}
}

override func tearDown() async throws { {{#fluent}}
try await app.autoRevert(){{/fluent}}
try await self.app.asyncShutdown()
self.app = nil
{{#fluent}}@Suite("App Tests with DB", .serialized)
{{/fluent}}{{^fluent}}@Suite("App Tests")
{{/fluent}}
struct AppTests {
private func withApp(_ test: (Application) async throws -> ()) async throws {
let app = try await Application.make(.testing)
try await configure(app)
{{#fluent}}try await app.autoMigrate()
{{/fluent}} try await test(app)
{{#fluent}}try await app.autoRevert()
{{/fluent}} try await app.asyncShutdown()
}

func testHelloWorld() async throws {
try await self.app.test(.GET, "hello", afterResponse: { res async in
XCTAssertEqual(res.status, .ok)
XCTAssertEqual(res.body.string, "Hello, world!")
})
@Test("Test Hello World Route")
func helloWorld() async throws {
try await withApp { app in
try await app.test(.GET, "hello", afterResponse: { res async in
#expect(res.status == .ok)
#expect(res.body.string == "Hello, world!")
})
}
}{{#fluent}}

func testTodoIndex() async throws {
let sampleTodos = [Todo(title: "sample1"), Todo(title: "sample2")]
try await sampleTodos.create(on: self.app.db)

try await self.app.test(.GET, "todos", afterResponse: { res async throws in
XCTAssertEqual(res.status, .ok)
XCTAssertEqual(
try res.content.decode([TodoDTO].self).sorted(by: { $0.title ?? "" < $1.title ?? "" }),
sampleTodos.map { $0.toDTO() }.sorted(by: { $0.title ?? "" < $1.title ?? "" })
)
})
@Test("Getting all the Todos")
func getAllTodos() async throws {
try await withApp { app in
let sampleTodos = [Todo(title: "sample1"), Todo(title: "sample2")]
try await sampleTodos.create(on: app.db)

try await app.test(.GET, "todos", afterResponse: { res async throws in
#expect(res.status == .ok)
#expect(try res.content.decode([TodoDTO].self) == sampleTodos.map { $0.toDTO()} )
})
}
}

func testTodoCreate() async throws {
@Test("Creating a Todo")
func createTodo() async throws {
let newDTO = TodoDTO(id: nil, title: "test")

try await self.app.test(.POST, "todos", beforeRequest: { req in
try req.content.encode(newDTO)
}, afterResponse: { res async throws in
XCTAssertEqual(res.status, .ok)
let models = try await Todo.query(on: self.app.db).all()
XCTAssertEqual(models.map { $0.toDTO().title }, [newDTO.title])
})
try await withApp { app in
try await app.test(.POST, "todos", beforeRequest: { req in
try req.content.encode(newDTO)
}, afterResponse: { res async throws in
#expect(res.status == .ok)
let models = try await Todo.query(on: app.db).all()
#expect(models.map({ $0.toDTO().title }) == [newDTO.title])
XCTAssertEqual(models.map { $0.toDTO() }, [newDTO])
})
}
}

func testTodoDelete() async throws {
@Test("Deleting a Todo")
func deleteTodo() async throws {
let testTodos = [Todo(title: "test1"), Todo(title: "test2")]
try await testTodos.create(on: app.db)

try await self.app.test(.DELETE, "todos/\(testTodos[0].requireID())", afterResponse: { res async throws in
XCTAssertEqual(res.status, .noContent)
let model = try await Todo.find(testTodos[0].id, on: self.app.db)
XCTAssertNil(model)
})
try await withApp { app in
try await testTodos.create(on: app.db)

try await app.test(.DELETE, "todos/\(testTodos[0].requireID())", afterResponse: { res async throws in
#expect(res.status == .noContent)
let model = try await Todo.find(testTodos[0].id, on: app.db)
#expect(model == nil)
})
}
}{{/fluent}}
}
{{#fluent}}
Expand Down

0 comments on commit b925739

Please sign in to comment.