import XCTest @testable import MultiSimAdmin final class MessageServiceTests: XCTestCase { func testParsesUpstreamEnvelopeAndNormalizesMessages() async throws { let store = InstanceSessionStore() let client = MessageTestClient() let service = MessageService(client: client, sessions: store) let instance = Self.instance() await store.set(instance.id, origin: instance.endpoint.directOrigin!, cookie: "simadmin_session=token") let page = try await service.messages([instance], limit: 2) XCTAssertEqual(client.routes.first, .smsList(limit: 2, offset: 0, direction: nil)) XCTAssertEqual(page.messages.count, 1) let message = try XCTUnwrap(page.messages.first) XCTAssertEqual(message.upstreamID, "42") XCTAssertEqual(message.id, "\(instance.id.uuidString)#42") XCTAssertEqual(message.instanceID, instance.id) XCTAssertEqual(message.instanceName, "Message Node") XCTAssertEqual(message.phoneNumber, "13800138000") XCTAssertEqual(message.content, "device ready") XCTAssertEqual(message.direction, .outgoing) XCTAssertEqual(message.status, "sent") } func testSendUsesStoredSessionAndRejectsInvalidInput() async throws { let store = InstanceSessionStore() let client = MessageTestClient() let service = MessageService(client: client, sessions: store) let instance = Self.instance() await store.set(instance.id, origin: instance.endpoint.directOrigin!, cookie: "simadmin_session=token") try await service.send(instance, phoneNumber: "13800138000", content: "hello") XCTAssertEqual(client.routes.last, .smsSend(phoneNumber: "13800138000", content: "hello")) do { try await service.send(instance, phoneNumber: "", content: "hello") XCTFail("Empty phone number should fail.") } catch { XCTAssertEqual(error as? MessageServiceError, .invalidInput) } } private static func instance() -> Instance { Instance( id: UUID(), name: "Message Node", origin: URL(string: "http://192.168.1.20:3000")!, configRevision: 1, createdAt: Date(), updatedAt: Date() ) } } private final class MessageTestClient: SimAdminClientProtocol, @unchecked Sendable { var routes: [UpstreamRoute] = [] func send(_ route: UpstreamRoute, baseURL: URL, cookie: String?) async throws -> UpstreamResponse { routes.append(route) guard cookie == "simadmin_session=token" else { throw UpstreamError.unauthorized } switch route { case .smsList: let json = """ {"status":"ok","data":{"total":1,"messages":[{"id":42,"phone_number":"13800138000", "content":"device ready","direction":"outgoing","status":"sent","timestamp":"2026-09-08T10:00:00Z"}]}} """ return UpstreamResponse(status: 200, data: Data(json.utf8)) case .smsSend: return UpstreamResponse(status: 200, data: Data(#"{"status":"ok"}"#.utf8)) default: return UpstreamResponse(status: 200, data: Data("{}".utf8)) } } }