Files
element-x-ios/UnitTests/Sources/HealthChat/ExtendedProfileServiceTests.swift
T
joel.poyet e5bfc3a26d Add HealthChat API and extended profile services (#9)
Expose OpenID tokens and homeserver credentials from the client proxy, and
add fork-only services for the HealthChat member directory backend
(api.healthchat.ch) plus a service that reads MSC4133 extended profile
fields directly from the homeserver. Groundwork for the members tab and
the profile screens.

Claude-Session: https://claude.ai/code/session_013qdTbAWsZj2quQzEkagXaS
2026-10-19 09:17:29 +02:00

118 lines
4.3 KiB
Swift

//
// Copyright 2026 Element Creations Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
// Please see LICENSE files in the repository root for full details.
//
@testable import ElementX
import Foundation
import Synchronization
import Testing
final class ExtendedProfileServiceTests {
private let clientProxy = ClientProxyMock(.init())
private let service: ExtendedProfileService
init() {
let configuration = URLSessionConfiguration.ephemeral
configuration.protocolClasses = [MockProfileURLProtocol.self]
service = ExtendedProfileService(clientProxy: clientProxy,
urlSession: URLSession(configuration: configuration))
clientProxy.homeserverCredentialsReturnValue = .success(.init(homeserverURL: "https://matrix.example.com",
accessToken: MockProfileURLProtocol.accessToken))
}
@Test
func fetchesProfileWithBearerToken() async throws {
let result = await service.profile(for: "@alice:healthchat.ch")
let profile = try result.get()
#expect(profile.displayName == "Dr. Alice Alpha")
#expect(profile.businessTel == "+41 44 123 45 67")
let request = try #require(MockProfileURLProtocol.lastRequest)
#expect(request.url?.path(percentEncoded: false) == "/_matrix/client/v3/profile/@alice:healthchat.ch")
#expect(request.value(forHTTPHeaderField: "Authorization") == "Bearer \(MockProfileURLProtocol.accessToken)")
}
@Test
func missingCredentialsFailWithoutRequest() async {
clientProxy.homeserverCredentialsReturnValue = .failure(.sdkError(ClientProxyMockError.generic))
let result = await service.profile(for: "@alice:healthchat.ch")
#expect(result == .failure(.missingCredentials))
}
@Test
func unknownUserFailsWithHTTPStatus() async {
let result = await service.profile(for: "@unknown:healthchat.ch")
#expect(result == .failure(.httpStatus(404)))
}
@Test
func credentialsDescriptionRedactsAccessToken() {
let credentials = HomeserverCredentials(homeserverURL: "https://matrix.example.com",
accessToken: "supersecret")
#expect(!String(describing: credentials).contains("supersecret"))
}
}
// MARK: - MockProfileURLProtocol
private nonisolated class MockProfileURLProtocol: URLProtocol {
static let accessToken = "testAccessToken"
private static let _lastRequest: Mutex<URLRequest?> = .init(nil)
static var lastRequest: URLRequest? {
_lastRequest.withLock { $0 }
}
private static let profileJSON = """
{
"displayname": "Dr. Alice Alpha",
"io.element.profile.business_tel": "+41 44 123 45 67"
}
"""
private static let responses: [String: (Int, Data)] = [
"/_matrix/client/v3/profile/@alice:healthchat.ch": (200, Data(profileJSON.utf8))
]
override func startLoading() {
MockProfileURLProtocol._lastRequest.withLock { $0 = request }
guard let url = request.url else {
client?.urlProtocol(self, didFailWithError: URLError(.badURL))
return
}
let isAuthenticated = request.value(forHTTPHeaderField: "Authorization") == "Bearer \(MockProfileURLProtocol.accessToken)"
let path = url.path(percentEncoded: false)
let (statusCode, data) = isAuthenticated ? MockProfileURLProtocol.responses[path] ?? (404, Data()) : (401, Data())
guard let response = HTTPURLResponse(url: url, statusCode: statusCode, httpVersion: nil, headerFields: nil) else {
client?.urlProtocol(self, didFailWithError: URLError(.badServerResponse))
return
}
client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
client?.urlProtocol(self, didLoad: data)
client?.urlProtocolDidFinishLoading(self)
}
override func stopLoading() { }
override class func canonicalRequest(for request: URLRequest) -> URLRequest {
request
}
override class func canInit(with request: URLRequest) -> Bool {
true
}
}