diff --git a/Tests/AppTests/PackageController+routesTests.swift b/Tests/AppTests/PackageController+routesTests.swift index c70248b13..f0f486544 100644 --- a/Tests/AppTests/PackageController+routesTests.swift +++ b/Tests/AppTests/PackageController+routesTests.swift @@ -12,217 +12,237 @@ // See the License for the specific language governing permissions and // limitations under the License. -import XCTest - @testable import App import Dependencies import SnapshotTesting import SwiftSoup +import Testing import Vapor -class PackageController_routesTests: SnapshotTestCase { +@Suite struct PackageController_routesTests { - func test_show() async throws { + @Test func show() async throws { try await withDependencies { + $0.date.now = .t0 $0.environment.dbId = { nil } $0.environment.processingBuildBacklog = { false } } operation: { - // setup - let pkg = try await savePackage(on: app.db, "1") - try await Repository(package: pkg, name: "package", owner: "owner") - .save(on: app.db) - try await Version(package: pkg, latest: .defaultBranch).save(on: app.db) + try await withApp { app in + // setup + let pkg = try await savePackage(on: app.db, "1") + try await Repository(package: pkg, name: "package", owner: "owner") + .save(on: app.db) + try await Version(package: pkg, latest: .defaultBranch).save(on: app.db) - // MUT - try await app.test(.GET, "/owner/package") { res async in - XCTAssertEqual(res.status, .ok) + // MUT + try await app.test(.GET, "/owner/package") { res async in + #expect(res.status == .ok) + } } } } - func test_show_checkingGitHubRepository_notFound() throws { - try withDependencies { + @Test func show_checkingGitHubRepository_notFound() async throws { + try await withDependencies { $0.environment.dbId = { nil } $0.httpClient.fetchHTTPStatusCode = { @Sendable _ in .notFound } } operation: { - // MUT - try app.test(.GET, "/unknown/package") { - XCTAssertEqual($0.status, .notFound) + try await withApp { app in + // MUT + try await app.test(.GET, "/unknown/package") { res async in + #expect(res.status == .notFound) + } } } } - func test_show_checkingGitHubRepository_found() throws { - try withDependencies { + @Test func show_checkingGitHubRepository_found() async throws { + try await withDependencies { $0.environment.dbId = { nil } $0.httpClient.fetchHTTPStatusCode = { @Sendable _ in .ok } } operation: { + try await withApp { app in // MUT - try app.test(.GET, "/unknown/package") { - XCTAssertEqual($0.status, .notFound) + try await app.test(.GET, "/unknown/package") { res async in + #expect(res.status == .notFound) + } } } } - func test_show_checkingGitHubRepository_error() throws { + @Test func show_checkingGitHubRepository_error() async throws { // Make sure we don't throw an internal server error in case // fetchHTTPStatusCode fails - try withDependencies { + try await withDependencies { $0.environment.dbId = { nil } $0.httpClient.fetchHTTPStatusCode = { @Sendable _ in throw FetchError() } } operation: { + try await withApp { app in // MUT - try app.test(.GET, "/unknown/package") { - XCTAssertEqual($0.status, .notFound) + try await app.test(.GET, "/unknown/package") { res async in + #expect(res.status == .notFound) + } } } } - func test_ShowModel_packageAvailable() async throws { - // setup - let pkg = try await savePackage(on: app.db, "1") - try await Repository(package: pkg, name: "package", owner: "owner") - .save(on: app.db) - try await Version(package: pkg, latest: .defaultBranch).save(on: app.db) - - // MUT - let model = try await PackageController.ShowModel(db: app.db, owner: "owner", repository: "package") - - // validate - switch model { - case .packageAvailable: - // don't check model details, we simply want to assert the flow logic - break - case .packageMissing, .packageDoesNotExist: - XCTFail("expected package to be available") - } - } + @Test func ShowModel_packageAvailable() async throws { + try await withApp { app in + // setup + let pkg = try await savePackage(on: app.db, "1") + try await Repository(package: pkg, name: "package", owner: "owner") + .save(on: app.db) + try await Version(package: pkg, latest: .defaultBranch).save(on: app.db) - func test_ShowModel_packageMissing() async throws { - try await withDependencies { - $0.httpClient.fetchHTTPStatusCode = { @Sendable _ in .ok } - } operation: { // MUT let model = try await PackageController.ShowModel(db: app.db, owner: "owner", repository: "package") // validate switch model { - case .packageAvailable, .packageDoesNotExist: - XCTFail("expected package to be missing") - case .packageMissing: + case .packageAvailable: + // don't check model details, we simply want to assert the flow logic break + case .packageMissing, .packageDoesNotExist: + Issue.record("expected package to be available") } } } - func test_ShowModel_packageDoesNotExist() async throws { + @Test func ShowModel_packageMissing() async throws { try await withDependencies { - $0.httpClient.fetchHTTPStatusCode = { @Sendable _ in .notFound } + $0.httpClient.fetchHTTPStatusCode = { @Sendable _ in .ok } } operation: { - // MUT - let model = try await PackageController.ShowModel(db: app.db, owner: "owner", repository: "package") + try await withApp { app in + // MUT + let model = try await PackageController.ShowModel(db: app.db, owner: "owner", repository: "package") + + // validate + switch model { + case .packageAvailable, .packageDoesNotExist: + Issue.record("expected package to be missing") + case .packageMissing: + break + } + } + } + } - // validate - switch model { - case .packageAvailable, .packageMissing: - XCTFail("expected package not to exist") - case .packageDoesNotExist: - break + @Test func ShowModel_packageDoesNotExist() async throws { + try await withDependencies { + $0.httpClient.fetchHTTPStatusCode = { @Sendable _ in .notFound } + } operation: { + try await withApp { app in + // MUT + let model = try await PackageController.ShowModel(db: app.db, owner: "owner", repository: "package") + + // validate + switch model { + case .packageAvailable, .packageMissing: + Issue.record("expected package not to exist") + case .packageDoesNotExist: + break + } } } } - func test_ShowModel_fetchHTTPStatusCode_error() async throws { + @Test func ShowModel_fetchHTTPStatusCode_error() async throws { try await withDependencies { $0.httpClient.fetchHTTPStatusCode = { @Sendable _ in throw FetchError() } } operation: { - // MUT - let model = try await PackageController.ShowModel(db: app.db, owner: "owner", repository: "package") - - // validate - switch model { - case .packageAvailable, .packageMissing: - XCTFail("expected package not to exist") - case .packageDoesNotExist: - break + try await withApp { app in + // MUT + let model = try await PackageController.ShowModel(db: app.db, owner: "owner", repository: "package") + + // validate + switch model { + case .packageAvailable, .packageMissing: + Issue.record("expected package not to exist") + case .packageDoesNotExist: + break + } } } } - func test_readme_route() async throws { + @Test func readme_route() async throws { // Test that readme route is set up - // setup - let pkg = try await savePackage(on: app.db, "1") - try await Repository(package: pkg, name: "package", owner: "owner") - .save(on: app.db) - try await Version(package: pkg, latest: .defaultBranch).save(on: app.db) + try await withApp { app in + // setup + let pkg = try await savePackage(on: app.db, "1") + try await Repository(package: pkg, name: "package", owner: "owner") + .save(on: app.db) + try await Version(package: pkg, latest: .defaultBranch).save(on: app.db) - // MUT - try await app.test(.GET, "/owner/package/readme") { res async in - XCTAssertEqual(res.status, .ok) + // MUT + try await app.test(.GET, "/owner/package/readme") { res async in + #expect(res.status == .ok) + } } } - func test_readme_basic() async throws { + @Test func readme_basic() async throws { // Test readme fragment happy path try await withDependencies { $0.s3.fetchReadme = { @Sendable _, _ in #"
readme content
"# } } operation: { - // setup - let pkg = try await savePackage(on: app.db, "1") - try await Repository(package: pkg, defaultBranch: "main", name: "package", owner: "owner", readmeHtmlUrl: "html url") - .save(on: app.db) - try await Version(package: pkg, latest: .defaultBranch).save(on: app.db) - let req = Request(application: app, on: app.eventLoopGroup.next()) - req.parameters.set("owner", to: "owner") - req.parameters.set("repository", to: "package") + try await withApp { app in + // setup + let pkg = try await savePackage(on: app.db, "1") + try await Repository(package: pkg, defaultBranch: "main", name: "package", owner: "owner", readmeHtmlUrl: "html url") + .save(on: app.db) + try await Version(package: pkg, latest: .defaultBranch).save(on: app.db) + let req = Request(application: app, on: app.eventLoopGroup.next()) + req.parameters.set("owner", to: "owner") + req.parameters.set("repository", to: "package") - // MUT - let node = try await PackageController.readme(req: req) + // MUT + let node = try await PackageController.readme(req: req) - // validate - XCTAssertEqual(node.render(indentedBy: .spaces(2)), - """ + // validate + #expect(node.render(indentedBy: .spaces(2)) == """ readme content """) + } } } - func test_readme_no_readmeHtmlUrl() async throws { + @Test func readme_no_readmeHtmlUrl() async throws { // Test readme fragment when there's no readme html url try await withDependencies { $0.s3.fetchReadme = { @Sendable _, _ in - XCTFail("must not be called") + Issue.record("must not be called") return "" } } operation: { - // setup - let pkg = try await savePackage(on: app.db, "1") - try await Repository(package: pkg, name: "package", owner: "owner", readmeHtmlUrl: nil) - .save(on: app.db) - try await Version(package: pkg, latest: .defaultBranch).save(on: app.db) - let req = Request(application: app, on: app.eventLoopGroup.next()) - req.parameters.set("owner", to: "owner") - req.parameters.set("repository", to: "package") + try await withApp { app in + // setup + let pkg = try await savePackage(on: app.db, "1") + try await Repository(package: pkg, name: "package", owner: "owner", readmeHtmlUrl: nil) + .save(on: app.db) + try await Version(package: pkg, latest: .defaultBranch).save(on: app.db) + let req = Request(application: app, on: app.eventLoopGroup.next()) + req.parameters.set("owner", to: "owner") + req.parameters.set("repository", to: "package") - // MUT - let node = try await PackageController.readme(req: req) + // MUT + let node = try await PackageController.readme(req: req) - // validate - XCTAssertEqual(node.render(indentedBy: .spaces(2)), - """ + // validate + #expect(node.render(indentedBy: .spaces(2)) == """

This package does not have a README file.

""") + } } } - func test_readme_error() async throws { + @Test func readme_error() async throws { // Test readme fragment when fetchS3Readme throws try await withDependencies { $0.s3.fetchReadme = { @Sendable _, _ in @@ -230,361 +250,281 @@ class PackageController_routesTests: SnapshotTestCase { throw Error() } } operation: { - // setup - let pkg = try await savePackage(on: app.db, "1") - try await Repository(package: pkg, - name: "package", - owner: "owner", - readmeHtmlUrl: "html url", - s3Readme: .cached(s3ObjectUrl: "", githubEtag: "") - ).save(on: app.db) - try await Version(package: pkg, latest: .defaultBranch).save(on: app.db) - let req = Request(application: app, on: app.eventLoopGroup.next()) - req.parameters.set("owner", to: "owner") - req.parameters.set("repository", to: "package") + try await withApp { app in + // setup + let pkg = try await savePackage(on: app.db, "1") + try await Repository(package: pkg, + name: "package", + owner: "owner", + readmeHtmlUrl: "html url", + s3Readme: .cached(s3ObjectUrl: "", githubEtag: "") + ).save(on: app.db) + try await Version(package: pkg, latest: .defaultBranch).save(on: app.db) + let req = Request(application: app, on: app.eventLoopGroup.next()) + req.parameters.set("owner", to: "owner") + req.parameters.set("repository", to: "package") - // MUT - let node = try await PackageController.readme(req: req) + // MUT + let node = try await PackageController.readme(req: req) - // validate - XCTAssertEqual(node.render(indentedBy: .spaces(2)), - """ + // validate + #expect(node.render(indentedBy: .spaces(2)) == """

This package's README file couldn't be loaded. Try viewing it on GitHub.

