Files
multi-simadmin/apps/ios/Tests/MultiSimAdminTests/SettingsViewModelTests.swift
T
chick 9ad0c46088 release(ios): 0.2.2 构建 8 中性化批量标签禁用态并去蓝化图标底色
- FleetTagBatchSheet:无勾选时标签胶囊不再走 Button 禁用态(系统会压成灰块,像加载
  失败),改为中性标签原样渲染;覆盖度文案留空,图标改 tag;新建标签输入与按钮置灰
  并补一句「勾选节点后即可新建标签并批量贴上」。
- 应用图标:底色与插槽从偏青绿的石墨(R>G=B,亮壁纸上读作蓝)改为严格等值中性灰
  20/19/10,金色跨度拉大(#FADD98 -> #C78D2F)去掉芥末黄塑料感;AppIcon.svg 同步。
- 版本号:marketing 0.2.2 不带括号,构建号 7 -> 8;文档里 0.2.x (N) 一律改写为
  「0.2.x 构建 N」,README 与现行说明同步。
2026-09-15 05:07:03 +08:00

162 lines
4.8 KiB
Swift

import XCTest
@testable import MultiSimAdmin
final class SettingsViewModelTests: XCTestCase {
@MainActor
func testLoadsRuntimeWithoutServerCapabilities() async throws {
let store = SettingsMemoryLogStore()
let model = SettingsViewModel(store: store)
await model.refresh()
XCTAssertEqual(model.runtime?.appName, "MultiSimAdmin")
XCTAssertEqual(model.runtime?.marketingVersion, "0.2.2")
XCTAssertEqual(model.runtime?.buildVersion, "8")
XCTAssertEqual(model.runtime?.platform, "iOS")
XCTAssertTrue(model.runtime?.systemVersion.hasPrefix("iOS") ?? false)
XCTAssertNil(model.errorMessage)
}
@MainActor
func testClearsRuntimeLogsOnlyAndReportsRemovedCount() async throws {
let store = SettingsMemoryLogStore()
try await store.append(
runtime: [Self.runtimeLog()],
connections: [Self.connectionLog()]
)
let model = SettingsViewModel(store: store)
let removed = await model.clearRuntimeLogs()
XCTAssertEqual(removed, 1)
XCTAssertTrue(store.runtimeStorage.isEmpty)
XCTAssertEqual(store.connectionStorage.count, 1)
XCTAssertEqual(model.notice, "已清除 1 条运行日志。")
XCTAssertNil(model.errorMessage)
}
@MainActor
func testClearFailureSetsErrorWithoutChangingStorage() async throws {
let store = SettingsMemoryLogStore()
try await store.append(
runtime: [Self.runtimeLog()],
connections: [Self.connectionLog()]
)
let failing = FailingLogStore(base: store)
let model = SettingsViewModel(store: failing)
let removed = await model.clearRuntimeLogs()
XCTAssertEqual(removed, 0)
XCTAssertEqual(store.runtimeStorage.count, 1)
XCTAssertEqual(store.connectionStorage.count, 1)
XCTAssertEqual(model.errorMessage, "清除运行日志失败。")
XCTAssertNil(model.notice)
}
private static func runtimeLog() -> RuntimeLogEntry {
RuntimeLogEntry(
id: "runtime-1",
source: .delivery,
level: .info,
message: "测试日志",
occurredAt: Date()
)
}
private static func connectionLog() -> ConnectionLogEntry {
ConnectionLogEntry(
id: "connection-1",
instanceID: UUID(),
instanceName: "Node",
outcome: .success,
state: "online",
durationMs: 20,
observedAt: Date()
)
}
}
private final class SettingsMemoryLogStore: LogStore, @unchecked Sendable {
private let lock = NSLock()
private var runtimeItems: [RuntimeLogEntry] = []
private var connectionItems: [ConnectionLogEntry] = []
var runtimeStorage: [RuntimeLogEntry] {
lock.withLock { runtimeItems }
}
var connectionStorage: [ConnectionLogEntry] {
lock.withLock { connectionItems }
}
func runtimeLogs() async throws -> [RuntimeLogEntry] {
runtimeStorage
}
func connectionLogs() async throws -> [ConnectionLogEntry] {
connectionStorage
}
func append(
runtime: [RuntimeLogEntry],
connections: [ConnectionLogEntry]
) async throws {
lock.withLock {
var mergedRuntime = runtime
for existing in runtimeItems where !runtime.contains(where: { $0.id == existing.id }) {
mergedRuntime.append(existing)
}
runtimeItems = mergedRuntime.sorted { $0.occurredAt > $1.occurredAt }
var mergedConnections = connections
for existing in connectionItems
where !connections.contains(where: { $0.id == existing.id }) {
mergedConnections.append(existing)
}
connectionItems = mergedConnections.sorted { $0.observedAt > $1.observedAt }
}
}
func pruneConnections(before _: Date) async throws -> Int {
0
}
func clearRuntimeLogs() async throws -> Int {
lock.withLock {
let removed = runtimeItems.count
runtimeItems = []
return removed
}
}
}
private struct FailingLogStore: LogStore {
let base: SettingsMemoryLogStore
func runtimeLogs() async throws -> [RuntimeLogEntry] {
try await base.runtimeLogs()
}
func connectionLogs() async throws -> [ConnectionLogEntry] {
try await base.connectionLogs()
}
func append(
runtime: [RuntimeLogEntry],
connections: [ConnectionLogEntry]
) async throws {
try await base.append(runtime: runtime, connections: connections)
}
func pruneConnections(before cutoff: Date) async throws -> Int {
try await base.pruneConnections(before: cutoff)
}
func clearRuntimeLogs() async throws -> Int {
throw RepositoryError.databaseFailed
}
}