From 35dce07b3da0b8a4cec898213703da80c16d1ef8 Mon Sep 17 00:00:00 2001 From: "Sven A. Schmidt" Date: Sun, 2 Mar 2025 16:39:55 +0100 Subject: [PATCH 1/7] Convert ReAnalyzeVersionsTests --- Tests/AppTests/ReAnalyzeVersionsTests.swift | 279 ++++++++++---------- 1 file changed, 142 insertions(+), 137 deletions(-) diff --git a/Tests/AppTests/ReAnalyzeVersionsTests.swift b/Tests/AppTests/ReAnalyzeVersionsTests.swift index ff8de0c54..05c49a84b 100644 --- a/Tests/AppTests/ReAnalyzeVersionsTests.swift +++ b/Tests/AppTests/ReAnalyzeVersionsTests.swift @@ -12,19 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -import XCTest - @testable import App import Dependencies import Fluent import SQLKit +import Testing import Vapor -class ReAnalyzeVersionsTests: AppTestCase { +@Suite struct ReAnalyzeVersionsTests { - func test_reAnalyzeVersions() async throws { + @Test func reAnalyzeVersions() async throws { // Basic end-to-end test try await withDependencies { $0.date.now = .t0 @@ -44,56 +43,57 @@ class ReAnalyzeVersionsTests: AppTestCase { } $0.httpClient.mastodonPost = { @Sendable _ in } } operation: { - // setup - // - package dump does not include toolsVersion, targets to simulate an "old version" - // - run analysis to create existing version - // - validate that initial state is reflected - // - then change input data in fields that are affecting existing versions (which `analysis` is "blind" to) - // - run analysis again to confirm "blindness" - // - run re-analysis and confirm changes are now reflected - let pkg = try await savePackage(on: app.db, - "https://github.com/foo/1".url, - processingStage: .ingestion) - let repoId = UUID() - try await Repository(id: repoId, - package: pkg, - defaultBranch: "main", - name: "1", - owner: "foo").save(on: app.db) + try await withApp { app in + // setup + // - package dump does not include toolsVersion, targets to simulate an "old version" + // - run analysis to create existing version + // - validate that initial state is reflected + // - then change input data in fields that are affecting existing versions (which `analysis` is "blind" to) + // - run analysis again to confirm "blindness" + // - run re-analysis and confirm changes are now reflected + let pkg = try await savePackage(on: app.db, + "https://github.com/foo/1".url, + processingStage: .ingestion) + let repoId = UUID() + try await Repository(id: repoId, + package: pkg, + defaultBranch: "main", + name: "1", + owner: "foo").save(on: app.db) - try await withDependencies { - $0.git.revisionInfo = { @Sendable _, _ in .init(commit: "sha", date: .t0) } - $0.shell.run = { @Sendable cmd, path in - if cmd.description.hasSuffix("swift package dump-package") { - return #""" + try await withDependencies { + $0.git.revisionInfo = { @Sendable _, _ in .init(commit: "sha", date: .t0) } + $0.shell.run = { @Sendable cmd, path in + if cmd.description.hasSuffix("swift package dump-package") { + return #""" { "name": "SPI-Server", "products": [], "targets": [] } """# + } + return "" + } + } operation: { + do { + // run initial analysis and assert initial state for versions + try await Analyze.analyze(client: app.client, + database: app.db, + mode: .limit(10)) + let versions = try await Version.query(on: app.db) + .with(\.$targets) + .all() + #expect(versions.map(\.toolsVersion) == [nil, nil]) + #expect(versions.map { $0.targets.map(\.name) } == [[], []]) + #expect(versions.map(\.releaseNotes) == [nil, nil]) } - return "" - } - } operation: { - do { - // run initial analysis and assert initial state for versions - try await Analyze.analyze(client: app.client, - database: app.db, - mode: .limit(10)) - let versions = try await Version.query(on: app.db) - .with(\.$targets) - .all() - XCTAssertEqual(versions.map(\.toolsVersion), [nil, nil]) - XCTAssertEqual(versions.map { $0.targets.map(\.name) } , [[], []]) - XCTAssertEqual(versions.map(\.releaseNotes) , [nil, nil]) - } - try await withDependencies { - // Update state that would normally not be affecting existing versions, effectively simulating the situation where we only started parsing it after versions had already been created - $0.shell.run = { @Sendable cmd, path in - if cmd.description.hasSuffix("swift package dump-package") { - return #""" + try await withDependencies { + // Update state that would normally not be affecting existing versions, effectively simulating the situation where we only started parsing it after versions had already been created + $0.shell.run = { @Sendable cmd, path in + if cmd.description.hasSuffix("swift package dump-package") { + return #""" { "name": "SPI-Server", "products": [], @@ -103,83 +103,86 @@ class ReAnalyzeVersionsTests: AppTestCase { } } """# + } + return "" } - return "" - } - // also, update release notes to ensure mergeReleaseInfo is being called - let r = try await Repository.find(repoId, on: app.db).unwrap() - r.releases = [ - .mock(description: "rel 1.2.3", tagName: "1.2.3") - ] - try await r.save(on: app.db) - } operation: { - do { // assert running analysis again does not update existing versions - try await Analyze.analyze(client: app.client, - database: app.db, - mode: .limit(10)) + // also, update release notes to ensure mergeReleaseInfo is being called + let r = try await Repository.find(repoId, on: app.db).unwrap() + r.releases = [ + .mock(description: "rel 1.2.3", tagName: "1.2.3") + ] + try await r.save(on: app.db) + } operation: { + do { // assert running analysis again does not update existing versions + try await Analyze.analyze(client: app.client, + database: app.db, + mode: .limit(10)) + let versions = try await Version.query(on: app.db) + .with(\.$targets) + .all() + #expect(versions.map(\.toolsVersion) == [nil, nil]) + #expect(versions.map { $0.targets.map(\.name) } == [[], []]) + #expect(versions.map(\.releaseNotes) == [nil, nil]) + #expect(versions.map(\.docArchives) == [nil, nil]) + } + + // MUT + try await ReAnalyzeVersions.reAnalyzeVersions(client: app.client, + database: app.db, + before: Date.now, + refreshCheckouts: false, + limit: 10) + + // validate that re-analysis has now updated existing versions let versions = try await Version.query(on: app.db) .with(\.$targets) + .sort(\.$createdAt) .all() - XCTAssertEqual(versions.map(\.toolsVersion), [nil, nil]) - XCTAssertEqual(versions.map { $0.targets.map(\.name) } , [[], []]) - XCTAssertEqual(versions.map(\.releaseNotes) , [nil, nil]) - XCTAssertEqual(versions.map(\.docArchives), [nil, nil]) + #expect(versions.map(\.toolsVersion) == ["5.3", "5.3"]) + #expect(versions.map { $0.targets.map(\.name) } == [["t1"], ["t1"]]) + #expect(versions.compactMap(\.releaseNotes) == ["rel 1.2.3"]) } - - // MUT - try await ReAnalyzeVersions.reAnalyzeVersions(client: app.client, - database: app.db, - before: Date.now, - refreshCheckouts: false, - limit: 10) - - // validate that re-analysis has now updated existing versions - let versions = try await Version.query(on: app.db) - .with(\.$targets) - .sort(\.$createdAt) - .all() - XCTAssertEqual(versions.map(\.toolsVersion), ["5.3", "5.3"]) - XCTAssertEqual(versions.map { $0.targets.map(\.name) } , [["t1"], ["t1"]]) - XCTAssertEqual(versions.compactMap(\.releaseNotes) , ["rel 1.2.3"]) } } } } - func test_Package_fetchReAnalysisCandidates() async throws { + @Test func Package_fetchReAnalysisCandidates() async throws { // Three packages with two versions: // 1) both versions updated before cutoff -> candidate // 2) one version update before cutoff, one after -> candidate // 3) both version updated after cutoff -> not a candidate - let cutoff = Date(timeIntervalSince1970: 2) - do { - let p = Package(url: "1") - try await p.save(on: app.db) - try await createVersion(app.db, p, updatedAt: .t0) - try await createVersion(app.db, p, updatedAt: .t1) - } - do { - let p = Package(url: "2") - try await p.save(on: app.db) - try await createVersion(app.db, p, updatedAt: .t1) - try await createVersion(app.db, p, updatedAt: .t3) - } - do { - let p = Package(url: "3") - try await p.save(on: app.db) - try await createVersion(app.db, p, updatedAt: .t3) - try await createVersion(app.db, p, updatedAt: .t4) - } + try await withApp { app in + let cutoff = Date(timeIntervalSince1970: 2) + do { + let p = Package(url: "1") + try await p.save(on: app.db) + try await createVersion(app.db, p, updatedAt: .t0) + try await createVersion(app.db, p, updatedAt: .t1) + } + do { + let p = Package(url: "2") + try await p.save(on: app.db) + try await createVersion(app.db, p, updatedAt: .t1) + try await createVersion(app.db, p, updatedAt: .t3) + } + do { + let p = Package(url: "3") + try await p.save(on: app.db) + try await createVersion(app.db, p, updatedAt: .t3) + try await createVersion(app.db, p, updatedAt: .t4) + } - // MUT - let res = try await Package - .fetchReAnalysisCandidates(app.db, before: cutoff, limit: 10) + // MUT + let res = try await Package + .fetchReAnalysisCandidates(app.db, before: cutoff, limit: 10) - // validate - XCTAssertEqual(res.map(\.model.url), ["1", "2"]) + // validate + #expect(res.map(\.model.url) == ["1", "2"]) + } } - func test_versionsUpdatedOnError() async throws { + @Test func versionsUpdatedOnError() async throws { // Test to ensure versions are updated even if processing throws errors. // This is to ensure our candidate selection shrinks and we don't // churn over and over on failing versions. @@ -213,41 +216,43 @@ class ReAnalyzeVersionsTests: AppTestCase { return "" } } operation: { - let pkg = try await savePackage(on: app.db, - "https://github.com/foo/1".url, - processingStage: .ingestion) - try await Repository(package: pkg, defaultBranch: "main").save(on: app.db) - try await Analyze.analyze(client: app.client, - database: app.db, - mode: .limit(10)) - try await setAllVersionsUpdatedAt(app.db, updatedAt: .t0) - do { - let candidates = try await Package - .fetchReAnalysisCandidates(app.db, before: cutoff, limit: 10) - XCTAssertEqual(candidates.count, 1) - } - - try await withDependencies { - $0.shell.run = { @Sendable cmd, path in - if cmd == .swiftDumpPackage { - // simulate error during package dump - struct Error: Swift.Error { } - throw Error() + try await withApp { app in + let pkg = try await savePackage(on: app.db, + "https://github.com/foo/1".url, + processingStage: .ingestion) + try await Repository(package: pkg, defaultBranch: "main").save(on: app.db) + try await Analyze.analyze(client: app.client, + database: app.db, + mode: .limit(10)) + try await setAllVersionsUpdatedAt(app.db, updatedAt: .t0) + do { + let candidates = try await Package + .fetchReAnalysisCandidates(app.db, before: cutoff, limit: 10) + #expect(candidates.count == 1) + } + + try await withDependencies { + $0.shell.run = { @Sendable cmd, path in + if cmd == .swiftDumpPackage { + // simulate error during package dump + struct Error: Swift.Error { } + throw Error() + } + return "" } - return "" + } operation: { + // MUT + try await ReAnalyzeVersions.reAnalyzeVersions(client: app.client, + database: app.db, + before: Date.now, + refreshCheckouts: false, + limit: 10) + + // validate + let candidates = try await Package + .fetchReAnalysisCandidates(app.db, before: cutoff, limit: 10) + #expect(candidates.count == 0) } - } operation: { - // MUT - try await ReAnalyzeVersions.reAnalyzeVersions(client: app.client, - database: app.db, - before: Date.now, - refreshCheckouts: false, - limit: 10) - - // validate - let candidates = try await Package - .fetchReAnalysisCandidates(app.db, before: cutoff, limit: 10) - XCTAssertEqual(candidates.count, 0) } } } From 00d0010959176a0e9d12ca45cdafd2edbff73167 Mon Sep 17 00:00:00 2001 From: "Sven A. Schmidt" Date: Sun, 2 Mar 2025 16:42:41 +0100 Subject: [PATCH 2/7] Convert RecentViewsTests --- Tests/AppTests/RecentViewsTests.swift | 367 +++++++++++++------------- 1 file changed, 188 insertions(+), 179 deletions(-) diff --git a/Tests/AppTests/RecentViewsTests.swift b/Tests/AppTests/RecentViewsTests.swift index 35d7f7df6..2d6f57843 100644 --- a/Tests/AppTests/RecentViewsTests.swift +++ b/Tests/AppTests/RecentViewsTests.swift @@ -12,137 +12,143 @@ // See the License for the specific language governing permissions and // limitations under the License. -@testable import App - -import XCTVapor - +import Foundation -class RecentViewsTests: AppTestCase { +@testable import App - func test_recentPackages() async throws { - // setup - do { // 1st package is eligible - let pkg = Package(id: UUID(), url: "1") - try await pkg.save(on: app.db) - try await Repository(package: pkg, - name: "1", - owner: "foo", - summary: "pkg 1").create(on: app.db) - try await Version(package: pkg, packageName: "1").save(on: app.db) +import Testing + + +@Suite struct RecentViewsTests { + + @Test func recentPackages() async throws { + try await withApp { app in + // setup + do { // 1st package is eligible + let pkg = Package(id: UUID(), url: "1") + try await pkg.save(on: app.db) + try await Repository(package: pkg, + name: "1", + owner: "foo", + summary: "pkg 1").create(on: app.db) + try await Version(package: pkg, packageName: "1").save(on: app.db) + } + do { // 2nd package should not be selected, because it has no package name + let pkg = Package(id: UUID(), url: "2") + try await pkg.save(on: app.db) + try await Repository(package: pkg, + name: "2", + owner: "foo", + summary: "pkg 2").create(on: app.db) + try await Version(package: pkg).save(on: app.db) + } + do { // 3rd package is eligible + let pkg = Package(id: UUID(), url: "3") + try await pkg.save(on: app.db) + try await Repository(package: pkg, + name: "3", + owner: "foo", + summary: "pkg 3").create(on: app.db) + try await Version(package: pkg, packageName: "3").save(on: app.db) + } + // make sure to refresh the materialized view + try await RecentPackage.refresh(on: app.db) + + // MUT + let res = try await RecentPackage.fetch(on: app.db) + + // validate + #expect(res.map(\.packageName) == ["3", "1"]) + #expect(res.map(\.packageSummary) == ["pkg 3", "pkg 1"]) } - do { // 2nd package should not be selected, because it has no package name - let pkg = Package(id: UUID(), url: "2") - try await pkg.save(on: app.db) - try await Repository(package: pkg, - name: "2", - owner: "foo", - summary: "pkg 2").create(on: app.db) - try await Version(package: pkg).save(on: app.db) - } - do { // 3rd package is eligible - let pkg = Package(id: UUID(), url: "3") - try await pkg.save(on: app.db) - try await Repository(package: pkg, - name: "3", - owner: "foo", - summary: "pkg 3").create(on: app.db) - try await Version(package: pkg, packageName: "3").save(on: app.db) - } - // make sure to refresh the materialized view - try await RecentPackage.refresh(on: app.db) - - // MUT - let res = try await RecentPackage.fetch(on: app.db) - - // validate - XCTAssertEqual(res.map(\.packageName), ["3", "1"]) - XCTAssertEqual(res.map(\.packageSummary), ["pkg 3", "pkg 1"]) } - func test_recentReleases() async throws { - // setup - do { // 1st package is eligible - let pkg = Package(id: UUID(), url: "1") - try await pkg.save(on: app.db) - try await Repository(package: pkg, - defaultBranch: "default", - name: "1", - owner: "foo", - summary: "pkg 1").create(on: app.db) - try await Version(package: pkg, - commitDate: Date(timeIntervalSince1970: 0), - packageName: "1", - reference: .tag(.init(1, 2, 3)), - url: "1/release/1.2.3").save(on: app.db) + @Test func recentReleases() async throws { + try await withApp { app in + // setup + do { // 1st package is eligible + let pkg = Package(id: UUID(), url: "1") + try await pkg.save(on: app.db) + try await Repository(package: pkg, + defaultBranch: "default", + name: "1", + owner: "foo", + summary: "pkg 1").create(on: app.db) + try await Version(package: pkg, + commitDate: Date(timeIntervalSince1970: 0), + packageName: "1", + reference: .tag(.init(1, 2, 3)), + url: "1/release/1.2.3").save(on: app.db) + } + do { // 2nd package is ineligible, because it has a branch reference + let pkg = Package(id: UUID(), url: "2") + try await pkg.save(on: app.db) + try await Repository(package: pkg, + defaultBranch: "default", + name: "2", + owner: "foo", + summary: "pkg 2").create(on: app.db) + try await Version(package: pkg, + commitDate: Date(timeIntervalSince1970: 0), + packageName: "2", + reference: .branch("default"), + url: "2/branch/default").save(on: app.db) + } + do { // 3rd package is ineligible, because it has no package name + let pkg = Package(id: UUID(), url: "3") + try await pkg.save(on: app.db) + try await Repository(package: pkg, + defaultBranch: "default", + name: "3", + owner: "foo", + summary: "pkg 3").create(on: app.db) + try await Version(package: pkg, + commitDate: Date(timeIntervalSince1970: 0), + reference: .branch("default"), + url: "2/branch/default").save(on: app.db) + } + do { // 4th package is ineligible, because it has no reference + let pkg = Package(id: UUID(), url: "4") + try await pkg.save(on: app.db) + try await Repository(package: pkg, + defaultBranch: "default", + name: "4", + owner: "foo", + summary: "pkg 4").create(on: app.db) + try await Version(package: pkg, + commitDate: Date(timeIntervalSince1970: 0), + packageName: "4").save(on: app.db) + } + do { // 5th package is eligible - should come before 1st because of more recent commit date + let pkg = Package(id: UUID(), url: "5") + try await pkg.save(on: app.db) + try await Repository(package: pkg, + defaultBranch: "default", + name: "5", + owner: "foo", + summary: "pkg 5").create(on: app.db) + try await Version(package: pkg, + commitDate: Date(timeIntervalSince1970: 1), + packageName: "5", + reference: .tag(.init(2, 0, 0)), + url: "5/release/2.0.0").save(on: app.db) + } + + // make sure to refresh the materialized view + try await RecentRelease.refresh(on: app.db) + + // MUT + let res = try await RecentRelease.fetch(on: app.db) + + // validate + #expect(res.map(\.packageName) == ["5", "1"]) + #expect(res.map(\.version) == ["2.0.0", "1.2.3"]) + #expect(res.map(\.packageSummary) == ["pkg 5", "pkg 1"]) + #expect(res.map(\.releaseUrl) == ["5/release/2.0.0", "1/release/1.2.3"]) } - do { // 2nd package is ineligible, because it has a branch reference - let pkg = Package(id: UUID(), url: "2") - try await pkg.save(on: app.db) - try await Repository(package: pkg, - defaultBranch: "default", - name: "2", - owner: "foo", - summary: "pkg 2").create(on: app.db) - try await Version(package: pkg, - commitDate: Date(timeIntervalSince1970: 0), - packageName: "2", - reference: .branch("default"), - url: "2/branch/default").save(on: app.db) - } - do { // 3rd package is ineligible, because it has no package name - let pkg = Package(id: UUID(), url: "3") - try await pkg.save(on: app.db) - try await Repository(package: pkg, - defaultBranch: "default", - name: "3", - owner: "foo", - summary: "pkg 3").create(on: app.db) - try await Version(package: pkg, - commitDate: Date(timeIntervalSince1970: 0), - reference: .branch("default"), - url: "2/branch/default").save(on: app.db) - } - do { // 4th package is ineligible, because it has no reference - let pkg = Package(id: UUID(), url: "4") - try await pkg.save(on: app.db) - try await Repository(package: pkg, - defaultBranch: "default", - name: "4", - owner: "foo", - summary: "pkg 4").create(on: app.db) - try await Version(package: pkg, - commitDate: Date(timeIntervalSince1970: 0), - packageName: "4").save(on: app.db) - } - do { // 5th package is eligible - should come before 1st because of more recent commit date - let pkg = Package(id: UUID(), url: "5") - try await pkg.save(on: app.db) - try await Repository(package: pkg, - defaultBranch: "default", - name: "5", - owner: "foo", - summary: "pkg 5").create(on: app.db) - try await Version(package: pkg, - commitDate: Date(timeIntervalSince1970: 1), - packageName: "5", - reference: .tag(.init(2, 0, 0)), - url: "5/release/2.0.0").save(on: app.db) - } - - // make sure to refresh the materialized view - try await RecentRelease.refresh(on: app.db) - - // MUT - let res = try await RecentRelease.fetch(on: app.db) - - // validate - XCTAssertEqual(res.map(\.packageName), ["5", "1"]) - XCTAssertEqual(res.map(\.version), ["2.0.0", "1.2.3"]) - XCTAssertEqual(res.map(\.packageSummary), ["pkg 5", "pkg 1"]) - XCTAssertEqual(res.map(\.releaseUrl), ["5/release/2.0.0", "1/release/1.2.3"]) } - func test_Array_RecentReleases_filter() throws { + @Test func Array_RecentReleases_filter() throws { // List only major releases // setup let releases: [RecentRelease] = (1...12).map { @@ -168,71 +174,74 @@ class RecentViewsTests: AppTestCase { let pre = releases.filter(by: [.pre]) // validate - XCTAssertEqual( - all.map(\.version), - ["0.1.1", "0.2.0", "1.0.1", "1.1.0", "1.2.1", "2.0.0", "2.1.1", "2.2.0", "3.0.1", "3.1.0", + #expect( + all.map(\.version) == ["0.1.1", "0.2.0", "1.0.1", "1.1.0", "1.2.1", "2.0.0", "2.1.1", "2.2.0", "3.0.1", "3.1.0", "3.2.1-b1", "4.0.0-b1"]) - XCTAssertEqual( - majorOnly.map(\.version), ["2.0.0"]) - XCTAssertEqual( - minorOnly.map(\.version), ["0.2.0", "1.1.0", "2.2.0", "3.1.0"]) - XCTAssertEqual( - majorMinor.map(\.version), ["0.2.0", "1.1.0", "2.0.0", "2.2.0", "3.1.0"]) - XCTAssertEqual( - pre.map(\.version), ["3.2.1-b1", "4.0.0-b1"]) + #expect( + majorOnly.map(\.version) == ["2.0.0"]) + #expect( + minorOnly.map(\.version) == ["0.2.0", "1.1.0", "2.2.0", "3.1.0"]) + #expect( + majorMinor.map(\.version) == ["0.2.0", "1.1.0", "2.0.0", "2.2.0", "3.1.0"]) + #expect( + pre.map(\.version) == ["3.2.1-b1", "4.0.0-b1"]) } - func test_recentPackages_dedupe_issue() async throws { + @Test func recentPackages_dedupe_issue() async throws { // https://github.com/SwiftPackageIndex/SwiftPackageIndex-Server/issues/315 - // setup - // Package with two eligible versions that differ in package name - let pkg = Package(id: UUID(), url: "1") - try await pkg.save(on: app.db) - try await Repository(package: pkg, - name: "bar", - owner: "foo", - summary: "pkg summary").create(on: app.db) - try await Version(package: pkg, - commitDate: Date(timeIntervalSince1970: 0), - packageName: "pkg-bar").save(on: app.db) - try await Version(package: pkg, - commitDate: Date(timeIntervalSince1970: 1), - packageName: "pkg-bar-updated").save(on: app.db) - // make sure to refresh the materialized view - try await RecentPackage.refresh(on: app.db) + try await withApp { app in + // setup + // Package with two eligible versions that differ in package name + let pkg = Package(id: UUID(), url: "1") + try await pkg.save(on: app.db) + try await Repository(package: pkg, + name: "bar", + owner: "foo", + summary: "pkg summary").create(on: app.db) + try await Version(package: pkg, + commitDate: Date(timeIntervalSince1970: 0), + packageName: "pkg-bar").save(on: app.db) + try await Version(package: pkg, + commitDate: Date(timeIntervalSince1970: 1), + packageName: "pkg-bar-updated").save(on: app.db) + // make sure to refresh the materialized view + try await RecentPackage.refresh(on: app.db) - // MUT - let res = try await RecentPackage.fetch(on: app.db) + // MUT + let res = try await RecentPackage.fetch(on: app.db) - // validate - XCTAssertEqual(res.map(\.packageName), ["pkg-bar-updated"]) + // validate + #expect(res.map(\.packageName) == ["pkg-bar-updated"]) + } } - func test_recentReleases_dedupe_issue() async throws { + @Test func recentReleases_dedupe_issue() async throws { // https://github.com/SwiftPackageIndex/SwiftPackageIndex-Server/issues/315 - // setup - let pkg = Package(id: UUID(), url: "1") - try await pkg.save(on: app.db) - try await Repository(package: pkg, - name: "bar", - owner: "foo", - summary: "pkg summary").create(on: app.db) - try await Version(package: pkg, - commitDate: Date(timeIntervalSince1970: 0), - packageName: "pkg-bar", - reference: .tag(.init(1, 0, 0))).save(on: app.db) - try await Version(package: pkg, - commitDate: Date(timeIntervalSince1970: 1), - packageName: "pkg-bar-updated", - reference: .tag(.init(1, 0, 1))).save(on: app.db) - // make sure to refresh the materialized view - try await RecentRelease.refresh(on: app.db) - - // MUT - let res = try await RecentRelease.fetch(on: app.db) - - // validate - XCTAssertEqual(res.map(\.packageName), ["pkg-bar-updated"]) + try await withApp { app in + // setup + let pkg = Package(id: UUID(), url: "1") + try await pkg.save(on: app.db) + try await Repository(package: pkg, + name: "bar", + owner: "foo", + summary: "pkg summary").create(on: app.db) + try await Version(package: pkg, + commitDate: Date(timeIntervalSince1970: 0), + packageName: "pkg-bar", + reference: .tag(.init(1, 0, 0))).save(on: app.db) + try await Version(package: pkg, + commitDate: Date(timeIntervalSince1970: 1), + packageName: "pkg-bar-updated", + reference: .tag(.init(1, 0, 1))).save(on: app.db) + // make sure to refresh the materialized view + try await RecentRelease.refresh(on: app.db) + + // MUT + let res = try await RecentRelease.fetch(on: app.db) + + // validate + #expect(res.map(\.packageName) == ["pkg-bar-updated"]) + } } } From 6b72cb9ac05aab931f77f03d3901c9e0b78d57f8 Mon Sep 17 00:00:00 2001 From: "Sven A. Schmidt" Date: Sun, 2 Mar 2025 16:46:42 +0100 Subject: [PATCH 3/7] Convert ReconcilerTests --- Tests/AppTests/ReconcilerTests.swift | 366 ++++++++++++++------------- 1 file changed, 190 insertions(+), 176 deletions(-) diff --git a/Tests/AppTests/ReconcilerTests.swift b/Tests/AppTests/ReconcilerTests.swift index ee8b537b4..980c295b2 100644 --- a/Tests/AppTests/ReconcilerTests.swift +++ b/Tests/AppTests/ReconcilerTests.swift @@ -12,213 +12,226 @@ // See the License for the specific language governing permissions and // limitations under the License. -import XCTest - @testable import App import Dependencies +import Testing import Vapor -class ReconcilerTests: AppTestCase { - - func test_fetchCurrentPackageList() async throws { - // setup - for url in ["1", "2", "3"].asURLs { - try await Package(url: url).save(on: app.db) - } +@Suite struct ReconcilerTests { - // MUT - let urls = try await fetchCurrentPackageList(app.db) - - // validate - XCTAssertEqual(urls.map(\.absoluteString).sorted(), ["1", "2", "3"]) - } + @Test func fetchCurrentPackageList() async throws { + try await withApp { app in + // setup + for url in ["1", "2", "3"].asURLs { + try await Package(url: url).save(on: app.db) + } - func test_reconcileMainPackageList() async throws { - let urls = ["1", "2", "3"] - try await withDependencies { - $0.packageListRepository.fetchPackageList = { @Sendable _ in urls.asURLs } - $0.packageListRepository.fetchPackageDenyList = { @Sendable _ in [] } - } operation: { // MUT - _ = try await reconcileMainPackageList(client: app.client, database: app.db) - } + let urls = try await App.fetchCurrentPackageList(app.db) - // validate - let packages = try await Package.query(on: app.db).all() - XCTAssertEqual(packages.map(\.url).sorted(), urls.sorted()) - packages.forEach { - XCTAssertNotNil($0.id) - XCTAssertNotNil($0.createdAt) - XCTAssertNotNil($0.updatedAt) - XCTAssertEqual($0.status, .new) - XCTAssertEqual($0.processingStage, .reconciliation) + // validate + #expect(urls.map(\.absoluteString).sorted() == ["1", "2", "3"]) } } - func test_reconcileMainPackageList_adds_and_deletes() async throws { - // save intial set of packages 1, 2, 3 - for url in ["1", "2", "3"].asURLs { - try await Package(url: url).save(on: app.db) - } - - // new package list drops 2, 3, adds 4, 5 - let urls = ["1", "4", "5"] + @Test func reconcileMainPackageList() async throws { + try await withApp { app in + let urls = ["1", "2", "3"] + try await withDependencies { + $0.packageListRepository.fetchPackageList = { @Sendable _ in urls.asURLs } + $0.packageListRepository.fetchPackageDenyList = { @Sendable _ in [] } + } operation: { + // MUT + _ = try await App.reconcileMainPackageList(client: app.client, database: app.db) + } - try await withDependencies { - $0.packageListRepository.fetchPackageList = { @Sendable _ in urls.asURLs } - $0.packageListRepository.fetchPackageDenyList = { @Sendable _ in [] } - } operation: { - // MUT - _ = try await reconcileMainPackageList(client: app.client, database: app.db) + // validate + let packages = try await Package.query(on: app.db).all() + #expect(packages.map(\.url).sorted() == urls.sorted()) + packages.forEach { + #expect($0.id != nil) + #expect($0.createdAt != nil) + #expect($0.updatedAt != nil) + #expect($0.status == .new) + #expect($0.processingStage == .reconciliation) + } } - - // validate - let packages = try await Package.query(on: app.db).all() - XCTAssertEqual(packages.map(\.url).sorted(), urls.sorted()) } - func test_reconcileMainPackageList_packageDenyList() async throws { - // Save the intial set of packages - for url in ["1", "2", "3"].asURLs { - try await Package(url: url).save(on: app.db) - } + @Test func reconcileMainPackageList_adds_and_deletes() async throws { + try await withApp { app in + // save intial set of packages 1, 2, 3 + for url in ["1", "2", "3"].asURLs { + try await Package(url: url).save(on: app.db) + } - // New list adds two new packages 4, 5 - let packageList = ["1", "2", "3", "4", "5"] + // new package list drops 2, 3, adds 4, 5 + let urls = ["1", "4", "5"] - // Deny list denies 2 and 4 (one existing and one new) - let packageDenyList = ["2", "4"] + try await withDependencies { + $0.packageListRepository.fetchPackageList = { @Sendable _ in urls.asURLs } + $0.packageListRepository.fetchPackageDenyList = { @Sendable _ in [] } + } operation: { + // MUT + _ = try await App.reconcileMainPackageList(client: app.client, database: app.db) + } - try await withDependencies { - $0.packageListRepository.fetchPackageList = { @Sendable _ in packageList.asURLs } - $0.packageListRepository.fetchPackageDenyList = { @Sendable _ in packageDenyList.asURLs } - } operation: { - // MUT - _ = try await reconcileMainPackageList(client: app.client, database: app.db) + // validate + let packages = try await Package.query(on: app.db).all() + #expect(packages.map(\.url).sorted() == urls.sorted()) } - - // validate - let packages = try await Package.query(on: app.db).all() - XCTAssertEqual(packages.map(\.url).sorted(), ["1", "3", "5"]) } - func test_reconcileMainPackageList_packageDenyList_caseSensitivity() async throws { - // Save the intial set of packages - for url in ["https://example.com/one/one", "https://example.com/two/two"].asURLs { - try await Package(url: url).save(on: app.db) - } + @Test func reconcileMainPackageList_packageDenyList() async throws { + try await withApp { app in + // Save the intial set of packages + for url in ["1", "2", "3"].asURLs { + try await Package(url: url).save(on: app.db) + } - // New list adds no new packages - let packageList = ["https://example.com/one/one", "https://example.com/two/two"] + // New list adds two new packages 4, 5 + let packageList = ["1", "2", "3", "4", "5"] - // Deny list denies one/one, but with incorrect casing. - let packageDenyList = ["https://example.com/OnE/oNe"] + // Deny list denies 2 and 4 (one existing and one new) + let packageDenyList = ["2", "4"] - try await withDependencies { - $0.packageListRepository.fetchPackageList = { @Sendable _ in packageList.asURLs } - $0.packageListRepository.fetchPackageDenyList = { @Sendable _ in packageDenyList.asURLs } - } operation: { - // MUT - _ = try await reconcileMainPackageList(client: app.client, database: app.db) - } + try await withDependencies { + $0.packageListRepository.fetchPackageList = { @Sendable _ in packageList.asURLs } + $0.packageListRepository.fetchPackageDenyList = { @Sendable _ in packageDenyList.asURLs } + } operation: { + // MUT + _ = try await App.reconcileMainPackageList(client: app.client, database: app.db) + } - // validate - let packages = try await Package.query(on: app.db).all() - XCTAssertEqual(packages.map(\.url).sorted(), ["https://example.com/two/two"]) + // validate + let packages = try await Package.query(on: app.db).all() + #expect(packages.map(\.url).sorted() == ["1", "3", "5"]) + } } - func test_reconcileCustomCollections() async throws { - // Test single custom collection reconciliation - // setup - var fullPackageList = [URL("https://github.com/a.git"), URL("https://github.com/b.git"), URL("https://github.com/c.git")] - for url in fullPackageList { try await Package(url: url).save(on: app.db) } + @Test func reconcileMainPackageList_packageDenyList_caseSensitivity() async throws { + try await withApp { app in + // Save the intial set of packages + for url in ["https://example.com/one/one", "https://example.com/two/two"].asURLs { + try await Package(url: url).save(on: app.db) + } - // Initial run - try await withDependencies { - $0.packageListRepository.fetchCustomCollection = { @Sendable _, _ in [URL("https://github.com/b.git")] } - } operation: { - // MUT - try await reconcileCustomCollection(client: app.client, - database: app.db, - fullPackageList: fullPackageList, - .init(key: "list", name: "List", url: "url")) + // New list adds no new packages + let packageList = ["https://example.com/one/one", "https://example.com/two/two"] - // validate - let count = try await CustomCollection.query(on: app.db).count() - XCTAssertEqual(count, 1) - let collection = try await CustomCollection.query(on: app.db).first().unwrap() - try await collection.$packages.load(on: app.db) - XCTAssertEqual(collection.packages.map(\.url), ["https://github.com/b.git"]) - } + // Deny list denies one/one, but with incorrect casing. + let packageDenyList = ["https://example.com/OnE/oNe"] - // Reconcile again with an updated list of packages in the collection - try await withDependencies { - $0.packageListRepository.fetchCustomCollection = { @Sendable _, _ in [URL("https://github.com/c.git")] } - } operation: { - // MUT - try await reconcileCustomCollection(client: app.client, - database: app.db, - fullPackageList: fullPackageList, - .init(key: "list", name: "List", url: "url")) + try await withDependencies { + $0.packageListRepository.fetchPackageList = { @Sendable _ in packageList.asURLs } + $0.packageListRepository.fetchPackageDenyList = { @Sendable _ in packageDenyList.asURLs } + } operation: { + // MUT + _ = try await App.reconcileMainPackageList(client: app.client, database: app.db) + } // validate - let count = try await CustomCollection.query(on: app.db).count() - XCTAssertEqual(count, 1) - let collection = try await CustomCollection.query(on: app.db).first().unwrap() - try await collection.$packages.load(on: app.db) - XCTAssertEqual(collection.packages.map(\.url), ["https://github.com/c.git"]) + let packages = try await Package.query(on: app.db).all() + #expect(packages.map(\.url).sorted() == ["https://example.com/two/two"]) } + } - // Re-run after the single package in the list has been deleted in the full package list - fullPackageList = [URL("https://github.com/a.git"), URL("https://github.com/b.git")] - try await Package.query(on: app.db).filter(by: URL("https://github.com/c.git")).first()?.delete(on: app.db) - try await withDependencies { - $0.packageListRepository.fetchCustomCollection = { @Sendable _, _ in [URL("https://github.com/c.git")] } - } operation: { - // MUT - try await reconcileCustomCollection(client: app.client, - database: app.db, - fullPackageList: fullPackageList, - .init(key: "list", name: "List", url: "url")) + @Test func reconcileCustomCollections() async throws { + // Test single custom collection reconciliation + try await withApp { app in + // setup + var fullPackageList = [URL("https://github.com/a.git"), URL("https://github.com/b.git"), URL("https://github.com/c.git")] + for url in fullPackageList { try await Package(url: url).save(on: app.db) } + + // Initial run + try await withDependencies { + $0.packageListRepository.fetchCustomCollection = { @Sendable _, _ in [URL("https://github.com/b.git")] } + } operation: { + // MUT + try await reconcileCustomCollection(client: app.client, + database: app.db, + fullPackageList: fullPackageList, + .init(key: "list", name: "List", url: "url")) + + // validate + let count = try await CustomCollection.query(on: app.db).count() + #expect(count == 1) + let collection = try await CustomCollection.query(on: app.db).first().unwrap() + try await collection.$packages.load(on: app.db) + #expect(collection.packages.map(\.url) == ["https://github.com/b.git"]) + } - // validate - let count = try await CustomCollection.query(on: app.db).count() - XCTAssertEqual(count, 1) - let collection = try await CustomCollection.query(on: app.db).first().unwrap() - try await collection.$packages.load(on: app.db) - XCTAssertEqual(collection.packages.map(\.url), []) + // Reconcile again with an updated list of packages in the collection + try await withDependencies { + $0.packageListRepository.fetchCustomCollection = { @Sendable _, _ in [URL("https://github.com/c.git")] } + } operation: { + // MUT + try await reconcileCustomCollection(client: app.client, + database: app.db, + fullPackageList: fullPackageList, + .init(key: "list", name: "List", url: "url")) + + // validate + let count = try await CustomCollection.query(on: app.db).count() + #expect(count == 1) + let collection = try await CustomCollection.query(on: app.db).first().unwrap() + try await collection.$packages.load(on: app.db) + #expect(collection.packages.map(\.url) == ["https://github.com/c.git"]) + } + + // Re-run after the single package in the list has been deleted in the full package list + fullPackageList = [URL("https://github.com/a.git"), URL("https://github.com/b.git")] + try await Package.query(on: app.db).filter(by: URL("https://github.com/c.git")).first()?.delete(on: app.db) + try await withDependencies { + $0.packageListRepository.fetchCustomCollection = { @Sendable _, _ in [URL("https://github.com/c.git")] } + } operation: { + // MUT + try await reconcileCustomCollection(client: app.client, + database: app.db, + fullPackageList: fullPackageList, + .init(key: "list", name: "List", url: "url")) + + // validate + let count = try await CustomCollection.query(on: app.db).count() + #expect(count == 1) + let collection = try await CustomCollection.query(on: app.db).first().unwrap() + try await collection.$packages.load(on: app.db) + #expect(collection.packages.map(\.url) == []) + } } } - func test_reconcileCustomCollections_limit() async throws { + @Test func reconcileCustomCollections_limit() async throws { // Test custom collection reconciliation size limit - // setup - let fullPackageList = (1...60).map { URL(string: "https://github.com/\($0).git")! } - for url in fullPackageList { try await Package(url: url).save(on: app.db) } - - try await withDependencies { - $0.packageListRepository.fetchCustomCollection = { @Sendable _, _ in - fullPackageList + try await withApp { app in + // setup + let fullPackageList = (1...60).map { URL(string: "https://github.com/\($0).git")! } + for url in fullPackageList { try await Package(url: url).save(on: app.db) } + + try await withDependencies { + $0.packageListRepository.fetchCustomCollection = { @Sendable _, _ in + fullPackageList + } + } operation: { + // MUT + try await reconcileCustomCollection(client: app.client, + database: app.db, + fullPackageList: fullPackageList, + .init(key: "list", name: "List", url: "url")) + + // validate + let collection = try await CustomCollection.query(on: app.db).first().unwrap() + try await collection.$packages.load(on: app.db) + #expect(collection.packages.count == 50) + #expect(collection.packages.first?.url == "https://github.com/1.git") + #expect(collection.packages.last?.url == "https://github.com/50.git") } - } operation: { - // MUT - try await reconcileCustomCollection(client: app.client, - database: app.db, - fullPackageList: fullPackageList, - .init(key: "list", name: "List", url: "url")) - - // validate - let collection = try await CustomCollection.query(on: app.db).first().unwrap() - try await collection.$packages.load(on: app.db) - XCTAssertEqual(collection.packages.count, 50) - XCTAssertEqual(collection.packages.first?.url, "https://github.com/1.git") - XCTAssertEqual(collection.packages.last?.url, "https://github.com/50.git") } } - func test_reconcile() async throws { + @Test func reconcile() async throws { let fullPackageList = (1...3).map { URL(string: "https://github.com/\($0).git")! } struct TestError: Error { var message: String } @@ -236,20 +249,21 @@ class ReconcilerTests: AppTestCase { [.init(key: "list", name: "List", url: "collectionURL")] } } operation: { - // MUT - _ = try await reconcile(client: app.client, database: app.db) - - // validate - let packages = try await Package.query(on: app.db).all() - XCTAssertEqual(packages.map(\.url).sorted(), - fullPackageList.map(\.absoluteString).sorted()) - let count = try await CustomCollection.query(on: app.db).count() - XCTAssertEqual(count, 1) - let collection = try await CustomCollection.query(on: app.db).first().unwrap() - XCTAssertEqual(collection.name, "List") - XCTAssertEqual(collection.url, "collectionURL") - try await collection.$packages.load(on: app.db) - XCTAssertEqual(collection.packages.map(\.url), ["https://github.com/2.git"]) + try await withApp { app in + // MUT + _ = try await App.reconcile(client: app.client, database: app.db) + + // validate + let packages = try await Package.query(on: app.db).all() + #expect(packages.map(\.url).sorted() == fullPackageList.map(\.absoluteString).sorted()) + let count = try await CustomCollection.query(on: app.db).count() + #expect(count == 1) + let collection = try await CustomCollection.query(on: app.db).first().unwrap() + #expect(collection.name == "List") + #expect(collection.url == "collectionURL") + try await collection.$packages.load(on: app.db) + #expect(collection.packages.map(\.url) == ["https://github.com/2.git"]) + } } } From 056a532870e1b2c5ca19a082944b8369fd5e7dc1 Mon Sep 17 00:00:00 2001 From: "Sven A. Schmidt" Date: Sun, 2 Mar 2025 16:51:06 +0100 Subject: [PATCH 4/7] Convert ReferenceTests --- Tests/AppTests/ReferenceTests.swift | 70 ++++++++++++++--------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/Tests/AppTests/ReferenceTests.swift b/Tests/AppTests/ReferenceTests.swift index 91844a2c0..458c1fae3 100644 --- a/Tests/AppTests/ReferenceTests.swift +++ b/Tests/AppTests/ReferenceTests.swift @@ -12,71 +12,71 @@ // See the License for the specific language governing permissions and // limitations under the License. -import XCTest +import Foundation @testable import App import SemanticVersion +import Testing -class ReferenceTests: XCTestCase { +@Suite struct ReferenceTests { - func test_init() throws { - XCTAssertEqual(Reference("1.2.3"), .tag(1, 2, 3)) - XCTAssertEqual(Reference("1.2.3-b1"), .tag(1, 2, 3, "b1")) - XCTAssertEqual(Reference("main"), .branch("main")) + @Test func Refernce_init() throws { + #expect(Reference("1.2.3") == .tag(1, 2, 3)) + #expect(Reference("1.2.3-b1") == .tag(1, 2, 3, "b1")) + #expect(Reference("main") == .branch("main")) } - func test_Codable() throws { + @Test func Codable() throws { do { // branch let ref = Reference.branch("foo") let json = try JSONEncoder().encode(ref) let decoded = try JSONDecoder().decode(Reference.self, from: json) - XCTAssertEqual(decoded, .branch("foo")) + #expect(decoded == .branch("foo")) } do { // tag let ref = Reference.tag(.init(1, 2, 3)) let json = try JSONEncoder().encode(ref) let decoded = try JSONDecoder().decode(Reference.self, from: json) - XCTAssertEqual(decoded, .tag(.init(1, 2, 3))) + #expect(decoded == .tag(.init(1, 2, 3))) } } - func test_isRelease() throws { - XCTAssertTrue(Reference.tag(.init(1, 0, 0)).isRelease) - XCTAssertFalse(Reference.tag(.init(1, 0, 0, "beta1")).isRelease) - XCTAssertFalse(Reference.branch("main").isRelease) + @Test func isRelease() throws { + #expect(Reference.tag(.init(1, 0, 0)).isRelease) + #expect(!Reference.tag(.init(1, 0, 0, "beta1")).isRelease) + #expect(!Reference.branch("main").isRelease) } - func test_tagName() throws { - XCTAssertEqual(Reference.tag(.init(1, 2, 3)).tagName, "1.2.3") - XCTAssertEqual(Reference.tag(.init(1, 2, 3), "v1.2.3").tagName, "v1.2.3") - XCTAssertEqual(Reference.tag(.init(1, 2, 3, "b1")).tagName, "1.2.3-b1") - XCTAssertEqual(Reference.tag(.init(1, 2, 3, "b1", "test")).tagName, - "1.2.3-b1+test") - XCTAssertEqual(Reference.branch("").tagName, nil) + @Test func tagName() throws { + #expect(Reference.tag(.init(1, 2, 3)).tagName == "1.2.3") + #expect(Reference.tag(.init(1, 2, 3), "v1.2.3").tagName == "v1.2.3") + #expect(Reference.tag(.init(1, 2, 3, "b1")).tagName == "1.2.3-b1") + #expect(Reference.tag(.init(1, 2, 3, "b1", "test")).tagName == "1.2.3-b1+test") + #expect(Reference.branch("").tagName == nil) } - func test_versionKind() throws { - XCTAssertEqual(Reference.tag(.init(1, 2, 3)).versionKind, .release) - XCTAssertEqual(Reference.tag(.init(1, 2, 3, "b1")).versionKind, .preRelease) - XCTAssertEqual(Reference.tag(.init(1, 2, 3, "b1", "test")).versionKind, .preRelease) - XCTAssertEqual(Reference.branch("main").versionKind, .defaultBranch) - XCTAssertEqual(Reference.branch("").versionKind, .defaultBranch) + @Test func versionKind() throws { + #expect(Reference.tag(.init(1, 2, 3)).versionKind == .release) + #expect(Reference.tag(.init(1, 2, 3, "b1")).versionKind == .preRelease) + #expect(Reference.tag(.init(1, 2, 3, "b1", "test")).versionKind == .preRelease) + #expect(Reference.branch("main").versionKind == .defaultBranch) + #expect(Reference.branch("").versionKind == .defaultBranch) } - func test_pathEncoded() throws { - XCTAssertEqual(Reference.branch("foo").pathEncoded, "foo") - XCTAssertEqual(Reference.branch("foo/bar").pathEncoded, "foo-bar") - XCTAssertEqual(Reference.branch("foo-bar").pathEncoded, "foo-bar") - XCTAssertEqual(Reference.tag(.init("1.2.3")!).pathEncoded, "1.2.3") + @Test func pathEncoded() throws { + #expect(Reference.branch("foo").pathEncoded == "foo") + #expect(Reference.branch("foo/bar").pathEncoded == "foo-bar") + #expect(Reference.branch("foo-bar").pathEncoded == "foo-bar") + #expect(Reference.tag(.init("1.2.3")!).pathEncoded == "1.2.3") do { - let s = try XCTUnwrap(SemanticVersion(1, 2, 3, "foo/bar")) - XCTAssertEqual(Reference.tag(s).pathEncoded, "1.2.3-foo-bar") + let s = try #require(SemanticVersion(1, 2, 3, "foo/bar")) + #expect(Reference.tag(s).pathEncoded == "1.2.3-foo-bar") } do { - let s = try XCTUnwrap(SemanticVersion(1, 2, 3, "foo/bar", "bar/baz")) - XCTAssertEqual(Reference.tag(s).pathEncoded, "1.2.3-foo-bar+bar-baz") + let s = try #require(SemanticVersion(1, 2, 3, "foo/bar", "bar/baz")) + #expect(Reference.tag(s).pathEncoded == "1.2.3-foo-bar+bar-baz") } } From 229733163357e0bbb51d550584a601acecdbe25f Mon Sep 17 00:00:00 2001 From: "Sven A. Schmidt" Date: Sun, 2 Mar 2025 16:51:15 +0100 Subject: [PATCH 5/7] Convert RepositoryTests --- Tests/AppTests/RepositoryTests.swift | 423 ++++++++++++++------------- 1 file changed, 221 insertions(+), 202 deletions(-) diff --git a/Tests/AppTests/RepositoryTests.swift b/Tests/AppTests/RepositoryTests.swift index 92e53fefa..1a01d5aeb 100644 --- a/Tests/AppTests/RepositoryTests.swift +++ b/Tests/AppTests/RepositoryTests.swift @@ -12,250 +12,269 @@ // See the License for the specific language governing permissions and // limitations under the License. +import Foundation + @testable import App import SQLKit -import XCTVapor - - -final class RepositoryTests: AppTestCase { - - func test_save() async throws { - let pkg = Package(id: UUID(), url: "1") - try await pkg.save(on: app.db) - let repo = try Repository(id: UUID(), - package: pkg, - authors: PackageAuthors(authors: [ - .init(name: "Foo"), - .init(name: "Bar")], numberOfContributors: 0), - commitCount: 123, - defaultBranch: "branch", - firstCommitDate: Date(timeIntervalSince1970: 0), - forks: 17, - forkedFrom: nil, - isArchived: true, - keywords: ["foo", "bar"], - lastCommitDate: Date(timeIntervalSince1970: 1), - lastIssueClosedAt: Date(timeIntervalSince1970: 2), - lastPullRequestClosedAt: Date(timeIntervalSince1970: 3), - license: .mit, - licenseUrl: "https://github.com/foo/bar/blob/main/LICENSE", - openIssues: 3, - openPullRequests: 4, - readmeHtmlUrl: "https://github.com/foo/bar/blob/main/README.md", - releases: [ - .init(description: "a release", - isDraft: false, - publishedAt: Date(timeIntervalSince1970: 1), - tagName: "1.2.3", - url: "https://example.com/release/1.2.3") - ], - s3Readme: .cached(s3ObjectUrl: "objectUrl", githubEtag: "etag"), - stars: 42, - summary: "desc") - - try await repo.save(on: app.db) - - do { - let r = try await XCTUnwrapAsync(try await Repository.find(repo.id, on: app.db)) - XCTAssertEqual(r.$package.id, pkg.id) - XCTAssertEqual(r.authors, - PackageAuthors(authors: [ .init(name: "Foo"), .init(name: "Bar")], - numberOfContributors: 0)) - XCTAssertEqual(r.commitCount, 123) - XCTAssertEqual(r.defaultBranch, "branch") - XCTAssertEqual(r.firstCommitDate, Date(timeIntervalSince1970: 0)) - XCTAssertEqual(r.forks, 17) - XCTAssertEqual(r.forkedFrom, nil) - XCTAssertEqual(r.isArchived, true) - XCTAssertEqual(r.keywords, ["foo", "bar"]) - XCTAssertEqual(r.lastCommitDate, Date(timeIntervalSince1970: 1)) - XCTAssertEqual(r.lastIssueClosedAt, Date(timeIntervalSince1970: 2)) - XCTAssertEqual(r.lastPullRequestClosedAt, Date(timeIntervalSince1970: 3)) - XCTAssertEqual(r.license, .mit) - XCTAssertEqual(r.licenseUrl, "https://github.com/foo/bar/blob/main/LICENSE") - XCTAssertEqual(r.openIssues, 3) - XCTAssertEqual(r.openPullRequests, 4) - XCTAssertEqual(r.s3Readme, .cached(s3ObjectUrl: "objectUrl", githubEtag: "etag")) - XCTAssertEqual(r.readmeHtmlUrl, "https://github.com/foo/bar/blob/main/README.md") - XCTAssertEqual(r.releases, [ - .init(description: "a release", - isDraft: false, - publishedAt: Date(timeIntervalSince1970: 1), - tagName: "1.2.3", - url: "https://example.com/release/1.2.3") - ]) - XCTAssertEqual(r.stars, 42) - XCTAssertEqual(r.summary, "desc") +import Testing + + +@Suite struct RepositoryTests { + + @Test func save() async throws { + try await withApp { app in + let pkg = Package(id: UUID(), url: "1") + try await pkg.save(on: app.db) + let repo = try Repository(id: UUID(), + package: pkg, + authors: PackageAuthors(authors: [ + .init(name: "Foo"), + .init(name: "Bar")], numberOfContributors: 0), + commitCount: 123, + defaultBranch: "branch", + firstCommitDate: Date(timeIntervalSince1970: 0), + forks: 17, + forkedFrom: nil, + isArchived: true, + keywords: ["foo", "bar"], + lastCommitDate: Date(timeIntervalSince1970: 1), + lastIssueClosedAt: Date(timeIntervalSince1970: 2), + lastPullRequestClosedAt: Date(timeIntervalSince1970: 3), + license: .mit, + licenseUrl: "https://github.com/foo/bar/blob/main/LICENSE", + openIssues: 3, + openPullRequests: 4, + readmeHtmlUrl: "https://github.com/foo/bar/blob/main/README.md", + releases: [ + .init(description: "a release", + isDraft: false, + publishedAt: Date(timeIntervalSince1970: 1), + tagName: "1.2.3", + url: "https://example.com/release/1.2.3") + ], + s3Readme: .cached(s3ObjectUrl: "objectUrl", githubEtag: "etag"), + stars: 42, + summary: "desc") + + try await repo.save(on: app.db) + + do { + let r = try await XCTUnwrapAsync(try await Repository.find(repo.id, on: app.db)) + #expect(r.$package.id == pkg.id) + #expect(r.authors == PackageAuthors(authors: [ .init(name: "Foo"), .init(name: "Bar")], + numberOfContributors: 0)) + #expect(r.commitCount == 123) + #expect(r.defaultBranch == "branch") + #expect(r.firstCommitDate == Date(timeIntervalSince1970: 0)) + #expect(r.forks == 17) + #expect(r.forkedFrom == nil) + #expect(r.isArchived == true) + #expect(r.keywords == ["foo", "bar"]) + #expect(r.lastCommitDate == Date(timeIntervalSince1970: 1)) + #expect(r.lastIssueClosedAt == Date(timeIntervalSince1970: 2)) + #expect(r.lastPullRequestClosedAt == Date(timeIntervalSince1970: 3)) + #expect(r.license == .mit) + #expect(r.licenseUrl == "https://github.com/foo/bar/blob/main/LICENSE") + #expect(r.openIssues == 3) + #expect(r.openPullRequests == 4) + #expect(r.s3Readme == .cached(s3ObjectUrl: "objectUrl", githubEtag: "etag")) + #expect(r.readmeHtmlUrl == "https://github.com/foo/bar/blob/main/README.md") + #expect(r.releases == [ + .init(description: "a release", + isDraft: false, + publishedAt: Date(timeIntervalSince1970: 1), + tagName: "1.2.3", + url: "https://example.com/release/1.2.3") + ]) + #expect(r.stars == 42) + #expect(r.summary == "desc") + } } } - func test_generated_lastActivityAt_lastCommitDate() async throws { - let pkg = Package(url: "p1") - try await pkg.save(on: app.db) + @Test func generated_lastActivityAt_lastCommitDate() async throws { + try await withApp { app in + let pkg = Package(url: "p1") + try await pkg.save(on: app.db) - let oldestDate = Date(timeIntervalSinceReferenceDate: 0) - let moreRecentDate = Date(timeIntervalSinceReferenceDate: 100) + let oldestDate = Date(timeIntervalSinceReferenceDate: 0) + let moreRecentDate = Date(timeIntervalSinceReferenceDate: 100) - let repo = try Repository(package: pkg) - repo.lastCommitDate = moreRecentDate - repo.lastIssueClosedAt = oldestDate - repo.lastPullRequestClosedAt = oldestDate - try await repo.save(on: app.db) + let repo = try Repository(package: pkg) + repo.lastCommitDate = moreRecentDate + repo.lastIssueClosedAt = oldestDate + repo.lastPullRequestClosedAt = oldestDate + try await repo.save(on: app.db) - let fetchedRepo = try await XCTUnwrapAsync(try await Repository.find(repo.id, on: app.db)) - XCTAssertEqual(fetchedRepo.lastActivityAt, moreRecentDate) + let fetchedRepo = try await XCTUnwrapAsync(try await Repository.find(repo.id, on: app.db)) + #expect(fetchedRepo.lastActivityAt == moreRecentDate) + } } - func test_generated_lastActivityAt_lastIssueClosedAt() async throws { - let pkg = Package(url: "p1") - try await pkg.save(on: app.db) + @Test func generated_lastActivityAt_lastIssueClosedAt() async throws { + try await withApp { app in + let pkg = Package(url: "p1") + try await pkg.save(on: app.db) - let oldestDate = Date(timeIntervalSinceReferenceDate: 0) - let moreRecentDate = Date(timeIntervalSinceReferenceDate: 100) + let oldestDate = Date(timeIntervalSinceReferenceDate: 0) + let moreRecentDate = Date(timeIntervalSinceReferenceDate: 100) - let repo = try Repository(package: pkg) - repo.lastCommitDate = oldestDate - repo.lastIssueClosedAt = moreRecentDate - repo.lastPullRequestClosedAt = oldestDate - try await repo.save(on: app.db) + let repo = try Repository(package: pkg) + repo.lastCommitDate = oldestDate + repo.lastIssueClosedAt = moreRecentDate + repo.lastPullRequestClosedAt = oldestDate + try await repo.save(on: app.db) - let fetchedRepo = try await XCTUnwrapAsync(try await Repository.find(repo.id, on: app.db)) - XCTAssertEqual(fetchedRepo.lastActivityAt, moreRecentDate) + let fetchedRepo = try await XCTUnwrapAsync(try await Repository.find(repo.id, on: app.db)) + #expect(fetchedRepo.lastActivityAt == moreRecentDate) + } } - func test_generated_lastActivityAt_lastPullRequestClosedAt() async throws { - let pkg = Package(url: "p1") - try await pkg.save(on: app.db) + @Test func generated_lastActivityAt_lastPullRequestClosedAt() async throws { + try await withApp { app in + let pkg = Package(url: "p1") + try await pkg.save(on: app.db) - let oldestDate = Date(timeIntervalSinceReferenceDate: 0) - let moreRecentDate = Date(timeIntervalSinceReferenceDate: 100) + let oldestDate = Date(timeIntervalSinceReferenceDate: 0) + let moreRecentDate = Date(timeIntervalSinceReferenceDate: 100) - let repo = try Repository(package: pkg) - repo.lastCommitDate = oldestDate - repo.lastIssueClosedAt = oldestDate - repo.lastPullRequestClosedAt = moreRecentDate - try await repo.save(on: app.db) + let repo = try Repository(package: pkg) + repo.lastCommitDate = oldestDate + repo.lastIssueClosedAt = oldestDate + repo.lastPullRequestClosedAt = moreRecentDate + try await repo.save(on: app.db) - let fetchedRepo = try await XCTUnwrapAsync(try await Repository.find(repo.id, on: app.db)) - XCTAssertEqual(fetchedRepo.lastActivityAt, moreRecentDate) + let fetchedRepo = try await XCTUnwrapAsync(try await Repository.find(repo.id, on: app.db)) + #expect(fetchedRepo.lastActivityAt == moreRecentDate) + } } - func test_generated_lastActivityAt_nullValues() async throws { - let pkg = Package(url: "p1") - try await pkg.save(on: app.db) + @Test func generated_lastActivityAt_nullValues() async throws { + try await withApp { app in + let pkg = Package(url: "p1") + try await pkg.save(on: app.db) - let date = Date(timeIntervalSinceReferenceDate: 0) + let date = Date(timeIntervalSinceReferenceDate: 0) - let repo = try Repository(package: pkg) - repo.lastCommitDate = date - repo.lastIssueClosedAt = nil - repo.lastPullRequestClosedAt = nil - try await repo.save(on: app.db) + let repo = try Repository(package: pkg) + repo.lastCommitDate = date + repo.lastIssueClosedAt = nil + repo.lastPullRequestClosedAt = nil + try await repo.save(on: app.db) - let fetchedRepo = try await XCTUnwrapAsync(try await Repository.find(repo.id, on: app.db)) - XCTAssertEqual(fetchedRepo.lastActivityAt, date) + let fetchedRepo = try await XCTUnwrapAsync(try await Repository.find(repo.id, on: app.db)) + #expect(fetchedRepo.lastActivityAt == date) + } } - func test_package_relationship() async throws { - let pkg = Package(url: "p1") - try await pkg.save(on: app.db) - let repo = try Repository(package: pkg) - try await repo.save(on: app.db) - // test some ways to resolve the relationship - XCTAssertEqual(repo.$package.id, pkg.id) - let db = app.db - try await XCTAssertEqualAsync(try await repo.$package.get(on: db).url, "p1") - - // ensure one-to-one is in place - do { + @Test func package_relationship() async throws { + try await withApp { app in + let pkg = Package(url: "p1") + try await pkg.save(on: app.db) let repo = try Repository(package: pkg) + try await repo.save(on: app.db) + // test some ways to resolve the relationship + #expect(repo.$package.id == pkg.id) + let db = app.db + try await XCTAssertEqualAsync(try await repo.$package.get(on: db).url, "p1") + + // ensure one-to-one is in place do { - try await repo.save(on: app.db) - XCTFail("Expected error") - } catch { } - try await XCTAssertEqualAsync(try await Repository.query(on: db).all().count, 1) + let repo = try Repository(package: pkg) + do { + try await repo.save(on: app.db) + Issue.record("Expected error") + } catch { } + try await XCTAssertEqualAsync(try await Repository.query(on: db).all().count, 1) + } } } - func test_delete_cascade() async throws { + @Test func delete_cascade() async throws { // delete package must delete repository - let pkg = Package(id: UUID(), url: "1") - let repo = try Repository(id: UUID(), package: pkg) - try await pkg.save(on: app.db) - try await repo.save(on: app.db) - - let db = app.db - try await XCTAssertEqualAsync(try await Package.query(on: db).count(), 1) - try await XCTAssertEqualAsync(try await Repository.query(on: db).count(), 1) - - // MUT - try await pkg.delete(on: app.db) - - // version and product should be deleted - try await XCTAssertEqualAsync(try await Package.query(on: db).count(), 0) - try await XCTAssertEqualAsync(try await Repository.query(on: db).count(), 0) + try await withApp { app in + let pkg = Package(id: UUID(), url: "1") + let repo = try Repository(id: UUID(), package: pkg) + try await pkg.save(on: app.db) + try await repo.save(on: app.db) + + let db = app.db + try await XCTAssertEqualAsync(try await Package.query(on: db).count(), 1) + try await XCTAssertEqualAsync(try await Repository.query(on: db).count(), 1) + + // MUT + try await pkg.delete(on: app.db) + + // version and product should be deleted + try await XCTAssertEqualAsync(try await Package.query(on: db).count(), 0) + try await XCTAssertEqualAsync(try await Repository.query(on: db).count(), 0) + } } - func test_uniqueOwnerRepository() async throws { + @Test func uniqueOwnerRepository() async throws { // Ensure owner/repository is unique, testing various combinations with // matching/non-matching case - let p1 = try await savePackage(on: app.db, "1") - try await Repository(id: UUID(), package: p1, name: "bar", owner: "foo").save(on: app.db) - let p2 = try await savePackage(on: app.db, "2") - let db = app.db - - do { - // MUT - identical - try await Repository(id: UUID(), package: p2, name: "bar", owner: "foo").save(on: app.db) - XCTFail("Expected error") - } catch { - XCTAssert(String(reflecting: error).contains( - #"duplicate key value violates unique constraint "idx_repositories_owner_name""#), - "was: \(error.localizedDescription)" - ) - try await XCTAssertEqualAsync(try await Repository.query(on: db).all().count, 1) - } + try await withApp { app in + let p1 = try await savePackage(on: app.db, "1") + try await Repository(id: UUID(), package: p1, name: "bar", owner: "foo").save(on: app.db) + let p2 = try await savePackage(on: app.db, "2") + let db = app.db - do { - // MUT - diffrent case repository - try await Repository(id: UUID(), package: p2, name: "Bar", owner: "foo").save(on: app.db) - XCTFail("Expected error") - } catch { - XCTAssert(String(reflecting: error).contains( - #"duplicate key value violates unique constraint "idx_repositories_owner_name""#), - "was: \(error.localizedDescription)" - ) - try await XCTAssertEqualAsync(try await Repository.query(on: db).all().count, 1) - } + do { + // MUT - identical + try await Repository(id: UUID(), package: p2, name: "bar", owner: "foo").save(on: app.db) + Issue.record("Expected error") + } catch { + #expect(String(reflecting: error).contains( + #"duplicate key value violates unique constraint "idx_repositories_owner_name""#), + "was: \(error.localizedDescription)" + ) + try await XCTAssertEqualAsync(try await Repository.query(on: db).all().count, 1) + } + + do { + // MUT - diffrent case repository + try await Repository(id: UUID(), package: p2, name: "Bar", owner: "foo").save(on: app.db) + Issue.record("Expected error") + } catch { + #expect(String(reflecting: error).contains( + #"duplicate key value violates unique constraint "idx_repositories_owner_name""#), + "was: \(error.localizedDescription)" + ) + try await XCTAssertEqualAsync(try await Repository.query(on: db).all().count, 1) + } - do { - // MUT - diffrent case owner - try await Repository(id: UUID(), package: p2, name: "bar", owner: "Foo").save(on: app.db) - XCTFail("Expected error") - } catch { - XCTAssert(String(reflecting: error).contains( - #"duplicate key value violates unique constraint "idx_repositories_owner_name""#), - "was: \(error.localizedDescription)" - ) - try await XCTAssertEqualAsync(try await Repository.query(on: db).all().count, 1) + do { + // MUT - diffrent case owner + try await Repository(id: UUID(), package: p2, name: "bar", owner: "Foo").save(on: app.db) + Issue.record("Expected error") + } catch { + #expect(String(reflecting: error).contains( + #"duplicate key value violates unique constraint "idx_repositories_owner_name""#), + "was: \(error.localizedDescription)" + ) + try await XCTAssertEqualAsync(try await Repository.query(on: db).all().count, 1) + } } } - func test_name_index() async throws { - let db = try XCTUnwrap(app.db as? SQLDatabase) - // Quick way to check index exists - this will throw - // "server: index "idx_repositories_name" does not exist (DropErrorMsgNonExistent)" - // if it doesn't - try await db.raw("DROP INDEX idx_repositories_name").run() - // Recreate index or else the revert in the next tests setUp is going to fail - try await db.raw("CREATE INDEX idx_repositories_name ON repositories USING gin (name gin_trgm_ops)").run() + @Test func name_index() async throws { + try await withApp { app in + let db = try #require(app.db as? SQLDatabase) + // Quick way to check index exists - this will throw + // "server: index "idx_repositories_name" does not exist (DropErrorMsgNonExistent)" + // if it doesn't + try await db.raw("DROP INDEX idx_repositories_name").run() + // Recreate index or else the revert in the next tests setUp is going to fail + try await db.raw("CREATE INDEX idx_repositories_name ON repositories USING gin (name gin_trgm_ops)").run() + } } - - func test_S3Readme_needsUpdate() { - XCTAssertTrue(S3Readme.error("").needsUpdate(upstreamEtag: "etag")) - XCTAssertFalse(S3Readme.cached(s3ObjectUrl: "", githubEtag: "old etag").needsUpdate(upstreamEtag: "old etag")) - XCTAssertTrue(S3Readme.cached(s3ObjectUrl: "", githubEtag: "old etag").needsUpdate(upstreamEtag: "new etag")) + + @Test func S3Readme_needsUpdate() { + #expect(S3Readme.error("").needsUpdate(upstreamEtag: "etag")) + #expect(!S3Readme.cached(s3ObjectUrl: "", githubEtag: "old etag").needsUpdate(upstreamEtag: "old etag")) + #expect(S3Readme.cached(s3ObjectUrl: "", githubEtag: "old etag").needsUpdate(upstreamEtag: "new etag")) } } From d145d3d4e45b1e912d134f8d51c576efd15d4e9c Mon Sep 17 00:00:00 2001 From: "Sven A. Schmidt" Date: Sun, 2 Mar 2025 16:51:55 +0100 Subject: [PATCH 6/7] Convert ResourceReloadIdentifierTests --- Tests/AppTests/ResourceReloadIdentifierTests.swift | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Tests/AppTests/ResourceReloadIdentifierTests.swift b/Tests/AppTests/ResourceReloadIdentifierTests.swift index 0e05f6879..5844b2d07 100644 --- a/Tests/AppTests/ResourceReloadIdentifierTests.swift +++ b/Tests/AppTests/ResourceReloadIdentifierTests.swift @@ -12,20 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -import XCTest - @testable import App import Dependencies +import Testing import Vapor -class ResourceReloadIdentifierTests: AppTestCase { - func test_withAppVersion() throws { +@Suite struct ResourceReloadIdentifierTests { + @Test func withAppVersion() throws { withDependencies { $0.environment.appVersion = { "1.2.3" } } operation: { - XCTAssertEqual(ResourceReloadIdentifier.value, "1.2.3") + #expect(ResourceReloadIdentifier.value == "1.2.3") } } } From 336983123aaa2574e54240e10dd4d1575143680e Mon Sep 17 00:00:00 2001 From: "Sven A. Schmidt" Date: Sun, 2 Mar 2025 16:57:46 +0100 Subject: [PATCH 7/7] Convert RoutesTests --- Tests/AppTests/RoutesTests.swift | 88 ++++++++++++++++++-------------- 1 file changed, 50 insertions(+), 38 deletions(-) diff --git a/Tests/AppTests/RoutesTests.swift b/Tests/AppTests/RoutesTests.swift index b5d5e32e9..cbcd6b944 100644 --- a/Tests/AppTests/RoutesTests.swift +++ b/Tests/AppTests/RoutesTests.swift @@ -12,82 +12,94 @@ // See the License for the specific language governing permissions and // limitations under the License. +import Foundation + @testable import App import Dependencies -import XCTVapor +import Testing -final class RoutesTests: AppTestCase { +@Suite struct RoutesTests { - func test_documentation_images() async throws { + @Test func documentation_images() async throws { try await withDependencies { $0.environment.awsDocsBucket = { "docs-bucket" } $0.httpClient.fetchDocumentation = App.HTTPClient.echoURL() } operation: { - // MUT - try await app.test(.GET, "foo/bar/1.2.3/images/baz.png") { res async in - // validation - XCTAssertEqual(res.status, .ok) - XCTAssertEqual(res.content.contentType?.description, "application/octet-stream") - XCTAssertEqual(res.body.asString(), "/foo/bar/1.2.3/images/baz.png") - } - try await app.test(.GET, "foo/bar/1.2.3/images/BAZ.png") { res async in - // validation - XCTAssertEqual(res.status, .ok) - XCTAssertEqual(res.content.contentType?.description, "application/octet-stream") - XCTAssertEqual(res.body.asString(), "/foo/bar/1.2.3/images/BAZ.png") + try await withApp { app in + // MUT + try await app.test(.GET, "foo/bar/1.2.3/images/baz.png") { res async in + // validation + #expect(res.status == .ok) + #expect(res.content.contentType?.description == "application/octet-stream") + #expect(res.body.asString() == "/foo/bar/1.2.3/images/baz.png") + } + try await app.test(.GET, "foo/bar/1.2.3/images/BAZ.png") { res async in + // validation + #expect(res.status == .ok) + #expect(res.content.contentType?.description == "application/octet-stream") + #expect(res.body.asString() == "/foo/bar/1.2.3/images/BAZ.png") + } } } } - func test_documentation_img() async throws { + @Test func documentation_img() async throws { try await withDependencies { $0.environment.awsDocsBucket = { "docs-bucket" } $0.httpClient.fetchDocumentation = { @Sendable _ in .ok } } operation: { - // MUT - try await app.test(.GET, "foo/bar/1.2.3/img/baz.png") { res async in - // validation - XCTAssertEqual(res.status, .ok) + try await withApp { app in + // MUT + try await app.test(.GET, "foo/bar/1.2.3/img/baz.png") { res async in + // validation + #expect(res.status == .ok) + } } } } - func test_documentation_videos() async throws { + @Test func documentation_videos() async throws { try await withDependencies { $0.environment.awsDocsBucket = { "docs-bucket" } $0.httpClient.fetchDocumentation = { @Sendable _ in .ok } } operation: { - // MUT - try await app.test(.GET, "foo/bar/1.2.3/videos/baz.mov") { res async in - // validation - XCTAssertEqual(res.status, .ok) + try await withApp { app in + // MUT + try await app.test(.GET, "foo/bar/1.2.3/videos/baz.mov") { res async in + // validation + #expect(res.status == .ok) + } } } } - func test_openapi() async throws { - try await app.test(.GET, "openapi/openapi.json") { res async in - XCTAssertEqual(res.status, .ok) - struct Response: Codable, Equatable { - var info: Info - struct Info: Codable, Equatable { - var title: String + @Test func openapi() async throws { + try await withApp { app in + try await app.test(.GET, "openapi/openapi.json") { res async throws in + #expect(res.status == .ok) + struct Response: Codable, Equatable { + var info: Info + struct Info: Codable, Equatable { + var title: String + } } + let decoded = try JSONDecoder().decode(Response.self, from: res.body) + #expect(decoded == Response(info: .init(title: "Swift Package Index API"))) } - XCTAssertEqualJSON(res.body.asString(), Response(info: .init(title: "Swift Package Index API"))) } } - func test_maintenanceMessage() throws { - try withDependencies { + @Test func maintenanceMessage() async throws { + try await withDependencies { $0.environment.dbId = { nil } $0.environment.maintenanceMessage = { "MAINTENANCE_MESSAGE" } } operation: { - - try app.test(.GET, "/") { res in - XCTAssertContains(res.body.string, "MAINTENANCE_MESSAGE") + try await withApp { app in + try await app.test(.GET, "/") { res async in + #expect(res.body.string.contains("MAINTENANCE_MESSAGE")) + } } } }