Skip to content

Commit 531b3da

Browse files
Merge pull request #3484 from SwiftPackageIndex/issue-3469-dependency-transition-3
Issue 3469 dependency transition 3
2 parents f1513f9 + 14e2805 commit 531b3da

File tree

12 files changed

+238
-223
lines changed

12 files changed

+238
-223
lines changed

Sources/App/Core/AppEnvironment.swift

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import FoundationNetworking
2323

2424

2525
struct AppEnvironment: Sendable {
26-
var allowTwitterPosts: @Sendable () -> Bool
2726
var apiSigningKey: @Sendable () -> String?
2827
var appVersion: @Sendable () -> String?
2928
var awsAccessKeyId: @Sendable () -> String?
@@ -57,8 +56,6 @@ struct AppEnvironment: Sendable {
5756
var httpClient: @Sendable () -> Client
5857
var loadSPIManifest: @Sendable (String) -> SPIManifest.Manifest?
5958
var logger: @Sendable () -> Logger
60-
var mastodonCredentials: @Sendable () -> Mastodon.Credentials?
61-
var mastodonPost: @Sendable (_ client: Client, _ post: String) async throws -> Void
6259
var metricsPushGatewayUrl: @Sendable () -> String?
6360
var plausibleBackendReportingSiteID: @Sendable () -> String?
6461
var postPlausibleEvent: @Sendable (Client, Plausible.Event.Kind, Plausible.Path, User?) async throws -> Void
@@ -109,11 +106,6 @@ extension AppEnvironment {
109106
nonisolated(unsafe) static var logger: Logger!
110107

111108
static let live = AppEnvironment(
112-
allowTwitterPosts: {
113-
Environment.get("ALLOW_TWITTER_POSTS")
114-
.flatMap(\.asBool)
115-
?? Constants.defaultAllowTwitterPosts
116-
},
117109
apiSigningKey: { Environment.get("API_SIGNING_KEY") },
118110
appVersion: { App.appVersion },
119111
awsAccessKeyId: { Environment.get("AWS_ACCESS_KEY_ID") },
@@ -186,11 +178,6 @@ extension AppEnvironment {
186178
httpClient: { httpClient },
187179
loadSPIManifest: { path in SPIManifest.Manifest.load(in: path) },
188180
logger: { logger },
189-
mastodonCredentials: {
190-
Environment.get("MASTODON_ACCESS_TOKEN")
191-
.map(Mastodon.Credentials.init(accessToken:))
192-
},
193-
mastodonPost: { client, message in try await Mastodon.post(client: client, message: message) },
194181
metricsPushGatewayUrl: { Environment.get("METRICS_PUSHGATEWAY_URL") },
195182
plausibleBackendReportingSiteID: { Environment.get("PLAUSIBLE_BACKEND_REPORTING_SITE_ID") },
196183
postPlausibleEvent: { client, kind, path, user in try await Plausible.postEvent(client: client, kind: kind, path: path, user: user) },

Sources/App/Core/Constants.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import Vapor
1717

1818
enum Constants {
1919
static let defaultAllowBuildTriggering = true
20-
static let defaultAllowTwitterPosts = true
20+
static let defaultAllowSocialPosts = true
2121
static let defaultGitlabPipelineLimit = 200
2222
static let defaultHideStagingBanner = false
2323

Sources/App/Core/Dependencies/EnvironmentClient.swift

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,15 @@ struct EnvironmentClient {
2222
// See https://swiftpackageindex.com/pointfreeco/swift-dependencies/main/documentation/dependenciesmacros/dependencyclient()#Restrictions
2323
// regarding the use of XCTFail here.
2424
var allowBuildTriggers: @Sendable () -> Bool = { XCTFail(#function); return true }
25+
var allowSocialPosts: @Sendable () -> Bool = { XCTFail(#function); return true }
2526
// We're not defaulting current to XCTFail, because its use is too pervasive and would require the vast
2627
// majority of tests to be wrapped with `withDependencies`.
2728
// We can do so at a later time once more tests are transitioned over for other dependencies. This is
2829
// the exact same default behaviour we have with the Current dependency injection: it defaults to
2930
// .development and does not raise an error when not injected.
3031
var current: @Sendable () -> Environment = { .development }
32+
var mastodonCredentials: @Sendable () -> Mastodon.Credentials?
33+
var mastodonPost: @Sendable (_ client: Client, _ post: String) async throws -> Void
3134
}
3235

3336

@@ -37,7 +40,17 @@ extension EnvironmentClient: DependencyKey {
3740
allowBuildTriggers: {
3841
Environment.get("ALLOW_BUILD_TRIGGERS").flatMap(\.asBool) ?? Constants.defaultAllowBuildTriggering
3942
},
40-
current: { (try? Environment.detect()) ?? .development }
43+
allowSocialPosts: {
44+
Environment.get("ALLOW_SOCIAL_POSTS")
45+
.flatMap(\.asBool)
46+
?? Constants.defaultAllowSocialPosts
47+
},
48+
current: { (try? Environment.detect()) ?? .development },
49+
mastodonCredentials: {
50+
Environment.get("MASTODON_ACCESS_TOKEN")
51+
.map(Mastodon.Credentials.init(accessToken:))
52+
},
53+
mastodonPost: { client, message in try await Mastodon.post(client: client, message: message) }
4154
)
4255
}
4356
}

Sources/App/Core/Mastodon.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
import Dependencies
1516
import Vapor
1617

1718

@@ -27,7 +28,8 @@ enum Mastodon {
2728

2829
// NB: _testEncodedURL is a callback that exists purely to be able to regression test the encoded value
2930
static func post(client: Client, message: String, _testEncodedURL: (String) -> Void = { _ in }) async throws {
30-
guard let credentials = Current.mastodonCredentials() else {
31+
@Dependency(\.environment) var environment
32+
guard let credentials = environment.mastodonCredentials() else {
3133
throw Social.Error.missingCredentials
3234
}
3335

Sources/App/Core/Social.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
import Dependencies
1516
import SemanticVersion
1617
import Vapor
1718

@@ -96,16 +97,15 @@ enum Social {
9697
static func postToFirehose(client: Client,
9798
package: Joined<Package, Repository>,
9899
version: Version) async throws {
99-
guard Current.allowTwitterPosts() else {
100-
throw Error.postingDisabled
101-
}
100+
@Dependency(\.environment) var environment
101+
guard environment.allowSocialPosts() else { throw Error.postingDisabled }
102102
guard let message = firehoseMessage(package: package,
103103
version: version,
104104
maxLength: postMaxLength) else {
105105
throw Error.invalidMessage
106106
}
107107
// Ignore errors from here for now to keep concurrency simpler
108-
async let _ = try? await Current.mastodonPost(client, message)
108+
async let _ = try? await environment.mastodonPost(client, message)
109109
}
110110

111111
static func postToFirehose(client: Client,

Tests/AppTests/AnalyzeErrorTests.swift

Lines changed: 90 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -98,125 +98,117 @@ final class AnalyzeErrorTests: AppTestCase {
9898
}
9999

100100
Current.shell.run = Self.defaultShellRun
101+
}
101102

102-
Current.mastodonPost = { [socialPosts = self.socialPosts] _, message in
103-
socialPosts.withValue { $0.append(message) }
103+
override func invokeTest() {
104+
withDependencies {
105+
$0.date.now = .t0
106+
$0.environment.allowSocialPosts = { true }
107+
$0.environment.mastodonPost = { @Sendable [socialPosts = self.socialPosts] _, message in
108+
socialPosts.withValue { $0.append(message) }
109+
}
110+
} operation: {
111+
super.invokeTest()
104112
}
105113
}
106114

107115
func test_analyze_refreshCheckout_failed() async throws {
108-
try await withDependencies {
109-
$0.date.now = .t0
110-
} operation: {
111-
Current.shell.run = { @Sendable cmd, path in
112-
switch cmd {
113-
case _ where cmd.description.contains("git clone https://github.com/foo/1"):
114-
throw SimulatedError()
115-
116-
case .gitFetchAndPruneTags where path.hasSuffix("foo-1"):
117-
throw SimulatedError()
118-
119-
default:
120-
return try Self.defaultShellRun(cmd, path)
121-
}
122-
}
123-
124-
// MUT
125-
try await Analyze.analyze(client: app.client,
126-
database: app.db,
127-
mode: .limit(10))
128-
129-
// validate
130-
try await defaultValidation()
131-
try logger.logs.withValue { logs in
132-
XCTAssertEqual(logs.count, 2)
133-
let error = try logs.last.unwrap()
134-
XCTAssertTrue(error.message.contains("refreshCheckout failed"), "was: \(error.message)")
116+
Current.shell.run = { @Sendable cmd, path in
117+
switch cmd {
118+
case _ where cmd.description.contains("git clone https://github.com/foo/1"):
119+
throw SimulatedError()
120+
121+
case .gitFetchAndPruneTags where path.hasSuffix("foo-1"):
122+
throw SimulatedError()
123+
124+
default:
125+
return try Self.defaultShellRun(cmd, path)
135126
}
136127
}
128+
129+
// MUT
130+
try await Analyze.analyze(client: app.client,
131+
database: app.db,
132+
mode: .limit(10))
133+
134+
// validate
135+
try await defaultValidation()
136+
try logger.logs.withValue { logs in
137+
XCTAssertEqual(logs.count, 2)
138+
let error = try logs.last.unwrap()
139+
XCTAssertTrue(error.message.contains("refreshCheckout failed"), "was: \(error.message)")
140+
}
137141
}
138142

139143
func test_analyze_updateRepository_invalidPackageCachePath() async throws {
140-
try await withDependencies {
141-
$0.date.now = .t0
142-
} operation: {
143-
// setup
144-
let pkg = try await Package.find(badPackageID, on: app.db).unwrap()
145-
// This may look weird but its currently the only way to actually create an
146-
// invalid package cache path - we need to mess up the package url.
147-
pkg.url = "foo/1"
148-
XCTAssertNil(pkg.cacheDirectoryName)
149-
try await pkg.save(on: app.db)
150-
151-
// MUT
152-
try await Analyze.analyze(client: app.client,
153-
database: app.db,
154-
mode: .limit(10))
155-
156-
// validate
157-
try await defaultValidation()
158-
try logger.logs.withValue { logs in
159-
XCTAssertEqual(logs.count, 2)
160-
let error = try logs.last.unwrap()
161-
XCTAssertTrue(error.message.contains( "AppError.invalidPackageCachePath"), "was: \(error.message)")
162-
}
144+
// setup
145+
let pkg = try await Package.find(badPackageID, on: app.db).unwrap()
146+
// This may look weird but its currently the only way to actually create an
147+
// invalid package cache path - we need to mess up the package url.
148+
pkg.url = "foo/1"
149+
XCTAssertNil(pkg.cacheDirectoryName)
150+
try await pkg.save(on: app.db)
151+
152+
// MUT
153+
try await Analyze.analyze(client: app.client,
154+
database: app.db,
155+
mode: .limit(10))
156+
157+
// validate
158+
try await defaultValidation()
159+
try logger.logs.withValue { logs in
160+
XCTAssertEqual(logs.count, 2)
161+
let error = try logs.last.unwrap()
162+
XCTAssertTrue(error.message.contains( "AppError.invalidPackageCachePath"), "was: \(error.message)")
163163
}
164164
}
165165

166166
func test_analyze_getPackageInfo_gitCheckout_error() async throws {
167-
try await withDependencies {
168-
$0.date.now = .t0
169-
} operation: {
170-
// setup
171-
Current.shell.run = { @Sendable cmd, path in
172-
switch cmd {
173-
case .gitCheckout(branch: "main", quiet: true) where path.hasSuffix("foo-1"):
174-
throw SimulatedError()
175-
176-
default:
177-
return try Self.defaultShellRun(cmd, path)
178-
}
167+
// setup
168+
Current.shell.run = { @Sendable cmd, path in
169+
switch cmd {
170+
case .gitCheckout(branch: "main", quiet: true) where path.hasSuffix("foo-1"):
171+
throw SimulatedError()
172+
173+
default:
174+
return try Self.defaultShellRun(cmd, path)
179175
}
176+
}
180177

181-
// MUT
182-
try await Analyze.analyze(client: app.client,
183-
database: app.db,
184-
mode: .limit(10))
185-
186-
// validate
187-
try await defaultValidation()
188-
try logger.logs.withValue { logs in
189-
XCTAssertEqual(logs.count, 2)
190-
let error = try logs.last.unwrap()
191-
XCTAssertTrue(error.message.contains("AppError.noValidVersions"), "was: \(error.message)")
192-
}
178+
// MUT
179+
try await Analyze.analyze(client: app.client,
180+
database: app.db,
181+
mode: .limit(10))
182+
183+
// validate
184+
try await defaultValidation()
185+
try logger.logs.withValue { logs in
186+
XCTAssertEqual(logs.count, 2)
187+
let error = try logs.last.unwrap()
188+
XCTAssertTrue(error.message.contains("AppError.noValidVersions"), "was: \(error.message)")
193189
}
194190
}
195191

196192
func test_analyze_dumpPackage_missing_manifest() async throws {
197-
try await withDependencies {
198-
$0.date.now = .t0
199-
} operation: {
200-
// setup
201-
Current.fileManager.fileExists = { @Sendable path in
202-
if path.hasSuffix("github.com-foo-1/Package.swift") {
203-
return false
204-
}
205-
return true
206-
}
207-
208-
// MUT
209-
try await Analyze.analyze(client: app.client,
210-
database: app.db,
211-
mode: .limit(10))
212-
213-
// validate
214-
try await defaultValidation()
215-
try logger.logs.withValue { logs in
216-
XCTAssertEqual(logs.count, 2)
217-
let error = try logs.last.unwrap()
218-
XCTAssertTrue(error.message.contains("AppError.noValidVersions"), "was: \(error.message)")
193+
// setup
194+
Current.fileManager.fileExists = { @Sendable path in
195+
if path.hasSuffix("github.com-foo-1/Package.swift") {
196+
return false
219197
}
198+
return true
199+
}
200+
201+
// MUT
202+
try await Analyze.analyze(client: app.client,
203+
database: app.db,
204+
mode: .limit(10))
205+
206+
// validate
207+
try await defaultValidation()
208+
try logger.logs.withValue { logs in
209+
XCTAssertEqual(logs.count, 2)
210+
let error = try logs.last.unwrap()
211+
XCTAssertTrue(error.message.contains("AppError.noValidVersions"), "was: \(error.message)")
220212
}
221213
}
222214

Tests/AppTests/AnalyzerTests.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ class AnalyzerTests: AppTestCase {
3535
// expected shell commands for the happy path.)
3636
try await withDependencies {
3737
$0.date.now = .now
38+
$0.environment.allowSocialPosts = { true }
39+
$0.environment.mastodonPost = { @Sendable _, _ in }
3840
} operation: {
3941
// setup
4042
let urls = ["https://github.com/foo/1", "https://github.com/foo/2"]
@@ -216,6 +218,8 @@ class AnalyzerTests: AppTestCase {
216218
// changing as well as a tag being moved to a different commit.
217219
try await withDependencies {
218220
$0.date.now = .now
221+
$0.environment.allowSocialPosts = { true }
222+
$0.environment.mastodonPost = { @Sendable _, _ in }
219223
} operation: {
220224
// setup
221225
let pkgId = UUID()
@@ -354,6 +358,7 @@ class AnalyzerTests: AppTestCase {
354358
// Ensure packages record success/error status
355359
try await withDependencies {
356360
$0.date.now = .now
361+
$0.environment.allowSocialPosts = { true }
357362
} operation: {
358363
// setup
359364
let urls = ["https://github.com/foo/1", "https://github.com/foo/2"]
@@ -404,6 +409,7 @@ class AnalyzerTests: AppTestCase {
404409
// Test to ensure exceptions don't interrupt processing
405410
try await withDependencies {
406411
$0.date.now = .now
412+
$0.environment.allowSocialPosts = { true }
407413
} operation: {
408414
// setup
409415
let urls = ["https://github.com/foo/1", "https://github.com/foo/2"]
@@ -881,6 +887,7 @@ class AnalyzerTests: AppTestCase {
881887
// https://github.com/SwiftPackageIndex/SwiftPackageIndex-Server/issues/29
882888
try await withDependencies {
883889
$0.date.now = .now
890+
$0.environment.allowSocialPosts = { true }
884891
} operation: {
885892
// setup
886893
Current.git.commitCount = { @Sendable _ in 12 }

0 commit comments

Comments
 (0)