@@ -154,5 +154,134 @@ struct TestSwiftly: AsyncParsableCommand {
154
154
} else if swiftReady {
155
155
try await sh ( executable: . path( shell) , . login, . command( " swift --version " ) ) . run ( currentPlatform, env: env, quiet: false )
156
156
}
157
+
158
+ // Test self-uninstall functionality
159
+ print( " Testing self-uninstall functionality " )
160
+ try await testSelfUninstall( customLoc: customLoc, shell: shell, env: env)
161
+ }
162
+
163
+ private func testSelfUninstall( customLoc: FilePath? , shell: FilePath, env: [ String: String] ) async throws {
164
+ if let customLoc = customLoc {
165
+ // Test self-uninstall for custom location
166
+ try await sh ( executable: . path( shell) , . login, . command( " . \" \( customLoc / " env.sh " ) \" && swiftly self-uninstall --assume-yes " ) ) . run ( currentPlatform, env: env, quiet: false )
167
+
168
+ // Verify cleanup for custom location
169
+ try await verifyCustomLocationCleanup ( customLoc: customLoc)
170
+ } else {
171
+ // Test self-uninstall for default location
172
+ try await sh ( executable: . path( shell) , . login, . command( " swiftly self-uninstall --assume-yes " ) ) . run ( currentPlatform, env: env, quiet: false )
173
+
174
+ // Verify cleanup for default location
175
+ try await verifyDefaultLocationCleanup ( shell: shell, env: env)
176
+ }
177
+ }
178
+
179
+ private func verifyCustomLocationCleanup( customLoc: FilePath) async throws {
180
+ print ( " Verifying cleanup for custom location at \( customLoc) " )
181
+
182
+ // Check that swiftly binary is removed
183
+ let swiftlyBinary = customLoc / " bin/swiftly "
184
+ guard !( try await fs. exists ( atPath: swiftlyBinary) ) else {
185
+ throw TestError ( " Swiftly binary still exists at \( swiftlyBinary) " )
186
+ }
187
+
188
+ // Check that env files are removed
189
+ let envSh = customLoc / " env.sh "
190
+ let envFish = customLoc / " env.fish "
191
+ guard !( try await fs. exists ( atPath: envSh) ) else {
192
+ throw TestError ( " env.sh still exists at \( envSh) " )
193
+ }
194
+ guard !( try await fs. exists ( atPath: envFish) ) else {
195
+ throw TestError ( " env.fish still exists at \( envFish) " )
196
+ }
197
+
198
+ // Check that config is removed
199
+ let config = customLoc / " config.json "
200
+ guard !( try await fs. exists ( atPath: config) ) else {
201
+ throw TestError ( " config.json still exists at \( config) " )
202
+ }
203
+
204
+ print ( " ✓ Custom location cleanup verification passed " )
205
+ }
206
+
207
+ private func verifyDefaultLocationCleanup( shell: FilePath, env: [ String: String] ) async throws {
208
+ print ( " Verifying cleanup for default location " )
209
+
210
+ let swiftlyHome = fs. home / " .swiftly "
211
+ let swiftlyBin = swiftlyHome / " bin "
212
+
213
+ // Check that swiftly binary is removed
214
+ let swiftlyBinary = swiftlyBin / " swiftly "
215
+ guard !( try await fs. exists ( atPath: swiftlyBinary) ) else {
216
+ throw TestError ( " Swiftly binary still exists at \( swiftlyBinary) " )
217
+ }
218
+
219
+ // Check that env files are removed
220
+ let envSh = swiftlyHome / " env.sh "
221
+ let envFish = swiftlyHome / " env.fish "
222
+ guard !( try await fs. exists ( atPath: envSh) ) else {
223
+ throw TestError ( " env.sh still exists at \( envSh) " )
224
+ }
225
+ guard !( try await fs. exists ( atPath: envFish) ) else {
226
+ throw TestError ( " env.fish still exists at \( envFish) " )
227
+ }
228
+
229
+ // Check that config is removed
230
+ let config = swiftlyHome / " config.json "
231
+ guard !( try await fs. exists ( atPath: config) ) else {
232
+ throw TestError ( " config.json still exists at \( config) " )
233
+ }
234
+
235
+ // Check that shell profile files have been cleaned up
236
+ try await verifyProfileCleanup ( )
237
+
238
+ // Verify swiftly command is no longer available
239
+ do {
240
+ try await sh ( executable: . path( shell) , . login, . command( " which swiftly " ) ) . run ( currentPlatform, env: env, quiet: true )
241
+ throw TestError ( " swiftly command is still available in PATH after uninstall " )
242
+ } catch {
243
+ // Expected - swiftly should not be found
244
+ }
245
+
246
+ print ( " ✓ Default location cleanup verification passed " )
247
+ }
248
+
249
+ private func verifyProfileCleanup( ) async throws {
250
+ print ( " Verifying shell profile cleanup " )
251
+
252
+ let profilePaths : [ FilePath ] = [
253
+ fs. home / " .zprofile " ,
254
+ fs. home / " .bash_profile " ,
255
+ fs. home / " .bash_login " ,
256
+ fs. home / " .profile " ,
257
+ fs. home / " .config/fish/conf.d/swiftly.fish "
258
+ ]
259
+
260
+ let swiftlySourcePattern = " . \" .* \\ .swiftly/env \\ .sh \" "
261
+ let fishSourcePattern = " source \" .* \\ .swiftly/env \\ .fish \" "
262
+ let commentPattern = " # Added by swiftly "
263
+
264
+ for profilePath in profilePaths {
265
+ guard try await fs. exists ( atPath: profilePath) else { continue }
266
+
267
+ let contents = try String ( contentsOf: profilePath, encoding: . utf8)
268
+
269
+ // Check that swiftly-related lines are removed
270
+ if contents. range ( of: swiftlySourcePattern, options: . regularExpression) != nil ||
271
+ contents. range ( of: fishSourcePattern, options: . regularExpression) != nil ||
272
+ contents. contains ( commentPattern) {
273
+ throw TestError ( " Swiftly references still found in profile file: \( profilePath) " )
274
+ }
275
+ }
276
+
277
+ print ( " ✓ Shell profile cleanup verification passed " )
278
+ }
279
+ }
280
+
281
+ struct TestError: Error {
282
+ let message : String
283
+
284
+ init ( _ message: String) {
285
+ self . message = message
157
286
}
158
287
}
0 commit comments