Files
OSGKeyboard/OSGKeyboardTests/AccountSnapshotLoaderTests.swift
T
Rocky 7bd77509b6
CI / Validate manifests (push) Has been cancelled
CI / SwiftLint (push) Has been cancelled
CI / iOS / Extension (push) Has been cancelled
CI / macOS (push) Has been cancelled
fix(account): load permanent referral profiles
Use authenticated server invitation URLs with account-scoped caching and bounded state transitions, then advance the iOS build metadata to 86.
2026-08-20 22:59:58 +08:00

107 lines
3.4 KiB
Swift

// AccountSnapshotLoaderTests.swift
// OSGKeyboardTests
//
// Resilient account aggregation keeps cached optional data when a secondary
// endpoint is temporarily unavailable.
@testable import OSGKeyboard
@testable import OSGKeyboardHostSupport
import XCTest
final class AccountSnapshotLoaderTests: XCTestCase {
@MainActor
func testOptionalReferralListFailureUsesCachedAccountData() async throws {
let account = OSGKeyboard.AccountSession(
accountID: UUID(),
createdAtEpochSeconds: 1_700_000_000
)
let cachedReferral = AccountReferral(
id: UUID(),
status: .rewarded,
createdAtEpochSeconds: 1_700_000_100,
rewardCredits: 1_000
)
let cached = AccountCenterSnapshot(
account: account,
credits: AccountCreditSummary(balance: 1_000, usedCredits: 0),
referrals: [cachedReferral]
)
let transport = AccountCenterRoutingTransport(accountID: account.accountID)
let client = AccountAPIClient(
baseURL: URL(string: "https://account.test")!,
transport: transport,
sessionVault: InMemoryAccountSecurityStore(
session: makeAccountSession()
)
)
let loader = AccountCenterSnapshotLoader(apiClient: client)
let refreshed = try await loader.load(cachedSnapshot: cached)
XCTAssertEqual(
refreshed.credits,
AccountCreditSummary(balance: 750, usedCredits: 250)
)
XCTAssertEqual(refreshed.referrals, cached.referrals)
let requests = await transport.requests
XCTAssertEqual(
requests.count { $0.url?.path == "/v1/referrals/me" },
0
)
XCTAssertEqual(
requests.count { $0.url?.path == "/v1/referrals/code" },
0
)
XCTAssertEqual(
requests.count { $0.url?.path == "/v1/referrals" },
2
)
}
}
private actor AccountCenterRoutingTransport: AccountHTTPTransport {
let accountID: UUID
private(set) var requests: [URLRequest] = []
init(accountID: UUID) {
self.accountID = accountID
}
func data(for request: URLRequest) async throws -> (Data, HTTPURLResponse) {
requests.append(request)
let response: QueueAccountTransport.Stub
switch request.url?.path {
case "/v1/account":
response = .init(
statusCode: 200,
body: Data(
"""
{"data":{"id":"\(accountID.uuidString)","createdAtEpochSeconds":1700000000}}
""".utf8
)
)
case "/v1/credits/balance":
response = .init(
statusCode: 200,
body: Data(#"{"balance":750,"lifetimeUsed":250}"#.utf8)
)
case "/v1/referrals":
response = .init(
statusCode: 503,
body: apiErrorData(
code: "external_service_unavailable",
message: "temporarily unavailable"
)
)
case "/v1/referrals/campaigns":
response = .init(statusCode: 200, body: Data("[]".utf8))
default:
throw AccountAPIError.transport
}
return (
response.body,
makeHTTPResponse(request: request, statusCode: response.statusCode)
)
}
}