""") - let app = self.app! - try await XCTAssertEqualAsync(try await Repository.query(on: app.db).count(), 1) - let s3Readme = try await XCTUnwrapAsync(try await Repository.query(on: app.db).first()?.s3Readme) - XCTAssert(s3Readme.isError) + #expect(try await Repository.query(on: app.db).count() == 1) + let s3Readme = try await XCTUnwrapAsync(try await Repository.query(on: app.db).first()?.s3Readme) + #expect(s3Readme.isError) + } } } - func test_releases() async throws { - // setup - let pkg = try await savePackage(on: app.db, "1") - try await Repository(package: pkg, name: "package", owner: "owner") - .save(on: app.db) - try await Version(package: pkg, latest: .defaultBranch).save(on: app.db) + @Test func releases() async throws { + try await withApp { app in + // setup + let pkg = try await savePackage(on: app.db, "1") + try await Repository(package: pkg, name: "package", owner: "owner") + .save(on: app.db) + try await Version(package: pkg, latest: .defaultBranch).save(on: app.db) - // MUT - try await app.test(.GET, "/owner/package/releases") { res async in - XCTAssertEqual(res.status, .ok) + // MUT + try await app.test(.GET, "/owner/package/releases") { res async in + #expect(res.status == .ok) + } } } - func test_builds() async throws { + @Test func builds() async throws { try await withDependencies { $0.environment.dbId = { nil } } operation: { - // setup - let pkg = try await savePackage(on: app.db, "1") - try await Repository(package: pkg, name: "package", owner: "owner") - .save(on: app.db) - try await Version(package: pkg, latest: .defaultBranch).save(on: app.db) + try await withApp { app in + // setup + let pkg = try await savePackage(on: app.db, "1") + try await Repository(package: pkg, name: "package", owner: "owner") + .save(on: app.db) + try await Version(package: pkg, latest: .defaultBranch).save(on: app.db) - // MUT - try await app.test(.GET, "/owner/package/builds") { res async in - XCTAssertEqual(res.status, .ok) + // MUT + try await app.test(.GET, "/owner/package/builds") { res async in + #expect(res.status == .ok) + } } } } - func test_maintainerInfo() async throws { + @Test func maintainerInfo() async throws { try await withDependencies { $0.environment.dbId = { nil } } operation: { - // setup - let pkg = try await savePackage(on: app.db, "1") - try await Repository(package: pkg, name: "package", owner: "owner") - .save(on: app.db) - try await Version(package: pkg, latest: .defaultBranch, packageName: "pkg") - .save(on: app.db) + try await withApp { app in + // setup + let pkg = try await savePackage(on: app.db, "1") + try await Repository(package: pkg, name: "package", owner: "owner") + .save(on: app.db) + try await Version(package: pkg, latest: .defaultBranch, packageName: "pkg") + .save(on: app.db) - // MUT - try await app.test(.GET, "/owner/package/information-for-package-maintainers") { res async in - XCTAssertEqual(res.status, .ok) + // MUT + try await app.test(.GET, "/owner/package/information-for-package-maintainers") { res async in + #expect(res.status == .ok) + } } } } - func test_maintainerInfo_no_packageName() async throws { + @Test func maintainerInfo_no_packageName() async throws { // Ensure we display the page even if packageName is not set try await withDependencies { $0.environment.dbId = { nil } } operation: { - // setup - let pkg = try await savePackage(on: app.db, "1") - try await Repository(package: pkg, name: "package", owner: "owner") - .save(on: app.db) - try await Version(package: pkg, latest: .defaultBranch, packageName: nil) - .save(on: app.db) + try await withApp { app in + // setup + let pkg = try await savePackage(on: app.db, "1") + try await Repository(package: pkg, name: "package", owner: "owner") + .save(on: app.db) + try await Version(package: pkg, latest: .defaultBranch, packageName: nil) + .save(on: app.db) - // MUT - try await app.test(.GET, "/owner/package/information-for-package-maintainers") { res async in - XCTAssertEqual(res.status, .ok) + // MUT + try await app.test(.GET, "/owner/package/information-for-package-maintainers") { res async in + #expect(res.status == .ok) + } } } } - func test_DocRoute_baseURL() throws { - XCTAssertEqual( - DocRoute(owner: "Foo", repository: "Bar", docVersion: .reference("1.2.3"), fragment: .documentation).baseURL, - "foo/bar/1.2.3" + @Test func DocRoute_baseURL() throws { + #expect( + DocRoute(owner: "Foo", repository: "Bar", docVersion: .reference("1.2.3"), fragment: .documentation).baseURL == "foo/bar/1.2.3" ) - XCTAssertEqual( - DocRoute(owner: "Foo", repository: "Bar", docVersion: .reference("1.2.3"), fragment: .css).baseURL, - "foo/bar/1.2.3" + #expect( + DocRoute(owner: "Foo", repository: "Bar", docVersion: .reference("1.2.3"), fragment: .css).baseURL == "foo/bar/1.2.3" ) - XCTAssertEqual( - DocRoute(owner: "Foo", repository: "Bar", docVersion: .reference("main"), fragment: .documentation).baseURL, - "foo/bar/main" + #expect( + DocRoute(owner: "Foo", repository: "Bar", docVersion: .reference("main"), fragment: .documentation).baseURL == "foo/bar/main" ) - XCTAssertEqual( - DocRoute(owner: "Foo", repository: "Bar", docVersion: .reference("Main"), fragment: .documentation).baseURL, - "foo/bar/main" + #expect( + DocRoute(owner: "Foo", repository: "Bar", docVersion: .reference("Main"), fragment: .documentation).baseURL == "foo/bar/main" ) - XCTAssertEqual( - DocRoute(owner: "Foo", repository: "Bar", docVersion: .reference("feature/a"), fragment: .documentation).baseURL, - "foo/bar/feature-a" + #expect( + DocRoute(owner: "Foo", repository: "Bar", docVersion: .reference("feature/a"), fragment: .documentation).baseURL == "foo/bar/feature-a" ) - XCTAssertEqual( - DocRoute(owner: "Foo", repository: "Bar", docVersion: .current(referencing: "1.2.3"), fragment: .documentation).baseURL, - "foo/bar/1.2.3" + #expect( + DocRoute(owner: "Foo", repository: "Bar", docVersion: .current(referencing: "1.2.3"), fragment: .documentation).baseURL == "foo/bar/1.2.3" ) - XCTAssertEqual( - DocRoute(owner: "Foo", repository: "Bar", docVersion: .current(referencing: "1.2.3"), fragment: .documentation).baseURL, - "foo/bar/1.2.3" + #expect( + DocRoute(owner: "Foo", repository: "Bar", docVersion: .current(referencing: "1.2.3"), fragment: .documentation).baseURL == "foo/bar/1.2.3" ) - XCTAssertEqual( - DocRoute(owner: "Foo", repository: "Bar", docVersion: .current(referencing: "main"), fragment: .documentation).baseURL, - "foo/bar/main" + #expect( + DocRoute(owner: "Foo", repository: "Bar", docVersion: .current(referencing: "main"), fragment: .documentation).baseURL == "foo/bar/main" ) - XCTAssertEqual( - DocRoute(owner: "Foo", repository: "Bar", docVersion: .current(referencing: "Main"), fragment: .documentation).baseURL, - "foo/bar/main" + #expect( + DocRoute(owner: "Foo", repository: "Bar", docVersion: .current(referencing: "Main"), fragment: .documentation).baseURL == "foo/bar/main" ) } - func test_awsDocumentationURL() throws { + @Test func awsDocumentationURL() throws { try withDependencies { $0.environment.awsDocsBucket = { "docs-bucket" } - } operation: { - XCTAssertEqual( - try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .reference("Main"), fragment: .documentation, pathElements: ["path"])).string, - "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/main/documentation/path" + } operation: { () throws in + #expect( + try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .reference("Main"), fragment: .documentation, pathElements: ["path"])).string == "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/main/documentation/path" ) - XCTAssertEqual( - try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .reference("1.2.3"), fragment: .css, pathElements: ["path"])).string, - "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/1.2.3/css/path" + #expect( + try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .reference("1.2.3"), fragment: .css, pathElements: ["path"])).string == "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/1.2.3/css/path" ) - XCTAssertEqual( - try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .reference("1.2.3"), fragment: .documentation, pathElements: ["path"])).string, - "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/1.2.3/documentation/path" + #expect( + try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .reference("1.2.3"), fragment: .documentation, pathElements: ["path"])).string == "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/1.2.3/documentation/path" ) - XCTAssertEqual( - try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .reference("1.2.3"), fragment: .data, pathElements: ["path"])).string, - "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/1.2.3/data/path" + #expect( + try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .reference("1.2.3"), fragment: .data, pathElements: ["path"])).string == "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/1.2.3/data/path" ) - XCTAssertEqual( - try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .reference("1.2.3"), fragment: .images, pathElements: ["path"])).string, - "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/1.2.3/images/path" + #expect( + try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .reference("1.2.3"), fragment: .images, pathElements: ["path"])).string == "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/1.2.3/images/path" ) - XCTAssertEqual( - try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .reference("1.2.3"), fragment: .img, pathElements: ["path"])).string, - "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/1.2.3/img/path" + #expect( + try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .reference("1.2.3"), fragment: .img, pathElements: ["path"])).string == "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/1.2.3/img/path" ) - XCTAssertEqual( - try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .reference("1.2.3"), fragment: .svgImages, pathElements: ["path"])).string, - "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/1.2.3/images/path" + #expect( + try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .reference("1.2.3"), fragment: .svgImages, pathElements: ["path"])).string == "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/1.2.3/images/path" ) - XCTAssertEqual( - try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .reference("1.2.3"), fragment: .svgImg, pathElements: ["path"])).string, - "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/1.2.3/img/path" + #expect( + try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .reference("1.2.3"), fragment: .svgImg, pathElements: ["path"])).string == "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/1.2.3/img/path" ) - XCTAssertEqual( - try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .reference("1.2.3"), fragment: .js, pathElements: ["path"])).string, - "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/1.2.3/js/path" + #expect( + try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .reference("1.2.3"), fragment: .js, pathElements: ["path"])).string == "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/1.2.3/js/path" ) - XCTAssertEqual( - try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .reference("1.2.3"), fragment: .linkablePaths, pathElements: [""])).string, - "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/1.2.3/linkable-paths.json" + #expect( + try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .reference("1.2.3"), fragment: .linkablePaths, pathElements: [""])).string == "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/1.2.3/linkable-paths.json" ) - XCTAssertEqual( - try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .reference("1.2.3"), fragment: .linkablePaths, pathElements: ["ignored"])).string, - "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/1.2.3/linkable-paths.json" + #expect( + try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .reference("1.2.3"), fragment: .linkablePaths, pathElements: ["ignored"])).string == "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/1.2.3/linkable-paths.json" ) - XCTAssertEqual( - try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .reference("1.2.3"), fragment: .themeSettings, pathElements: ["path"])).string, - "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/1.2.3/path/theme-settings.json" + #expect( + try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .reference("1.2.3"), fragment: .themeSettings, pathElements: ["path"])).string == "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/1.2.3/path/theme-settings.json" ) - XCTAssertEqual( - try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .reference("1.2.3"), fragment: .themeSettings, pathElements: [""])).string, - "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/1.2.3/theme-settings.json" + #expect( + try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .reference("1.2.3"), fragment: .themeSettings, pathElements: [""])).string == "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/1.2.3/theme-settings.json" ) - XCTAssertEqual( - try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .reference("feature/a"), fragment: .documentation, pathElements: ["path"])).string, - "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/feature-a/documentation/path" + #expect( + try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .reference("feature/a"), fragment: .documentation, pathElements: ["path"])).string == "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/feature-a/documentation/path" ) } } - func test_awsDocumentationURL_current() throws { + @Test func awsDocumentationURL_current() throws { try withDependencies { $0.environment.awsDocsBucket = { "docs-bucket" } - } operation: { - XCTAssertEqual( - try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .current(referencing: "Main"), fragment: .documentation, pathElements: ["path"])).string, - "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/main/documentation/path" + } operation: { () throws in + #expect( + try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .current(referencing: "Main"), fragment: .documentation, pathElements: ["path"])).string == "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/main/documentation/path" ) - XCTAssertEqual( - try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .current(referencing: "1.2.3"), fragment: .documentation, pathElements: ["path"])).string, - "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/1.2.3/documentation/path" + #expect( + try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .current(referencing: "1.2.3"), fragment: .documentation, pathElements: ["path"])).string == "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/1.2.3/documentation/path" ) - XCTAssertEqual( - try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .current(referencing: "feature/a"), fragment: .documentation, pathElements: ["path"])).string, - "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/feature-a/documentation/path" + #expect( + try PackageController.awsDocumentationURL(route: .init(owner: "Foo", repository: "Bar", docVersion: .current(referencing: "feature/a"), fragment: .documentation, pathElements: ["path"])).string == "http://docs-bucket.s3-website.us-east-2.amazonaws.com/foo/bar/feature-a/documentation/path" ) } } - func test_awsDocumentationURL_issue2287() throws { + @Test func awsDocumentationURL_issue2287() throws { // https://github.com/SwiftPackageIndex/SwiftPackageIndex-Server/issues/2287 // reference with / needs to be escaped try withDependencies { $0.environment.awsDocsBucket = { "docs-bucket" } - } operation: { - XCTAssertEqual( - try PackageController.awsDocumentationURL(route: .init(owner: "linhay", repository: "SectionKit", docVersion: .reference("feature/2.0.0"), fragment: .documentation, pathElements: ["sectionui"])).string, - "http://docs-bucket.s3-website.us-east-2.amazonaws.com/linhay/sectionkit/feature-2.0.0/documentation/sectionui" + } operation: { () throws in + #expect( + try PackageController.awsDocumentationURL(route: .init(owner: "linhay", repository: "SectionKit", docVersion: .reference("feature/2.0.0"), fragment: .documentation, pathElements: ["sectionui"])).string == "http://docs-bucket.s3-website.us-east-2.amazonaws.com/linhay/sectionkit/feature-2.0.0/documentation/sectionui" ) } } - func test_canonicalDocumentationUrl() throws { + @Test func canonicalDocumentationUrl() throws { // There is no canonical URL for external or universal cases of the canonical target. - XCTAssertNil(PackageController.canonicalDocumentationUrl(from: "", owner: "", repository: "", docVersion: .reference(""), - toTarget: .external(url: "https://example.com"))) + #expect(PackageController.canonicalDocumentationUrl(from: "", owner: "", repository: "", docVersion: .reference(""), + toTarget: .external(url: "https://example.com")) == nil) - XCTAssertNil(PackageController.canonicalDocumentationUrl(from: "", owner: "", repository: "", docVersion: .reference(""), - toTarget: .internal(docVersion: .reference(""), archive: ""))) + #expect(PackageController.canonicalDocumentationUrl(from: "", owner: "", repository: "", docVersion: .reference(""), + toTarget: .internal(docVersion: .reference(""), archive: "")) == nil) // There should be no canonical URL if the package owner/repo/ref prefix doesn't match even with a valid canonical target. - XCTAssertNil(PackageController.canonicalDocumentationUrl(from: "/some/random/url/without/matching/prefix", + #expect(PackageController.canonicalDocumentationUrl(from: "/some/random/url/without/matching/prefix", owner: "owner", repository: "repo", docVersion: .reference("non-canonical-ref"), - toTarget: .internal(docVersion: .reference("canonical-ref"), archive: "archive"))) + toTarget: .internal(docVersion: .reference("canonical-ref"), archive: "archive")) == nil) // Switching a non-canonical reference for a canonical one at the root of the documentation - XCTAssertEqual(PackageController.canonicalDocumentationUrl(from: "/owner/repo/non-canonical-ref/documentation/archive", + #expect(PackageController.canonicalDocumentationUrl(from: "/owner/repo/non-canonical-ref/documentation/archive", owner: "owner", repository: "repo", docVersion: .reference("non-canonical-ref"), - toTarget: .internal(docVersion: .reference("canonical-ref"), archive: "archive")), - "/owner/repo/canonical-ref/documentation/archive") + toTarget: .internal(docVersion: .reference("canonical-ref"), archive: "archive")) == "/owner/repo/canonical-ref/documentation/archive") - XCTAssertEqual(PackageController.canonicalDocumentationUrl(from: "/owner/repo/non-canonical-ref/documentation/archive/symbol:$-%", + #expect(PackageController.canonicalDocumentationUrl(from: "/owner/repo/non-canonical-ref/documentation/archive/symbol:$-%", owner: "owner", repository: "repo", docVersion: .reference("non-canonical-ref"), - toTarget: .internal(docVersion: .reference("canonical-ref"), archive: "archive")), - "/owner/repo/canonical-ref/documentation/archive/symbol:$-%") + toTarget: .internal(docVersion: .reference("canonical-ref"), archive: "archive")) == "/owner/repo/canonical-ref/documentation/archive/symbol:$-%") } - func test_documentation_routes_contentType() async throws { + @Test func documentation_routes_contentType() async throws { try await withDependencies { $0.environment.awsDocsBucket = { "docs-bucket" } $0.httpClient.fetchDocumentation = { @Sendable _ in .ok } } operation: { - try await app.test(.GET, "/owner/package/main/images/foo/bar.jpeg") { res async in - XCTAssertEqual(res.headers.contentType, .init(type: "application", subType: "octet-stream")) - } - try await app.test(.GET, "/owner/package/main/images/foo/bar.svg") { res async in - XCTAssertEqual(res.headers.contentType, .init(type: "image", subType: "svg+xml")) - } - try await app.test(.GET, "/owner/package/main/images/foo/bar.SVG") { res async in - XCTAssertEqual(res.headers.contentType, .init(type: "image", subType: "svg+xml")) - } - try await app.test(.GET, "/owner/package/main/img/foo/bar.jpeg") { res async in - XCTAssertEqual(res.headers.contentType, .init(type: "application", subType: "octet-stream")) - } - try await app.test(.GET, "/owner/package/main/img/foo/bar.svg") { res async in - XCTAssertEqual(res.headers.contentType, .init(type: "image", subType: "svg+xml")) - } - try await app.test(.GET, "/owner/package/main/img/foo/bar.SVG") { res async in - XCTAssertEqual(res.headers.contentType, .init(type: "image", subType: "svg+xml")) + try await withApp { app in + try await app.test(.GET, "/owner/package/main/images/foo/bar.jpeg") { res async in + #expect(res.headers.contentType == .init(type: "application", subType: "octet-stream")) + } + try await app.test(.GET, "/owner/package/main/images/foo/bar.svg") { res async in + #expect(res.headers.contentType == .init(type: "image", subType: "svg+xml")) + } + try await app.test(.GET, "/owner/package/main/images/foo/bar.SVG") { res async in + #expect(res.headers.contentType == .init(type: "image", subType: "svg+xml")) + } + try await app.test(.GET, "/owner/package/main/img/foo/bar.jpeg") { res async in + #expect(res.headers.contentType == .init(type: "application", subType: "octet-stream")) + } + try await app.test(.GET, "/owner/package/main/img/foo/bar.svg") { res async in + #expect(res.headers.contentType == .init(type: "image", subType: "svg+xml")) + } + try await app.test(.GET, "/owner/package/main/img/foo/bar.SVG") { res async in + #expect(res.headers.contentType == .init(type: "image", subType: "svg+xml")) + } } } } - func test_documentation_routes_redirect() async throws { + @Test func documentation_routes_redirect() async throws { // Test the redirect documentation routes without any reference: // /owner/package/documentation + various path elements - // setup - let pkg = try await savePackage(on: app.db, "1") - try await Repository(package: pkg, name: "package", owner: "owner") - .save(on: app.db) - try await Version(package: pkg, - commit: "0123456789", - commitDate: .t0, - docArchives: [.init(name: "target", title: "Target")], - latest: .defaultBranch, - packageName: "pkg", - reference: .branch("main")) - .save(on: app.db) - try await Version(package: pkg, - commit: "9876543210", - commitDate: .t0, - docArchives: [.init(name: "target", title: "Target")], - latest: .release, - packageName: "pkg", - reference: .tag(1, 0, 0)) - .save(on: app.db) - - // MUT - try await app.test(.GET, "/owner/package/documentation") { res async in - XCTAssertEqual(res.status, .seeOther) - XCTAssertEqual(res.headers.location, "/owner/package/1.0.0/documentation/target") - } - try await app.test(.GET, "/owner/package/documentation/target/symbol") { res async in - XCTAssertEqual(res.status, .seeOther) - XCTAssertEqual(res.headers.location, "/owner/package/1.0.0/documentation/target/symbol") - } - // We do not validate the catchall - authors need to make sure they point - // the path after `documentation/` at a valid doc path. We do not try and map it to - // generated docs (i.e. `target` in this test) as that would prevent them from - // cross-target linking. - // Effectively, all we're doing is inserting the correct `ref` before `documentation`. - try await app.test(.GET, "/owner/package/documentation/foo") { res async in - XCTAssertEqual(res.status, .seeOther) - XCTAssertEqual(res.headers.location, "/owner/package/1.0.0/documentation/foo") - } - try await app.test(.GET, "/owner/package/documentation/foo#anchor") { res async in - XCTAssertEqual(res.status, .seeOther) - XCTAssertEqual(res.headers.location, "/owner/package/1.0.0/documentation/foo#anchor") - } - try await app.test(.GET, "/owner/package/documentation/FOO") { res async in - XCTAssertEqual(res.status, .seeOther) - XCTAssertEqual(res.headers.location, "/owner/package/1.0.0/documentation/foo") - } - try await app.test(.GET, "/owner/package/tutorials/foo") { res async in - XCTAssertEqual(res.status, .seeOther) - XCTAssertEqual(res.headers.location, "/owner/package/1.0.0/tutorials/foo") - } - } - - func test_documentation_routes_current() async throws { - // Test the current (~) documentation routes: - // /owner/package/documentation/~ + various path elements - try await withDependencies { - $0.currentReferenceCache = .disabled - $0.environment.awsDocsBucket = { "docs-bucket" } - $0.httpClient.fetchDocumentation = { @Sendable _ in .ok(body: .mockIndexHTML()) } - $0.timeZone = .utc - } operation: { + try await withApp { app in // setup let pkg = try await savePackage(on: app.db, "1") try await Repository(package: pkg, name: "package", owner: "owner") @@ -607,323 +547,389 @@ class PackageController_routesTests: SnapshotTestCase { .save(on: app.db) // MUT - - // test partially qualified route (no archive) - try await app.test(.GET, "/owner/package/~/documentation") { @Sendable res async in - XCTAssertEqual(res.status, .seeOther) - XCTAssertEqual(res.headers.location, "/owner/package/1.0.0/documentation/target") - } - - // test fully qualified route - try await app.test(.GET, "/owner/package/~/documentation/target") { @MainActor res in - await Task.yield() // essential to avoid deadlocking - XCTAssertEqual(res.status, .ok) - XCTAssertEqual(res.content.contentType?.description, "text/html; charset=utf-8") - let body = String(buffer: res.body) - assertSnapshot(of: body, as: .html, named: "index") - // Call out a couple of specific snippets in the html - XCTAssert(body.contains(#"var baseUrl = "/owner/package/~/""#)) - XCTAssert(body.contains(#""#)) - XCTAssertFalse(body.contains(#"1.0.0"#)) - } - - // test catchall - try await app.test(.GET, "/owner/package/~/documentation/target/a/b#anchor") { @MainActor res in - await Task.yield() // essential to avoid deadlocking - XCTAssertEqual(res.status, .ok) - XCTAssertEqual(res.content.contentType?.description, "text/html; charset=utf-8") - let body = String(buffer: res.body) - assertSnapshot(of: body, as: .html, named: "index") - // Call out a couple of specific snippets in the html - XCTAssert(body.contains(#"var baseUrl = "/owner/package/~/""#)) - XCTAssert(body.contains(#""#)) - XCTAssertFalse(body.contains(#"1.0.0"#)) - } - - // Test case insensitive path. - try await app.test(.GET, "/Owner/Package/~/documentation/target/A/b#anchor") { @MainActor res in - await Task.yield() // essential to avoid deadlocking - XCTAssertEqual(res.status, .ok) - XCTAssertEqual(res.content.contentType?.description, "text/html; charset=utf-8") - let body = String(buffer: res.body) - assertSnapshot(of: body, as: .html, named: "index-mixed-case") - // Call out a couple of specific snippets in the html - XCTAssert(body.contains(#"var baseUrl = "/owner/package/~/""#)) - XCTAssert(body.contains(#""#)) - XCTAssertFalse(body.contains(#"1.0.0"#)) + try await app.test(.GET, "/owner/package/documentation") { res async in + #expect(res.status == .seeOther) + #expect(res.headers.location == "/owner/package/1.0.0/documentation/target") + } + try await app.test(.GET, "/owner/package/documentation/target/symbol") { res async in + #expect(res.status == .seeOther) + #expect(res.headers.location == "/owner/package/1.0.0/documentation/target/symbol") + } + // We do not validate the catchall - authors need to make sure they point + // the path after `documentation/` at a valid doc path. We do not try and map it to + // generated docs (i.e. `target` in this test) as that would prevent them from + // cross-target linking. + // Effectively, all we're doing is inserting the correct `ref` before `documentation`. + try await app.test(.GET, "/owner/package/documentation/foo") { res async in + #expect(res.status == .seeOther) + #expect(res.headers.location == "/owner/package/1.0.0/documentation/foo") + } + try await app.test(.GET, "/owner/package/documentation/foo#anchor") { res async in + #expect(res.status == .seeOther) + #expect(res.headers.location == "/owner/package/1.0.0/documentation/foo#anchor") + } + try await app.test(.GET, "/owner/package/documentation/FOO") { res async in + #expect(res.status == .seeOther) + #expect(res.headers.location == "/owner/package/1.0.0/documentation/foo") + } + try await app.test(.GET, "/owner/package/tutorials/foo") { res async in + #expect(res.status == .seeOther) + #expect(res.headers.location == "/owner/package/1.0.0/tutorials/foo") } } } - func test_documentation_routes_current_rewrite() async throws { - // Test the current (~) documentation routes with baseURL rewriting: + @Test func documentation_routes_current() async throws { + // Test the current (~) documentation routes: // /owner/package/documentation/~ + various path elements try await withDependencies { $0.currentReferenceCache = .disabled $0.environment.awsDocsBucket = { "docs-bucket" } - $0.httpClient.fetchDocumentation = { @Sendable _ in .ok(body: .mockIndexHTML(baseURL: "/owner/package/1.0.0")) } + $0.httpClient.fetchDocumentation = { @Sendable _ in .ok(body: .mockIndexHTML()) } $0.timeZone = .utc } operation: { - // setup - let pkg = try await savePackage(on: app.db, "1") - try await Repository(package: pkg, name: "package", owner: "owner") + try await withApp { app in + // setup + let pkg = try await savePackage(on: app.db, "1") + try await Repository(package: pkg, name: "package", owner: "owner") + .save(on: app.db) + try await Version(package: pkg, + commit: "0123456789", + commitDate: .t0, + docArchives: [.init(name: "target", title: "Target")], + latest: .defaultBranch, + packageName: "pkg", + reference: .branch("main")) + .save(on: app.db) + try await Version(package: pkg, + commit: "9876543210", + commitDate: .t0, + docArchives: [.init(name: "target", title: "Target")], + latest: .release, + packageName: "pkg", + reference: .tag(1, 0, 0)) .save(on: app.db) - try await Version(package: pkg, - commit: "0123456789", - commitDate: .t0, - docArchives: [.init(name: "target", title: "Target")], - latest: .defaultBranch, - packageName: "pkg", - reference: .branch("main")) - .save(on: app.db) - try await Version(package: pkg, - commit: "9876543210", - commitDate: .t0, - docArchives: [.init(name: "target", title: "Target")], - latest: .release, - packageName: "pkg", - reference: .tag(1, 0, 0)) - .save(on: app.db) - // MUT + // MUT - // test fully qualified route - try await app.test(.GET, "/owner/package/~/documentation/target") { @MainActor res in - await Task.yield() // essential to avoid deadlocking - XCTAssertEqual(res.status, .ok) - XCTAssertEqual(res.content.contentType?.description, "text/html; charset=utf-8") - let body = String(buffer: res.body) - assertSnapshot(of: body, as: .html, named: "index") - // Call out a couple of specific snippets in the html - XCTAssert(body.contains(#"var baseUrl = "/owner/package/~/""#)) - XCTAssert(body.contains(#""#)) - XCTAssertFalse(body.contains(#"1.0.0"#)) - } + // test partially qualified route (no archive) + try await app.test(.GET, "/owner/package/~/documentation") { @Sendable res async in + #expect(res.status == .seeOther) + #expect(res.headers.location == "/owner/package/1.0.0/documentation/target") + } - // test catchall - try await app.test(.GET, "/owner/package/~/documentation/target/a/b#anchor") { @MainActor res in - await Task.yield() // essential to avoid deadlocking - XCTAssertEqual(res.status, .ok) - XCTAssertEqual(res.content.contentType?.description, "text/html; charset=utf-8") - let body = String(buffer: res.body) - assertSnapshot(of: body, as: .html, named: "index") - // Call out a couple of specific snippets in the html - XCTAssert(body.contains(#""#)) - XCTAssertFalse(body.contains(#"1.0.0"#)) - } + // test fully qualified route + try await app.test(.GET, "/owner/package/~/documentation/target") { res async in + #expect(res.status == .ok) + #expect(res.content.contentType?.description == "text/html; charset=utf-8") + let body = String(buffer: res.body) + assertSnapshot(of: body, as: .html, named: "index") + // Call out a couple of specific snippets in the html + #expect(body.contains(#"var baseUrl = "/owner/package/~/""#)) + #expect(body.contains(#""#)) + #expect(!body.contains(#"1.0.0"#)) + } + + // test catchall + try await app.test(.GET, "/owner/package/~/documentation/target/a/b#anchor") { res async in + #expect(res.status == .ok) + #expect(res.content.contentType?.description == "text/html; charset=utf-8") + let body = String(buffer: res.body) + assertSnapshot(of: body, as: .html, named: "index") + // Call out a couple of specific snippets in the html + #expect(body.contains(#"var baseUrl = "/owner/package/~/""#)) + #expect(body.contains(#""#)) + #expect(!body.contains(#"1.0.0"#)) + } - // Test case insensitive path. - try await app.test(.GET, "/Owner/Package/~/documentation/target/A/b#anchor") { @MainActor res in - await Task.yield() // essential to avoid deadlocking - XCTAssertEqual(res.status, .ok) - XCTAssertEqual(res.content.contentType?.description, "text/html; charset=utf-8") - let body = String(buffer: res.body) - assertSnapshot(of: body, as: .html, named: "index-mixed-case") - // Call out a couple of specific snippets in the html - XCTAssert(body.contains(#""#)) - XCTAssertFalse(body.contains(#"1.0.0"#)) + // Test case insensitive path. + try await app.test(.GET, "/Owner/Package/~/documentation/target/A/b#anchor") { res async in + #expect(res.status == .ok) + #expect(res.content.contentType?.description == "text/html; charset=utf-8") + let body = String(buffer: res.body) + assertSnapshot(of: body, as: .html, named: "index-mixed-case") + // Call out a couple of specific snippets in the html + #expect(body.contains(#"var baseUrl = "/owner/package/~/""#)) + #expect(body.contains(#""#)) + #expect(!body.contains(#"1.0.0"#)) + } } } } - func test_documentation_routes_ref() async throws { - // Test the documentation routes with a reference: - // /owner/package/documentation/{reference} + various path elements + @Test func documentation_routes_current_rewrite() async throws { + // Test the current (~) documentation routes with baseURL rewriting: + // /owner/package/documentation/~ + various path elements try await withDependencies { + $0.currentReferenceCache = .disabled $0.environment.awsDocsBucket = { "docs-bucket" } - $0.httpClient.fetchDocumentation = { @Sendable _ in .ok(body: .mockIndexHTML()) } + $0.httpClient.fetchDocumentation = { @Sendable _ in .ok(body: .mockIndexHTML(baseURL: "/owner/package/1.0.0")) } $0.timeZone = .utc } operation: { - // setup - let pkg = try await savePackage(on: app.db, "1") - try await Repository(package: pkg, name: "package", owner: "owner") + try await withApp { app in + // setup + let pkg = try await savePackage(on: app.db, "1") + try await Repository(package: pkg, name: "package", owner: "owner") + .save(on: app.db) + try await Version(package: pkg, + commit: "0123456789", + commitDate: .t0, + docArchives: [.init(name: "target", title: "Target")], + latest: .defaultBranch, + packageName: "pkg", + reference: .branch("main")) + .save(on: app.db) + try await Version(package: pkg, + commit: "9876543210", + commitDate: .t0, + docArchives: [.init(name: "target", title: "Target")], + latest: .release, + packageName: "pkg", + reference: .tag(1, 0, 0)) .save(on: app.db) - try await Version(package: pkg, - commit: "0123456789", - commitDate: .t0, - docArchives: [.init(name: "target", title: "Target")], - latest: .defaultBranch, - packageName: "pkg", - reference: .branch("main")) - .save(on: app.db) - try await Version(package: pkg, - commit: "9876543210", - commitDate: .t0, - docArchives: [.init(name: "target", title: "Target")], - latest: .release, - packageName: "pkg", - reference: .tag(1, 2, 3)) - .save(on: app.db) - - // MUT - // test partially qualified route (no archive) - try await app.test(.GET, "/owner/package/1.2.3/documentation") { @Sendable res async in - XCTAssertEqual(res.status, .seeOther) - XCTAssertEqual(res.headers.location, "/owner/package/1.2.3/documentation/target") - } + // MUT - // test fully qualified route - try await app.test(.GET, "/owner/package/1.2.3/documentation/target") { @MainActor res in - await Task.yield() // essential to avoid deadlocking - XCTAssertEqual(res.status, .ok) - XCTAssertEqual(res.content.contentType?.description, "text/html; charset=utf-8") - let body = String(buffer: res.body) - assertSnapshot(of: body, as: .html, named: "index-target") - // Call out a couple of specific snippets in the html - XCTAssert(body.contains(#"var baseUrl = "/owner/package/1.2.3/""#)) - XCTAssert(body.contains(#""#)) - XCTAssert(body.contains(#""#)) - XCTAssert(body.contains(#"1.2.3"#)) - } + // test fully qualified route + try await app.test(.GET, "/owner/package/~/documentation/target") { res async in + #expect(res.status == .ok) + #expect(res.content.contentType?.description == "text/html; charset=utf-8") + let body = String(buffer: res.body) + assertSnapshot(of: body, as: .html, named: "index") + // Call out a couple of specific snippets in the html + #expect(body.contains(#"var baseUrl = "/owner/package/~/""#)) + #expect(body.contains(#""#)) + #expect(!body.contains(#"1.0.0"#)) + } - // test catchall - try await app.test(.GET, "/owner/package/1.2.3/documentation/target/a/b#anchor") { @MainActor res in - await Task.yield() // essential to avoid deadlocking - XCTAssertEqual(res.status, .ok) - XCTAssertEqual(res.content.contentType?.description, "text/html; charset=utf-8") - let body = String(buffer: res.body) - assertSnapshot(of: body, as: .html, named: "index-target-a-b") - // Call out a couple of specific snippets in the html - XCTAssert(body.contains(#"var baseUrl = "/owner/package/1.2.3/""#)) - XCTAssert(body.contains(#""#)) - XCTAssert(body.contains(#""#)) - XCTAssert(body.contains(#"1.2.3"#)) - } + // test catchall + try await app.test(.GET, "/owner/package/~/documentation/target/a/b#anchor") { res async in + #expect(res.status == .ok) + #expect(res.content.contentType?.description == "text/html; charset=utf-8") + let body = String(buffer: res.body) + assertSnapshot(of: body, as: .html, named: "index") + // Call out a couple of specific snippets in the html + #expect(body.contains(#""#)) + #expect(!body.contains(#"1.0.0"#)) + } - // Test case insensitive path. - try await app.test(.GET, "/Owner/Package/1.2.3/documentation/target/A/b#Anchor") { @MainActor res in - await Task.yield() // essential to avoid deadlocking - XCTAssertEqual(res.status, .ok) - XCTAssertEqual(res.content.contentType?.description, "text/html; charset=utf-8") - let body = String(buffer: res.body) - assertSnapshot(of: body, as: .html, named: "index-target-a-b-mixed-case") - // Call out a couple of specific snippets in the html - XCTAssert(body.contains(#"var baseUrl = "/owner/package/1.2.3/""#)) - XCTAssert(body.contains(#""#)) - XCTAssert(body.contains(#""#)) - XCTAssert(body.contains(#"1.2.3"#)) + // Test case insensitive path. + try await app.test(.GET, "/Owner/Package/~/documentation/target/A/b#anchor") { res async in + #expect(res.status == .ok) + #expect(res.content.contentType?.description == "text/html; charset=utf-8") + let body = String(buffer: res.body) + assertSnapshot(of: body, as: .html, named: "index-mixed-case") + // Call out a couple of specific snippets in the html + #expect(body.contains(#""#)) + #expect(!body.contains(#"1.0.0"#)) + } } } } - func test_documentation_routes_no_archive() async throws { - // Test documentation routes when no archive is in the path + @Test func documentation_routes_ref() async throws { + // Test the documentation routes with a reference: + // /owner/package/documentation/{reference} + various path elements try await withDependencies { $0.environment.awsDocsBucket = { "docs-bucket" } $0.httpClient.fetchDocumentation = { @Sendable _ in .ok(body: .mockIndexHTML()) } + $0.timeZone = .utc } operation: { - // setup - let pkg = try await savePackage(on: app.db, "1") - try await Repository(package: pkg, name: "package", owner: "owner") + try await withApp { app in + // setup + let pkg = try await savePackage(on: app.db, "1") + try await Repository(package: pkg, name: "package", owner: "owner") + .save(on: app.db) + try await Version(package: pkg, + commit: "0123456789", + commitDate: .t0, + docArchives: [.init(name: "target", title: "Target")], + latest: .defaultBranch, + packageName: "pkg", + reference: .branch("main")) + .save(on: app.db) + try await Version(package: pkg, + commit: "9876543210", + commitDate: .t0, + docArchives: [.init(name: "target", title: "Target")], + latest: .release, + packageName: "pkg", + reference: .tag(1, 2, 3)) .save(on: app.db) - try await Version(package: pkg, - commit: "0123456789", - commitDate: .t0, - docArchives: [.init(name: "target", title: "Target")], - latest: .defaultBranch, - packageName: "pkg", - reference: .branch("main")) - .save(on: app.db) - try await Version(package: pkg, - commit: "9876543210", - commitDate: .t0, - docArchives: [.init(name: "target", title: "Target")], - latest: .release, - packageName: "pkg", - reference: .tag(1, 0, 0)) - .save(on: app.db) - // MUT - try await app.test(.GET, "/owner/package/main/documentation") { res async in - XCTAssertEqual(res.status, .seeOther) - XCTAssertEqual(res.headers.location, "/owner/package/main/documentation/target") - } - try await app.test(.GET, "/owner/package/1.0.0/documentation") { res async in - XCTAssertEqual(res.status, .seeOther) - XCTAssertEqual(res.headers.location, "/owner/package/1.0.0/documentation/target") + // MUT + + // test partially qualified route (no archive) + try await app.test(.GET, "/owner/package/1.2.3/documentation") { @Sendable res async in + #expect(res.status == .seeOther) + #expect(res.headers.location == "/owner/package/1.2.3/documentation/target") + } + + // test fully qualified route + try await app.test(.GET, "/owner/package/1.2.3/documentation/target") { res async in + #expect(res.status == .ok) + #expect(res.content.contentType?.description == "text/html; charset=utf-8") + let body = String(buffer: res.body) + assertSnapshot(of: body, as: .html, named: "index-target") + // Call out a couple of specific snippets in the html + #expect(body.contains(#"var baseUrl = "/owner/package/1.2.3/""#)) + #expect(body.contains(#""#)) + #expect(body.contains(#""#)) + #expect(body.contains(#"1.2.3"#)) + } + + // test catchall + try await app.test(.GET, "/owner/package/1.2.3/documentation/target/a/b#anchor") { res async in + #expect(res.status == .ok) + #expect(res.content.contentType?.description == "text/html; charset=utf-8") + let body = String(buffer: res.body) + assertSnapshot(of: body, as: .html, named: "index-target-a-b") + // Call out a couple of specific snippets in the html + #expect(body.contains(#"var baseUrl = "/owner/package/1.2.3/""#)) + #expect(body.contains(#""#)) + #expect(body.contains(#""#)) + #expect(body.contains(#"1.2.3"#)) + } + + // Test case insensitive path. + try await app.test(.GET, "/Owner/Package/1.2.3/documentation/target/A/b#Anchor") { res async in + #expect(res.status == .ok) + #expect(res.content.contentType?.description == "text/html; charset=utf-8") + let body = String(buffer: res.body) + assertSnapshot(of: body, as: .html, named: "index-target-a-b-mixed-case") + // Call out a couple of specific snippets in the html + #expect(body.contains(#"var baseUrl = "/owner/package/1.2.3/""#)) + #expect(body.contains(#""#)) + #expect(body.contains(#""#)) + #expect(body.contains(#"1.2.3"#)) + } } - try await app.test(.GET, "/owner/package/~/documentation") { res async in - XCTAssertEqual(res.status, .seeOther) - XCTAssertEqual(res.headers.location, "/owner/package/1.0.0/documentation/target") + } + } + + @Test func documentation_routes_no_archive() async throws { + // Test documentation routes when no archive is in the path + try await withDependencies { + $0.environment.awsDocsBucket = { "docs-bucket" } + $0.httpClient.fetchDocumentation = { @Sendable _ in .ok(body: .mockIndexHTML()) } + } operation: { + try await withApp { app in + // setup + let pkg = try await savePackage(on: app.db, "1") + try await Repository(package: pkg, name: "package", owner: "owner") + .save(on: app.db) + try await Version(package: pkg, + commit: "0123456789", + commitDate: .t0, + docArchives: [.init(name: "target", title: "Target")], + latest: .defaultBranch, + packageName: "pkg", + reference: .branch("main")) + .save(on: app.db) + try await Version(package: pkg, + commit: "9876543210", + commitDate: .t0, + docArchives: [.init(name: "target", title: "Target")], + latest: .release, + packageName: "pkg", + reference: .tag(1, 0, 0)) + .save(on: app.db) + + // MUT + try await app.test(.GET, "/owner/package/main/documentation") { res async in + #expect(res.status == .seeOther) + #expect(res.headers.location == "/owner/package/main/documentation/target") + } + try await app.test(.GET, "/owner/package/1.0.0/documentation") { res async in + #expect(res.status == .seeOther) + #expect(res.headers.location == "/owner/package/1.0.0/documentation/target") + } + try await app.test(.GET, "/owner/package/~/documentation") { res async in + #expect(res.status == .seeOther) + #expect(res.headers.location == "/owner/package/1.0.0/documentation/target") + } } } } - func test_documentationRoot_notFound() async throws { + @Test func documentationRoot_notFound() async throws { try await withDependencies { $0.environment.dbId = { nil } $0.httpClient.fetchDocumentation = { @Sendable _ in .notFound } } operation: { - // setup - let pkg = try await savePackage(on: app.db, "1") - try await Repository(package: pkg, name: "package", owner: "owner") + try await withApp { app in + // setup + let pkg = try await savePackage(on: app.db, "1") + try await Repository(package: pkg, name: "package", owner: "owner") + .save(on: app.db) + try await Version(package: pkg, + commit: "0123456789", + commitDate: .t0, + docArchives: [], // No docArchives! + latest: .defaultBranch, + packageName: "pkg", + reference: .branch("main")) + .save(on: app.db) + try await Version(package: pkg, + commit: "9876543210", + commitDate: .t0, + docArchives: [], // No docArchives! + latest: .release, + packageName: "pkg", + reference: .tag(1, 0, 0)) .save(on: app.db) - try await Version(package: pkg, - commit: "0123456789", - commitDate: .t0, - docArchives: [], // No docArchives! - latest: .defaultBranch, - packageName: "pkg", - reference: .branch("main")) - .save(on: app.db) - try await Version(package: pkg, - commit: "9876543210", - commitDate: .t0, - docArchives: [], // No docArchives! - latest: .release, - packageName: "pkg", - reference: .tag(1, 0, 0)) - .save(on: app.db) - // MUT - try await app.test(.GET, "/owner/package/main/documentation") { res async in - XCTAssertEqual(res.status, .notFound) - } - try await app.test(.GET, "/owner/package/1.0.0/documentation") { res async in - XCTAssertEqual(res.status, .notFound) + // MUT + try await app.test(.GET, "/owner/package/main/documentation") { res async in + #expect(res.status == .notFound) + } + try await app.test(.GET, "/owner/package/1.0.0/documentation") { res async in + #expect(res.status == .notFound) + } } } } - func test_documentation_404() async throws { + @Test func documentation_404() async throws { // Test conversion of any doc fetching errors into 404s. try await withDependencies { $0.environment.awsDocsBucket = { "docs-bucket" } $0.environment.dbId = { nil } $0.httpClient.fetchDocumentation = { @Sendable uri in .badRequest } } operation: { - // setup - let pkg = try await savePackage(on: app.db, "1") - try await Repository(package: pkg, name: "package", owner: "owner") - .save(on: app.db) - try await Version(package: pkg, latest: .defaultBranch, packageName: "pkg") - .save(on: app.db) + try await withApp { app in + // setup + let pkg = try await savePackage(on: app.db, "1") + try await Repository(package: pkg, name: "package", owner: "owner") + .save(on: app.db) + try await Version(package: pkg, latest: .defaultBranch, packageName: "pkg") + .save(on: app.db) - // MUT - // test base url - try await app.test(.GET, "/owner/package/1.2.3/documentation") { res async in - XCTAssertEqual(res.status, .notFound) - } + // MUT + // test base url + try await app.test(.GET, "/owner/package/1.2.3/documentation") { res async in + #expect(res.status == .notFound) + } - // test path a/b - try await app.test(.GET, "/owner/package/1.2.3/documentation/a/b") { res async in - XCTAssertEqual(res.status, .notFound) + // test path a/b + try await app.test(.GET, "/owner/package/1.2.3/documentation/a/b") { res async in + #expect(res.status == .notFound) + } } } } - func test_documentation_error() async throws { + @Test func documentation_error() async throws { // Test behaviour when fetchDocumentation throws struct SomeError: Error { } try await withDependencies { @@ -931,246 +937,260 @@ class PackageController_routesTests: SnapshotTestCase { $0.environment.dbId = { nil } $0.httpClient.fetchDocumentation = { @Sendable _ in throw SomeError() } } operation: { - let pkg = try await savePackage(on: app.db, "1") - try await Repository(package: pkg, name: "package", owner: "owner") + try await withApp { app in + let pkg = try await savePackage(on: app.db, "1") + try await Repository(package: pkg, name: "package", owner: "owner") + .save(on: app.db) + try await Version(package: pkg, + commit: "123", + commitDate: .t0, + docArchives: [.init(name: "foo", title: "Foo")], + latest: .defaultBranch, + packageName: "pkg", + reference: .tag(1, 2, 3)) .save(on: app.db) - try await Version(package: pkg, - commit: "123", - commitDate: .t0, - docArchives: [.init(name: "foo", title: "Foo")], - latest: .defaultBranch, - packageName: "pkg", - reference: .tag(1, 2, 3)) - .save(on: app.db) - // MUT - try await app.test(.GET, "/owner/package/1.2.3/documentation") { res async in - XCTAssertEqual(res.status, .seeOther) - XCTAssertEqual(res.headers.location, "/owner/package/1.2.3/documentation/foo") - } - try await app.test(.GET, "/owner/package/1.2.3/documentation/foo") { res async in - // hits Current.fetchDocumentation which throws, converted to notFound - // Regression test for https://github.com/SwiftPackageIndex/SwiftPackageIndex-Server/issues/2015 - XCTAssertEqual(res.status, .notFound) + // MUT + try await app.test(.GET, "/owner/package/1.2.3/documentation") { res async in + #expect(res.status == .seeOther) + #expect(res.headers.location == "/owner/package/1.2.3/documentation/foo") + } + try await app.test(.GET, "/owner/package/1.2.3/documentation/foo") { res async in + // hits Current.fetchDocumentation which throws, converted to notFound + // Regression test for https://github.com/SwiftPackageIndex/SwiftPackageIndex-Server/issues/2015 + #expect(res.status == .notFound) + } } } } - func test_documentation_current_css() async throws { + @Test func documentation_current_css() async throws { try await withDependencies { $0.currentReferenceCache = .disabled $0.environment.awsDocsBucket = { "docs-bucket" } $0.httpClient.fetchDocumentation = App.HTTPClient.echoURL() } operation: { - // setup - let pkg = try await savePackage(on: app.db, "1") - try await Repository(package: pkg, name: "package", owner: "owner") + try await withApp { app in + // setup + let pkg = try await savePackage(on: app.db, "1") + try await Repository(package: pkg, name: "package", owner: "owner") + .save(on: app.db) + try await Version(package: pkg, + docArchives: [.init(name: "target", title: "Target")], + latest: .defaultBranch, + reference: .branch("main")) .save(on: app.db) - try await Version(package: pkg, - docArchives: [.init(name: "target", title: "Target")], - latest: .defaultBranch, - reference: .branch("main")) - .save(on: app.db) - // MUT - // test base url - try await app.test(.GET, "/owner/package/~/css/a") { res async in - XCTAssertEqual(res.status, .ok) - XCTAssertEqual(res.content.contentType?.description, "text/css") - XCTAssertEqual(res.body.asString(), "/owner/package/main/css/a") - } + // MUT + // test base url + try await app.test(.GET, "/owner/package/~/css/a") { res async in + #expect(res.status == .ok) + #expect(res.content.contentType?.description == "text/css") + #expect(res.body.asString() == "/owner/package/main/css/a") + } - // test path a/b - try await app.test(.GET, "/owner/package/~/css/a/b") { res async in - XCTAssertEqual(res.status, .ok) - XCTAssertEqual(res.content.contentType?.description, "text/css") - XCTAssertEqual(res.body.asString(), "/owner/package/main/css/a/b") + // test path a/b + try await app.test(.GET, "/owner/package/~/css/a/b") { res async in + #expect(res.status == .ok) + #expect(res.content.contentType?.description == "text/css") + #expect(res.body.asString() == "/owner/package/main/css/a/b") + } } } } - func test_documentation_ref_css() throws { - try withDependencies { + @Test func documentation_ref_css() async throws { + try await withDependencies { $0.environment.awsDocsBucket = { "docs-bucket" } $0.httpClient.fetchDocumentation = App.HTTPClient.echoURL() } operation: { - // MUT - // test base url - try app.test(.GET, "/owner/package/1.2.3/css/a") { - XCTAssertEqual($0.status, .ok) - XCTAssertEqual($0.content.contentType?.description, "text/css") - XCTAssertEqual($0.body.asString(), "/owner/package/1.2.3/css/a") - } + try await withApp { app in + // MUT + // test base url + try await app.test(.GET, "/owner/package/1.2.3/css/a") { res async in + #expect(res.status == .ok) + #expect(res.content.contentType?.description == "text/css") + #expect(res.body.asString() == "/owner/package/1.2.3/css/a") + } - // test path a/b - try app.test(.GET, "/owner/package/1.2.3/css/a/b") { - XCTAssertEqual($0.status, .ok) - XCTAssertEqual($0.content.contentType?.description, "text/css") - XCTAssertEqual($0.body.asString(), "/owner/package/1.2.3/css/a/b") + // test path a/b + try await app.test(.GET, "/owner/package/1.2.3/css/a/b") { res async in + #expect(res.status == .ok) + #expect(res.content.contentType?.description == "text/css") + #expect(res.body.asString() == "/owner/package/1.2.3/css/a/b") + } } } } - func test_documentation_current_js() async throws { + @Test func documentation_current_js() async throws { try await withDependencies { $0.currentReferenceCache = .disabled $0.environment.awsDocsBucket = { "docs-bucket" } $0.httpClient.fetchDocumentation = App.HTTPClient.echoURL() } operation: { - // setup - let pkg = try await savePackage(on: app.db, "1") - try await Repository(package: pkg, name: "package", owner: "owner") + try await withApp { app in + // setup + let pkg = try await savePackage(on: app.db, "1") + try await Repository(package: pkg, name: "package", owner: "owner") + .save(on: app.db) + try await Version(package: pkg, + docArchives: [.init(name: "target", title: "Target")], + latest: .defaultBranch, + reference: .branch("main")) .save(on: app.db) - try await Version(package: pkg, - docArchives: [.init(name: "target", title: "Target")], - latest: .defaultBranch, - reference: .branch("main")) - .save(on: app.db) - // MUT - // test base url - try await app.test(.GET, "/owner/package/~/js/a") { res async in - XCTAssertEqual(res.status, .ok) - XCTAssertEqual(res.content.contentType?.description, "application/javascript") - XCTAssertEqual(res.body.asString(), "/owner/package/main/js/a") - } + // MUT + // test base url + try await app.test(.GET, "/owner/package/~/js/a") { res async in + #expect(res.status == .ok) + #expect(res.content.contentType?.description == "application/javascript") + #expect(res.body.asString() == "/owner/package/main/js/a") + } - // test path a/b - try await app.test(.GET, "/owner/package/~/js/a/b") { res async in - XCTAssertEqual(res.status, .ok) - XCTAssertEqual(res.content.contentType?.description, "application/javascript") - XCTAssertEqual(res.body.asString(), "/owner/package/main/js/a/b") + // test path a/b + try await app.test(.GET, "/owner/package/~/js/a/b") { res async in + #expect(res.status == .ok) + #expect(res.content.contentType?.description == "application/javascript") + #expect(res.body.asString() == "/owner/package/main/js/a/b") + } } } } - func test_documentation_ref_js() throws { - try withDependencies { + @Test func documentation_ref_js() async throws { + try await withDependencies { $0.environment.awsDocsBucket = { "docs-bucket" } $0.httpClient.fetchDocumentation = App.HTTPClient.echoURL() } operation: { - // MUT - // test base url - try app.test(.GET, "/owner/package/1.2.3/js/a") { - XCTAssertEqual($0.status, .ok) - XCTAssertEqual($0.content.contentType?.description, "application/javascript") - XCTAssertEqual($0.body.asString(), "/owner/package/1.2.3/js/a") - } + try await withApp { app in + // MUT + // test base url + try await app.test(.GET, "/owner/package/1.2.3/js/a") { res async in + #expect(res.status == .ok) + #expect(res.content.contentType?.description == "application/javascript") + #expect(res.body.asString() == "/owner/package/1.2.3/js/a") + } - // test path a/b - try app.test(.GET, "/owner/package/1.2.3/js/a/b") { - XCTAssertEqual($0.status, .ok) - XCTAssertEqual($0.content.contentType?.description, "application/javascript") - XCTAssertEqual($0.body.asString(), "/owner/package/1.2.3/js/a/b") + // test path a/b + try await app.test(.GET, "/owner/package/1.2.3/js/a/b") { res async in + #expect(res.status == .ok) + #expect(res.content.contentType?.description == "application/javascript") + #expect(res.body.asString() == "/owner/package/1.2.3/js/a/b") + } } } } - func test_documentation_current_data() async throws { + @Test func documentation_current_data() async throws { try await withDependencies { $0.currentReferenceCache = .disabled $0.environment.awsDocsBucket = { "docs-bucket" } $0.httpClient.fetchDocumentation = App.HTTPClient.echoURL() } operation: { - // setup - let pkg = try await savePackage(on: app.db, "1") - try await Repository(package: pkg, name: "package", owner: "owner") + try await withApp { app in + // setup + let pkg = try await savePackage(on: app.db, "1") + try await Repository(package: pkg, name: "package", owner: "owner") + .save(on: app.db) + try await Version(package: pkg, + docArchives: [.init(name: "target", title: "Target")], + latest: .defaultBranch, + reference: .branch("main")) .save(on: app.db) - try await Version(package: pkg, - docArchives: [.init(name: "target", title: "Target")], - latest: .defaultBranch, - reference: .branch("main")) - .save(on: app.db) - // MUT - // test base url - try await app.test(.GET, "/owner/package/~/data/a") { res async in - XCTAssertEqual(res.status, .ok) - XCTAssertEqual(res.content.contentType?.description, "application/octet-stream") - XCTAssertEqual(res.body.asString(), "/owner/package/main/data/a") - } + // MUT + // test base url + try await app.test(.GET, "/owner/package/~/data/a") { res async in + #expect(res.status == .ok) + #expect(res.content.contentType?.description == "application/octet-stream") + #expect(res.body.asString() == "/owner/package/main/data/a") + } - // test path a/b - try await app.test(.GET, "/owner/package/~/data/a/b") { res async in - XCTAssertEqual(res.status, .ok) - XCTAssertEqual(res.content.contentType?.description, "application/octet-stream") - XCTAssertEqual(res.body.asString(), "/owner/package/main/data/a/b") - } + // test path a/b + try await app.test(.GET, "/owner/package/~/data/a/b") { res async in + #expect(res.status == .ok) + #expect(res.content.contentType?.description == "application/octet-stream") + #expect(res.body.asString() == "/owner/package/main/data/a/b") + } - // test case-insensitivity - // https://github.com/SwiftPackageIndex/SwiftPackageIndex-Server/issues/2168 - try await app.test(.GET, "/owner/package/~/data/documentation/Foo.json") { res async in - XCTAssertEqual(res.status, .ok) - XCTAssertEqual(res.content.contentType?.description, "application/octet-stream") - XCTAssertEqual(res.body.asString(), - "/owner/package/main/data/documentation/foo.json") + // test case-insensitivity + // https://github.com/SwiftPackageIndex/SwiftPackageIndex-Server/issues/2168 + try await app.test(.GET, "/owner/package/~/data/documentation/Foo.json") { res async in + #expect(res.status == .ok) + #expect(res.content.contentType?.description == "application/octet-stream") + #expect(res.body.asString() == "/owner/package/main/data/documentation/foo.json") + } } } } - func test_documentation_ref_data() throws { - try withDependencies { + @Test func documentation_ref_data() async throws { + try await withDependencies { $0.environment.awsDocsBucket = { "docs-bucket" } $0.httpClient.fetchDocumentation = App.HTTPClient.echoURL() } operation: { - // MUT - // test base url - try app.test(.GET, "/owner/package/1.2.3/data/a") { - XCTAssertEqual($0.status, .ok) - XCTAssertEqual($0.content.contentType?.description, "application/octet-stream") - XCTAssertEqual($0.body.asString(), "/owner/package/1.2.3/data/a") - } + try await withApp { app in + // MUT + // test base url + try await app.test(.GET, "/owner/package/1.2.3/data/a") { res async in + #expect(res.status == .ok) + #expect(res.content.contentType?.description == "application/octet-stream") + #expect(res.body.asString() == "/owner/package/1.2.3/data/a") + } - // test path a/b - try app.test(.GET, "/owner/package/1.2.3/data/a/b") { - XCTAssertEqual($0.status, .ok) - XCTAssertEqual($0.content.contentType?.description, "application/octet-stream") - XCTAssertEqual($0.body.asString(), "/owner/package/1.2.3/data/a/b") - } + // test path a/b + try await app.test(.GET, "/owner/package/1.2.3/data/a/b") { res async in + #expect(res.status == .ok) + #expect(res.content.contentType?.description == "application/octet-stream") + #expect(res.body.asString() == "/owner/package/1.2.3/data/a/b") + } - // test case-insensitivity - // https://github.com/SwiftPackageIndex/SwiftPackageIndex-Server/issues/2168 - try app.test(.GET, "/apple/swift-nio/main/data/documentation/NIOCore.json") { - XCTAssertEqual($0.status, .ok) - XCTAssertEqual($0.content.contentType?.description, "application/octet-stream") - XCTAssertEqual($0.body.asString(), - "/apple/swift-nio/main/data/documentation/niocore.json") + // test case-insensitivity + // https://github.com/SwiftPackageIndex/SwiftPackageIndex-Server/issues/2168 + try await app.test(.GET, "/apple/swift-nio/main/data/documentation/NIOCore.json") { res async in + #expect(res.status == .ok) + #expect(res.content.contentType?.description == "application/octet-stream") + #expect(res.body.asString() == "/apple/swift-nio/main/data/documentation/niocore.json") + } } } } - func test_documentation_canonicalCapitalisation() async throws { + @Test func documentation_canonicalCapitalisation() async throws { + // The `packageName` property on the `Version` has been set to the lower-cased version so + // we can be sure the canonical URL is built from the properties on the `Repository` model. try await withDependencies { $0.environment.awsDocsBucket = { "docs-bucket" } $0.httpClient.fetchDocumentation = App.HTTPClient.echoURL() $0.timeZone = .utc } operation: { - // The `packageName` property on the `Version` has been set to the lower-cased version so - // we can be sure the canonical URL is built from the properties on the `Repository` model. - let pkg = try await savePackage(on: app.db, "1") - try await Repository(package: pkg, name: "Package", owner: "Owner") + try await withApp { app in + let pkg = try await savePackage(on: app.db, "1") + try await Repository(package: pkg, name: "Package", owner: "Owner") + .save(on: app.db) + try await Version(package: pkg, + commit: "0123456789", + commitDate: .t0, + docArchives: [.init(name: "docs", title: "Docs")], + latest: .defaultBranch, + packageName: "package", + reference: .tag(1, 2, 3)) .save(on: app.db) - try await Version(package: pkg, - commit: "0123456789", - commitDate: .t0, - docArchives: [.init(name: "docs", title: "Docs")], - latest: .defaultBranch, - packageName: "package", - reference: .tag(1, 2, 3)) - .save(on: app.db) - try await app.test(.GET, "/owner/package/1.2.3/documentation/a/b") { res async throws in - let document = try SwiftSoup.parse(res.body.string) - let linkElements = try document.select("link[rel='canonical']") - XCTAssertEqual(linkElements.count, 1) + try await app.test(.GET, "/owner/package/1.2.3/documentation/a/b") { res async throws in + let document = try SwiftSoup.parse(res.body.string) + let linkElements = try document.select("link[rel='canonical']") + #expect(linkElements.count == 1) - let href = try XCTUnwrap(linkElements.first()?.attr("href")) - XCTAssertEqual(href, "/Owner/Package/1.2.3/documentation/a/b") + let href = try #require(try linkElements.first()?.attr("href")) + #expect(href == "/Owner/Package/1.2.3/documentation/a/b") + } } } } - func test_documentation_issue_2287() async throws { + @Test func documentation_issue_2287() async throws { // https://github.com/SwiftPackageIndex/SwiftPackageIndex-Server/issues/2287 // Ensure references are path encoded try await withDependencies { @@ -1179,65 +1199,64 @@ class PackageController_routesTests: SnapshotTestCase { $0.httpClient.fetchDocumentation = { @Sendable _ in .ok(body: .mockIndexHTML()) } $0.timeZone = .utc } operation: { - // setup - let pkg = try await savePackage(on: app.db, "1") - try await Repository(package: pkg, name: "package", owner: "owner") + try await withApp { app in + // setup + let pkg = try await savePackage(on: app.db, "1") + try await Repository(package: pkg, name: "package", owner: "owner") + .save(on: app.db) + try await Version(package: pkg, + commit: "0123456789", + commitDate: .t0, + docArchives: [.init(name: "target", title: "Target")], + latest: .defaultBranch, + packageName: "pkg", + reference: .branch("feature/1.2.3")) .save(on: app.db) - try await Version(package: pkg, - commit: "0123456789", - commitDate: .t0, - docArchives: [.init(name: "target", title: "Target")], - latest: .defaultBranch, - packageName: "pkg", - reference: .branch("feature/1.2.3")) - .save(on: app.db) - // MUT + // MUT - // test default path - try await app.test(.GET, "/owner/package/~/documentation/target") { @MainActor res in - await Task.yield() // essential to avoid deadlocking - XCTAssertEqual(res.status, .ok) - let body = String(buffer: res.body) - assertSnapshot(of: body, as: .html, named: "current-index") - // Call out a couple of specific snippets in the html - XCTAssert(body.contains(#"var baseUrl = "/owner/package/~/""#)) - XCTAssert(body.contains(#""#)) - XCTAssertFalse(body.contains(#"feature/1.2.3"#)) - XCTAssert(body.contains(#"
  • feature/1.2.3
  • "#)) - } + // test default path + try await app.test(.GET, "/owner/package/~/documentation/target") { res async in + #expect(res.status == .ok) + let body = String(buffer: res.body) + assertSnapshot(of: body, as: .html, named: "current-index") + // Call out a couple of specific snippets in the html + #expect(body.contains(#"var baseUrl = "/owner/package/~/""#)) + #expect(body.contains(#""#)) + #expect(!body.contains(#"feature/1.2.3"#)) + #expect(body.contains(#"
  • feature/1.2.3
  • "#)) + } - // test reference root path - try await app.test(.GET, "/owner/package/feature-1.2.3/documentation/target") { @MainActor res in - await Task.yield() // essential to avoid deadlocking - XCTAssertEqual(res.status, .ok) - let body = String(buffer: res.body) - assertSnapshot(of: body, as: .html, named: "ref-index") - // Call out a couple of specific snippets in the html - XCTAssert(body.contains(#"var baseUrl = "/owner/package/feature-1.2.3/""#)) - XCTAssert(body.contains(#""#)) - XCTAssert(body.contains(#""#)) - XCTAssert(body.contains(#"feature-1.2.3"#)) - } + // test reference root path + try await app.test(.GET, "/owner/package/feature-1.2.3/documentation/target") { res async in + #expect(res.status == .ok) + let body = String(buffer: res.body) + assertSnapshot(of: body, as: .html, named: "ref-index") + // Call out a couple of specific snippets in the html + #expect(body.contains(#"var baseUrl = "/owner/package/feature-1.2.3/""#)) + #expect(body.contains(#""#)) + #expect(body.contains(#""#)) + #expect(body.contains(#"feature-1.2.3"#)) + } - // test path a/b - try await app.test(.GET, "/owner/package/feature-1.2.3/documentation/a/b") { @MainActor res in - await Task.yield() // essential to avoid deadlocking - XCTAssertEqual(res.status, .ok) - XCTAssertEqual(res.content.contentType?.description, "text/html; charset=utf-8") - let body = String(buffer: res.body) - assertSnapshot(of: body, as: .html, named: "ref-index-path") - // Call out a couple of specific snippets in the html - XCTAssert(body.contains(#"var baseUrl = "/owner/package/feature-1.2.3/""#)) - XCTAssert(body.contains(#""#)) - XCTAssert(body.contains(#""#)) - XCTAssert(body.contains(#"feature-1.2.3"#)) + // test path a/b + try await app.test(.GET, "/owner/package/feature-1.2.3/documentation/a/b") { res async in + #expect(res.status == .ok) + #expect(res.content.contentType?.description == "text/html; charset=utf-8") + let body = String(buffer: res.body) + assertSnapshot(of: body, as: .html, named: "ref-index-path") + // Call out a couple of specific snippets in the html + #expect(body.contains(#"var baseUrl = "/owner/package/feature-1.2.3/""#)) + #expect(body.contains(#""#)) + #expect(body.contains(#""#)) + #expect(body.contains(#"feature-1.2.3"#)) + } } } } - func test_documentation_routes_tutorials() async throws { + @Test func documentation_routes_tutorials() async throws { try await withDependencies { $0.currentReferenceCache = .disabled $0.environment.awsDocsBucket = { "docs-bucket" } @@ -1245,101 +1264,107 @@ class PackageController_routesTests: SnapshotTestCase { $0.httpClient.fetchDocumentation = { @Sendable _ in .ok(body: .mockIndexHTML()) } $0.timeZone = .utc } operation: { - // setup - let pkg = try await savePackage(on: app.db, "1") - try await Repository(package: pkg, name: "package", owner: "owner") + try await withApp { app in + // setup + let pkg = try await savePackage(on: app.db, "1") + try await Repository(package: pkg, name: "package", owner: "owner") + .save(on: app.db) + try await Version(package: pkg, + commit: "0123456789", + commitDate: .t0, + docArchives: [.init(name: "docs", title: "Docs")], + latest: .defaultBranch, + packageName: "pkg", + reference: .branch("main")) + .save(on: app.db) + try await Version(package: pkg, + commit: "9876543210", + commitDate: .t0, + docArchives: [.init(name: "docs", title: "Docs")], + latest: .release, + packageName: "pkg", + reference: .tag(1, 0, 0)) .save(on: app.db) - try await Version(package: pkg, - commit: "0123456789", - commitDate: .t0, - docArchives: [.init(name: "docs", title: "Docs")], - latest: .defaultBranch, - packageName: "pkg", - reference: .branch("main")) - .save(on: app.db) - try await Version(package: pkg, - commit: "9876543210", - commitDate: .t0, - docArchives: [.init(name: "docs", title: "Docs")], - latest: .release, - packageName: "pkg", - reference: .tag(1, 0, 0)) - .save(on: app.db) - // MUT - try await app.test(.GET, "/owner/package/~/tutorials") { res async in - XCTAssertEqual(res.status, .notFound) - } - try await app.test(.GET, "/owner/package/~/tutorials/foo") { @MainActor res in - await Task.yield() // essential to avoid deadlocking - XCTAssertEqual(res.status, .ok) - let body = String(buffer: res.body) - assertSnapshot(of: body, as: .html, named: "index") - XCTAssert(body.contains(#"var baseUrl = "/owner/package/~/""#)) - } - try await app.test(.GET, "/owner/package/~/tutorials/foo#anchor") { @MainActor res in - await Task.yield() // essential to avoid deadlocking - XCTAssertEqual(res.status, .ok) - let body = String(buffer: res.body) - assertSnapshot(of: body, as: .html, named: "index") - XCTAssert(body.contains(#"var baseUrl = "/owner/package/~/""#)) + // MUT + try await app.test(.GET, "/owner/package/~/tutorials") { res async in + #expect(res.status == .notFound) + } + try await app.test(.GET, "/owner/package/~/tutorials/foo") { res async in + #expect(res.status == .ok) + let body = String(buffer: res.body) + assertSnapshot(of: body, as: .html, named: "index") + #expect(body.contains(#"var baseUrl = "/owner/package/~/""#)) + } + try await app.test(.GET, "/owner/package/~/tutorials/foo#anchor") { res async in + #expect(res.status == .ok) + let body = String(buffer: res.body) + assertSnapshot(of: body, as: .html, named: "index") + #expect(body.contains(#"var baseUrl = "/owner/package/~/""#)) + } } } } - func test_favicon() throws { - try withDependencies { + @Test func favicon() async throws { + try await withDependencies { $0.environment.awsDocsBucket = { "docs-bucket" } $0.httpClient.fetchDocumentation = App.HTTPClient .echoURL(headers: ["content-type": "application/octet-stream"]) } operation: { - // MUT - try app.test(.GET, "/owner/package/1.2.3/favicon.ico") { - XCTAssertEqual($0.status, .ok) - XCTAssertEqual($0.content.contentType?.description, "application/octet-stream") - XCTAssertEqual($0.body.asString(), "/owner/package/1.2.3/favicon.ico") - } + try await withApp { app in + // MUT + try await app.test(.GET, "/owner/package/1.2.3/favicon.ico") { res async in + #expect(res.status == .ok) + #expect(res.content.contentType?.description == "application/octet-stream") + #expect(res.body.asString() == "/owner/package/1.2.3/favicon.ico") + } - try app.test(.GET, "/owner/package/1.2.3/favicon.svg") { - XCTAssertEqual($0.status, .ok) - XCTAssertEqual($0.content.contentType?.description, "application/octet-stream") - XCTAssertEqual($0.body.asString(), "/owner/package/1.2.3/favicon.svg") + try await app.test(.GET, "/owner/package/1.2.3/favicon.svg") { res async in + #expect(res.status == .ok) + #expect(res.content.contentType?.description == "application/octet-stream") + #expect(res.body.asString() == "/owner/package/1.2.3/favicon.svg") + } } } } - func test_themeSettings() throws { - try withDependencies { + @Test func themeSettings() async throws { + try await withDependencies { $0.environment.awsDocsBucket = { "docs-bucket" } $0.httpClient.fetchDocumentation = App.HTTPClient .echoURL(headers: ["content-type": "application/json"]) } operation: { - // MUT - try app.test(.GET, "/owner/package/1.2.3/theme-settings.json") { - XCTAssertEqual($0.status, .ok) - XCTAssertEqual($0.content.contentType?.description, "application/json") - XCTAssertEqual($0.body.asString(), "/owner/package/1.2.3/theme-settings.json") + try await withApp { app in + // MUT + try await app.test(.GET, "/owner/package/1.2.3/theme-settings.json") { res async in + #expect(res.status == .ok) + #expect(res.content.contentType?.description == "application/json") + #expect(res.body.asString() == "/owner/package/1.2.3/theme-settings.json") + } } } } - func test_linkablePaths() throws { - try withDependencies { + @Test func linkablePaths() async throws { + try await withDependencies { $0.environment.awsDocsBucket = { "docs-bucket" } $0.httpClient.fetchDocumentation = App.HTTPClient .echoURL(headers: ["content-type": "application/json"]) } operation: { + try await withApp { app in // MUT - try app.test(.GET, "/owner/package/1.2.3/linkable-paths.json") { - XCTAssertEqual($0.status, .ok) - XCTAssertEqual($0.content.contentType?.description, "application/json") - XCTAssertEqual($0.body.asString(), "/owner/package/1.2.3/linkable-paths.json") + try await app.test(.GET, "/owner/package/1.2.3/linkable-paths.json") { res async in + #expect(res.status == .ok) + #expect(res.content.contentType?.description == "application/json") + #expect(res.body.asString() == "/owner/package/1.2.3/linkable-paths.json") + } } } } - func test_tutorial() async throws { + @Test func tutorial() async throws { try await withDependencies { $0.environment.awsDocsBucket = { "docs-bucket" } $0.httpClient.fetchDocumentation = { @Sendable uri in @@ -1348,41 +1373,43 @@ class PackageController_routesTests: SnapshotTestCase { } $0.timeZone = .utc } operation: { - // setup - let pkg = try await savePackage(on: app.db, "1") - try await Repository(package: pkg, name: "package", owner: "owner") + try await withApp { app in + // setup + let pkg = try await savePackage(on: app.db, "1") + try await Repository(package: pkg, name: "package", owner: "owner") + .save(on: app.db) + try await Version(package: pkg, + commit: "0123456789", + commitDate: Date(timeIntervalSince1970: 0), + docArchives: [.init(name: "docs", title: "Docs")], + latest: .defaultBranch, + packageName: "pkg", + reference: .tag(.init(1, 2, 3))) .save(on: app.db) - try await Version(package: pkg, - commit: "0123456789", - commitDate: Date(timeIntervalSince1970: 0), - docArchives: [.init(name: "docs", title: "Docs")], - latest: .defaultBranch, - packageName: "pkg", - reference: .tag(.init(1, 2, 3))) - .save(on: app.db) - // MUT - // test path a/b - try await app.test(.GET, "/owner/package/1.2.3/tutorials/a/b") { res async in - XCTAssertEqual(res.status, .ok) - XCTAssertEqual(res.content.contentType?.description, "text/html; charset=utf-8") - XCTAssertTrue( - res.body.asString().contains("

    /owner/package/1.2.3/tutorials/a/b

    "), - "was: \(res.body.asString())" - ) - // Assert body includes the docc.css stylesheet link (as a test that our proxy header injection works) - XCTAssertTrue(res.body.asString().contains(#""#), - "was: \(res.body.asString())") - } + // MUT + // test path a/b + try await app.test(.GET, "/owner/package/1.2.3/tutorials/a/b") { res async in + #expect(res.status == .ok) + #expect(res.content.contentType?.description == "text/html; charset=utf-8") + #expect( + res.body.asString().contains("

    /owner/package/1.2.3/tutorials/a/b

    "), + "was: \(res.body.asString())" + ) + // Assert body includes the docc.css stylesheet link (as a test that our proxy header injection works) + #expect(res.body.asString().contains(#""#), + "was: \(res.body.asString())") + } - // Test case insensitive path. - try await app.test(.GET, "/Owner/Package/1.2.3/tutorials/a/b") { res async in - XCTAssertEqual(res.status, .ok) + // Test case insensitive path. + try await app.test(.GET, "/Owner/Package/1.2.3/tutorials/a/b") { res async in + #expect(res.status == .ok) + } } } } - func test_documentationVersionArray_subscriptByReference() throws { + @Test func documentationVersionArray_subscriptByReference() throws { let updatedAt = Date(timeIntervalSince1970: 0) let versions: [DocumentationVersion] = [ .init(reference: .branch("main"), ownerName: "owner", @@ -1396,17 +1423,17 @@ class PackageController_routesTests: SnapshotTestCase { ] // MUT - let versionTwoBeta = try XCTUnwrap(versions[reference: "2.0.0-beta1"]) - let semVer = try XCTUnwrap(versionTwoBeta.reference.semVer) - - XCTAssertEqual(semVer.major, 2) - XCTAssertEqual(semVer.minor, 0) - XCTAssertEqual(semVer.patch, 0) - XCTAssertEqual(semVer.preRelease, "beta1") - XCTAssertEqual(semVer.build, "") + let versionTwoBeta = try #require(versions[reference: "2.0.0-beta1"]) + let semVer = try #require(versionTwoBeta.reference.semVer) + + #expect(semVer.major == 2) + #expect(semVer.minor == 0) + #expect(semVer.patch == 0) + #expect(semVer.preRelease == "beta1") + #expect(semVer.build == "") } - func test_documentationVersionArray_latestMajorVersions() throws { + @Test func documentationVersionArray_latestMajorVersions() throws { let updatedAt = Date(timeIntervalSince1970: 0) let docs = DocArchive(name: "docs", title: "Docs") let versions: [DocumentationVersion] = [ @@ -1435,66 +1462,56 @@ class PackageController_routesTests: SnapshotTestCase { // MUT let latestMajorVersions = versions.latestMajorVersions() let latestMajorRerefences = latestMajorVersions.map { "\($0.reference)" } - XCTAssertEqual(latestMajorRerefences, ["1.1.2", "2.1.1", "3.0.0"]) + #expect(latestMajorRerefences == ["1.1.2", "2.1.1", "3.0.0"]) } - func test_siteMap_prod() async throws { + @Test func siteMap_prod() async throws { // Ensure sitemap routing is configured in prod try await withDependencies { $0.environment.current = { .production } } operation: { - // We also need to set up a new app that's configured for production, - // because app.test is not affected by @Dependency overrides. - let prodApp = try await setup(.production) - - try await App.run { + try await withApp(environment: .production) { prodApp in // setup let package = Package(url: URL(stringLiteral: "https://example.com/owner/repo0")) - try await package.save(on: app.db) + try await package.save(on: prodApp.db) try await Repository(package: package, defaultBranch: "default", lastCommitDate: Date.now, - name: "Repo0", owner: "Owner").save(on: app.db) + name: "Repo0", owner: "Owner").save(on: prodApp.db) try await Version(package: package, latest: .defaultBranch, packageName: "SomePackage", - reference: .branch("default")).save(on: app.db) + reference: .branch("default")).save(on: prodApp.db) // MUT try await prodApp.test(.GET, "/owner/repo0/sitemap.xml") { res async in - XCTAssertEqual(res.status, .ok) + #expect(res.status == .ok) } - } defer: { - try await prodApp.asyncShutdown() } } } - func test_siteMap_dev() async throws { + @Test func siteMap_dev() async throws { // Ensure we don't serve sitemaps in dev try await withDependencies { $0.environment.dbId = { nil } } operation: { - let devApp = try await setup(.development) - - try await App.run { + try await withApp(environment: .development) { devApp in // setup let package = Package(url: URL(stringLiteral: "https://example.com/owner/repo0")) - try await package.save(on: app.db) + try await package.save(on: devApp.db) try await Repository(package: package, defaultBranch: "default", lastCommitDate: Date.now, - name: "Repo0", owner: "Owner").save(on: app.db) + name: "Repo0", owner: "Owner").save(on: devApp.db) try await Version(package: package, latest: .defaultBranch, packageName: "SomePackage", - reference: .branch("default")).save(on: app.db) + reference: .branch("default")).save(on: devApp.db) // MUT try await devApp.test(.GET, "/owner/repo0/sitemap.xml") { res async in - XCTAssertEqual(res.status, .notFound) + #expect(res.status == .notFound) } - } defer: { - try await devApp.asyncShutdown() } } } - func test_issue_2288() async throws { + @Test func issue_2288() async throws { // Ensures default branch updates don't introduce a "documentation gap" // https://github.com/SwiftPackageIndex/SwiftPackageIndex-Server/issues/2288 try await withDependencies { @@ -1522,115 +1539,122 @@ class PackageController_routesTests: SnapshotTestCase { } $0.timeZone = .utc } operation: { - // setup - let pkg = try await savePackage(on: app.db, "https://github.com/foo/bar".url, processingStage: .ingestion) - try await Repository(package: pkg, defaultBranch: "main", name: "bar", owner: "foo") - .save(on: app.db) - try await Version(package: pkg, - commit: "0123456789", - commitDate: .t0, - docArchives: [.init(name: "target", title: "Target")], - latest: .defaultBranch, - packageName: "bar", - reference: .branch("main")) + try await withApp { app in + // setup + let pkg = try await savePackage(on: app.db, "https://github.com/foo/bar".url, processingStage: .ingestion) + try await Repository(package: pkg, defaultBranch: "main", name: "bar", owner: "foo") + .save(on: app.db) + try await Version(package: pkg, + commit: "0123456789", + commitDate: .t0, + docArchives: [.init(name: "target", title: "Target")], + latest: .defaultBranch, + packageName: "bar", + reference: .branch("main")) .save(on: app.db) - // Make sure the new commit doesn't get throttled - try await withDependencies { - $0.date.now = .t1 + Constants.branchVersionRefreshDelay + 1 - } operation: { - // Ensure documentation is resolved - try await app.test(.GET, "/foo/bar/~/documentation/target") { @MainActor res in - await Task.yield() // essential to avoid deadlocking - XCTAssertEqual(res.status, .ok) - assertSnapshot(of: String(buffer: res.body), as: .html, named: "index") - } - - // Run analyze to detect a new default branch version - try await Analyze.analyze(client: app.client, database: app.db, mode: .limit(1)) - - // Confirm that analysis has picked up the new version - let commit = try await Version.query(on: app.db).all().map(\.commit) - XCTAssertEqual(commit, ["new-commit"]) - - // Ensure documentation is still being resolved - try await app.test(.GET, "/foo/bar/~/documentation/target") { @MainActor res in - await Task.yield() // essential to avoid deadlocking - XCTAssertEqual(res.status, .ok) - assertSnapshot(of: String(buffer: res.body), as: .html, named: "index") + // Make sure the new commit doesn't get throttled + try await withDependencies { + $0.date.now = .t1 + Constants.branchVersionRefreshDelay + 1 + } operation: { + // Ensure documentation is resolved + try await app.test(.GET, "/foo/bar/~/documentation/target") { res async in + #expect(res.status == .ok) + assertSnapshot(of: String(buffer: res.body), as: .html, named: "index") + } + + // Run analyze to detect a new default branch version + try await Analyze.analyze(client: app.client, database: app.db, mode: .limit(1)) + + // Confirm that analysis has picked up the new version + let commit = try await Version.query(on: app.db).all().map(\.commit) + #expect(commit == ["new-commit"]) + + // Ensure documentation is still being resolved + try await app.test(.GET, "/foo/bar/~/documentation/target") { res async in + #expect(res.status == .ok) + assertSnapshot(of: String(buffer: res.body), as: .html, named: "index") + } } } } } - func test_getDocRoute_documentation() async throws { + @Test func getDocRoute_documentation() async throws { // owner/repo/1.2.3/documentation/archive - let req = Request(application: app, url: "", on: app.eventLoopGroup.next()) - req.parameters.set("owner", to: "owner") - req.parameters.set("repository", to: "repo") - req.parameters.set("reference", to: "1.2.3") - req.parameters.set("archive", to: "archive") - - let route = try await req.getDocRoute(fragment: .documentation) - XCTAssertEqual(route, .init(owner: "owner", repository: "repo", docVersion: .reference("1.2.3"), fragment: .documentation, pathElements: ["archive"])) - } - - func test_getDocRoute_documentation_current() async throws { - try await withDependencies { - $0.currentReferenceCache = .inMemory - } operation: { - // owner/repo/~/documentation/archive + try await withApp { app in let req = Request(application: app, url: "", on: app.eventLoopGroup.next()) req.parameters.set("owner", to: "owner") req.parameters.set("repository", to: "repo") - req.parameters.set("reference", to: "~") + req.parameters.set("reference", to: "1.2.3") req.parameters.set("archive", to: "archive") - do { // No cache value available and we've not set up the db with a record to be found -> notFound must be raised - _ = try await req.getDocRoute(fragment: .documentation) - XCTFail("expected a .notFound error") - } catch let error as Abort where error.status == .notFound { - // expected error - } catch { - XCTFail("unexpected error: \(error)") - } + let route = try await req.getDocRoute(fragment: .documentation) + #expect(route == .init(owner: "owner", repository: "repo", docVersion: .reference("1.2.3"), fragment: .documentation, pathElements: ["archive"])) + } + } + + @Test func getDocRoute_documentation_current() async throws { + // owner/repo/~/documentation/archive + try await withDependencies { + $0.currentReferenceCache = .inMemory + } operation: { + try await withApp { app in + let req = Request(application: app, url: "", on: app.eventLoopGroup.next()) + req.parameters.set("owner", to: "owner") + req.parameters.set("repository", to: "repo") + req.parameters.set("reference", to: "~") + req.parameters.set("archive", to: "archive") + + do { // No cache value available and we've not set up the db with a record to be found -> notFound must be raised + _ = try await req.getDocRoute(fragment: .documentation) + Issue.record("expected a .notFound error") + } catch let error as Abort where error.status == .notFound { + // expected error + } catch { + Issue.record("unexpected error: \(error)") + } - @Dependency(\.currentReferenceCache) var cache - await cache.set(owner: "owner", repository: "repo", reference: "1.2.3") + @Dependency(\.currentReferenceCache) var cache + await cache.set(owner: "owner", repository: "repo", reference: "1.2.3") - do { // Now with the cache in place this resolves - let route = try await req.getDocRoute(fragment: .documentation) - XCTAssertEqual(route, .init(owner: "owner", repository: "repo", docVersion: .current(referencing: "1.2.3"), fragment: .documentation, pathElements: ["archive"])) + do { // Now with the cache in place this resolves + let route = try await req.getDocRoute(fragment: .documentation) + #expect(route == .init(owner: "owner", repository: "repo", docVersion: .current(referencing: "1.2.3"), fragment: .documentation, pathElements: ["archive"])) + } } } } - func test_getDocRoute_missing_reference() async throws { - do { - let req = Request(application: app, on: app.eventLoopGroup.next()) - req.parameters.set("owner", to: "owner") - req.parameters.set("repository", to: "repo") - _ = try await req.getDocRoute(fragment: .documentation) - XCTFail("expected a .badRequest error") - } catch let error as Abort where error.status == .badRequest { - // expected error - } catch { - XCTFail("unexpected error: \(error)") + @Test func getDocRoute_missing_reference() async throws { + try await withApp { app in + do { + let req = Request(application: app, on: app.eventLoopGroup.next()) + req.parameters.set("owner", to: "owner") + req.parameters.set("repository", to: "repo") + _ = try await req.getDocRoute(fragment: .documentation) + Issue.record("expected a .badRequest error") + } catch let error as Abort where error.status == .badRequest { + // expected error + } catch { + Issue.record("unexpected error: \(error)") + } } } - func test_getDocRoute_missing_archive() async throws { - do { // reference but no archive + @Test func getDocRoute_missing_archive() async throws { + // reference but no archive + try await withApp { app in do { let req = Request(application: app, on: app.eventLoopGroup.next()) req.parameters.set("owner", to: "owner") req.parameters.set("repository", to: "repo") req.parameters.set("reference", to: "1.2.3") _ = try await req.getDocRoute(fragment: .documentation) - XCTFail("expected a .badRequest error") + Issue.record("expected a .badRequest error") } catch let error as Abort where error.status == .badRequest { // expected error } catch { - XCTFail("unexpected error: \(error)") + Issue.record("unexpected error: \(error)") } } } diff --git a/Tests/AppTests/__Snapshots__/PackageController+routesTests/test_documentation_issue_2287.current-index.html b/Tests/AppTests/__Snapshots__/PackageController+routesTests/documentation_issue_2287.current-index.html similarity index 100% rename from Tests/AppTests/__Snapshots__/PackageController+routesTests/test_documentation_issue_2287.current-index.html rename to Tests/AppTests/__Snapshots__/PackageController+routesTests/documentation_issue_2287.current-index.html diff --git a/Tests/AppTests/__Snapshots__/PackageController+routesTests/test_documentation_issue_2287.ref-index-path.html b/Tests/AppTests/__Snapshots__/PackageController+routesTests/documentation_issue_2287.ref-index-path.html similarity index 100% rename from Tests/AppTests/__Snapshots__/PackageController+routesTests/test_documentation_issue_2287.ref-index-path.html rename to Tests/AppTests/__Snapshots__/PackageController+routesTests/documentation_issue_2287.ref-index-path.html diff --git a/Tests/AppTests/__Snapshots__/PackageController+routesTests/test_documentation_issue_2287.ref-index.html b/Tests/AppTests/__Snapshots__/PackageController+routesTests/documentation_issue_2287.ref-index.html similarity index 100% rename from Tests/AppTests/__Snapshots__/PackageController+routesTests/test_documentation_issue_2287.ref-index.html rename to Tests/AppTests/__Snapshots__/PackageController+routesTests/documentation_issue_2287.ref-index.html diff --git a/Tests/AppTests/__Snapshots__/PackageController+routesTests/test_documentation_routes_current.index-mixed-case.html b/Tests/AppTests/__Snapshots__/PackageController+routesTests/documentation_routes_current.index-mixed-case.html similarity index 100% rename from Tests/AppTests/__Snapshots__/PackageController+routesTests/test_documentation_routes_current.index-mixed-case.html rename to Tests/AppTests/__Snapshots__/PackageController+routesTests/documentation_routes_current.index-mixed-case.html diff --git a/Tests/AppTests/__Snapshots__/PackageController+routesTests/test_documentation_routes_current.index.html b/Tests/AppTests/__Snapshots__/PackageController+routesTests/documentation_routes_current.index.html similarity index 100% rename from Tests/AppTests/__Snapshots__/PackageController+routesTests/test_documentation_routes_current.index.html rename to Tests/AppTests/__Snapshots__/PackageController+routesTests/documentation_routes_current.index.html diff --git a/Tests/AppTests/__Snapshots__/PackageController+routesTests/test_documentation_routes_current_rewrite.index-mixed-case.html b/Tests/AppTests/__Snapshots__/PackageController+routesTests/documentation_routes_current_rewrite.index-mixed-case.html similarity index 100% rename from Tests/AppTests/__Snapshots__/PackageController+routesTests/test_documentation_routes_current_rewrite.index-mixed-case.html rename to Tests/AppTests/__Snapshots__/PackageController+routesTests/documentation_routes_current_rewrite.index-mixed-case.html diff --git a/Tests/AppTests/__Snapshots__/PackageController+routesTests/test_documentation_routes_current_rewrite.index.html b/Tests/AppTests/__Snapshots__/PackageController+routesTests/documentation_routes_current_rewrite.index.html similarity index 100% rename from Tests/AppTests/__Snapshots__/PackageController+routesTests/test_documentation_routes_current_rewrite.index.html rename to Tests/AppTests/__Snapshots__/PackageController+routesTests/documentation_routes_current_rewrite.index.html diff --git a/Tests/AppTests/__Snapshots__/PackageController+routesTests/test_documentation_routes_ref.index-target-a-b-mixed-case.html b/Tests/AppTests/__Snapshots__/PackageController+routesTests/documentation_routes_ref.index-target-a-b-mixed-case.html similarity index 100% rename from Tests/AppTests/__Snapshots__/PackageController+routesTests/test_documentation_routes_ref.index-target-a-b-mixed-case.html rename to Tests/AppTests/__Snapshots__/PackageController+routesTests/documentation_routes_ref.index-target-a-b-mixed-case.html diff --git a/Tests/AppTests/__Snapshots__/PackageController+routesTests/test_documentation_routes_ref.index-target-a-b.html b/Tests/AppTests/__Snapshots__/PackageController+routesTests/documentation_routes_ref.index-target-a-b.html similarity index 100% rename from Tests/AppTests/__Snapshots__/PackageController+routesTests/test_documentation_routes_ref.index-target-a-b.html rename to Tests/AppTests/__Snapshots__/PackageController+routesTests/documentation_routes_ref.index-target-a-b.html diff --git a/Tests/AppTests/__Snapshots__/PackageController+routesTests/test_documentation_routes_ref.index-target.html b/Tests/AppTests/__Snapshots__/PackageController+routesTests/documentation_routes_ref.index-target.html similarity index 100% rename from Tests/AppTests/__Snapshots__/PackageController+routesTests/test_documentation_routes_ref.index-target.html rename to Tests/AppTests/__Snapshots__/PackageController+routesTests/documentation_routes_ref.index-target.html diff --git a/Tests/AppTests/__Snapshots__/PackageController+routesTests/test_documentation_routes_tutorials.index.html b/Tests/AppTests/__Snapshots__/PackageController+routesTests/documentation_routes_tutorials.index.html similarity index 100% rename from Tests/AppTests/__Snapshots__/PackageController+routesTests/test_documentation_routes_tutorials.index.html rename to Tests/AppTests/__Snapshots__/PackageController+routesTests/documentation_routes_tutorials.index.html diff --git a/Tests/AppTests/__Snapshots__/PackageController+routesTests/test_issue_2288.index.html b/Tests/AppTests/__Snapshots__/PackageController+routesTests/issue_2288.index.html similarity index 100% rename from Tests/AppTests/__Snapshots__/PackageController+routesTests/test_issue_2288.index.html rename to Tests/AppTests/__Snapshots__/PackageController+routesTests/issue_2288.index.html diff --git a/Tests/AppTests/__Snapshots__/PackageController+routesTests/test_documentation_routes_no_archive.index-1-0-0.html b/Tests/AppTests/__Snapshots__/PackageController+routesTests/test_documentation_routes_no_archive.index-1-0-0.html deleted file mode 100644 index a45e02ddb..000000000 --- a/Tests/AppTests/__Snapshots__/PackageController+routesTests/test_documentation_routes_no_archive.index-1-0-0.html +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - pkg Documentation – Swift Package Index - - - - - - - - -
    -
    - This is a staging environment. For live and up-to-date documentation, - visit swiftpackageindex.com. -
    - -
    - -
    - - - \ No newline at end of file diff --git a/Tests/AppTests/__Snapshots__/PackageController+routesTests/test_documentation_routes_no_archive.index-current.html b/Tests/AppTests/__Snapshots__/PackageController+routesTests/test_documentation_routes_no_archive.index-current.html deleted file mode 100644 index 198182b5e..000000000 --- a/Tests/AppTests/__Snapshots__/PackageController+routesTests/test_documentation_routes_no_archive.index-current.html +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - pkg Documentation – Swift Package Index - - - - - - - -
    -
    - This is a staging environment. For live and up-to-date documentation, - visit swiftpackageindex.com. -
    - -
    - -
    - - - \ No newline at end of file diff --git a/Tests/AppTests/__Snapshots__/PackageController+routesTests/test_documentation_routes_no_archive.index-main.html b/Tests/AppTests/__Snapshots__/PackageController+routesTests/test_documentation_routes_no_archive.index-main.html deleted file mode 100644 index d76377df5..000000000 --- a/Tests/AppTests/__Snapshots__/PackageController+routesTests/test_documentation_routes_no_archive.index-main.html +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - pkg Documentation – Swift Package Index - - - - - - - - -
    -
    - This is a staging environment. For live and up-to-date documentation, - visit swiftpackageindex.com. -
    - -
    -
    - This documentation is from the main branch and may not reflect the latest released version. - View latest release documentation. -
    -
    -
    - -
    - - - \ No newline at end of file