import XCTest @testable import MultiSimAdmin @MainActor final class FleetViewModelTests: XCTestCase { func testRefreshSortsByNameAndReportsStatus() async throws { let repository = MemoryInstanceRepository() let model = FleetViewModel( repository: repository, probe: ConnectionProbe(client: AlwaysOnlineClient()), credentials: NoopCredentialStore() ) try await model.add(InstanceInput( name: "Zulu", origin: URL(string: "http://192.168.1.30:3000")!, tags: ["lab"], password: nil )) try await model.add(InstanceInput( name: "Alpha", origin: URL(string: "http://192.168.1.20:3000")!, tags: [], password: nil )) await model.refresh() XCTAssertEqual(model.rows.map(\.instance.name), ["Alpha", "Zulu"]) XCTAssertEqual(model.rows.map(\.status), [.online, .online]) } func testAddValidatesPasswordAndDeleteRemovesCredential() async throws { let repository = MemoryInstanceRepository() let credentials = NoopCredentialStore() let model = FleetViewModel( repository: repository, probe: ConnectionProbe(client: AlwaysOnlineClient()), credentials: credentials ) try await model.add(InstanceInput( name: "Secret", origin: URL(string: "http://192.168.1.40:3000")!, password: "pass" )) let row = try XCTUnwrap(model.rows.first) XCTAssertTrue(row.instance.credentialConfigured) XCTAssertEqual(credentials.saved[row.instance.id], "pass") await model.delete([row.instance.id]) XCTAssertTrue(model.rows.isEmpty) XCTAssertNil(credentials.saved[row.instance.id]) } } struct AlwaysOnlineClient: SimAdminClientProtocol { func send(_: UpstreamRoute, baseURL: URL, cookie: String?) async throws -> UpstreamResponse { UpstreamResponse(status: 200, data: Data("{}".utf8)) } } final class NoopCredentialStore: CredentialStore { private let lock = NSLock() private var savedStorage: [UUID: String] = [:] private var removedStorage: [UUID] = [] var saved: [UUID: String] { lock.withLock { savedStorage } } var removed: [UUID] { lock.withLock { removedStorage } } func get(instanceID: UUID) throws -> String? { saved[instanceID] } func set(instanceID: UUID, password: String) throws { lock.withLock { savedStorage[instanceID] = password } } func remove(instanceID: UUID) throws { lock.withLock { savedStorage[instanceID] = nil removedStorage.append(instanceID) } } } final class MemoryInstanceRepository: InstanceRepository { private let lock = NSLock() private var itemsStorage: [Instance] = [] private var items: [Instance] { get { lock.withLock { itemsStorage } } set { lock.withLock { itemsStorage = newValue } } } func list(_: InstanceQuery) async throws -> [Instance] { items.sorted { $0.name.localizedCompare($1.name) == .orderedAscending } } func get(_ id: UUID) async throws -> Instance? { items.first { $0.id == id } } func create(_ input: InstanceInput) async throws -> Instance { let now = Date() let instance = Instance( id: UUID(), name: input.name, origin: input.origin, tags: input.tags, groupId: input.groupId, credentialConfigured: !(input.password ?? "").isEmpty, configRevision: 1, createdAt: now, updatedAt: now ) items.append(instance) return instance } func update(_: UUID, expectedRevision: Int, _: InstancePatch) async throws -> Instance { throw RepositoryError.notFound } func delete(_ id: UUID, expectedRevision: Int) async throws { items.removeAll { $0.id == id } } }