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

231 lines
7.3 KiB
Swift

import Foundation
import Testing
@testable import CodexBarCore
struct WarpUsageFetcherTests {
@Test
func `parses snapshot and aggregates bonus credits`() throws {
let json = """
{
"data": {
"user": {
"__typename": "UserOutput",
"user": {
"requestLimitInfo": {
"isUnlimited": false,
"nextRefreshTime": "2026-02-28T19:16:33.462988Z",
"requestLimit": 1500,
"requestsUsedSinceLastRefresh": 5
},
"bonusGrants": [
{
"requestCreditsGranted": 20,
"requestCreditsRemaining": 10,
"expiration": "2026-03-01T10:00:00Z"
}
],
"workspaces": [
{
"bonusGrantsInfo": {
"grants": [
{
"requestCreditsGranted": "15",
"requestCreditsRemaining": "5",
"expiration": "2026-03-15T10:00:00Z"
}
]
}
}
]
}
}
}
}
"""
let snapshot = try WarpUsageFetcher._parseResponseForTesting(Data(json.utf8))
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
let expectedRefresh = formatter.date(from: "2026-02-28T19:16:33.462988Z")
let expectedExpiry = ISO8601DateFormatter().date(from: "2026-03-01T10:00:00Z")
#expect(snapshot.requestLimit == 1500)
#expect(snapshot.requestsUsed == 5)
#expect(snapshot.isUnlimited == false)
#expect(snapshot.nextRefreshTime != nil)
#expect(abs((snapshot.nextRefreshTime?.timeIntervalSince1970 ?? 0) -
(expectedRefresh?.timeIntervalSince1970 ?? 0))
< 0.5)
#expect(snapshot.bonusCreditsTotal == 35)
#expect(snapshot.bonusCreditsRemaining == 15)
#expect(snapshot.bonusNextExpirationRemaining == 10)
#expect(abs((snapshot.bonusNextExpiration?.timeIntervalSince1970 ?? 0) -
(expectedExpiry?.timeIntervalSince1970 ?? 0))
< 0.5)
}
@Test
func `graph QL errors throw API error`() {
let json = """
{
"errors": [
{ "message": "Unauthorized" }
]
}
"""
#expect {
_ = try WarpUsageFetcher._parseResponseForTesting(Data(json.utf8))
} throws: { error in
guard case let WarpUsageError.apiError(code, message) = error else { return false }
return code == 200 && message.contains("Unauthorized")
}
}
@Test
func `null unlimited and string numerics parse safely`() throws {
let json = """
{
"data": {
"user": {
"__typename": "UserOutput",
"user": {
"requestLimitInfo": {
"isUnlimited": null,
"nextRefreshTime": "2026-02-28T19:16:33Z",
"requestLimit": "1500",
"requestsUsedSinceLastRefresh": "5"
}
}
}
}
}
"""
let snapshot = try WarpUsageFetcher._parseResponseForTesting(Data(json.utf8))
#expect(snapshot.isUnlimited == false)
#expect(snapshot.requestLimit == 1500)
#expect(snapshot.requestsUsed == 5)
#expect(snapshot.nextRefreshTime != nil)
}
@Test
func `unexpected typename returns parse error`() {
let json = """
{
"data": {
"user": {
"__typename": "AuthError"
}
}
}
"""
#expect {
_ = try WarpUsageFetcher._parseResponseForTesting(Data(json.utf8))
} throws: { error in
guard case let WarpUsageError.parseFailed(message) = error else { return false }
return message.contains("Unexpected user type")
}
}
@Test
func `missing request limit info returns parse error`() {
let json = """
{
"data": {
"user": {
"__typename": "UserOutput",
"user": {}
}
}
}
"""
#expect {
_ = try WarpUsageFetcher._parseResponseForTesting(Data(json.utf8))
} throws: { error in
guard case let WarpUsageError.parseFailed(message) = error else { return false }
return message.contains("requestLimitInfo")
}
}
@Test
func `invalid root returns parse error`() {
let json = """
[{ "data": {} }]
"""
#expect {
_ = try WarpUsageFetcher._parseResponseForTesting(Data(json.utf8))
} throws: { error in
guard case let WarpUsageError.parseFailed(message) = error else { return false }
return message == "Root JSON is not an object."
}
}
@Test
func `to usage snapshot omits secondary when no bonus credits`() {
let source = WarpUsageSnapshot(
requestLimit: 100,
requestsUsed: 10,
nextRefreshTime: Date().addingTimeInterval(3600),
isUnlimited: false,
updatedAt: Date(),
bonusCreditsRemaining: 0,
bonusCreditsTotal: 0,
bonusNextExpiration: nil,
bonusNextExpirationRemaining: 0)
let snapshot = source.toUsageSnapshot()
#expect(snapshot.secondary == nil)
}
@Test
func `to usage snapshot keeps bonus window when bonus exists`() throws {
let source = WarpUsageSnapshot(
requestLimit: 100,
requestsUsed: 10,
nextRefreshTime: Date().addingTimeInterval(3600),
isUnlimited: false,
updatedAt: Date(),
bonusCreditsRemaining: 0,
bonusCreditsTotal: 20,
bonusNextExpiration: nil,
bonusNextExpirationRemaining: 0)
let snapshot = source.toUsageSnapshot()
let secondary = try #require(snapshot.secondary)
#expect(secondary.usedPercent == 100)
}
@Test
func `to usage snapshot unlimited primary does not show reset date`() throws {
let source = WarpUsageSnapshot(
requestLimit: 0,
requestsUsed: 0,
nextRefreshTime: Date().addingTimeInterval(3600),
isUnlimited: true,
updatedAt: Date(),
bonusCreditsRemaining: 0,
bonusCreditsTotal: 0,
bonusNextExpiration: nil,
bonusNextExpirationRemaining: 0)
let snapshot = source.toUsageSnapshot()
let primary = try #require(snapshot.primary)
#expect(primary.resetsAt == nil)
#expect(primary.resetDescription == "Unlimited")
}
@Test
func `api error summary includes plain text bodies`() {
// Regression: Warp edge returns 429 with a non-JSON body ("Rate exceeded.") when User-Agent is missing/wrong.
let summary = WarpUsageFetcher._apiErrorSummaryForTesting(
statusCode: 429,
data: Data("Rate exceeded.".utf8))
#expect(summary.contains("Rate exceeded."))
}
}