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

1500 lines
55 KiB
Swift

import Foundation
import SQLite3
import Testing
@testable import CodexBarCore
@Suite(.serialized)
struct CursorStatusProbeTests {
// MARK: - Usage Summary Parsing
@Test
func `parses basic usage summary`() throws {
let json = """
{
"billingCycleStart": "2025-01-01T00:00:00.000Z",
"billingCycleEnd": "2025-02-01T00:00:00.000Z",
"membershipType": "pro",
"individualUsage": {
"plan": {
"enabled": true,
"used": 1500,
"limit": 5000,
"remaining": 3500,
"totalPercentUsed": 30.0
},
"onDemand": {
"enabled": true,
"used": 500,
"limit": 10000,
"remaining": 9500
}
},
"teamUsage": {
"onDemand": {
"enabled": true,
"used": 2000,
"limit": 50000,
"remaining": 48000
}
}
}
"""
let data = try #require(json.data(using: .utf8))
let summary = try JSONDecoder().decode(CursorUsageSummary.self, from: data)
#expect(summary.membershipType == "pro")
#expect(summary.individualUsage?.plan?.used == 1500)
#expect(summary.individualUsage?.plan?.limit == 5000)
#expect(summary.individualUsage?.plan?.totalPercentUsed == 30.0)
#expect(summary.individualUsage?.onDemand?.used == 500)
#expect(summary.teamUsage?.onDemand?.used == 2000)
#expect(summary.teamUsage?.onDemand?.limit == 50000)
}
@Test
func `parses minimal usage summary`() throws {
let json = """
{
"membershipType": "hobby",
"individualUsage": {
"plan": {
"used": 0,
"limit": 2000
}
}
}
"""
let data = try #require(json.data(using: .utf8))
let summary = try JSONDecoder().decode(CursorUsageSummary.self, from: data)
#expect(summary.membershipType == "hobby")
#expect(summary.individualUsage?.plan?.used == 0)
#expect(summary.individualUsage?.plan?.limit == 2000)
#expect(summary.teamUsage == nil)
}
@Test
func `parses enterprise usage summary`() throws {
let json = """
{
"membershipType": "enterprise",
"isUnlimited": true,
"individualUsage": {
"plan": {
"enabled": true,
"used": 50000,
"limit": 100000,
"totalPercentUsed": 50.0
}
}
}
"""
let data = try #require(json.data(using: .utf8))
let summary = try JSONDecoder().decode(CursorUsageSummary.self, from: data)
#expect(summary.membershipType == "enterprise")
#expect(summary.isUnlimited == true)
#expect(summary.individualUsage?.plan?.totalPercentUsed == 50.0)
}
// MARK: - User Info Parsing
@Test
func `parses user info`() throws {
let json = """
{
"email": "user@example.com",
"email_verified": true,
"name": "Test User",
"sub": "auth0|12345"
}
"""
let data = try #require(json.data(using: .utf8))
let userInfo = try JSONDecoder().decode(CursorUserInfo.self, from: data)
#expect(userInfo.email == "user@example.com")
#expect(userInfo.emailVerified == true)
#expect(userInfo.name == "Test User")
#expect(userInfo.sub == "auth0|12345")
}
// MARK: - Snapshot Conversion
@Test
func `prefers plan ratio over percent field`() {
let snapshot = CursorStatusProbe(browserDetection: BrowserDetection(cacheTTL: 0))
.parseUsageSummary(
CursorUsageSummary(
billingCycleStart: nil,
billingCycleEnd: nil,
membershipType: "enterprise",
limitType: nil,
isUnlimited: false,
autoModelSelectedDisplayMessage: nil,
namedModelSelectedDisplayMessage: nil,
individualUsage: CursorIndividualUsage(
plan: CursorPlanUsage(
enabled: true,
used: 4900,
limit: 50000,
remaining: nil,
breakdown: nil,
autoPercentUsed: nil,
apiPercentUsed: nil,
totalPercentUsed: 0.40625),
onDemand: nil),
teamUsage: nil),
userInfo: nil,
rawJSON: nil)
// totalPercentUsed is already expressed in percentage units.
#expect(snapshot.planPercentUsed == 0.40625)
}
@Test
func `uses percent field when limit missing`() {
let snapshot = CursorStatusProbe(browserDetection: BrowserDetection(cacheTTL: 0))
.parseUsageSummary(
CursorUsageSummary(
billingCycleStart: nil,
billingCycleEnd: nil,
membershipType: "pro",
limitType: nil,
isUnlimited: false,
autoModelSelectedDisplayMessage: nil,
namedModelSelectedDisplayMessage: nil,
individualUsage: CursorIndividualUsage(
plan: CursorPlanUsage(
enabled: true,
used: 0,
limit: nil,
remaining: nil,
breakdown: nil,
autoPercentUsed: nil,
apiPercentUsed: nil,
totalPercentUsed: 0.5),
onDemand: nil),
teamUsage: nil),
userInfo: nil,
rawJSON: nil)
#expect(snapshot.planPercentUsed == 0.5)
}
@Test
func `headline total prefers provided total percent over lane average`() {
let snapshot = CursorStatusProbe(browserDetection: BrowserDetection(cacheTTL: 0))
.parseUsageSummary(
CursorUsageSummary(
billingCycleStart: nil,
billingCycleEnd: nil,
membershipType: "pro",
limitType: nil,
isUnlimited: false,
autoModelSelectedDisplayMessage: nil,
namedModelSelectedDisplayMessage: nil,
individualUsage: CursorIndividualUsage(
plan: CursorPlanUsage(
enabled: true,
used: 5400,
limit: 2000,
remaining: nil,
breakdown: nil,
autoPercentUsed: 0,
apiPercentUsed: 0.01,
totalPercentUsed: 0.27),
onDemand: nil),
teamUsage: nil),
userInfo: nil,
rawJSON: nil)
#expect(snapshot.planPercentUsed == 0.27)
#expect(snapshot.autoPercentUsed == 0)
#expect(snapshot.apiPercentUsed == 0.01)
}
@Test
func `sub pool percents accept plain percent scale`() {
let snapshot = CursorStatusProbe(browserDetection: BrowserDetection(cacheTTL: 0))
.parseUsageSummary(
CursorUsageSummary(
billingCycleStart: nil,
billingCycleEnd: nil,
membershipType: "pro",
limitType: nil,
isUnlimited: false,
autoModelSelectedDisplayMessage: nil,
namedModelSelectedDisplayMessage: nil,
individualUsage: CursorIndividualUsage(
plan: CursorPlanUsage(
enabled: true,
used: 0,
limit: 2000,
remaining: nil,
breakdown: nil,
autoPercentUsed: 12.5,
apiPercentUsed: 3.0,
totalPercentUsed: nil),
onDemand: nil),
teamUsage: nil),
userInfo: nil,
rawJSON: nil)
#expect(snapshot.autoPercentUsed == 12.5)
#expect(snapshot.apiPercentUsed == 3.0)
// Dashboard-style total ≈ average of Auto and API lanes
#expect(snapshot.planPercentUsed == 7.75)
}
@Test
func `headline total matches dashboard blend when lanes match totalPercentUsed`() {
let snapshot = CursorStatusProbe(browserDetection: BrowserDetection(cacheTTL: 0))
.parseUsageSummary(
CursorUsageSummary(
billingCycleStart: nil,
billingCycleEnd: nil,
membershipType: "pro",
limitType: nil,
isUnlimited: false,
autoModelSelectedDisplayMessage: nil,
namedModelSelectedDisplayMessage: nil,
individualUsage: CursorIndividualUsage(
plan: CursorPlanUsage(
enabled: true,
used: 0,
limit: 2000,
remaining: nil,
breakdown: nil,
autoPercentUsed: 35,
apiPercentUsed: 97,
totalPercentUsed: 66),
onDemand: nil),
teamUsage: nil),
userInfo: nil,
rawJSON: nil)
#expect(snapshot.planPercentUsed == 66)
#expect(snapshot.autoPercentUsed == 35)
#expect(snapshot.apiPercentUsed == 97)
}
@Test
func `live cursor payload keeps fractional percents without scaling`() {
let snapshot = CursorStatusProbe(browserDetection: BrowserDetection(cacheTTL: 0))
.parseUsageSummary(
CursorUsageSummary(
billingCycleStart: "2026-03-18T20:45:42.000Z",
billingCycleEnd: "2026-04-18T20:45:42.000Z",
membershipType: "pro",
limitType: "user",
isUnlimited: false,
autoModelSelectedDisplayMessage: "You've used 1% of your included total usage",
namedModelSelectedDisplayMessage: "You've used 1% of your included API usage",
individualUsage: CursorIndividualUsage(
plan: CursorPlanUsage(
enabled: true,
used: 86,
limit: 2000,
remaining: 1914,
breakdown: CursorPlanBreakdown(
included: 86,
bonus: 0,
total: 86),
autoPercentUsed: 0.36,
apiPercentUsed: 0.7111111111111111,
totalPercentUsed: 0.441025641025641),
onDemand: CursorOnDemandUsage(
enabled: false,
used: 0,
limit: nil,
remaining: nil)),
teamUsage: CursorTeamUsage(onDemand: nil)),
userInfo: nil,
rawJSON: nil)
#expect(snapshot.planPercentUsed == 0.441025641025641)
#expect(snapshot.autoPercentUsed == 0.36)
#expect(snapshot.apiPercentUsed == 0.7111111111111111)
#expect(snapshot.billingCycleStart != nil)
let usageSnapshot = snapshot.toUsageSnapshot()
#expect(usageSnapshot.primary?.remainingPercent == 99.55897435897436)
#expect(usageSnapshot.primary?.windowMinutes == 44640)
#expect(usageSnapshot.secondary?.windowMinutes == 44640)
#expect(usageSnapshot.tertiary?.windowMinutes == 44640)
}
@Test
func `converts snapshot to usage snapshot`() {
let snapshot = CursorStatusSnapshot(
planPercentUsed: 45.0,
autoPercentUsed: 5.0,
apiPercentUsed: nil,
planUsedUSD: 22.50,
planLimitUSD: 50.0,
onDemandUsedUSD: 5.0,
onDemandLimitUSD: 100.0,
teamOnDemandUsedUSD: 25.0,
teamOnDemandLimitUSD: 500.0,
billingCycleStart: Date(timeIntervalSince1970: 1_735_689_600), // Jan 1, 2025
billingCycleEnd: Date(timeIntervalSince1970: 1_738_368_000), // Feb 1, 2025
membershipType: "pro",
accountEmail: "user@example.com",
accountName: "Test User",
rawJSON: nil)
let usageSnapshot = snapshot.toUsageSnapshot()
#expect(usageSnapshot.primary?.usedPercent == 45.0)
#expect(usageSnapshot.accountEmail(for: .cursor) == "user@example.com")
#expect(usageSnapshot.loginMethod(for: .cursor) == "Cursor Pro")
#expect(usageSnapshot.secondary != nil)
#expect(usageSnapshot.secondary?.usedPercent == 5.0)
#expect(usageSnapshot.primary?.windowMinutes == 44640)
#expect(usageSnapshot.secondary?.windowMinutes == 44640)
#expect(usageSnapshot.providerCost?.used == 5.0)
#expect(usageSnapshot.providerCost?.limit == 100.0)
#expect(usageSnapshot.providerCost?.currencyCode == "USD")
}
@Test
func `provider cost includes on demand budget before first spend`() {
let snapshot = CursorStatusSnapshot(
planPercentUsed: 10.0,
autoPercentUsed: 5.0,
apiPercentUsed: nil,
planUsedUSD: 5.0,
planLimitUSD: 50.0,
onDemandUsedUSD: 0,
onDemandLimitUSD: 75.0,
teamOnDemandUsedUSD: nil,
teamOnDemandLimitUSD: nil,
billingCycleEnd: nil,
membershipType: "pro",
accountEmail: nil,
accountName: nil,
rawJSON: nil)
let usageSnapshot = snapshot.toUsageSnapshot()
#expect(usageSnapshot.providerCost != nil)
#expect(usageSnapshot.providerCost?.used == 0.0)
#expect(usageSnapshot.providerCost?.limit == 75.0)
#expect(usageSnapshot.providerCost?.period == "Monthly")
}
@Test
func `uses individual on demand when no team usage`() {
let snapshot = CursorStatusSnapshot(
planPercentUsed: 10.0,
autoPercentUsed: 20.0,
apiPercentUsed: nil,
planUsedUSD: 5.0,
planLimitUSD: 50.0,
onDemandUsedUSD: 12.0,
onDemandLimitUSD: 60.0,
teamOnDemandUsedUSD: nil,
teamOnDemandLimitUSD: nil,
billingCycleEnd: nil,
membershipType: "pro",
accountEmail: nil,
accountName: nil,
rawJSON: nil)
let usageSnapshot = snapshot.toUsageSnapshot()
#expect(usageSnapshot.secondary?.usedPercent == 20.0)
#expect(usageSnapshot.providerCost?.used == 12.0)
#expect(usageSnapshot.providerCost?.limit == 60.0)
}
@Test
func `uses team on demand budget when individual usage has no cap`() {
let snapshot = CursorStatusSnapshot(
planPercentUsed: 0,
autoPercentUsed: 0,
apiPercentUsed: 0,
planUsedUSD: 0,
planLimitUSD: 20,
onDemandUsedUSD: 0,
onDemandLimitUSD: nil,
teamOnDemandUsedUSD: 0,
teamOnDemandLimitUSD: 2349,
billingCycleEnd: nil,
membershipType: "enterprise",
accountEmail: nil,
accountName: nil,
rawJSON: nil)
let usageSnapshot = snapshot.toUsageSnapshot()
#expect(usageSnapshot.providerCost?.used == 0)
#expect(usageSnapshot.providerCost?.limit == 2349)
#expect(usageSnapshot.providerCost?.currencyCode == "USD")
}
@Test
func `formats membership types`() {
let testCases: [(input: String, expected: String)] = [
("pro", "Cursor Pro"),
("hobby", "Cursor Hobby"),
("enterprise", "Cursor Enterprise"),
("team", "Cursor Team"),
("custom", "Cursor Custom"),
]
for testCase in testCases {
let snapshot = CursorStatusSnapshot(
planPercentUsed: 0,
planUsedUSD: 0,
planLimitUSD: 0,
onDemandUsedUSD: 0,
onDemandLimitUSD: nil,
teamOnDemandUsedUSD: nil,
teamOnDemandLimitUSD: nil,
billingCycleEnd: nil,
membershipType: testCase.input,
accountEmail: nil,
accountName: nil,
rawJSON: nil)
let usageSnapshot = snapshot.toUsageSnapshot()
#expect(usageSnapshot.loginMethod(for: .cursor) == testCase.expected)
}
}
@Test
func `handles nil on demand limit`() {
let snapshot = CursorStatusSnapshot(
planPercentUsed: 50.0,
planUsedUSD: 25.0,
planLimitUSD: 50.0,
onDemandUsedUSD: 10.0,
onDemandLimitUSD: nil,
teamOnDemandUsedUSD: nil,
teamOnDemandLimitUSD: nil,
billingCycleEnd: nil,
membershipType: "pro",
accountEmail: nil,
accountName: nil,
rawJSON: nil)
let usageSnapshot = snapshot.toUsageSnapshot()
// Should still have provider cost
#expect(usageSnapshot.providerCost != nil)
#expect(usageSnapshot.providerCost?.used == 10.0)
#expect(usageSnapshot.providerCost?.limit == 0.0)
// Secondary should be nil when no on-demand limit
#expect(usageSnapshot.secondary == nil)
}
// MARK: - Legacy Request-Based Plan
@Test
func `parses legacy request based plan`() {
let snapshot = CursorStatusSnapshot(
planPercentUsed: 100.0,
planUsedUSD: 0,
planLimitUSD: 0,
onDemandUsedUSD: 43.64,
onDemandLimitUSD: 200.0,
teamOnDemandUsedUSD: 92.91,
teamOnDemandLimitUSD: 20000.0,
billingCycleEnd: nil,
membershipType: "enterprise",
accountEmail: "user@company.com",
accountName: "Test User",
rawJSON: nil,
requestsUsed: 500,
requestsLimit: 500)
#expect(snapshot.isLegacyRequestPlan == true)
#expect(snapshot.requestsUsed == 500)
#expect(snapshot.requestsLimit == 500)
let usageSnapshot = snapshot.toUsageSnapshot()
#expect(usageSnapshot.cursorRequests != nil)
#expect(usageSnapshot.cursorRequests?.used == 500)
#expect(usageSnapshot.cursorRequests?.limit == 500)
#expect(usageSnapshot.cursorRequests?.usedPercent == 100.0)
#expect(usageSnapshot.cursorRequests?.remainingPercent == 0.0)
// Primary RateWindow should use request-based percentage for legacy plans
#expect(usageSnapshot.primary?.usedPercent == 100.0)
}
@Test
func `legacy plan primary uses requests not dollars`() {
// Regression: Legacy plans report planPercentUsed as 0 while requests are used
let snapshot = CursorStatusSnapshot(
planPercentUsed: 0.0, // Dollar-based shows 0
planUsedUSD: 0,
planLimitUSD: 0,
onDemandUsedUSD: 0,
onDemandLimitUSD: nil,
teamOnDemandUsedUSD: nil,
teamOnDemandLimitUSD: nil,
billingCycleEnd: nil,
membershipType: "enterprise",
accountEmail: "user@company.com",
accountName: nil,
rawJSON: nil,
requestsUsed: 250,
requestsLimit: 500)
#expect(snapshot.isLegacyRequestPlan == true)
let usageSnapshot = snapshot.toUsageSnapshot()
// Primary should reflect request usage (50%), not dollar usage (0%)
#expect(usageSnapshot.primary?.usedPercent == 50.0)
#expect(usageSnapshot.cursorRequests?.usedPercent == 50.0)
}
@Test
func `parse usage summary prefers request total`() {
let summary = CursorUsageSummary(
billingCycleStart: nil,
billingCycleEnd: nil,
membershipType: nil,
limitType: nil,
isUnlimited: nil,
autoModelSelectedDisplayMessage: nil,
namedModelSelectedDisplayMessage: nil,
individualUsage: nil,
teamUsage: nil)
let requestUsage = CursorUsageResponse(
gpt4: CursorModelUsage(
numRequests: 120,
numRequestsTotal: 240,
numTokens: nil,
maxRequestUsage: 500,
maxTokenUsage: nil),
startOfMonth: nil)
let snapshot = CursorStatusProbe(browserDetection: BrowserDetection(cacheTTL: 0)).parseUsageSummary(
summary,
userInfo: nil,
rawJSON: nil,
requestUsage: requestUsage)
#expect(snapshot.requestsUsed == 240)
#expect(snapshot.requestsLimit == 500)
}
// MARK: - Imported Session Scanning
@Test
func `imported session scan continues after non auth failure until later success`() async {
let probe = CursorStatusProbe(browserDetection: BrowserDetection(cacheTTL: 0))
let expected = CursorStatusSnapshot(
planPercentUsed: 0.441025641025641,
autoPercentUsed: 0.36,
apiPercentUsed: 0.7111111111111111,
planUsedUSD: 0.86,
planLimitUSD: 20.0,
onDemandUsedUSD: 0,
onDemandLimitUSD: nil,
teamOnDemandUsedUSD: nil,
teamOnDemandLimitUSD: nil,
billingCycleEnd: nil,
membershipType: "pro",
accountEmail: nil,
accountName: nil,
rawJSON: nil)
let result = await probe.scanImportedSessions([
Self.makeSessionInfo(sourceLabel: "Chrome"),
Self.makeSessionInfo(sourceLabel: "Safari"),
]) { session in
switch session.sourceLabel {
case "Chrome":
.failed(.networkError("HTTP 500"))
case "Safari":
.succeeded(expected)
default:
.tryNextBrowser
}
}
switch result {
case let .succeeded(snapshot):
#expect(snapshot.planPercentUsed == expected.planPercentUsed)
#expect(snapshot.autoPercentUsed == expected.autoPercentUsed)
#expect(snapshot.apiPercentUsed == expected.apiPercentUsed)
case .exhausted:
Issue.record("Expected scan to continue to the later successful browser session")
}
}
@Test
func `imported session scan preserves first non auth failure after exhausting sessions`() async {
let probe = CursorStatusProbe(browserDetection: BrowserDetection(cacheTTL: 0))
let result = await probe.scanImportedSessions([
Self.makeSessionInfo(sourceLabel: "Chrome"),
Self.makeSessionInfo(sourceLabel: "Safari"),
Self.makeSessionInfo(sourceLabel: "Arc"),
]) { session in
switch session.sourceLabel {
case "Chrome":
.failed(.networkError("HTTP 500"))
case "Safari":
.tryNextBrowser
case "Arc":
.failed(.parseFailed("bad payload"))
default:
.tryNextBrowser
}
}
switch result {
case .succeeded:
Issue.record("Expected scan to report the first recoverable error after exhausting sessions")
case let .exhausted(error):
guard let error else {
Issue.record("Expected first recoverable error to be preserved")
return
}
guard case let .networkError(message) = error else {
Issue.record("Expected first recoverable error to be the Chrome network failure")
return
}
#expect(message == "HTTP 500")
}
}
@Test
func `browser scan stops importing after later browser succeeds`() async {
let probe = CursorStatusProbe(browserDetection: BrowserDetection(cacheTTL: 0))
let expected = CursorStatusSnapshot(
planPercentUsed: 42,
autoPercentUsed: 12,
apiPercentUsed: 85,
planUsedUSD: 8.4,
planLimitUSD: 20,
onDemandUsedUSD: 0,
onDemandLimitUSD: nil,
teamOnDemandUsedUSD: nil,
teamOnDemandLimitUSD: nil,
billingCycleEnd: nil,
membershipType: "pro",
accountEmail: nil,
accountName: nil,
rawJSON: nil)
var importedLabels: [String] = []
let result = await probe.scanBrowsers(
[.chrome, .safari, .chromeBeta],
importSessions: { browser in
importedLabels.append(browser.displayName)
switch browser {
case .chrome:
return [Self.makeSessionInfo(sourceLabel: "Chrome")]
case .safari:
return [Self.makeSessionInfo(sourceLabel: "Safari")]
case .chromeBeta:
return [Self.makeSessionInfo(sourceLabel: "Chrome Beta")]
default:
return []
}
},
attemptFetch: { session in
switch session.sourceLabel {
case "Chrome":
.failed(.networkError("HTTP 500"))
case "Safari":
.succeeded(expected)
default:
.tryNextBrowser
}
})
switch result {
case let .succeeded(snapshot):
#expect(snapshot.planPercentUsed == expected.planPercentUsed)
#expect(importedLabels == ["Chrome", "Safari"])
case .exhausted:
Issue.record("Expected browser scan to stop after the later successful browser")
}
}
@Test
func `browser scan keeps trying later sources within the same browser`() async {
let probe = CursorStatusProbe(browserDetection: BrowserDetection(cacheTTL: 0))
let expected = CursorStatusSnapshot(
planPercentUsed: 12,
autoPercentUsed: 3,
apiPercentUsed: 45,
planUsedUSD: 2.4,
planLimitUSD: 20,
onDemandUsedUSD: 0,
onDemandLimitUSD: nil,
teamOnDemandUsedUSD: nil,
teamOnDemandLimitUSD: nil,
billingCycleEnd: nil,
membershipType: "pro",
accountEmail: nil,
accountName: nil,
rawJSON: nil)
var attemptedSources: [String] = []
let result = await probe.scanBrowsers(
[.chrome, .safari],
importSessions: { browser in
switch browser {
case .chrome:
[
Self.makeSessionInfo(sourceLabel: "Chrome Profile 1"),
Self.makeSessionInfo(sourceLabel: "Chrome Profile 2 (domain cookies)"),
]
case .safari:
[Self.makeSessionInfo(sourceLabel: "Safari")]
default:
[]
}
},
attemptFetch: { session in
attemptedSources.append(session.sourceLabel)
switch session.sourceLabel {
case "Chrome Profile 1":
return CursorStatusProbe.ImportedSessionFetchOutcome.failed(.networkError("HTTP 500"))
case "Chrome Profile 2 (domain cookies)":
return CursorStatusProbe.ImportedSessionFetchOutcome.succeeded(expected)
default:
return CursorStatusProbe.ImportedSessionFetchOutcome.tryNextBrowser
}
})
switch result {
case let .succeeded(snapshot):
#expect(snapshot.planPercentUsed == expected.planPercentUsed)
#expect(attemptedSources == ["Chrome Profile 1", "Chrome Profile 2 (domain cookies)"])
case .exhausted:
Issue.record("Expected browser scan to continue to later sources within the same browser")
}
}
@Test
func `detects non legacy plan`() {
let snapshot = CursorStatusSnapshot(
planPercentUsed: 50.0,
planUsedUSD: 25.0,
planLimitUSD: 50.0,
onDemandUsedUSD: 0,
onDemandLimitUSD: 100.0,
teamOnDemandUsedUSD: nil,
teamOnDemandLimitUSD: nil,
billingCycleEnd: nil,
membershipType: "pro",
accountEmail: nil,
accountName: nil,
rawJSON: nil)
#expect(snapshot.isLegacyRequestPlan == false)
#expect(snapshot.requestsUsed == nil)
#expect(snapshot.requestsLimit == nil)
let usageSnapshot = snapshot.toUsageSnapshot()
#expect(usageSnapshot.cursorRequests == nil)
}
// MARK: - Session Store Serialization
@Test
func `session store saves and loads cookies`() async {
let store = CursorSessionStore.shared
// Clear any existing cookies
await store.clearCookies()
// Create test cookies with Date properties
let cookieProps: [HTTPCookiePropertyKey: Any] = [
.name: "testCookie",
.value: "testValue",
.domain: "cursor.com",
.path: "/",
.expires: Date(timeIntervalSince1970: 1_800_000_000),
.secure: true,
]
guard let cookie = HTTPCookie(properties: cookieProps) else {
Issue.record("Failed to create test cookie")
return
}
// Save cookies
await store.setCookies([cookie])
// Verify cookies are stored
let storedCookies = await store.getCookies()
#expect(storedCookies.count == 1)
#expect(storedCookies.first?.name == "testCookie")
#expect(storedCookies.first?.value == "testValue")
// Clean up
await store.clearCookies()
}
@Test
func `session store reloads from disk when needed`() async {
let store = CursorSessionStore.shared
await store.resetForTesting()
let cookieProps: [HTTPCookiePropertyKey: Any] = [
.name: "diskCookie",
.value: "diskValue",
.domain: "cursor.com",
.path: "/",
.expires: Date(timeIntervalSince1970: 1_800_000_000),
.secure: true,
]
guard let cookie = HTTPCookie(properties: cookieProps) else {
Issue.record("Failed to create test cookie")
return
}
await store.setCookies([cookie])
await store.resetForTesting(clearDisk: false)
let reloaded = await store.getCookies()
#expect(reloaded.count == 1)
#expect(reloaded.first?.name == "diskCookie")
#expect(reloaded.first?.value == "diskValue")
await store.clearCookies()
}
@Test
func `session store has valid session loads from disk`() async {
let store = CursorSessionStore.shared
await store.resetForTesting()
let cookieProps: [HTTPCookiePropertyKey: Any] = [
.name: "validCookie",
.value: "validValue",
.domain: "cursor.com",
.path: "/",
.expires: Date(timeIntervalSince1970: 1_800_000_000),
.secure: true,
]
guard let cookie = HTTPCookie(properties: cookieProps) else {
Issue.record("Failed to create test cookie")
return
}
await store.setCookies([cookie])
await store.resetForTesting(clearDisk: false)
let hasSession = await store.hasValidSession()
#expect(hasSession)
await store.clearCookies()
}
private static func makeSessionInfo(sourceLabel: String) -> CursorCookieImporter.SessionInfo {
let cookieProps: [HTTPCookiePropertyKey: Any] = [
.name: "WorkosCursorSessionToken",
.value: sourceLabel.lowercased(),
.domain: "cursor.com",
.path: "/",
.secure: true,
]
let cookie = HTTPCookie(properties: cookieProps)!
return CursorCookieImporter.SessionInfo(cookies: [cookie], sourceLabel: sourceLabel)
}
}
private final class CursorStatusProbeTestSession {
let urlSession: URLSession
private let sessionID: String
init(handler: @escaping @Sendable (URLRequest) throws -> (HTTPURLResponse, Data)) {
let config = URLSessionConfiguration.ephemeral
config.protocolClasses = [CursorStatusProbeStubURLProtocol.self]
self.sessionID = CursorStatusProbeStubURLProtocol.configure(config, handler: handler)
self.urlSession = URLSession(configuration: config)
}
deinit {
self.urlSession.invalidateAndCancel()
CursorStatusProbeStubURLProtocol.removeSession(self.sessionID)
}
var requestCount: Int {
CursorStatusProbeStubURLProtocol.requests(for: self.sessionID).count
}
var requestPaths: [String] {
CursorStatusProbeStubURLProtocol.requests(for: self.sessionID).compactMap { $0.url?.path }
}
var requestCookies: [String] {
CursorStatusProbeStubURLProtocol.requests(for: self.sessionID)
.compactMap { $0.value(forHTTPHeaderField: "Cookie") }
}
}
private func makeCursorStatusProbeResponse(
url: URL,
body: String,
statusCode: Int,
contentType: String = "application/json") -> (HTTPURLResponse, Data)
{
let response = HTTPURLResponse(
url: url,
statusCode: statusCode,
httpVersion: nil,
headerFields: ["Content-Type": contentType])!
return (response, Data(body.utf8))
}
extension CursorStatusProbeTests {
@Test
func `app auth store reads Cursor global state database`() throws {
let directory = FileManager.default.temporaryDirectory
.appendingPathComponent("cursor-app-auth-\(UUID().uuidString)", isDirectory: true)
try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
defer { try? FileManager.default.removeItem(at: directory) }
let dbURL = directory.appendingPathComponent("state.vscdb")
var db: OpaquePointer?
try #require(sqlite3_open(dbURL.path, &db) == SQLITE_OK)
defer { sqlite3_close(db) }
let sql = """
CREATE TABLE ItemTable(key TEXT PRIMARY KEY, value BLOB);
INSERT INTO ItemTable VALUES('cursorAuth/accessToken', 'app-token');
"""
try #require(sqlite3_exec(db, sql, nil, nil, nil) == SQLITE_OK)
let session = try #require(try CursorAppAuthStore(dbPath: dbURL.path).loadSession())
#expect(session == CursorAppAuthSession(accessToken: "app-token"))
}
@Test
func `fetch ignores user info failure when usage summary succeeds`() async throws {
let testSession = CursorStatusProbeTestSession { request in
let requestURL = try #require(request.url)
switch requestURL.path {
case "/api/usage-summary":
return makeCursorStatusProbeResponse(
url: requestURL,
body: """
{
"membershipType": "pro",
"individualUsage": {
"plan": {
"used": 1500,
"limit": 5000,
"totalPercentUsed": 30.0
}
}
}
""",
statusCode: 200)
case "/api/auth/me":
return makeCursorStatusProbeResponse(
url: requestURL,
body: #"{"error":"nope"}"#,
statusCode: 500)
default:
throw URLError(.badURL)
}
}
let baseURL = try #require(URL(string: "https://cursor.test"))
let snapshot = try await CursorStatusProbe(
baseURL: baseURL,
browserDetection: BrowserDetection(cacheTTL: 0),
urlSession: testSession.urlSession).fetchWithManualCookies("auth=test")
#expect(snapshot.planPercentUsed == 30.0)
#expect(snapshot.accountEmail == nil)
#expect(testSession.requestCount == 2)
}
@Test
func `fetch fails cleanly when usage summary fails`() async {
let testSession = CursorStatusProbeTestSession { request in
let requestURL = try #require(request.url)
switch requestURL.path {
case "/api/usage-summary":
return makeCursorStatusProbeResponse(
url: requestURL,
body: #"{"error":"denied"}"#,
statusCode: 500)
case "/api/auth/me":
return makeCursorStatusProbeResponse(
url: requestURL,
body: """
{
"email": "user@example.com",
"email_verified": true,
"name": "Test User",
"sub": "auth0|12345"
}
""",
statusCode: 200)
default:
throw URLError(.badURL)
}
}
do {
let baseURL = try #require(URL(string: "https://cursor.test"))
_ = try await CursorStatusProbe(
baseURL: baseURL,
browserDetection: BrowserDetection(cacheTTL: 0),
urlSession: testSession.urlSession).fetchWithManualCookies("auth=test")
Issue.record("Expected usage summary failure to be surfaced")
} catch let error as CursorStatusProbeError {
guard case let .networkError(message) = error else {
Issue.record("Expected networkError, got: \(error)")
return
}
#expect(message == "HTTP 500")
#expect(testSession.requestPaths.contains("/api/usage-summary"))
} catch {
Issue.record("Expected CursorStatusProbeError, got: \(error)")
}
}
@Test
func `fetch uses Cursor app local auth when browser cookies are unavailable`() async throws {
let accessToken = try makeCursorAppAuthToken()
let expectedCookie = "WorkosCursorSessionToken=user_test%3A%3A\(accessToken)"
let testSession = CursorStatusProbeTestSession { request in
let requestURL = try #require(request.url)
#expect(request.value(forHTTPHeaderField: "Authorization") == nil)
#expect(request.value(forHTTPHeaderField: "Cookie") == expectedCookie)
#expect(request.httpMethod == "GET")
switch requestURL.path {
case "/api/usage-summary":
return makeCursorStatusProbeResponse(
url: requestURL,
body: """
{
"membershipType": "pro",
"billingCycleStart": "2026-05-23T10:27:04.000Z",
"billingCycleEnd": "2026-06-23T10:27:04.000Z",
"individualUsage": {
"plan": {
"used": 388,
"limit": 2000,
"totalPercentUsed": 19.4
},
"onDemand": {
"used": 450,
"limit": 1000
}
}
}
""",
statusCode: 200)
case "/api/auth/me":
return makeCursorStatusProbeResponse(
url: requestURL,
body: #"{"email":"user@example.com","name":"Test User"}"#,
statusCode: 200)
case "/api/usage":
return makeCursorStatusProbeResponse(
url: requestURL,
body: #"{"gpt-4":{},"startOfMonth":"2026-05-23"}"#,
statusCode: 200)
default:
throw URLError(.badURL)
}
}
let baseURL = try #require(URL(string: "https://cursor-web.test"))
let snapshot = try await CursorStatusProbe(
baseURL: baseURL,
browserDetection: BrowserDetection(cacheTTL: 0),
browserCookieImportOrder: [],
urlSession: testSession.urlSession,
appAuthStore: CursorAppAuthSessionProviderStub(session: CursorAppAuthSession(
accessToken: accessToken))).fetch(allowCachedSessions: false)
#expect(abs(snapshot.planPercentUsed - 19.4) < 0.0001)
#expect(snapshot.planUsedUSD == 3.88)
#expect(snapshot.planLimitUSD == 20.0)
#expect(snapshot.onDemandUsedUSD == 4.5)
#expect(snapshot.onDemandLimitUSD == 10.0)
#expect(snapshot.membershipType == "pro")
#expect(snapshot.accountEmail == "user@example.com")
#expect(snapshot.accountName == "Test User")
#expect(testSession.requestPaths.sorted() == [
"/api/auth/me",
"/api/usage",
"/api/usage-summary",
])
}
@Test
func `fetch can disable Cursor app auth during browser login verification`() async throws {
let testSession = CursorStatusProbeTestSession { request in
Issue.record("Disabled app auth unexpectedly requested \(request.url?.path ?? "<unknown>")")
throw URLError(.badURL)
}
let baseURL = try #require(URL(string: "https://cursor-web.test"))
let accessToken = try makeCursorAppAuthToken()
let probe = CursorStatusProbe(
baseURL: baseURL,
browserDetection: BrowserDetection(cacheTTL: 0),
browserCookieImportOrder: [],
urlSession: testSession.urlSession,
appAuthStore: CursorAppAuthSessionProviderStub(session: CursorAppAuthSession(
accessToken: accessToken)))
await #expect(throws: CursorStatusProbeError.self) {
_ = try await probe.fetch(
allowCachedSessions: false,
allowAppAuthFallback: false)
}
#expect(testSession.requestCount == 0)
}
@Test
func `fetch prefers stored session cookies before Cursor app auth fallback`() async throws {
let store = CursorSessionStore.shared
await store.clearCookies()
defer {
Task { await store.clearCookies() }
}
guard let cookie = HTTPCookie(properties: [
.name: "WorkosCursorSessionToken",
.value: "stored-session",
.domain: "cursor.com",
.path: "/",
.secure: true,
]) else {
Issue.record("Failed to create stored Cursor session cookie")
return
}
await store.setCookies([cookie])
let testSession = CursorStatusProbeTestSession { request in
let requestURL = try #require(request.url)
#expect(request.value(forHTTPHeaderField: "Authorization") == nil)
#expect(request.value(forHTTPHeaderField: "Cookie") == "WorkosCursorSessionToken=stored-session")
switch requestURL.path {
case "/api/usage-summary":
return makeCursorStatusProbeResponse(
url: requestURL,
body: """
{
"membershipType": "pro",
"individualUsage": {
"plan": {
"used": 1500,
"limit": 5000,
"totalPercentUsed": 30.0
}
}
}
""",
statusCode: 200)
case "/api/auth/me":
return makeCursorStatusProbeResponse(
url: requestURL,
body: #"{"email":"stored@example.com","name":"Stored User"}"#,
statusCode: 200)
default:
Issue.record("Stored-session precedence test unexpectedly requested \(requestURL.path)")
throw URLError(.badURL)
}
}
let baseURL = try #require(URL(string: "https://cursor.test"))
let accessToken = try makeCursorAppAuthToken()
let snapshot = try await CursorStatusProbe(
baseURL: baseURL,
browserDetection: BrowserDetection(cacheTTL: 0),
browserCookieImportOrder: [],
urlSession: testSession.urlSession,
appAuthStore: CursorAppAuthSessionProviderStub(session: CursorAppAuthSession(
accessToken: accessToken))).fetch()
#expect(snapshot.planPercentUsed == 30.0)
#expect(snapshot.accountEmail == "stored@example.com")
#expect(testSession.requestPaths.sorted() == [
"/api/auth/me",
"/api/usage-summary",
])
}
@Test
func `fetch with Cursor app auth preserves legacy request quotas`() async throws {
let accessToken = try makeCursorAppAuthToken()
let expectedCookie = "WorkosCursorSessionToken=user_test%3A%3A\(accessToken)"
let testSession = CursorStatusProbeTestSession { request in
let requestURL = try #require(request.url)
#expect(request.value(forHTTPHeaderField: "Cookie") == expectedCookie)
switch requestURL.path {
case "/api/usage-summary":
return makeCursorStatusProbeResponse(
url: requestURL,
body: """
{
"membershipType": "enterprise",
"individualUsage": {}
}
""",
statusCode: 200)
case "/api/auth/me":
return makeCursorStatusProbeResponse(
url: requestURL,
body: #"{"error":"temporary"}"#,
statusCode: 500)
case "/api/usage":
#expect(URLComponents(url: requestURL, resolvingAgainstBaseURL: false)?
.queryItems?.first(where: { $0.name == "user" })?.value == "user_test")
return makeCursorStatusProbeResponse(
url: requestURL,
body: """
{
"gpt-4": {
"numRequests": 200,
"numRequestsTotal": 240,
"maxRequestUsage": 500
}
}
""",
statusCode: 200)
default:
throw URLError(.badURL)
}
}
let baseURL = try #require(URL(string: "https://cursor.test"))
let probe = CursorStatusProbe(
baseURL: baseURL,
browserDetection: BrowserDetection(cacheTTL: 0),
urlSession: testSession.urlSession)
let snapshot = try await probe.fetchWithAppAuthSession(CursorAppAuthSession(accessToken: accessToken))
#expect(snapshot.requestsUsed == 240)
#expect(snapshot.requestsLimit == 500)
#expect(snapshot.toUsageSnapshot().primary?.usedPercent == 48)
#expect(snapshot.accountEmail == nil)
#expect(testSession.requestPaths.sorted() == [
"/api/auth/me",
"/api/usage",
"/api/usage-summary",
])
}
@Test
func `malformed Cursor app auth token is rejected before network access`() {
let session = CursorAppAuthSession(accessToken: "not-a-jwt")
#expect(throws: CursorStatusProbeError.self) {
_ = try session.cookieHeader()
}
#expect(!session.isUsable)
}
@Test
func `expired Cursor app auth token is skipped before network access`() async throws {
let testSession = CursorStatusProbeTestSession { request in
Issue.record("Expired app auth unexpectedly requested \(request.url?.path ?? "<unknown>")")
throw URLError(.badURL)
}
let accessToken = try makeCursorAppAuthToken(expiration: Date(timeIntervalSinceNow: -60))
let baseURL = try #require(URL(string: "https://cursor-web.test"))
let probe = CursorStatusProbe(
baseURL: baseURL,
browserDetection: BrowserDetection(cacheTTL: 0),
browserCookieImportOrder: [],
urlSession: testSession.urlSession,
appAuthStore: CursorAppAuthSessionProviderStub(session: CursorAppAuthSession(
accessToken: accessToken)))
await #expect(throws: CursorStatusProbeError.self) {
_ = try await probe.fetch(allowCachedSessions: false)
}
#expect(testSession.requestCount == 0)
}
@Test
func `Cursor app auth transient failure is preserved`() async throws {
let testSession = CursorStatusProbeTestSession { request in
let requestURL = try #require(request.url)
return makeCursorStatusProbeResponse(
url: requestURL,
body: #"{"error":"temporary"}"#,
statusCode: 500)
}
let accessToken = try makeCursorAppAuthToken()
let baseURL = try #require(URL(string: "https://cursor-web.test"))
let probe = CursorStatusProbe(
baseURL: baseURL,
browserDetection: BrowserDetection(cacheTTL: 0),
browserCookieImportOrder: [],
urlSession: testSession.urlSession,
appAuthStore: CursorAppAuthSessionProviderStub(session: CursorAppAuthSession(
accessToken: accessToken)))
do {
_ = try await probe.fetch(allowCachedSessions: false)
Issue.record("Expected Cursor.app auth request to fail")
} catch let error as CursorStatusProbeError {
guard case .networkError = error else {
Issue.record("Expected network error, got \(error)")
return
}
}
}
@Test
func `cached session transient failure does not switch to Cursor app auth`() async throws {
CookieHeaderCache.store(provider: .cursor, cookieHeader: "cached=bad", sourceLabel: "test")
defer {
CookieHeaderCache.clear(provider: .cursor)
}
let accessToken = try makeCursorAppAuthToken()
let appCookie = "WorkosCursorSessionToken=user_test%3A%3A\(accessToken)"
let testSession = CursorStatusProbeTestSession { request in
let requestURL = try #require(request.url)
let cookie = request.value(forHTTPHeaderField: "Cookie")
switch requestURL.path {
case "/api/usage-summary" where cookie == "cached=bad":
return makeCursorStatusProbeResponse(
url: requestURL,
body: #"{"error":"temporary"}"#,
statusCode: 500)
case _ where cookie == appCookie:
Issue.record("Transient cached-session failure unexpectedly switched to Cursor.app auth")
throw URLError(.userAuthenticationRequired)
default:
throw URLError(.badURL)
}
}
let baseURL = try #require(URL(string: "https://cursor-web.test"))
let probe = CursorStatusProbe(
baseURL: baseURL,
browserDetection: BrowserDetection(cacheTTL: 0),
browserCookieImportOrder: [],
urlSession: testSession.urlSession,
appAuthStore: CursorAppAuthSessionProviderStub(session: CursorAppAuthSession(
accessToken: accessToken)))
await #expect(throws: CursorStatusProbeError.self) {
_ = try await probe.fetch()
}
#expect(testSession.requestCookies.contains("cached=bad"))
#expect(!testSession.requestCookies.contains(appCookie))
}
}
private func makeCursorAppAuthToken(
subject: String = "auth0|user_test",
expiration: Date = Date(timeIntervalSinceNow: 3600)) throws -> String
{
let payload = try JSONSerialization.data(
withJSONObject: [
"exp": Int(expiration.timeIntervalSince1970),
"sub": subject,
],
options: [.sortedKeys])
let encodedPayload = payload.base64EncodedString()
.replacingOccurrences(of: "+", with: "-")
.replacingOccurrences(of: "/", with: "_")
.replacingOccurrences(of: "=", with: "")
return "header.\(encodedPayload).signature"
}
private struct CursorAppAuthSessionProviderStub: CursorAppAuthSessionProviding {
let session: CursorAppAuthSession?
func loadSession() throws -> CursorAppAuthSession? {
self.session
}
}
final class CursorStatusProbeStubURLProtocol: URLProtocol {
private struct SessionState {
var requests: [URLRequest] = []
let handler: @Sendable (URLRequest) throws -> (HTTPURLResponse, Data)
}
private static let sessionHeader = "X-CodexBar-Cursor-Test-Session"
private static let lock = NSLock()
private nonisolated(unsafe) static var sessions: [String: SessionState] = [:]
static func configure(
_ configuration: URLSessionConfiguration,
handler: @escaping @Sendable (URLRequest) throws -> (HTTPURLResponse, Data)) -> String
{
let sessionID = UUID().uuidString
self.lock.lock()
self.sessions[sessionID] = SessionState(handler: handler)
self.lock.unlock()
configuration.httpAdditionalHeaders = [self.sessionHeader: sessionID]
return sessionID
}
static func removeSession(_ sessionID: String) {
self.lock.lock()
self.sessions.removeValue(forKey: sessionID)
self.lock.unlock()
}
static func requests(for sessionID: String) -> [URLRequest] {
self.lock.lock()
defer { Self.lock.unlock() }
return self.sessions[sessionID]?.requests ?? []
}
override static func canInit(with request: URLRequest) -> Bool {
true
}
override static func canonicalRequest(for request: URLRequest) -> URLRequest {
request
}
override func startLoading() {
let handler: (@Sendable (URLRequest) throws -> (HTTPURLResponse, Data))?
let sessionID = self.request.value(forHTTPHeaderField: Self.sessionHeader)
Self.lock.lock()
if let sessionID, var state = Self.sessions[sessionID] {
state.requests.append(self.request)
handler = state.handler
Self.sessions[sessionID] = state
} else {
handler = nil
}
Self.lock.unlock()
do {
guard let handler else {
throw URLError(.cancelled)
}
let (response, data) = try handler(self.request)
self.client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
self.client?.urlProtocol(self, didLoad: data)
self.client?.urlProtocolDidFinishLoading(self)
} catch {
self.client?.urlProtocol(self, didFailWithError: error)
}
}
override func stopLoading() {}
}