import XCTest @testable import MultiSimAdmin @MainActor final class NotificationsViewModelTests: XCTestCase { func testRefreshLoadsInstancesAndAggregatedOverview() async throws { let repository = MemoryInstanceRepository() let client = NotificationViewModelClient() let service = NotificationService(client: client, sessions: InstanceSessionStore()) let model = NotificationsViewModel(repository: repository, service: service) _ = try await repository.create(InstanceInput( name: "Notification Node", origin: URL(string: "http://192.168.1.20:3000")!, tags: [], password: nil )) await model.refresh() XCTAssertEqual(model.instances.count, 1) XCTAssertEqual(model.overview?.devices.first?.state, .ready) XCTAssertEqual(model.overview?.queue.items.first?.id, "queue-1") XCTAssertNil(model.errorMessage) } func testRetryQueueTargetsOwningInstance() async throws { let repository = MemoryInstanceRepository() let client = NotificationViewModelClient() let service = NotificationService(client: client, sessions: InstanceSessionStore()) let model = NotificationsViewModel(repository: repository, service: service) _ = try await repository.create(InstanceInput( name: "Notification Node", origin: URL(string: "http://192.168.1.20:3000")!, tags: [], password: nil )) await model.refresh() let item = try XCTUnwrap(model.overview?.queue.items.first) await model.retryQueue(item) XCTAssertEqual(client.routes.last, .notificationRetryQueue(id: "queue-1")) } func testRetryAllTargetsEveryInstance() async throws { let repository = MemoryInstanceRepository() let client = NotificationViewModelClient() let service = NotificationService(client: client, sessions: InstanceSessionStore()) let model = NotificationsViewModel(repository: repository, service: service) _ = try await repository.create(InstanceInput( name: "First", origin: URL(string: "http://192.168.1.20:3000")!, tags: [], password: nil )) _ = try await repository.create(InstanceInput( name: "Second", origin: URL(string: "http://192.168.1.21:3000")!, tags: [], password: nil )) await model.refresh() await model.retryAll() XCTAssertEqual(client.routes.filter { $0 == .notificationRetryAllQueue }.count, 2) } } private final class NotificationViewModelClient: SimAdminClientProtocol, @unchecked Sendable { var routes: [UpstreamRoute] = [] func send(_ route: UpstreamRoute, baseURL: URL, cookie: String?) async throws -> UpstreamResponse { routes.append(route) switch route { case .notificationConfig: return Self.response(#"{"status":"ok","data":{"channels":[{"type":"webhook","enabled":true}]}}"#) case .notificationLogs: return Self.response(#"{"status":"ok","data":{"total":1,"logs":[{"id":"log-1","event_type":"system","status":"success","created_at":"2026-09-08T10:00:00Z"}]}}"#) case .notificationQueue: return Self.response(#"{"status":"ok","data":{"total":1,"items":[{"id":"queue-1","event_type":"system","status":"failed","created_at":"2026-09-08T10:00:00Z"}]}}"#) default: return Self.response(#"{"status":"ok"}"#) } } private static func response(_ json: String) -> UpstreamResponse { UpstreamResponse(status: 200, data: Data(json.utf8)) } }