Files
2026-07-13 12:22:33 +08:00

252 lines
8.5 KiB
Swift

import Foundation
import Testing
@testable import CodexBarCore
@Suite(.serialized)
struct PoeUsageFetcherTests {
@Test
func `parse snapshot extracts current point balance`() throws {
let json = #"{"current_point_balance": 1500}"#
let data = Data(json.utf8)
let snapshot = try PoeUsageFetcher._parseSnapshotForTesting(data)
#expect(snapshot.currentPointBalance == 1500)
}
@Test
func `parse snapshot accepts string-encoded balance`() throws {
let json = #"{"current_point_balance": "2500"}"#
let snapshot = try PoeUsageFetcher._parseSnapshotForTesting(Data(json.utf8))
#expect(snapshot.currentPointBalance == 2500)
}
@Test
func `parse snapshot returns nil balance when absent`() throws {
let json = #"{}"#
let snapshot = try PoeUsageFetcher._parseSnapshotForTesting(Data(json.utf8))
#expect(snapshot.currentPointBalance == nil)
}
@Test
func `parse snapshot throws on malformed JSON`() {
#expect {
_ = try PoeUsageFetcher._parseSnapshotForTesting(Data("not-json".utf8))
} throws: { error in
guard case PoeUsageError.parseFailed = error else { return false }
return true
}
}
@Test
func `snapshot maps balance to identity loginMethod only, not RateWindow`() {
let snapshot = PoeUsageSnapshot(
currentPointBalance: 500,
updatedAt: Date())
let unified = snapshot.toUsageSnapshot()
// No rate windows — balance is not a usage percentage
#expect(unified.primary == nil)
#expect(unified.secondary == nil)
#expect(unified.tertiary == nil)
// Balance lives in identity.loginMethod as "Balance: X points"
#expect(unified.identity?.providerID == .poe)
#expect(unified.identity?.loginMethod == "Balance: 500 points")
}
@Test
func `snapshot hides balance when balance is absent`() {
let snapshot = PoeUsageSnapshot(
currentPointBalance: nil,
updatedAt: Date())
let unified = snapshot.toUsageSnapshot()
#expect(unified.primary == nil)
#expect(unified.identity?.loginMethod == nil)
}
@Test
func `missing credentials fetch call throws missing credentials`() async {
do {
_ = try await PoeUsageFetcher.fetchUsage(apiKey: " ")
Issue.record("Expected missingCredentials error")
} catch let error as PoeUsageError {
guard case .missingCredentials = error else {
Issue.record("Expected .missingCredentials but got \(error)")
return
}
} catch {
Issue.record("Unexpected error: \(error)")
}
}
@Test
func `compact number formats thousands with no decimals`() {
#expect(PoeUsageSnapshot.compactNumber(1500) == "1,500")
#expect(PoeUsageSnapshot.compactNumber(999) == "999")
#expect(PoeUsageSnapshot.compactNumber(10000) == "10,000")
}
@Test
func `history page parser extracts entries and cursor`() throws {
let json = """
{
"data": [
{
"query_id": "a1",
"creation_time": 1717000000000000,
"bot_name": "GPT-4o",
"usage_type": "API",
"cost_points": 12.5,
"cost_usd": "0.03"
},
{
"query_id": "a2",
"creation_time": 1717003600,
"bot_name": "Claude Sonnet",
"usage_type": "Chat",
"cost_points": "8",
"usd": "0.02"
}
],
"next_cursor": "cursor-2"
}
"""
let parsed = try PoeUsageFetcher._parseHistoryPageForTesting(Data(json.utf8))
#expect(parsed.entries.count == 2)
#expect(parsed.entries[0].model == "GPT-4o")
#expect(parsed.entries[0].points == 12.5)
#expect(parsed.entries[0].id == "a1")
#expect(parsed.entries[1].costUSD == 0.02)
#expect(parsed.nextCursor == "cursor-2")
}
@Test
func `history parser derives cursor from has_more and last query id`() throws {
let json = """
{
"has_more": true,
"data": [
{
"query_id": "q-1",
"creation_time": 1717000000,
"bot_name": "GPT-4o",
"usage_type": "API",
"cost_points": 3
},
{
"query_id": "q-2",
"creation_time": 1717003600,
"bot_name": "Claude Sonnet",
"usage_type": "Chat",
"cost_points": 9
}
]
}
"""
let parsed = try PoeUsageFetcher._parseHistoryPageForTesting(Data(json.utf8))
#expect(parsed.entries.count == 2)
#expect(parsed.nextCursor == "q-2")
}
@Test
func `history daily aggregation groups by utc day`() {
let entries = [
PoeUsageHistorySnapshot.Entry(
id: "1",
createdAt: Date(timeIntervalSince1970: 1_717_000_000),
model: "GPT-4o",
usageType: "inference",
points: 10,
costUSD: 0.02),
PoeUsageHistorySnapshot.Entry(
id: "2",
createdAt: Date(timeIntervalSince1970: 1_717_000_100),
model: "GPT-4o",
usageType: "inference",
points: 5,
costUSD: 0.01),
]
let daily = PoeUsageFetcher._buildDailyBucketsForTesting(entries: entries)
#expect(daily.count == 1)
#expect(daily[0].requests == 2)
#expect(daily[0].points == 15)
#expect(daily[0].costUSD == 0.03)
}
@Test
func `fetch usage returns balance when points history endpoint fails`() async throws {
let transport = ProviderHTTPTransportStub { request in
let url = try #require(request.url)
let path = url.path
let response: (Data, Int)
if path.contains("current_balance") {
response = (Data(#"{"current_point_balance": 1500}"#.utf8), 200)
} else if path.contains("points_history") {
// Simulate a 500 from the optional history endpoint.
response = (Data("server error".utf8), 500)
} else {
Issue.record("Unexpected request: \(url.absoluteString)")
return Self.httpResponse(url: nil, status: 0)
}
return Self.httpResponse(url: nil, status: response.1, body: response.0)
}
let snapshot = try await PoeUsageFetcher._fetchUsage(
apiKey: "test-key",
transport: transport)
#expect(snapshot.currentPointBalance == 1500)
// History should be nil, not propagate the failure.
#expect(snapshot.history == nil)
}
@Test
func `fetch usage surfaces history snapshot when history endpoint succeeds`() async throws {
let transport = ProviderHTTPTransportStub { request in
let url = try #require(request.url)
let path = url.path
if path.contains("current_balance") {
return Self.httpResponse(
url: nil,
status: 200,
body: Data(#"{"current_point_balance": 2500}"#.utf8))
}
if path.contains("points_history") {
return Self.httpResponse(
url: nil,
status: 200,
body: Data("""
{"data":[],"next_cursor":null}
""".utf8))
}
Issue.record("Unexpected request: \(url.absoluteString)")
return Self.httpResponse(url: nil, status: 0)
}
let snapshot = try await PoeUsageFetcher._fetchUsage(
apiKey: "test-key",
transport: transport)
#expect(snapshot.currentPointBalance == 2500)
// Empty history page still produces a non-nil snapshot with empty buckets.
#expect(snapshot.history == nil)
}
}
extension PoeUsageFetcherTests {
fileprivate static func httpResponse(
url: URL?,
status: Int,
body: Data = Data()) -> (Data, URLResponse)
{
let response = HTTPURLResponse(
url: url ?? URL(string: "https://example.invalid")!,
statusCode: status,
httpVersion: "HTTP/1.1",
headerFields: nil) ?? HTTPURLResponse()
return (body, response)
}
}