1212// See the License for the specific language governing permissions and
1313// limitations under the License.
1414
15- import XCTest
16-
1715@testable import App
1816
1917import Dependencies
2018import Fluent
2119import SQLKit
20+ import Testing
2221import Vapor
2322
2423
25- class ReAnalyzeVersionsTests : AppTestCase {
24+ @ Suite struct ReAnalyzeVersionsTests {
2625
27- func test_reAnalyzeVersions ( ) async throws {
26+ @ Test func reAnalyzeVersions ( ) async throws {
2827 // Basic end-to-end test
2928 try await withDependencies {
3029 $0. date. now = . t0
@@ -44,56 +43,57 @@ class ReAnalyzeVersionsTests: AppTestCase {
4443 }
4544 $0. httpClient. mastodonPost = { @Sendable _ in }
4645 } operation: {
47- // setup
48- // - package dump does not include toolsVersion, targets to simulate an "old version"
49- // - run analysis to create existing version
50- // - validate that initial state is reflected
51- // - then change input data in fields that are affecting existing versions (which `analysis` is "blind" to)
52- // - run analysis again to confirm "blindness"
53- // - run re-analysis and confirm changes are now reflected
54- let pkg = try await savePackage ( on: app. db,
55- " https://github.com/foo/1 " . url,
56- processingStage: . ingestion)
57- let repoId = UUID ( )
58- try await Repository ( id: repoId,
59- package : pkg,
60- defaultBranch: " main " ,
61- name: " 1 " ,
62- owner: " foo " ) . save ( on: app. db)
46+ try await withApp { app in
47+ // setup
48+ // - package dump does not include toolsVersion, targets to simulate an "old version"
49+ // - run analysis to create existing version
50+ // - validate that initial state is reflected
51+ // - then change input data in fields that are affecting existing versions (which `analysis` is "blind" to)
52+ // - run analysis again to confirm "blindness"
53+ // - run re-analysis and confirm changes are now reflected
54+ let pkg = try await savePackage ( on: app. db,
55+ " https://github.com/foo/1 " . url,
56+ processingStage: . ingestion)
57+ let repoId = UUID ( )
58+ try await Repository ( id: repoId,
59+ package : pkg,
60+ defaultBranch: " main " ,
61+ name: " 1 " ,
62+ owner: " foo " ) . save ( on: app. db)
6363
64- try await withDependencies {
65- $0. git. revisionInfo = { @Sendable _, _ in . init( commit: " sha " , date: . t0) }
66- $0. shell. run = { @Sendable cmd, path in
67- if cmd. description. hasSuffix ( " swift package dump-package " ) {
68- return #"""
64+ try await withDependencies {
65+ $0. git. revisionInfo = { @Sendable _, _ in . init( commit: " sha " , date: . t0) }
66+ $0. shell. run = { @Sendable cmd, path in
67+ if cmd. description. hasSuffix ( " swift package dump-package " ) {
68+ return #"""
6969 {
7070 "name": "SPI-Server",
7171 "products": [],
7272 "targets": []
7373 }
7474 """#
75+ }
76+ return " "
77+ }
78+ } operation: {
79+ do {
80+ // run initial analysis and assert initial state for versions
81+ try await Analyze . analyze ( client: app. client,
82+ database: app. db,
83+ mode: . limit( 10 ) )
84+ let versions = try await Version . query ( on: app. db)
85+ . with ( \. $targets)
86+ . all ( )
87+ #expect( versions. map ( \. toolsVersion) == [ nil , nil ] )
88+ #expect( versions. map { $0. targets. map ( \. name) } == [ [ ] , [ ] ] )
89+ #expect( versions. map ( \. releaseNotes) == [ nil , nil ] )
7590 }
76- return " "
77- }
78- } operation: {
79- do {
80- // run initial analysis and assert initial state for versions
81- try await Analyze . analyze ( client: app. client,
82- database: app. db,
83- mode: . limit( 10 ) )
84- let versions = try await Version . query ( on: app. db)
85- . with ( \. $targets)
86- . all ( )
87- XCTAssertEqual ( versions. map ( \. toolsVersion) , [ nil , nil ] )
88- XCTAssertEqual ( versions. map { $0. targets. map ( \. name) } , [ [ ] , [ ] ] )
89- XCTAssertEqual ( versions. map ( \. releaseNotes) , [ nil , nil ] )
90- }
9191
92- try await withDependencies {
93- // Update state that would normally not be affecting existing versions, effectively simulating the situation where we only started parsing it after versions had already been created
94- $0. shell. run = { @Sendable cmd, path in
95- if cmd. description. hasSuffix ( " swift package dump-package " ) {
96- return #"""
92+ try await withDependencies {
93+ // Update state that would normally not be affecting existing versions, effectively simulating the situation where we only started parsing it after versions had already been created
94+ $0. shell. run = { @Sendable cmd, path in
95+ if cmd. description. hasSuffix ( " swift package dump-package " ) {
96+ return #"""
9797 {
9898 "name": "SPI-Server",
9999 "products": [],
@@ -103,83 +103,86 @@ class ReAnalyzeVersionsTests: AppTestCase {
103103 }
104104 }
105105 """#
106+ }
107+ return " "
106108 }
107- return " "
108- }
109- // also, update release notes to ensure mergeReleaseInfo is being called
110- let r = try await Repository . find ( repoId, on: app. db) . unwrap ( )
111- r. releases = [
112- . mock( description: " rel 1.2.3 " , tagName: " 1.2.3 " )
113- ]
114- try await r. save ( on: app. db)
115- } operation: {
116- do { // assert running analysis again does not update existing versions
117- try await Analyze . analyze ( client: app. client,
118- database: app. db,
119- mode: . limit( 10 ) )
109+ // also, update release notes to ensure mergeReleaseInfo is being called
110+ let r = try await Repository . find ( repoId, on: app. db) . unwrap ( )
111+ r. releases = [
112+ . mock( description: " rel 1.2.3 " , tagName: " 1.2.3 " )
113+ ]
114+ try await r. save ( on: app. db)
115+ } operation: {
116+ do { // assert running analysis again does not update existing versions
117+ try await Analyze . analyze ( client: app. client,
118+ database: app. db,
119+ mode: . limit( 10 ) )
120+ let versions = try await Version . query ( on: app. db)
121+ . with ( \. $targets)
122+ . all ( )
123+ #expect( versions. map ( \. toolsVersion) == [ nil , nil ] )
124+ #expect( versions. map { $0. targets. map ( \. name) } == [ [ ] , [ ] ] )
125+ #expect( versions. map ( \. releaseNotes) == [ nil , nil ] )
126+ #expect( versions. map ( \. docArchives) == [ nil , nil ] )
127+ }
128+
129+ // MUT
130+ try await ReAnalyzeVersions . reAnalyzeVersions ( client: app. client,
131+ database: app. db,
132+ before: Date . now,
133+ refreshCheckouts: false ,
134+ limit: 10 )
135+
136+ // validate that re-analysis has now updated existing versions
120137 let versions = try await Version . query ( on: app. db)
121138 . with ( \. $targets)
139+ . sort ( \. $createdAt)
122140 . all ( )
123- XCTAssertEqual ( versions. map ( \. toolsVersion) , [ nil , nil ] )
124- XCTAssertEqual ( versions. map { $0. targets. map ( \. name) } , [ [ ] , [ ] ] )
125- XCTAssertEqual ( versions. map ( \. releaseNotes) , [ nil , nil ] )
126- XCTAssertEqual ( versions. map ( \. docArchives) , [ nil , nil ] )
141+ #expect( versions. map ( \. toolsVersion) == [ " 5.3 " , " 5.3 " ] )
142+ #expect( versions. map { $0. targets. map ( \. name) } == [ [ " t1 " ] , [ " t1 " ] ] )
143+ #expect( versions. compactMap ( \. releaseNotes) == [ " rel 1.2.3 " ] )
127144 }
128-
129- // MUT
130- try await ReAnalyzeVersions . reAnalyzeVersions ( client: app. client,
131- database: app. db,
132- before: Date . now,
133- refreshCheckouts: false ,
134- limit: 10 )
135-
136- // validate that re-analysis has now updated existing versions
137- let versions = try await Version . query ( on: app. db)
138- . with ( \. $targets)
139- . sort ( \. $createdAt)
140- . all ( )
141- XCTAssertEqual ( versions. map ( \. toolsVersion) , [ " 5.3 " , " 5.3 " ] )
142- XCTAssertEqual ( versions. map { $0. targets. map ( \. name) } , [ [ " t1 " ] , [ " t1 " ] ] )
143- XCTAssertEqual ( versions. compactMap ( \. releaseNotes) , [ " rel 1.2.3 " ] )
144145 }
145146 }
146147 }
147148 }
148149
149- func test_Package_fetchReAnalysisCandidates ( ) async throws {
150+ @ Test func Package_fetchReAnalysisCandidates ( ) async throws {
150151 // Three packages with two versions:
151152 // 1) both versions updated before cutoff -> candidate
152153 // 2) one version update before cutoff, one after -> candidate
153154 // 3) both version updated after cutoff -> not a candidate
154- let cutoff = Date ( timeIntervalSince1970: 2 )
155- do {
156- let p = Package ( url: " 1 " )
157- try await p. save ( on: app. db)
158- try await createVersion ( app. db, p, updatedAt: . t0)
159- try await createVersion ( app. db, p, updatedAt: . t1)
160- }
161- do {
162- let p = Package ( url: " 2 " )
163- try await p. save ( on: app. db)
164- try await createVersion ( app. db, p, updatedAt: . t1)
165- try await createVersion ( app. db, p, updatedAt: . t3)
166- }
167- do {
168- let p = Package ( url: " 3 " )
169- try await p. save ( on: app. db)
170- try await createVersion ( app. db, p, updatedAt: . t3)
171- try await createVersion ( app. db, p, updatedAt: . t4)
172- }
155+ try await withApp { app in
156+ let cutoff = Date ( timeIntervalSince1970: 2 )
157+ do {
158+ let p = Package ( url: " 1 " )
159+ try await p. save ( on: app. db)
160+ try await createVersion ( app. db, p, updatedAt: . t0)
161+ try await createVersion ( app. db, p, updatedAt: . t1)
162+ }
163+ do {
164+ let p = Package ( url: " 2 " )
165+ try await p. save ( on: app. db)
166+ try await createVersion ( app. db, p, updatedAt: . t1)
167+ try await createVersion ( app. db, p, updatedAt: . t3)
168+ }
169+ do {
170+ let p = Package ( url: " 3 " )
171+ try await p. save ( on: app. db)
172+ try await createVersion ( app. db, p, updatedAt: . t3)
173+ try await createVersion ( app. db, p, updatedAt: . t4)
174+ }
173175
174- // MUT
175- let res = try await Package
176- . fetchReAnalysisCandidates ( app. db, before: cutoff, limit: 10 )
176+ // MUT
177+ let res = try await Package
178+ . fetchReAnalysisCandidates ( app. db, before: cutoff, limit: 10 )
177179
178- // validate
179- XCTAssertEqual ( res. map ( \. model. url) , [ " 1 " , " 2 " ] )
180+ // validate
181+ #expect( res. map ( \. model. url) == [ " 1 " , " 2 " ] )
182+ }
180183 }
181184
182- func test_versionsUpdatedOnError ( ) async throws {
185+ @ Test func versionsUpdatedOnError ( ) async throws {
183186 // Test to ensure versions are updated even if processing throws errors.
184187 // This is to ensure our candidate selection shrinks and we don't
185188 // churn over and over on failing versions.
@@ -213,41 +216,43 @@ class ReAnalyzeVersionsTests: AppTestCase {
213216 return " "
214217 }
215218 } operation: {
216- let pkg = try await savePackage ( on: app. db,
217- " https://github.com/foo/1 " . url,
218- processingStage: . ingestion)
219- try await Repository ( package : pkg, defaultBranch: " main " ) . save ( on: app. db)
220- try await Analyze . analyze ( client: app. client,
221- database: app. db,
222- mode: . limit( 10 ) )
223- try await setAllVersionsUpdatedAt ( app. db, updatedAt: . t0)
224- do {
225- let candidates = try await Package
226- . fetchReAnalysisCandidates ( app. db, before: cutoff, limit: 10 )
227- XCTAssertEqual ( candidates. count, 1 )
228- }
229-
230- try await withDependencies {
231- $0. shell. run = { @Sendable cmd, path in
232- if cmd == . swiftDumpPackage {
233- // simulate error during package dump
234- struct Error : Swift . Error { }
235- throw Error ( )
219+ try await withApp { app in
220+ let pkg = try await savePackage ( on: app. db,
221+ " https://github.com/foo/1 " . url,
222+ processingStage: . ingestion)
223+ try await Repository ( package : pkg, defaultBranch: " main " ) . save ( on: app. db)
224+ try await Analyze . analyze ( client: app. client,
225+ database: app. db,
226+ mode: . limit( 10 ) )
227+ try await setAllVersionsUpdatedAt ( app. db, updatedAt: . t0)
228+ do {
229+ let candidates = try await Package
230+ . fetchReAnalysisCandidates ( app. db, before: cutoff, limit: 10 )
231+ #expect( candidates. count == 1 )
232+ }
233+
234+ try await withDependencies {
235+ $0. shell. run = { @Sendable cmd, path in
236+ if cmd == . swiftDumpPackage {
237+ // simulate error during package dump
238+ struct Error : Swift . Error { }
239+ throw Error ( )
240+ }
241+ return " "
236242 }
237- return " "
243+ } operation: {
244+ // MUT
245+ try await ReAnalyzeVersions . reAnalyzeVersions ( client: app. client,
246+ database: app. db,
247+ before: Date . now,
248+ refreshCheckouts: false ,
249+ limit: 10 )
250+
251+ // validate
252+ let candidates = try await Package
253+ . fetchReAnalysisCandidates ( app. db, before: cutoff, limit: 10 )
254+ #expect( candidates. count == 0 )
238255 }
239- } operation: {
240- // MUT
241- try await ReAnalyzeVersions . reAnalyzeVersions ( client: app. client,
242- database: app. db,
243- before: Date . now,
244- refreshCheckouts: false ,
245- limit: 10 )
246-
247- // validate
248- let candidates = try await Package
249- . fetchReAnalysisCandidates ( app. db, before: cutoff, limit: 10 )
250- XCTAssertEqual ( candidates. count, 0 )
251256 }
252257 }
253258 }
0 commit comments