Files
multi-simadmin/apps/ios/Tests/MultiSimAdminTests/FleetViewModelTests.swift
T

134 lines
4.0 KiB
Swift

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 }
}
}