From ee2ef2593b8d28d0e8f60df51269f8fda6c1b118 Mon Sep 17 00:00:00 2001 From: "Sven A. Schmidt" Date: Fri, 23 Aug 2024 14:19:16 +0200 Subject: [PATCH] Convert PackageCollection related ELFs to a/a --- .../API/API+PackageCollectionController.swift | 6 +- .../PackageCollectionController.swift | 23 +- .../PackageCollection+VersionResult.swift | 6 +- .../App/Core/PackageCollection+generate.swift | 57 +- .../App/Core/PackageCollection+signing.swift | 40 +- Tests/AppTests/PackageCollectionTests.swift | 550 +++++++++--------- 6 files changed, 324 insertions(+), 358 deletions(-) diff --git a/Sources/App/Controllers/API/API+PackageCollectionController.swift b/Sources/App/Controllers/API/API+PackageCollectionController.swift index 567e12795..a52a92147 100644 --- a/Sources/App/Controllers/API/API+PackageCollectionController.swift +++ b/Sources/App/Controllers/API/API+PackageCollectionController.swift @@ -23,14 +23,14 @@ extension API { enum PackageCollectionController { @Sendable - static func generate(req: Request) throws -> EventLoopFuture { + static func generate(req: Request) async throws -> SignedCollection { AppMetrics.apiPackageCollectionGetTotal?.inc() let dto = try req.content.decode(PostPackageCollectionDTO.self) switch dto.selection { case let .author(author): - return SignedCollection.generate( + return try await SignedCollection.generate( db: req.db, filterBy: .author(author), authorName: dto.authorName ?? "Swift Package Index", @@ -44,7 +44,7 @@ extension API { guard packageURLs.count <= 20 else { throw Abort(.badRequest) } - return SignedCollection.generate( + return try await SignedCollection.generate( db: req.db, filterBy: .urls(packageURLs), authorName: dto.authorName ?? "Swift Package Index", diff --git a/Sources/App/Controllers/PackageCollectionController.swift b/Sources/App/Controllers/PackageCollectionController.swift index 9ca2479cc..63f832382 100644 --- a/Sources/App/Controllers/PackageCollectionController.swift +++ b/Sources/App/Controllers/PackageCollectionController.swift @@ -17,22 +17,19 @@ import Vapor enum PackageCollectionController { @Sendable - static func generate(req: Request) throws -> EventLoopFuture { + static func generate(req: Request) async throws -> SignedCollection { AppMetrics.packageCollectionGetTotal?.inc() - guard let owner = req.parameters.get("owner") else { - return req.eventLoop.future(error: Abort(.notFound)) - } + guard let owner = req.parameters.get("owner") else { throw Abort(.notFound) } - return SignedCollection.generate( - db: req.db, - filterBy: .author(owner), - authorName: "\(owner) via the Swift Package Index" - ).flatMapError { - if case PackageCollection.Error.noResults = $0 { - return req.eventLoop.makeFailedFuture(Abort(.notFound)) - } - return req.eventLoop.makeFailedFuture($0) + do { + return try await SignedCollection.generate( + db: req.db, + filterBy: .author(owner), + authorName: "\(owner) via the Swift Package Index" + ) + } catch PackageCollection.Error.noResults { + throw Abort(.notFound) } } } diff --git a/Sources/App/Core/PackageCollection+VersionResult.swift b/Sources/App/Core/PackageCollection+VersionResult.swift index c46f1554a..0f77a1add 100644 --- a/Sources/App/Core/PackageCollection+VersionResult.swift +++ b/Sources/App/Core/PackageCollection+VersionResult.swift @@ -40,7 +40,7 @@ extension PackageCollection.VersionResult { var targets: [App.Target] { version.targets } var version: App.Version { model.version } - static func query(on database: Database, filterBy filter: PackageCollection.Filter) -> EventLoopFuture<[Self]> { + static func query(on database: Database, filterBy filter: PackageCollection.Filter) async throws -> [Self] { let query = M .query( on: database, @@ -59,8 +59,8 @@ extension PackageCollection.VersionResult { query.filter(App.Package.self, \.$url ~~ packageURLs) } - return query.all() - .mapEach(Self.init(model:)) + return try await query.all() + .map(Self.init(model:)) } } diff --git a/Sources/App/Core/PackageCollection+generate.swift b/Sources/App/Core/PackageCollection+generate.swift index 4d2b81869..676cd2708 100644 --- a/Sources/App/Core/PackageCollection+generate.swift +++ b/Sources/App/Core/PackageCollection+generate.swift @@ -44,39 +44,30 @@ extension PackageCollection { collectionName: String? = nil, keywords: [String]? = nil, overview: String? = nil, - revision: Int? = nil) -> EventLoopFuture { - VersionResult.query(on: db, filterBy: filter) - .map { results in - // Multiple versions can reference the same package, therefore - // we need to group them so we don't create duplicate packages. - results.groupedByPackage(sortBy: .url) - } - .map { groups -> ([Package], String, String) in - let packages = groups.compactMap { - Package.init(resultGroup: $0, - keywords: keywords) - } - let authorLabel = authorLabel(repositories: groups.map(\.repository)) - let collectionName = collectionName ?? Self.collectionName(for: filter, authorLabel: authorLabel) - let overview = overview ?? Self.overview(for: filter, authorLabel: authorLabel) - return (packages, collectionName, overview) - } - .flatMap { packages, collectionName, overview in - guard !packages.isEmpty else { - return db.eventLoop.makeFailedFuture(Error.noResults) - } - return db.eventLoop.makeSucceededFuture( - PackageCollection.init( - name: collectionName, - overview: overview, - keywords: keywords, - packages: packages, - formatVersion: .v1_0, - revision: revision, - generatedAt: Current.date(), - generatedBy: authorName.map(Author.init(name:))) - ) - } + revision: Int? = nil) async throws -> PackageCollection { + let results = try await VersionResult.query(on: db, filterBy: filter) + + // Multiple versions can reference the same package, therefore + // we need to group them so we don't create duplicate packages. + let groups = results.groupedByPackage(sortBy: .url) + + let packages = groups.compactMap { Package.init(resultGroup: $0, keywords: keywords) } + let authorLabel = authorLabel(repositories: groups.map(\.repository)) + let collectionName = collectionName ?? Self.collectionName(for: filter, authorLabel: authorLabel) + let overview = overview ?? Self.overview(for: filter, authorLabel: authorLabel) + + guard !packages.isEmpty else { throw Error.noResults } + + return PackageCollection.init( + name: collectionName, + overview: overview, + keywords: keywords, + packages: packages, + formatVersion: .v1_0, + revision: revision, + generatedAt: Current.date(), + generatedBy: authorName.map(Author.init(name:)) + ) } static func authorLabel(repositories: [Repository]) -> String? { diff --git a/Sources/App/Core/PackageCollection+signing.swift b/Sources/App/Core/PackageCollection+signing.swift index dbf29988f..d2d476add 100644 --- a/Sources/App/Core/PackageCollection+signing.swift +++ b/Sources/App/Core/PackageCollection+signing.swift @@ -30,36 +30,30 @@ extension SignedCollection { collectionName: String? = nil, keywords: [String]? = nil, overview: String? = nil, - revision: Int? = nil) -> EventLoopFuture { - PackageCollection.generate(db: db, - filterBy: filter, - authorName: authorName, - collectionName: collectionName, - keywords: keywords, - overview: overview, - revision: revision) - .flatMap { - sign(eventLoop: db.eventLoop, collection: $0) - } + revision: Int? = nil) async throws -> SignedCollection { + let collection = try await PackageCollection.generate(db: db, + filterBy: filter, + authorName: authorName, + collectionName: collectionName, + keywords: keywords, + overview: overview, + revision: revision) + return try await sign(collection: collection) } - static func sign(eventLoop: EventLoop, collection: PackageCollection) -> EventLoopFuture { + static func sign(collection: PackageCollection) async throws -> SignedCollection { guard let privateKey = Current.collectionSigningPrivateKey() else { - return eventLoop.makeFailedFuture(AppError.envVariableNotSet("COLLECTION_SIGNING_PRIVATE_KEY")) + throw AppError.envVariableNotSet("COLLECTION_SIGNING_PRIVATE_KEY") } - return eventLoop.makeFutureWithTask { - try await signer.sign(collection: collection, - certChainPaths: Current.collectionSigningCertificateChain(), - privateKeyPEM: privateKey) - } + return try await signer.sign(collection: collection, + certChainPaths: Current.collectionSigningCertificateChain(), + privateKeyPEM: privateKey) } - static func validate(eventLoop: EventLoop, signedCollection: SignedCollection) -> EventLoopFuture { - eventLoop.makeFutureWithTask { - try await signer.validate(signedCollection: signedCollection) - return true - } + static func validate(signedCollection: SignedCollection) async throws -> Bool { + try await signer.validate(signedCollection: signedCollection) + return true } static let certsDir = URL(fileURLWithPath: Current.fileManager.workingDirectory()) diff --git a/Tests/AppTests/PackageCollectionTests.swift b/Tests/AppTests/PackageCollectionTests.swift index 8ec943cd3..0e58e2f5b 100644 --- a/Tests/AppTests/PackageCollectionTests.swift +++ b/Tests/AppTests/PackageCollectionTests.swift @@ -33,10 +33,10 @@ class PackageCollectionTests: AppTestCase { typealias VersionResult = PackageCollection.VersionResult typealias VersionResultGroup = PackageCollection.VersionResultGroup - func test_query_filter_urls() throws { + func test_query_filter_urls() async throws { // Tests PackageResult.query with the url filter option // setup - try (0..<3).forEach { index in + for index in (0..<3) { let pkg = try savePackage(on: app.db, "url-\(index)".url) do { let v = try Version(package: pkg, @@ -44,28 +44,27 @@ class PackageCollectionTests: AppTestCase { packageName: "package \(index)", reference: .tag(1, 2, 3), toolsVersion: "5.4") - try v.save(on: app.db).wait() - try Build(version: v, - buildCommand: "build \(index)", - platform: .iOS, - status: .ok, - swiftVersion: .v1) - .save(on: app.db).wait() - try Product(version: v, - type: .library(.automatic), - name: "product \(index)") - .save(on: app.db).wait() - try Target(version: v, name: "target \(index)") - .save(on: app.db).wait() + try await v.save(on: app.db) + try await Build(version: v, + buildCommand: "build \(index)", + platform: .iOS, + status: .ok, + swiftVersion: .v1) + .save(on: app.db) + try await Product(version: v, + type: .library(.automatic), + name: "product \(index)") + .save(on: app.db) + try await Target(version: v, name: "target \(index)") + .save(on: app.db) } - try Repository(package: pkg, - name: "repo \(index)") - .save(on: app.db).wait() + try await Repository(package: pkg, + name: "repo \(index)") + .save(on: app.db) } // MUT - let res = try VersionResult.query(on: app.db, - filterBy: .urls(["url-1"])).wait() + let res = try await VersionResult.query(on: app.db, filterBy: .urls(["url-1"])) // validate selection and all relations being loaded XCTAssertEqual(res.map(\.version.packageName), ["package 1"]) @@ -81,10 +80,10 @@ class PackageCollectionTests: AppTestCase { XCTAssertEqual(res.flatMap { $0.version.products.map(\.name) }, ["product 1"]) } - func test_query_filter_urls_no_results() throws { + func test_query_filter_urls_no_results() async throws { // Tests PackageResult.query without results has safe relationship accessors // setup - try (0..<3).forEach { index in + for index in (0..<3) { let pkg = try savePackage(on: app.db, "url-\(index)".url) do { let v = try Version(package: pkg, @@ -92,30 +91,30 @@ class PackageCollectionTests: AppTestCase { packageName: "package \(index)", reference: .tag(1, 2, 3), toolsVersion: "5.4") - try v.save(on: app.db).wait() - try Build(version: v, - buildCommand: "build \(index)", - platform: .iOS, - status: .ok, - swiftVersion: .v1) - .save(on: app.db).wait() - try Product(version: v, - type: .library(.automatic), - name: "product \(index)") - .save(on: app.db).wait() - try Target(version: v, name: "target \(index)") - .save(on: app.db).wait() + try await v.save(on: app.db) + try await Build(version: v, + buildCommand: "build \(index)", + platform: .iOS, + status: .ok, + swiftVersion: .v1) + .save(on: app.db) + try await Product(version: v, + type: .library(.automatic), + name: "product \(index)") + .save(on: app.db) + try await Target(version: v, name: "target \(index)") + .save(on: app.db) } - try Repository(package: pkg, - name: "repo \(index)") - .save(on: app.db).wait() + try await Repository(package: pkg, + name: "repo \(index)") + .save(on: app.db) } // MUT - let res = try VersionResult.query( + let res = try await VersionResult.query( on: app.db, filterBy: .urls(["non-existant"]) - ).wait() + ) // validate safe access XCTAssertEqual(res.map(\.version.packageName), []) @@ -126,12 +125,12 @@ class PackageCollectionTests: AppTestCase { XCTAssertEqual(res.map(\.repository.name), []) } - func test_query_author() throws { + func test_query_author() async throws { // Tests PackageResult.query with the author filter option // setup // first package let owners = ["foo", "foo", "someone else"] - try (0..<3).forEach { index in + for index in (0..<3) { let pkg = try savePackage(on: app.db, "url-\(index)".url) do { let v = try Version(package: pkg, @@ -139,41 +138,39 @@ class PackageCollectionTests: AppTestCase { packageName: "package \(index)", reference: .tag(1, 2, 3), toolsVersion: "5.4") - try v.save(on: app.db).wait() - try Build(version: v, - buildCommand: "build \(index)", - platform: .iOS, - status: .ok, - swiftVersion: .v1) - .save(on: app.db).wait() - try Product(version: v, - type: .library(.automatic), - name: "product \(index)") - .save(on: app.db).wait() - try Target(version: v, name: "target \(index)") - .save(on: app.db).wait() + try await v.save(on: app.db) + try await Build(version: v, + buildCommand: "build \(index)", + platform: .iOS, + status: .ok, + swiftVersion: .v1) + .save(on: app.db) + try await Product(version: v, + type: .library(.automatic), + name: "product \(index)") + .save(on: app.db) + try await Target(version: v, name: "target \(index)") + .save(on: app.db) } - try Repository(package: pkg, - name: "repo \(index)", - owner: owners[index]) - .save(on: app.db).wait() + try await Repository(package: pkg, + name: "repo \(index)", + owner: owners[index]) + .save(on: app.db) } // MUT - let res = try VersionResult.query(on: self.app.db, - filterBy: .author("foo")) - .wait() + let res = try await VersionResult.query(on: self.app.db, filterBy: .author("foo")) // validate selection (relationship loading is tested in test_query_filter_urls) XCTAssertEqual(res.map(\.version.packageName), ["package 0", "package 1"]) } - func test_Version_init() throws { + func test_Version_init() async throws { // Tests PackageCollection.Version initialisation from App.Version // setup let p = Package(url: "1") - try p.save(on: app.db).wait() + try await p.save(on: app.db) do { let v = try Version(package: p, latest: .release, @@ -183,44 +180,38 @@ class PackageCollectionTests: AppTestCase { releaseNotes: "Bar", supportedPlatforms: [.ios("14.0")], toolsVersion: "5.3") - try v.save(on: app.db).wait() - try Repository(package: p).save(on: app.db).wait() + try await v.save(on: app.db) + try await Repository(package: p).save(on: app.db) do { - try Product(version: v, + try await Product(version: v, type: .library(.automatic), name: "P1", - targets: ["T1"]).save(on: app.db).wait() - try Product(version: v, + targets: ["T1"]).save(on: app.db) + try await Product(version: v, type: .library(.automatic), name: "P2", - targets: ["T2"]).save(on: app.db).wait() + targets: ["T2"]).save(on: app.db) } do { - try Target(version: v, name: "T1").save(on: app.db).wait() - try Target(version: v, name: "T-2").save(on: app.db).wait() + try await Target(version: v, name: "T1").save(on: app.db) + try await Target(version: v, name: "T-2").save(on: app.db) } do { - try Build(version: v, + try await Build(version: v, platform: .iOS, status: .ok, - swiftVersion: .v1).save(on: app.db).wait() - try Build(version: v, + swiftVersion: .v1).save(on: app.db) + try await Build(version: v, platform: .macosXcodebuild, status: .ok, - swiftVersion: .v2).save(on: app.db).wait() + swiftVersion: .v2).save(on: app.db) } } - let v = try XCTUnwrap(VersionResult.query(on: app.db, - filterBy: .urls(["1"])) - .wait() - .first?.version) + let v = try await XCTUnwrapAsync(try await VersionResult.query(on: app.db,filterBy: .urls(["1"])).first?.version) // MUT let res = try XCTUnwrap( - PackageCollection.Package.Version( - version: v, - license: .init(name: "MIT", url: "https://foo/mit") - ) + PackageCollection.Package.Version(version: v, license: .init(name: "MIT", url: "https://foo/mit")) ) // validate the version @@ -250,31 +241,30 @@ class PackageCollectionTests: AppTestCase { XCTAssertEqual(manifest.minimumPlatformVersions, [.init(name: "ios", version: "14.0")]) } - func test_Package_init() throws { + func test_Package_init() async throws { // Tests PackageCollection.Package initialisation from App.Package // setup do { let p = Package(url: "1") - try p.save(on: app.db).wait() - try Repository(package: p, - license: .mit, - licenseUrl: "https://foo/mit", - readmeHtmlUrl: "readmeUrl", - summary: "summary") - .save(on: app.db).wait() + try await p.save(on: app.db) + try await Repository(package: p, + license: .mit, + licenseUrl: "https://foo/mit", + readmeHtmlUrl: "readmeUrl", + summary: "summary") + .save(on: app.db) let v = try Version(package: p, latest: .release, packageName: "Foo", reference: .tag(1, 2, 3), toolsVersion: "5.3") - try v.save(on: app.db).wait() - try Product(version: v, - type: .library(.automatic), - name: "product").save(on: app.db).wait() + try await v.save(on: app.db) + try await Product(version: v, + type: .library(.automatic), + name: "product").save(on: app.db) } - let result = try XCTUnwrap( - VersionResult.query(on: app.db, filterBy: .urls(["1"])) - .wait().first + let result = try await XCTUnwrapAsync( + try await VersionResult.query(on: app.db, filterBy: .urls(["1"])).first ) let group = VersionResultGroup(package: result.package, repository: result.repository, @@ -296,36 +286,35 @@ class PackageCollectionTests: AppTestCase { XCTAssertEqual(res.versions.count, 1) } - func test_groupedByPackage() throws { + func test_groupedByPackage() async throws { // setup // 2 packages by the same author (which we select) with two versions // each. do { let p = Package(url: "2") - try p.save(on: app.db).wait() - try Repository( + try await p.save(on: app.db) + try await Repository( package: p, owner: "a" - ).save(on: app.db).wait() - try Version(package: p, latest: .release, packageName: "2a") - .save(on: app.db).wait() - try Version(package: p, latest: .release, packageName: "2b") - .save(on: app.db).wait() + ).save(on: app.db) + try await Version(package: p, latest: .release, packageName: "2a") + .save(on: app.db) + try await Version(package: p, latest: .release, packageName: "2b") + .save(on: app.db) } do { let p = Package(url: "1") - try p.save(on: app.db).wait() - try Repository( + try await p.save(on: app.db) + try await Repository( package: p, owner: "a" - ).save(on: app.db).wait() - try Version(package: p, latest: .release, packageName: "1a") - .save(on: app.db).wait() - try Version(package: p, latest: .release, packageName: "1b") - .save(on: app.db).wait() + ).save(on: app.db) + try await Version(package: p, latest: .release, packageName: "1a") + .save(on: app.db) + try await Version(package: p, latest: .release, packageName: "1b") + .save(on: app.db) } - let results = try VersionResult.query(on: app.db, - filterBy: .author("a")).wait() + let results = try await VersionResult.query(on: app.db, filterBy: .author("a")) // MUT let res = results.groupedByPackage(sortBy: .url) @@ -354,7 +343,7 @@ class PackageCollectionTests: AppTestCase { XCTAssertTrue(res.isEmpty) } - func test_generate_from_urls() throws { + func test_generate_from_urls() async throws { // setup Current.date = { Date(timeIntervalSince1970: 1610112345) } let pkg = try savePackage(on: app.db, "1") @@ -364,43 +353,45 @@ class PackageCollectionTests: AppTestCase { packageName: "package", reference: .tag(1, 2, 3), toolsVersion: "5.4") - try v.save(on: app.db).wait() - try Product(version: v, type: .library(.automatic), name: "product") - .save(on: app.db).wait() + try await v.save(on: app.db) + try await Product(version: v, type: .library(.automatic), name: "product") + .save(on: app.db) } - try Repository(package: pkg, - license: .mit, - licenseUrl: "https://foo/mit", - summary: "summary").create(on: app.db).wait() + try await Repository(package: pkg, + license: .mit, + licenseUrl: "https://foo/mit", + summary: "summary").create(on: app.db) // MUT - let res = try PackageCollection.generate(db: self.app.db, - filterBy: .urls(["1"]), - authorName: "Foo", - collectionName: "Foo", - keywords: ["key", "word"], - overview: "overview") - .wait() + let res = try await PackageCollection.generate(db: self.app.db, + filterBy: .urls(["1"]), + authorName: "Foo", + collectionName: "Foo", + keywords: ["key", "word"], + overview: "overview") // validate assertSnapshot(of: res, as: .json(encoder)) } - func test_generate_from_urls_noResults() throws { + func test_generate_from_urls_noResults() async throws { // MUT - XCTAssertThrowsError( - try PackageCollection.generate(db: self.app.db, - filterBy: .urls(["1"]), - authorName: "Foo", - collectionName: "Foo", - keywords: ["key", "word"], - overview: "overview").wait() - ) { - XCTAssertEqual($0 as? PackageCollection.Error, .noResults) + do { + _ = try await PackageCollection.generate(db: self.app.db, + filterBy: .urls(["1"]), + authorName: "Foo", + collectionName: "Foo", + keywords: ["key", "word"], + overview: "overview") + XCTFail("Expected error") + } catch let error as PackageCollection.Error { + XCTAssertEqual(error, .noResults) + } catch { + XCTFail("Unexpected error: \(error)") } } - func test_generate_for_owner() throws { + func test_generate_for_owner() async throws { // setup Current.date = { Date(timeIntervalSince1970: 1610112345) } // first package @@ -411,9 +402,9 @@ class PackageCollectionTests: AppTestCase { packageName: "P1-main", reference: .branch("main"), toolsVersion: "5.0") - try v.save(on: app.db).wait() - try Product(version: v, type: .library(.automatic), name: "P1Lib") - .save(on: app.db).wait() + try await v.save(on: app.db) + try await Product(version: v, type: .library(.automatic), name: "P1Lib") + .save(on: app.db) } do { let v = try Version(id: UUID(), @@ -422,14 +413,14 @@ class PackageCollectionTests: AppTestCase { packageName: "P1-tag", reference: .tag(2, 0, 0), toolsVersion: "5.2") - try v.save(on: app.db).wait() - try Product(version: v, type: .library(.automatic), name: "P1Lib", targets: ["t1"]) - .save(on: app.db).wait() - try Build(version: v, - platform: .iOS, - status: .ok, - swiftVersion: .init(5, 6, 0)).save(on: app.db).wait() - try Target(version: v, name: "t1").save(on: app.db).wait() + try await v.save(on: app.db) + try await Product(version: v, type: .library(.automatic), name: "P1Lib", targets: ["t1"]) + .save(on: app.db) + try await Build(version: v, + platform: .iOS, + status: .ok, + swiftVersion: .init(5, 6, 0)).save(on: app.db) + try await Target(version: v, name: "t1").save(on: app.db) } // second package let p2 = try savePackage(on: app.db, "https://github.com/foo/2") @@ -439,9 +430,9 @@ class PackageCollectionTests: AppTestCase { packageName: "P2-main", reference: .branch("main"), toolsVersion: "5.3") - try v.save(on: app.db).wait() - try Product(version: v, type: .library(.automatic), name: "P1Lib") - .save(on: app.db).wait() + try await v.save(on: app.db) + try await Product(version: v, type: .library(.automatic), name: "P1Lib") + .save(on: app.db) } do { let v = try Version(id: UUID(), @@ -450,61 +441,63 @@ class PackageCollectionTests: AppTestCase { packageName: "P2-tag", reference: .tag(1, 2, 3), toolsVersion: "5.3") - try v.save(on: app.db).wait() - try Product(version: v, type: .library(.automatic), name: "P1Lib", targets: ["t2"]) - .save(on: app.db).wait() - try Target(version: v, name: "t2").save(on: app.db).wait() + try await v.save(on: app.db) + try await Product(version: v, type: .library(.automatic), name: "P1Lib", targets: ["t2"]) + .save(on: app.db) + try await Target(version: v, name: "t2").save(on: app.db) } // unrelated package _ = try savePackage(on: app.db, "https://github.com/bar/1") - try Repository(package: p1, - defaultBranch: "main", - license: .mit, - licenseUrl: "https://foo/mit", - owner: "foo", - summary: "summary 1").create(on: app.db).wait() - try Repository(package: p2, - defaultBranch: "main", - license: .mit, - licenseUrl: "https://foo/mit", - owner: "foo", - summary: "summary 2").create(on: app.db).wait() + try await Repository(package: p1, + defaultBranch: "main", + license: .mit, + licenseUrl: "https://foo/mit", + owner: "foo", + summary: "summary 1").create(on: app.db) + try await Repository(package: p2, + defaultBranch: "main", + license: .mit, + licenseUrl: "https://foo/mit", + owner: "foo", + summary: "summary 2").create(on: app.db) // MUT - let res = try PackageCollection.generate(db: self.app.db, - filterBy: .author("foo"), - authorName: "Foo", - keywords: ["key", "word"]) - .wait() + let res = try await PackageCollection.generate(db: self.app.db, + filterBy: .author("foo"), + authorName: "Foo", + keywords: ["key", "word"]) // validate assertSnapshot(of: res, as: .json(encoder)) } - func test_generate_for_owner_noResults() throws { + func test_generate_for_owner_noResults() async throws { // Ensure we return noResults when no packages are found // MUT - XCTAssertThrowsError( - try PackageCollection.generate(db: self.app.db, - filterBy: .author("foo"), - authorName: "Foo", - keywords: ["key", "word"]).wait() - ) { - XCTAssertEqual($0 as? PackageCollection.Error, .noResults) + do { + _ = try await PackageCollection.generate(db: self.app.db, + filterBy: .author("foo"), + authorName: "Foo", + keywords: ["key", "word"]) + XCTFail("Expected error") + } catch let error as PackageCollection.Error { + XCTAssertEqual(error, .noResults) + } catch { + XCTFail("Unexpected error: \(error)") } } - func test_includes_significant_versions_only() throws { + func test_includes_significant_versions_only() async throws { // Ensure we only export significant versions // https://github.com/SwiftPackageIndex/SwiftPackageIndex-Server/issues/1147 // setup let p = try savePackage(on: app.db, "https://github.com/foo/1") - try Repository(package: p, - defaultBranch: "main", - license: .mit, - licenseUrl: "https://foo/mit", - owner: "foo", - summary: "summary").create(on: app.db).wait() + try await Repository(package: p, + defaultBranch: "main", + license: .mit, + licenseUrl: "https://foo/mit", + owner: "foo", + summary: "summary").create(on: app.db) do { // default branch revision let v = try Version(id: UUID(), package: p, @@ -512,10 +505,10 @@ class PackageCollectionTests: AppTestCase { packageName: "P1-main", reference: .branch("main"), toolsVersion: "5.0") - try v.save(on: app.db).wait() - try Product(version: v, type: .library(.automatic), name: "P1Lib") - .save(on: app.db).wait() - try Target(version: v, name: "t1").save(on: app.db).wait() + try await v.save(on: app.db) + try await Product(version: v, type: .library(.automatic), name: "P1Lib") + .save(on: app.db) + try await Target(version: v, name: "t1").save(on: app.db) } do { // latest release let v = try Version(id: UUID(), @@ -524,10 +517,10 @@ class PackageCollectionTests: AppTestCase { packageName: "P1-main", reference: .tag(1, 2, 3), toolsVersion: "5.0") - try v.save(on: app.db).wait() - try Product(version: v, type: .library(.automatic), name: "P1Lib") - .save(on: app.db).wait() - try Target(version: v, name: "t1").save(on: app.db).wait() + try await v.save(on: app.db) + try await Product(version: v, type: .library(.automatic), name: "P1Lib") + .save(on: app.db) + try await Target(version: v, name: "t1").save(on: app.db) } do { // older release let v = try Version(id: UUID(), @@ -536,10 +529,10 @@ class PackageCollectionTests: AppTestCase { packageName: "P1-main", reference: .tag(1, 0, 0), toolsVersion: "5.0") - try v.save(on: app.db).wait() - try Product(version: v, type: .library(.automatic), name: "P1Lib") - .save(on: app.db).wait() - try Target(version: v, name: "t1").save(on: app.db).wait() + try await v.save(on: app.db) + try await Product(version: v, type: .library(.automatic), name: "P1Lib") + .save(on: app.db) + try await Target(version: v, name: "t1").save(on: app.db) } do { // latest beta release let v = try Version(id: UUID(), @@ -548,10 +541,10 @@ class PackageCollectionTests: AppTestCase { packageName: "P1-main", reference: .tag(2, 0, 0, "b1"), toolsVersion: "5.0") - try v.save(on: app.db).wait() - try Product(version: v, type: .library(.automatic), name: "P1Lib") - .save(on: app.db).wait() - try Target(version: v, name: "t1").save(on: app.db).wait() + try await v.save(on: app.db) + try await Product(version: v, type: .library(.automatic), name: "P1Lib") + .save(on: app.db) + try await Target(version: v, name: "t1").save(on: app.db) } do { // older beta release let v = try Version(id: UUID(), @@ -560,20 +553,19 @@ class PackageCollectionTests: AppTestCase { packageName: "P1-main", reference: .tag(1, 5, 0, "b1"), toolsVersion: "5.0") - try v.save(on: app.db).wait() - try Product(version: v, type: .library(.automatic), name: "P1Lib") - .save(on: app.db).wait() - try Target(version: v, name: "t1").save(on: app.db).wait() + try await v.save(on: app.db) + try await Product(version: v, type: .library(.automatic), name: "P1Lib") + .save(on: app.db) + try await Target(version: v, name: "t1").save(on: app.db) } // MUT - let res = try PackageCollection.generate(db: self.app.db, - filterBy: .author("foo"), - authorName: "Foo", - collectionName: "Foo", - keywords: ["key", "word"], - overview: "overview") - .wait() + let res = try await PackageCollection.generate(db: self.app.db, + filterBy: .author("foo"), + authorName: "Foo", + collectionName: "Foo", + keywords: ["key", "word"], + overview: "overview") // validate XCTAssertEqual(res.packages.count, 1) @@ -581,32 +573,32 @@ class PackageCollectionTests: AppTestCase { ["2.0.0-b1", "1.2.3"]) } - func test_require_products() throws { + func test_require_products() async throws { // Ensure we don't include versions without products (by ensuring // init? returns nil, which will be compact mapped away) let p = Package(url: "1".asGithubUrl.url) - try p.save(on: app.db).wait() + try await p.save(on: app.db) let v = try Version(package: p, packageName: "pkg", reference: .tag(1,2,3), toolsVersion: "5.3") - try v.save(on: app.db).wait() - try v.$builds.load(on: app.db).wait() - try v.$products.load(on: app.db).wait() - try v.$targets.load(on: app.db).wait() + try await v.save(on: app.db) + try await v.$builds.load(on: app.db) + try await v.$products.load(on: app.db) + try await v.$targets.load(on: app.db) XCTAssertNil(PackageCollection.Package.Version(version: v, license: nil)) } - func test_require_versions() throws { + func test_require_versions() async throws { // Ensure we don't include packages without versions (by ensuring // init? returns nil, which will be compact mapped away) do { // no versions at all // setup let pkg = Package(url: "1") - try pkg.save(on: app.db).wait() + try await pkg.save(on: app.db) let repo = try Repository(package: pkg) - try repo.save(on: app.db).wait() + try await repo.save(on: app.db) let group = VersionResultGroup(package: pkg, repository: repo, versions: []) @@ -620,13 +612,12 @@ class PackageCollectionTests: AppTestCase { // setup do { let p = Package(url: "2") - try p.save(on: app.db).wait() - try Version(package: p, latest: .release).save(on: app.db).wait() - try Repository(package: p).save(on: app.db).wait() + try await p.save(on: app.db) + try await Version(package: p, latest: .release).save(on: app.db) + try await Repository(package: p).save(on: app.db) } - let res = try XCTUnwrap( - VersionResult.query(on: app.db, filterBy: .urls(["2"])) - .wait().first + let res = try await XCTUnwrapAsync( + try await VersionResult.query(on: app.db, filterBy: .urls(["2"])).first ) let group = VersionResultGroup(package: res.package, repository: res.repository, @@ -638,7 +629,7 @@ class PackageCollectionTests: AppTestCase { } } - func test_case_insensitive_owner_matching() throws { + func test_case_insensitive_owner_matching() async throws { // setup let pkg = try savePackage(on: app.db, "https://github.com/foo/1") do { @@ -648,30 +639,29 @@ class PackageCollectionTests: AppTestCase { packageName: "P1-tag", reference: .tag(2, 0, 0), toolsVersion: "5.2") - try v.save(on: app.db).wait() - try Product(version: v, type: .library(.automatic), name: "P1Lib", targets: ["t1"]) - .save(on: app.db).wait() + try await v.save(on: app.db) + try await Product(version: v, type: .library(.automatic), name: "P1Lib", targets: ["t1"]) + .save(on: app.db) } // Owner "Foo" - try Repository(package: pkg, - defaultBranch: "main", - license: .mit, - licenseUrl: "https://foo/mit", - owner: "Foo", - summary: "summary 1").create(on: app.db).wait() + try await Repository(package: pkg, + defaultBranch: "main", + license: .mit, + licenseUrl: "https://foo/mit", + owner: "Foo", + summary: "summary 1").create(on: app.db) // MUT - let res = try PackageCollection.generate(db: self.app.db, - // looking for owner "foo" - filterBy: .author("foo"), - collectionName: "collection") - .wait() + let res = try await PackageCollection.generate(db: self.app.db, + // looking for owner "foo" + filterBy: .author("foo"), + collectionName: "collection") // validate XCTAssertEqual(res.packages.count, 1) } - func test_generate_ownerName() throws { + func test_generate_ownerName() async throws { // Ensure ownerName is used in collectionName and overview // setup // first package @@ -683,30 +673,29 @@ class PackageCollectionTests: AppTestCase { packageName: "P1-tag", reference: .tag(2, 0, 0), toolsVersion: "5.2") - try v.save(on: app.db).wait() - try Product(version: v, type: .library(.automatic), name: "P1Lib", targets: ["t1"]) - .save(on: app.db).wait() - try Build(version: v, - platform: .iOS, - status: .ok, - swiftVersion: .v2).save(on: app.db).wait() - try Target(version: v, name: "t1").save(on: app.db).wait() + try await v.save(on: app.db) + try await Product(version: v, type: .library(.automatic), name: "P1Lib", targets: ["t1"]) + .save(on: app.db) + try await Build(version: v, + platform: .iOS, + status: .ok, + swiftVersion: .v2).save(on: app.db) + try await Target(version: v, name: "t1").save(on: app.db) } // unrelated package - try Repository(package: p1, - defaultBranch: "main", - license: .mit, - licenseUrl: "https://foo/mit", - owner: "foo", - ownerName: "Foo Org", - summary: "summary 1").create(on: app.db).wait() + try await Repository(package: p1, + defaultBranch: "main", + license: .mit, + licenseUrl: "https://foo/mit", + owner: "foo", + ownerName: "Foo Org", + summary: "summary 1").create(on: app.db) // MUT - let res = try PackageCollection.generate(db: self.app.db, - filterBy: .author("foo"), - authorName: "Foo", - keywords: ["key", "word"]) - .wait() + let res = try await PackageCollection.generate(db: self.app.db, + filterBy: .author("foo"), + authorName: "Foo", + keywords: ["key", "word"]) // validate XCTAssertEqual(res.name, "Packages by Foo Org") @@ -741,10 +730,10 @@ class PackageCollectionTests: AppTestCase { [SwiftVersion.v1, .v2, .v3].map { $0.description(droppingZeroes: .patch) }.sorted()) } - func test_authorLabel() throws { + func test_authorLabel() async throws { // setup let p = Package(url: "1") - try p.save(on: app.db).wait() + try await p.save(on: app.db) let repositories = try (0..<3).map { try Repository(package: p, owner: "owner-\($0)") } @@ -768,28 +757,25 @@ class PackageCollectionTests: AppTestCase { ) } - func test_sign_collection() throws { + func test_sign_collection() async throws { try XCTSkipIf(!isRunningInCI && Current.collectionSigningPrivateKey() == nil, "Skip test for local user due to unset COLLECTION_SIGNING_PRIVATE_KEY env variable") // setup let collection: PackageCollection = .mock // MUT - let signedCollection = try SignedCollection.sign(eventLoop: app.eventLoopGroup.next(), - collection: collection).wait() + let signedCollection = try await SignedCollection.sign(collection: collection) // validate signed collection content XCTAssertFalse(signedCollection.signature.signature.isEmpty) assertSnapshot(of: signedCollection, as: .json(encoder)) // validate signature - let validated = try SignedCollection.validate(eventLoop: app.eventLoopGroup.next(), - signedCollection: signedCollection) - .wait() + let validated = try await SignedCollection.validate(signedCollection: signedCollection) XCTAssertTrue(validated) } - func test_sign_collection_revoked_key() throws { + func test_sign_collection_revoked_key() async throws { // Skipping until https://github.com/SwiftPackageIndex/SwiftPackageIndex-Server/issues/1583#issuecomment-1066592057 // is resolved try XCTSkipIf(true) @@ -815,13 +801,11 @@ class PackageCollectionTests: AppTestCase { // MUT do { - let signedCollection = try SignedCollection.sign(eventLoop: app.eventLoopGroup.next(), - collection: collection).wait() + let signedCollection = try await SignedCollection.sign(collection: collection) // NB: signing _can_ succeed in case of reachability issues to verify the cert // in this case we need to check the signature // https://github.com/SwiftPackageIndex/SwiftPackageIndex-Server/issues/1583#issuecomment-1048408400 - let validated = try SignedCollection.validate(eventLoop: app.eventLoopGroup.next(), - signedCollection: signedCollection).wait() + let validated = try await SignedCollection.validate(signedCollection: signedCollection) XCTAssertFalse(validated) } catch PackageCollectionSigningError.invalidCertChain { // ok