diff --git a/Sources/App/Commands/Alerting.swift b/Sources/App/Commands/Alerting.swift index cd437540a..301550684 100644 --- a/Sources/App/Commands/Alerting.swift +++ b/Sources/App/Commands/Alerting.swift @@ -245,7 +245,8 @@ extension [Alerting.BuildInfo] { } func validateRunnerIdsPresent() -> Alerting.Validation { - var notSeen = Set(Current.runnerIds()) + @Dependency(\.environment) var environment + var notSeen = Set(environment.runnerIds()) for build in self where build.runnerId != nil { notSeen.remove(build.runnerId!) if notSeen.isEmpty { return .ok } @@ -254,7 +255,8 @@ extension [Alerting.BuildInfo] { } func validateRunnerIdsSuccessful() -> Alerting.Validation { - var noSuccess = Set(Current.runnerIds()) + @Dependency(\.environment) var environment + var noSuccess = Set(environment.runnerIds()) for build in self where build.runnerId != nil && build.status == .ok { noSuccess.remove(build.runnerId!) if noSuccess.isEmpty { return .ok } diff --git a/Sources/App/Core/AppEnvironment.swift b/Sources/App/Core/AppEnvironment.swift index 74d1f61be..2e780efb0 100644 --- a/Sources/App/Core/AppEnvironment.swift +++ b/Sources/App/Core/AppEnvironment.swift @@ -31,13 +31,8 @@ struct AppEnvironment: Sendable { var gitlabPipelineToken: @Sendable () -> String? var gitlabPipelineLimit: @Sendable () -> Int var logger: @Sendable () -> Logger - var metricsPushGatewayUrl: @Sendable () -> String? - var plausibleBackendReportingSiteID: @Sendable () -> String? - var processingBuildBacklog: @Sendable () -> Bool - var runnerIds: @Sendable () -> [String] var setLogger: @Sendable (Logger) -> Void var shell: Shell - var siteURL: @Sendable () -> String var storeS3Readme: @Sendable (_ owner: String, _ repository: String, _ readme: String) async throws(S3Readme.Error) -> String @@ -75,20 +70,8 @@ extension AppEnvironment { ?? Constants.defaultGitlabPipelineLimit }, logger: { logger }, - metricsPushGatewayUrl: { Environment.get("METRICS_PUSHGATEWAY_URL") }, - plausibleBackendReportingSiteID: { Environment.get("PLAUSIBLE_BACKEND_REPORTING_SITE_ID") }, - processingBuildBacklog: { - Environment.get("PROCESSING_BUILD_BACKLOG").flatMap(\.asBool) ?? false - }, - runnerIds: { - Environment.get("RUNNER_IDS") - .map { Data($0.utf8) } - .flatMap { try? JSONDecoder().decode([String].self, from: $0) } - ?? [] - }, setLogger: { logger in Self.logger = logger }, shell: .live, - siteURL: { Environment.get("SITE_URL") ?? "http://localhost:8080" }, storeS3Readme: { owner, repo, readme throws(S3Readme.Error) in try await S3Readme.storeReadme(owner: owner, repository: repo, readme: readme) }, diff --git a/Sources/App/Core/AppMetrics.swift b/Sources/App/Core/AppMetrics.swift index 80e1d1548..db3cb1ada 100644 --- a/Sources/App/Core/AppMetrics.swift +++ b/Sources/App/Core/AppMetrics.swift @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +import Dependencies import Metrics import Prometheus import Vapor @@ -170,7 +171,8 @@ extension AppMetrics { /// scrape target. /// - Parameter client: client for POST request static func push(client: Client, jobName: String) async throws { - guard let pushGatewayUrl = Current.metricsPushGatewayUrl() else { + @Dependency(\.environment) var environment + guard let pushGatewayUrl = environment.metricsPushGatewayUrl() else { throw AppError.envVariableNotSet("METRICS_PUSHGATEWAY_URL") } let url = URI(string: "\(pushGatewayUrl)/metrics/job/\(jobName)") diff --git a/Sources/App/Core/Dependencies/EnvironmentClient.swift b/Sources/App/Core/Dependencies/EnvironmentClient.swift index fa1be0e44..b0bcc40a8 100644 --- a/Sources/App/Core/Dependencies/EnvironmentClient.swift +++ b/Sources/App/Core/Dependencies/EnvironmentClient.swift @@ -45,7 +45,11 @@ struct EnvironmentClient { var loadSPIManifest: @Sendable (String) -> SPIManifest.Manifest? var maintenanceMessage: @Sendable () -> String? var mastodonCredentials: @Sendable () -> Mastodon.Credentials? + var metricsPushGatewayUrl: @Sendable () -> String? + var plausibleBackendReportingSiteID: @Sendable () -> String? + var processingBuildBacklog: @Sendable () -> Bool = { XCTFail("processingBuildBacklog"); return false } var random: @Sendable (_ range: ClosedRange) -> Double = { XCTFail("random"); return Double.random(in: $0) } + var runnerIds: @Sendable () -> [String] = { XCTFail("runnerIds"); return [] } enum FailureMode: String { case fetchMetadataFailed @@ -55,7 +59,8 @@ struct EnvironmentClient { case repositorySaveFailed case repositorySaveUniqueViolation } - var shouldFail: @Sendable (_ failureMode: FailureMode) -> Bool = { _ in false } + var shouldFail: @Sendable (_ failureMode: FailureMode) -> Bool = { _ in XCTFail("shouldFail"); return false } + var siteURL: @Sendable () -> String = { XCTFail("siteURL"); return "" } } @@ -79,10 +84,7 @@ extension EnvironmentClient: DependencyKey { builderToken: { Environment.get("BUILDER_TOKEN") }, buildTimeout: { Environment.get("BUILD_TIMEOUT").flatMap(Int.init) ?? 10 }, buildTriggerAllowList: { - Environment.get("BUILD_TRIGGER_ALLOW_LIST") - .map { Data($0.utf8) } - .flatMap { try? JSONDecoder().decode([Package.Id].self, from: $0) } - ?? [] + Environment.decode("BUILD_TRIGGER_ALLOW_LIST", as: [Package.Id].self) ?? [] }, buildTriggerDownscaling: { Environment.get("BUILD_TRIGGER_DOWNSCALING") @@ -118,14 +120,19 @@ extension EnvironmentClient: DependencyKey { Environment.get("MASTODON_ACCESS_TOKEN") .map(Mastodon.Credentials.init(accessToken:)) }, + metricsPushGatewayUrl: { Environment.get("METRICS_PUSHGATEWAY_URL") }, + plausibleBackendReportingSiteID: { Environment.get("PLAUSIBLE_BACKEND_REPORTING_SITE_ID") }, + processingBuildBacklog: { + Environment.get("PROCESSING_BUILD_BACKLOG").flatMap(\.asBool) ?? false + }, random: { range in Double.random(in: range) }, + runnerIds: { Environment.decode("RUNNER_IDS", as: [String].self) ?? [] }, shouldFail: { failureMode in - let shouldFail = Environment.get("FAILURE_MODE") - .map { Data($0.utf8) } - .flatMap { try? JSONDecoder().decode([String: Double].self, from: $0) } ?? [:] + let shouldFail = Environment.decode("FAILURE_MODE", as: [String: Double].self) ?? [:] guard let rate = shouldFail[failureMode.rawValue] else { return false } return Double.random(in: 0...1) <= rate - } + }, + siteURL: { Environment.get("SITE_URL") ?? "http://localhost:8080" } ) } } @@ -156,6 +163,8 @@ extension EnvironmentClient: TestDependencyKey { mock.appVersion = { "test" } mock.current = { .development } mock.hideStagingBanner = { false } + mock.siteURL = { "http://localhost:8080" } + mock.shouldFail = { @Sendable _ in false } return mock } } @@ -167,3 +176,12 @@ extension DependencyValues { set { self[EnvironmentClient.self] = newValue } } } + + +private extension Environment { + static func decode(_ key: String, as type: T.Type) -> T? { + Environment.get(key) + .map { Data($0.utf8) } + .flatMap { try? JSONDecoder().decode(type, from: $0) } + } +} diff --git a/Sources/App/Core/Plausible.swift b/Sources/App/Core/Plausible.swift index 9408f1488..cd656cd12 100644 --- a/Sources/App/Core/Plausible.swift +++ b/Sources/App/Core/Plausible.swift @@ -47,7 +47,8 @@ enum Plausible { static let postEventURL = "https://plausible.io/api/event" static func postEvent(kind: Event.Kind, path: Path, user: User?) async throws { - guard let siteID = Current.plausibleBackendReportingSiteID() else { + @Dependency(\.environment) var environment + guard let siteID = environment.plausibleBackendReportingSiteID() else { throw Error(message: "PLAUSIBLE_BACKEND_REPORTING_SITE_ID not set") } let body = try JSONEncoder().encode(Event(name: .pageview, diff --git a/Sources/App/Core/SiteURL.swift b/Sources/App/Core/SiteURL.swift index 733d683c3..5a33dbf26 100644 --- a/Sources/App/Core/SiteURL.swift +++ b/Sources/App/Core/SiteURL.swift @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +import Dependencies import Plot import Vapor @@ -341,7 +342,8 @@ enum SiteURL: Resourceable, Sendable { } static let _absoluteURL: @Sendable (String) -> String = { path in - Current.siteURL() + relativeURL(path) + @Dependency(\.environment) var environment + return environment.siteURL() + relativeURL(path) } static let _relativeURL: @Sendable (String) -> String = { path in diff --git a/Sources/App/Views/PackageController/GetRoute.Model+ext.swift b/Sources/App/Views/PackageController/GetRoute.Model+ext.swift index c97bb2450..fd199650e 100644 --- a/Sources/App/Views/PackageController/GetRoute.Model+ext.swift +++ b/Sources/App/Views/PackageController/GetRoute.Model+ext.swift @@ -619,7 +619,9 @@ extension API.PackageController.GetRoute.Model { } func noCompatibilityInformationExplainer() -> Node { - .if(Current.processingBuildBacklog(), + @Dependency(\.environment) var environment + + return .if(environment.processingBuildBacklog(), .group( .p( .text("This package currently has no compatibility information. "), diff --git a/Tests/AppTests/AlertingTests.swift b/Tests/AppTests/AlertingTests.swift index 59a219e37..a0d0379af 100644 --- a/Tests/AppTests/AlertingTests.swift +++ b/Tests/AppTests/AlertingTests.swift @@ -12,9 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +import XCTest + @testable import App -import XCTest +import Dependencies class AlertingTests: XCTestCase { @@ -77,34 +79,40 @@ class AlertingTests: XCTestCase { func test_validateRunnerIdsPresent() throws { let runnerIds = ["a", "b", "c"] - Current.runnerIds = { runnerIds } - let all = runnerIds.map { - Alerting.BuildInfo.mock(runnerId: $0) + withDependencies { + $0.environment.runnerIds = { runnerIds } + } operation: { + let all = runnerIds.map { + Alerting.BuildInfo.mock(runnerId: $0) + } + XCTAssertEqual(all.validateRunnerIdsPresent(), .ok) + XCTAssertEqual(all.filter { $0.runnerId != "a" }.validateRunnerIdsPresent(), + .failed(reasons: ["Missing runner id: a"])) + XCTAssertEqual(all.filter { $0.runnerId != "a" && $0.runnerId != "b" }.validateRunnerIdsPresent(), + .failed(reasons: ["Missing runner id: a", "Missing runner id: b"])) } - XCTAssertEqual(all.validateRunnerIdsPresent(), .ok) - XCTAssertEqual(all.filter { $0.runnerId != "a" }.validateRunnerIdsPresent(), - .failed(reasons: ["Missing runner id: a"])) - XCTAssertEqual(all.filter { $0.runnerId != "a" && $0.runnerId != "b" }.validateRunnerIdsPresent(), - .failed(reasons: ["Missing runner id: a", "Missing runner id: b"])) } func test_validateRunnerIdsSuccessful() throws { let runnerIds = ["a", "b", "c"] - Current.runnerIds = { runnerIds } - let all = runnerIds.map { - Alerting.BuildInfo.mock(runnerId: $0, status: .ok) + withDependencies { + $0.environment.runnerIds = { runnerIds } + } operation: { + let all = runnerIds.map { + Alerting.BuildInfo.mock(runnerId: $0, status: .ok) + } + XCTAssertEqual(all.validateRunnerIdsSuccessful(), .ok) + XCTAssertEqual(all.filter { $0.runnerId != "a" }.validateRunnerIdsSuccessful(), + .failed(reasons: ["Runner id without successful builds: a"])) + XCTAssertEqual( + Array(all.filter { $0.runnerId != "a" }) + .appending(.mock(runnerId: "a", status: .failed)) + .validateRunnerIdsSuccessful(), + .failed(reasons: ["Runner id without successful builds: a"]) + ) + XCTAssertEqual(all.filter { $0.runnerId != "a" && $0.runnerId != "b" }.validateRunnerIdsSuccessful(), + .failed(reasons: ["Runner id without successful builds: a", "Runner id without successful builds: b"])) } - XCTAssertEqual(all.validateRunnerIdsSuccessful(), .ok) - XCTAssertEqual(all.filter { $0.runnerId != "a" }.validateRunnerIdsSuccessful(), - .failed(reasons: ["Runner id without successful builds: a"])) - XCTAssertEqual( - Array(all.filter { $0.runnerId != "a" }) - .appending(.mock(runnerId: "a", status: .failed)) - .validateRunnerIdsSuccessful(), - .failed(reasons: ["Runner id without successful builds: a"]) - ) - XCTAssertEqual(all.filter { $0.runnerId != "a" && $0.runnerId != "b" }.validateRunnerIdsSuccessful(), - .failed(reasons: ["Runner id without successful builds: a", "Runner id without successful builds: b"])) } func test_validateSuccessRateInRange() throws { diff --git a/Tests/AppTests/BuildTests.swift b/Tests/AppTests/BuildTests.swift index 089c67788..046340e6c 100644 --- a/Tests/AppTests/BuildTests.swift +++ b/Tests/AppTests/BuildTests.swift @@ -133,9 +133,9 @@ class BuildTests: AppTestCase { $0.environment.awsDocsBucket = { "awsDocsBucket" } $0.environment.builderToken = { "builder token" } $0.environment.buildTimeout = { 10 } + $0.environment.siteURL = { "http://example.com" } } operation: { Current.gitlabPipelineToken = { "pipeline token" } - Current.siteURL = { "http://example.com" } // setup let p = try await savePackage(on: app.db, "1") let v = try Version(package: p, reference: .branch("main")) @@ -201,11 +201,11 @@ class BuildTests: AppTestCase { $0.environment.awsDocsBucket = { "awsDocsBucket" } $0.environment.builderToken = { "builder token" } $0.environment.buildTimeout = { 10 } + $0.environment.siteURL = { "http://example.com" } } operation: { // Same test as test_trigger above, except we trigger with isDocBuild: true // and expect a 15m TIMEOUT instead of 10m Current.gitlabPipelineToken = { "pipeline token" } - Current.siteURL = { "http://example.com" } // setup let p = try await savePackage(on: app.db, "1") let v = try Version(package: p, reference: .branch("main")) diff --git a/Tests/AppTests/BuildTriggerTests.swift b/Tests/AppTests/BuildTriggerTests.swift index 43b630db9..a30cde1e0 100644 --- a/Tests/AppTests/BuildTriggerTests.swift +++ b/Tests/AppTests/BuildTriggerTests.swift @@ -345,10 +345,10 @@ class BuildTriggerTests: AppTestCase { $0.environment.awsDocsBucket = { "awsDocsBucket" } $0.environment.builderToken = { "builder token" } $0.environment.buildTimeout = { 10 } + $0.environment.siteURL = { "http://example.com" } } operation: { // setup Current.gitlabPipelineToken = { "pipeline token" } - Current.siteURL = { "http://example.com" } // Use live dependency but replace actual client with a mock so we can // assert on the details being sent without actually making a request @@ -408,11 +408,11 @@ class BuildTriggerTests: AppTestCase { $0.environment.builderToken = { "builder token" } $0.environment.buildTimeout = { 10 } $0.environment.buildTriggerAllowList = { [] } + $0.environment.siteURL = { "http://example.com" } } operation: { // Explicitly test the full range of all currently triggered platforms and swift versions // setup Current.gitlabPipelineToken = { "pipeline token" } - Current.siteURL = { "http://example.com" } // Use live dependency but replace actual client with a mock so we can // assert on the details being sent without actually making a request @@ -489,6 +489,7 @@ class BuildTriggerTests: AppTestCase { $0.environment.awsDocsBucket = { "awsDocsBucket" } $0.environment.builderToken = { "builder token" } $0.environment.buildTimeout = { 10 } + $0.environment.siteURL = { "http://example.com" } } operation: { // Tests error handling when a build record already exists and `create` raises a // uq:builds.version_id+builds.platform+builds.swift_version+v2 @@ -502,7 +503,6 @@ class BuildTriggerTests: AppTestCase { // See https://github.com/SwiftPackageIndex/SwiftPackageIndex-Server/issues/2237 // setup Current.gitlabPipelineToken = { "pipeline token" } - Current.siteURL = { "http://example.com" } // Use live dependency but replace actual client with a mock so we can // assert on the details being sent without actually making a request @@ -573,11 +573,11 @@ class BuildTriggerTests: AppTestCase { $0.environment.buildTriggerAllowList = { [] } $0.environment.buildTriggerDownscaling = { 1 } $0.environment.random = { @Sendable _ in 0 } + $0.environment.siteURL = { "http://example.com" } } operation: { // Ensure we respect the pipeline limit when triggering builds // setup Current.gitlabPipelineToken = { "pipeline token" } - Current.siteURL = { "http://example.com" } Current.gitlabPipelineLimit = { 300 } // Use live dependency but replace actual client with a mock so we can // assert on the details being sent without actually making a request @@ -690,11 +690,11 @@ class BuildTriggerTests: AppTestCase { $0.environment.buildTriggerDownscaling = { 1 } $0.environment.buildTriggerLatestSwiftVersionDownscaling = { 1 } $0.environment.random = { @Sendable _ in 0 } + $0.environment.siteURL = { "http://example.com" } } operation: { // Ensure we respect the pipeline limit when triggering builds for multiple package ids // setup Current.gitlabPipelineToken = { "pipeline token" } - Current.siteURL = { "http://example.com" } Current.gitlabPipelineLimit = { 300 } // Use live dependency but replace actual client with a mock so we can // assert on the details being sent without actually making a request @@ -743,11 +743,11 @@ class BuildTriggerTests: AppTestCase { $0.environment.buildTriggerAllowList = { [] } $0.environment.buildTriggerDownscaling = { 1 } $0.environment.random = { @Sendable _ in 0 } + $0.environment.siteURL = { "http://example.com" } } operation: { // Ensure we trim builds as part of triggering // setup Current.gitlabPipelineToken = { "pipeline token" } - Current.siteURL = { "http://example.com" } Current.gitlabPipelineLimit = { 300 } let client = MockClient { _, _ in } @@ -783,11 +783,11 @@ class BuildTriggerTests: AppTestCase { $0.environment.buildTriggerAllowList = { [] } $0.environment.buildTriggerDownscaling = { 1 } $0.environment.random = { @Sendable _ in 0 } + $0.environment.siteURL = { "http://example.com" } } operation: { // Ensure we trim builds as part of triggering // setup Current.gitlabPipelineToken = { "pipeline token" } - Current.siteURL = { "http://example.com" } Current.gitlabPipelineLimit = { 300 } // Use live dependency but replace actual client with a mock so we can // assert on the details being sent without actually making a request @@ -911,11 +911,11 @@ class BuildTriggerTests: AppTestCase { $0.environment.buildTriggerAllowList = { [] } $0.environment.buildTriggerDownscaling = { 1 } $0.environment.random = { @Sendable _ in 0 } + $0.environment.siteURL = { "http://example.com" } } operation: { // Ensure don't trigger if the override is off // setup Current.gitlabPipelineToken = { "pipeline token" } - Current.siteURL = { "http://example.com" } // Use live dependency but replace actual client with a mock so we can // assert on the details being sent without actually making a request Current.triggerBuild = { client, buildId, cloneURL, isDocBuild, platform, ref, swiftVersion, versionID in @@ -988,11 +988,11 @@ class BuildTriggerTests: AppTestCase { $0.environment.buildTimeout = { 10 } $0.environment.buildTriggerAllowList = { [] } $0.environment.buildTriggerDownscaling = { 0.05 } // 5% downscaling rate + $0.environment.siteURL = { "http://example.com" } } operation: { // Test build trigger downscaling behaviour // setup Current.gitlabPipelineToken = { "pipeline token" } - Current.siteURL = { "http://example.com" } // Use live dependency but replace actual client with a mock so we can // assert on the details being sent without actually making a request Current.triggerBuild = { client, buildId, cloneURL, isDocBuild, platform, ref, swiftVersion, versionID in @@ -1065,11 +1065,11 @@ class BuildTriggerTests: AppTestCase { $0.environment.buildTimeout = { 10 } $0.environment.buildTriggerAllowList = { [.id0] } $0.environment.buildTriggerDownscaling = { 0.05 } // 5% downscaling rate + $0.environment.siteURL = { "http://example.com" } } operation: { // Test build trigger downscaling behaviour for allow-listed packages // setup Current.gitlabPipelineToken = { "pipeline token" } - Current.siteURL = { "http://example.com" } // Use live dependency but replace actual client with a mock so we can // assert on the details being sent without actually making a request Current.triggerBuild = { client, buildId, cloneURL, isDocBuild, platform, ref, swiftVersion, versionID in diff --git a/Tests/AppTests/GitlabBuilderTests.swift b/Tests/AppTests/GitlabBuilderTests.swift index 2fa2edfc5..2884dbcf8 100644 --- a/Tests/AppTests/GitlabBuilderTests.swift +++ b/Tests/AppTests/GitlabBuilderTests.swift @@ -58,9 +58,9 @@ class GitlabBuilderTests: AppTestCase { $0.environment.awsDocsBucket = { "docs-bucket" } $0.environment.builderToken = { "builder token" } $0.environment.buildTimeout = { 10 } + $0.environment.siteURL = { "http://example.com" } } operation: { Current.gitlabPipelineToken = { "pipeline token" } - Current.siteURL = { "http://example.com" } let buildId = UUID() let versionID = UUID() @@ -107,9 +107,9 @@ class GitlabBuilderTests: AppTestCase { $0.environment.awsDocsBucket = { "docs-bucket" } $0.environment.builderToken = { "builder token" } $0.environment.buildTimeout = { 10 } + $0.environment.siteURL = { "http://example.com" } } operation: { Current.gitlabPipelineToken = { "pipeline token" } - Current.siteURL = { "http://example.com" } var called = false let client = MockClient { req, res in @@ -183,6 +183,7 @@ class LiveGitlabBuilderTests: AppTestCase { } // make sure environment variables are configured for live access $0.environment.awsDocsBucket = { "spi-dev-docs" } + $0.environment.siteURL = { "https://staging.swiftpackageindex.com" } } operation: { // set build branch to trigger on Gitlab.Builder.branch = "main" @@ -192,7 +193,6 @@ class LiveGitlabBuilderTests: AppTestCase { // This Gitlab token is required in order to trigger the pipeline ProcessInfo.processInfo.environment["LIVE_GITLAB_PIPELINE_TOKEN"] } - Current.siteURL = { "https://staging.swiftpackageindex.com" } let buildId = UUID() diff --git a/Tests/AppTests/MaintainerInfoIndexModelTests.swift b/Tests/AppTests/MaintainerInfoIndexModelTests.swift index e2f42f62a..c29f3596c 100644 --- a/Tests/AppTests/MaintainerInfoIndexModelTests.swift +++ b/Tests/AppTests/MaintainerInfoIndexModelTests.swift @@ -14,27 +14,34 @@ @testable import App -import XCTVapor +import Dependencies import SnapshotTesting +import XCTVapor class MaintainerInfoIndexModelTests: SnapshotTestCase { func test_badgeURL() throws { - Current.siteURL = { "https://spi.com" } - let model = MaintainerInfoIndex.Model.mock + withDependencies { + $0.environment.siteURL = { "https://spi.com" } + } operation: { + let model = MaintainerInfoIndex.Model.mock - XCTAssertEqual(model.badgeURL(for: .swiftVersions), "https://img.shields.io/endpoint?url=https%3A%2F%2Fspi.com%2Fapi%2Fpackages%2Fexample%2Fpackage%2Fbadge%3Ftype%3Dswift-versions") - XCTAssertEqual(model.badgeURL(for: .platforms), "https://img.shields.io/endpoint?url=https%3A%2F%2Fspi.com%2Fapi%2Fpackages%2Fexample%2Fpackage%2Fbadge%3Ftype%3Dplatforms") + XCTAssertEqual(model.badgeURL(for: .swiftVersions), "https://img.shields.io/endpoint?url=https%3A%2F%2Fspi.com%2Fapi%2Fpackages%2Fexample%2Fpackage%2Fbadge%3Ftype%3Dswift-versions") + XCTAssertEqual(model.badgeURL(for: .platforms), "https://img.shields.io/endpoint?url=https%3A%2F%2Fspi.com%2Fapi%2Fpackages%2Fexample%2Fpackage%2Fbadge%3Ftype%3Dplatforms") + } } func test_badgeMarkdown() throws { // Test badge markdown structure - Current.siteURL = { "https://spi.com" } - let model = MaintainerInfoIndex.Model.mock - - let badgeURL = model.badgeURL(for: .swiftVersions) - XCTAssertEqual(model.badgeMarkdown(for: .swiftVersions), "[![](\(badgeURL))](https://spi.com/example/package)") + withDependencies { + $0.environment.siteURL = { "https://spi.com" } + } operation: { + let model = MaintainerInfoIndex.Model.mock + + let badgeURL = model.badgeURL(for: .swiftVersions) + XCTAssertEqual(model.badgeMarkdown(for: .swiftVersions), "[![](\(badgeURL))](https://spi.com/example/package)") + } } func test_scoreCategories_dependencies() throws { diff --git a/Tests/AppTests/Mocks/AppEnvironment+mock.swift b/Tests/AppTests/Mocks/AppEnvironment+mock.swift index c738f91af..431a89238 100644 --- a/Tests/AppTests/Mocks/AppEnvironment+mock.swift +++ b/Tests/AppTests/Mocks/AppEnvironment+mock.swift @@ -30,13 +30,8 @@ extension AppEnvironment { gitlabPipelineToken: { nil }, gitlabPipelineLimit: { Constants.defaultGitlabPipelineLimit }, logger: { logger }, - metricsPushGatewayUrl: { "http://pushgateway:9091" }, - plausibleBackendReportingSiteID: { nil }, - processingBuildBacklog: { false }, - runnerIds: { [] }, setLogger: { logger in Self.logger = logger }, shell: .mock, - siteURL: { Environment.get("SITE_URL") ?? "http://localhost:8080" }, storeS3Readme: { _, _, _ in "s3ObjectUrl" }, storeS3ReadmeImages: { _, _ in }, triggerBuild: { _, _, _, _, _, _, _, _ in .init(status: .ok, webUrl: "http://web_url") } diff --git a/Tests/AppTests/PackageController+routesTests.swift b/Tests/AppTests/PackageController+routesTests.swift index 4180dd70f..3d84274b1 100644 --- a/Tests/AppTests/PackageController+routesTests.swift +++ b/Tests/AppTests/PackageController+routesTests.swift @@ -27,6 +27,7 @@ class PackageController_routesTests: SnapshotTestCase { func test_show() async throws { try await withDependencies { $0.environment.dbId = { nil } + $0.environment.processingBuildBacklog = { false } } operation: { // setup let pkg = try await savePackage(on: app.db, "1") diff --git a/Tests/AppTests/PlausibleTests.swift b/Tests/AppTests/PlausibleTests.swift index 9dce135d3..cc111921c 100644 --- a/Tests/AppTests/PlausibleTests.swift +++ b/Tests/AppTests/PlausibleTests.swift @@ -33,6 +33,7 @@ final class PlausibleTests: XCTestCase { func test_postEvent_anonymous() async throws { let called = ActorIsolated(false) try await withDependencies { + $0.environment.plausibleBackendReportingSiteID = { "foo.bar" } $0.httpClient.post = { @Sendable _, _, body in await called.withValue { $0 = true } // validate @@ -45,8 +46,6 @@ final class PlausibleTests: XCTestCase { return .ok } } operation: { - Current.plausibleBackendReportingSiteID = { "foo.bar" } - // MUT _ = try await Plausible.postEvent(kind: .pageview, path: .search, user: nil) @@ -57,6 +56,7 @@ final class PlausibleTests: XCTestCase { func test_postEvent_package() async throws { let called = ActorIsolated(false) try await withDependencies { + $0.environment.plausibleBackendReportingSiteID = { "foo.bar" } $0.httpClient.post = { @Sendable _, _, body in await called.withValue { $0 = true } // validate @@ -69,7 +69,6 @@ final class PlausibleTests: XCTestCase { return .ok } } operation: { - Current.plausibleBackendReportingSiteID = { "foo.bar" } let user = User(name: "api", identifier: "3c469e9d") // MUT diff --git a/Tests/AppTests/SiteURLTests.swift b/Tests/AppTests/SiteURLTests.swift index ac55fa533..ba78932c9 100644 --- a/Tests/AppTests/SiteURLTests.swift +++ b/Tests/AppTests/SiteURLTests.swift @@ -12,11 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. +import XCTest + @testable import App +import Dependencies import Plot import Vapor -import XCTest class SiteURLTests: XCTestCase { @@ -61,30 +63,42 @@ class SiteURLTests: XCTestCase { } func test_absoluteURL() throws { - Current.siteURL = { "https://indexsite.com" } - XCTAssertEqual(SiteURL.home.absoluteURL(), "https://indexsite.com/") - XCTAssertEqual(SiteURL.images("foo.png").absoluteURL(), "https://indexsite.com/images/foo.png") - XCTAssertEqual(SiteURL.privacy.absoluteURL(), "https://indexsite.com/privacy") + withDependencies { + $0.environment.siteURL = { "https://indexsite.com" } + } operation: { + XCTAssertEqual(SiteURL.home.absoluteURL(), "https://indexsite.com/") + XCTAssertEqual(SiteURL.images("foo.png").absoluteURL(), "https://indexsite.com/images/foo.png") + XCTAssertEqual(SiteURL.privacy.absoluteURL(), "https://indexsite.com/privacy") + } } func test_absoluteURL_with_anchor() throws { - Current.siteURL = { "https://indexsite.com" } - XCTAssertEqual(SiteURL.faq.absoluteURL(anchor: "hello"), "https://indexsite.com/faq#hello") + withDependencies { + $0.environment.siteURL = { "https://indexsite.com" } + } operation: { + XCTAssertEqual(SiteURL.faq.absoluteURL(anchor: "hello"), "https://indexsite.com/faq#hello") + } } func test_absoluteURL_with_parameters() throws { - Current.siteURL = { "https://indexsite.com" } - let url = SiteURL.rssReleases.absoluteURL(parameters: [ - QueryParameter(key: "c d", value: 2), - QueryParameter(key: "a b", value: 1) - ]) - XCTAssertEqual(url, "https://indexsite.com/releases.rss?c%20d=2&a%20b=1") + withDependencies { + $0.environment.siteURL = { "https://indexsite.com" } + } operation: { + let url = SiteURL.rssReleases.absoluteURL(parameters: [ + QueryParameter(key: "c d", value: 2), + QueryParameter(key: "a b", value: 1) + ]) + XCTAssertEqual(url, "https://indexsite.com/releases.rss?c%20d=2&a%20b=1") + } } func test_url_escaping() throws { - Current.siteURL = { "https://indexsite.com" } - XCTAssertEqual(SiteURL.package(.value("foo bar"), .value("some repo"), .none).absoluteURL(), - "https://indexsite.com/foo%20bar/some%20repo") + withDependencies { + $0.environment.siteURL = { "https://indexsite.com" } + } operation: { + XCTAssertEqual(SiteURL.package(.value("foo bar"), .value("some repo"), .none).absoluteURL(), + "https://indexsite.com/foo%20bar/some%20repo") + } } func test_static_relativeURL() throws { @@ -93,9 +107,12 @@ class SiteURLTests: XCTestCase { } func test_static_absoluteURL() throws { - Current.siteURL = { "https://indexsite.com" } - XCTAssertEqual(SiteURL.absoluteURL("foo"), "https://indexsite.com/foo") - XCTAssertEqual(SiteURL.absoluteURL("/foo"), "https://indexsite.com/foo") + withDependencies { + $0.environment.siteURL = { "https://indexsite.com" } + } operation: { + XCTAssertEqual(SiteURL.absoluteURL("foo"), "https://indexsite.com/foo") + XCTAssertEqual(SiteURL.absoluteURL("/foo"), "https://indexsite.com/foo") + } } func test_api_path() throws { @@ -123,8 +140,11 @@ class SiteURLTests: XCTestCase { } func test_apiBaseURL() throws { - Current.siteURL = { "http://example.com" } - XCTAssertEqual(SiteURL.apiBaseURL, "http://example.com/api") + withDependencies { + $0.environment.siteURL = { "https://example.com" } + } operation: { + XCTAssertEqual(SiteURL.apiBaseURL, "https://example.com/api") + } } func test_packageBuildsURL() throws { diff --git a/Tests/AppTests/SitemapTests.swift b/Tests/AppTests/SitemapTests.swift index 4c6d77e6f..1f7d1ebbe 100644 --- a/Tests/AppTests/SitemapTests.swift +++ b/Tests/AppTests/SitemapTests.swift @@ -140,6 +140,7 @@ class SitemapTests: SnapshotTestCase { func test_linkablePathUrls() async throws { try await withDependencies { $0.environment.awsDocsBucket = { "docs-bucket" } + $0.environment.siteURL = { "https://spi.com" } $0.httpClient.fetchDocumentation = { @Sendable url in guard url.path.hasSuffix("/owner/repo0/default/linkable-paths.json") else { throw Abort(.notFound) } return .ok(body: """ @@ -166,7 +167,6 @@ class SitemapTests: SnapshotTestCase { spiManifest: .init(builder: .init(configs: [.init(documentationTargets: ["t1", "t2"])]))).save(on: app.db) let packageResult = try await PackageController.PackageResult .query(on: app.db, owner: "owner", repository: "repo0") - Current.siteURL = { "https://spi.com" } // MUT let urls = await PackageController.linkablePathUrls(client: app.client, packageResult: packageResult) @@ -183,6 +183,7 @@ class SitemapTests: SnapshotTestCase { // https://github.com/SwiftPackageIndex/SwiftPackageIndex-Server/issues/2462 try await withDependencies { $0.environment.awsDocsBucket = { "docs-bucket" } + $0.environment.siteURL = { "https://spi.com" } $0.httpClient.fetchDocumentation = { @Sendable url in guard url.path.hasSuffix("/owner/repo0/a-b/linkable-paths.json") else { throw Abort(.notFound) } return .ok(body: """ @@ -209,7 +210,6 @@ class SitemapTests: SnapshotTestCase { spiManifest: .init(builder: .init(configs: [.init(documentationTargets: ["t1", "t2"])]))).save(on: app.db) let packageResult = try await PackageController.PackageResult .query(on: app.db, owner: "owner", repository: "repo0") - Current.siteURL = { "https://spi.com" } // MUT let urls = await PackageController.linkablePathUrls(client: app.client, packageResult: packageResult) diff --git a/Tests/AppTests/Snapshotting+html.swift b/Tests/AppTests/Snapshotting+html.swift index 37f40a0c1..5399590ef 100644 --- a/Tests/AppTests/Snapshotting+html.swift +++ b/Tests/AppTests/Snapshotting+html.swift @@ -16,6 +16,7 @@ import Plot import SnapshotTesting +import Dependencies extension Snapshotting where Value == String, Format == String { public static let html = Snapshotting(pathExtension: "html", diffing: .lines) @@ -24,8 +25,11 @@ extension Snapshotting where Value == String, Format == String { extension Snapshotting where Value == () -> HTML, Format == String { public static var html: Snapshotting { Snapshotting.init(pathExtension: "html", diffing: .lines).pullback { node in - Current.siteURL = { "http://localhost:8080" } - return node().render(indentedBy: .spaces(2)) + withDependencies { + $0.environment.siteURL = { "http://localhost:8080" } + } operation: { + return node().render(indentedBy: .spaces(2)) + } } } } diff --git a/Tests/AppTests/WebpageSnapshotTests.swift b/Tests/AppTests/WebpageSnapshotTests.swift index f933619e8..377be204b 100644 --- a/Tests/AppTests/WebpageSnapshotTests.swift +++ b/Tests/AppTests/WebpageSnapshotTests.swift @@ -34,6 +34,7 @@ class WebpageSnapshotTests: SnapshotTestCase { withDependencies { $0.environment.current = { .production } $0.environment.dbId = { "db-id" } + $0.environment.processingBuildBacklog = { false } $0.timeZone = .utc } operation: { super.invokeTest()