import XCTest #if canImport(cmux_DEV) @testable import cmux_DEV #elseif canImport(cmux) @testable import cmux #endif final class SocketControlPasswordStoreTests: XCTestCase { func testSaveLoadAndClearRoundTripUsesFileStorage() throws { let tempDir = FileManager.default.temporaryDirectory .appendingPathComponent("cmux-socket-password-tests-\(UUID().uuidString)", isDirectory: true) try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true) defer { try? FileManager.default.removeItem(at: tempDir) } let fileURL = tempDir.appendingPathComponent("socket-password.txt", isDirectory: false) XCTAssertFalse(SocketControlPasswordStore.hasConfiguredPassword(environment: [:], fileURL: fileURL)) try SocketControlPasswordStore.savePassword("hunter2", fileURL: fileURL) XCTAssertEqual(try SocketControlPasswordStore.loadPassword(fileURL: fileURL), "hunter2") XCTAssertTrue(SocketControlPasswordStore.hasConfiguredPassword(environment: [:], fileURL: fileURL)) try SocketControlPasswordStore.clearPassword(fileURL: fileURL) XCTAssertNil(try SocketControlPasswordStore.loadPassword(fileURL: fileURL)) XCTAssertFalse(SocketControlPasswordStore.hasConfiguredPassword(environment: [:], fileURL: fileURL)) } func testConfiguredPasswordPrefersEnvironmentOverStoredFile() throws { let tempDir = FileManager.default.temporaryDirectory .appendingPathComponent("cmux-socket-password-tests-\(UUID().uuidString)", isDirectory: true) try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true) defer { try? FileManager.default.removeItem(at: tempDir) } let fileURL = tempDir.appendingPathComponent("socket-password.txt", isDirectory: false) try SocketControlPasswordStore.savePassword("stored-secret", fileURL: fileURL) let environment = [SocketControlSettings.socketPasswordEnvKey: "env-secret"] let configured = SocketControlPasswordStore.configuredPassword( environment: environment, fileURL: fileURL ) XCTAssertEqual(configured, "env-secret") } func testDefaultPasswordFileURLUsesCmuxAppSupportPath() throws { let tempDir = FileManager.default.temporaryDirectory .appendingPathComponent("cmux-socket-password-tests-\(UUID().uuidString)", isDirectory: true) try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true) defer { try? FileManager.default.removeItem(at: tempDir) } let resolved = SocketControlPasswordStore.defaultPasswordFileURL(appSupportDirectory: tempDir) XCTAssertEqual( resolved?.path, tempDir.appendingPathComponent("cmux", isDirectory: true) .appendingPathComponent("socket-control-password", isDirectory: false).path ) } func testLegacyKeychainMigrationCopiesPasswordDeletesLegacyAndRunsOnlyOnce() throws { let tempDir = FileManager.default.temporaryDirectory .appendingPathComponent("cmux-socket-password-tests-\(UUID().uuidString)", isDirectory: true) try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true) defer { try? FileManager.default.removeItem(at: tempDir) } let fileURL = tempDir.appendingPathComponent("socket-password.txt", isDirectory: false) let defaultsSuiteName = "cmux-socket-password-migration-tests-\(UUID().uuidString)" guard let defaults = UserDefaults(suiteName: defaultsSuiteName) else { XCTFail("Expected isolated UserDefaults suite for migration test") return } defer { defaults.removePersistentDomain(forName: defaultsSuiteName) } var lookupCount = 0 var deleteCount = 0 SocketControlPasswordStore.migrateLegacyKeychainPasswordIfNeeded( defaults: defaults, fileURL: fileURL, loadLegacyPassword: { lookupCount += 1 return "legacy-secret" }, deleteLegacyPassword: { deleteCount += 1 return true } ) XCTAssertEqual(try SocketControlPasswordStore.loadPassword(fileURL: fileURL), "legacy-secret") XCTAssertEqual(lookupCount, 1) XCTAssertEqual(deleteCount, 1) SocketControlPasswordStore.migrateLegacyKeychainPasswordIfNeeded( defaults: defaults, fileURL: fileURL, loadLegacyPassword: { lookupCount += 1 return "new-value" }, deleteLegacyPassword: { deleteCount += 1 return true } ) XCTAssertEqual(lookupCount, 1) XCTAssertEqual(deleteCount, 1) XCTAssertEqual(try SocketControlPasswordStore.loadPassword(fileURL: fileURL), "legacy-secret") } }