258 lines · 11.2 KB · Swift
RawDownload
import Foundationimport Testing@testable import CodexBarCore @Suite(.serialized)struct CursorEnterpriseUsageTests {    @Test    func `legacy provider cost snapshot decodes without personal spend`() throws {        let json = """        {            "used": 12.5,            "limit": 100,            "currencyCode": "USD",            "period": "Monthly",            "resetsAt": null,            "nextRegenAmount": null,            "updatedAt": 0        }        """         let snapshot = try JSONDecoder().decode(ProviderCostSnapshot.self, from: Data(json.utf8))         #expect(snapshot.used == 12.5)        #expect(snapshot.personalUsed == nil)    }     @Test    func `parses enterprise overall and pooled usage summary`() throws {        // Live Cursor Enterprise payload (sanitized). The Pro/Hobby `plan` block is absent;        // instead Cursor reports `individualUsage.overall` (personal cap) and `teamUsage.pooled`        // (shared team pool). Both blocks use cents like the existing `plan` block.        let json = """        {            "billingCycleStart": "2026-04-01T00:00:00.000Z",            "billingCycleEnd": "2026-05-01T00:00:00.000Z",            "membershipType": "enterprise",            "limitType": "team",            "isUnlimited": false,            "individualUsage": {                "overall": {                    "enabled": true,                    "used": 7384,                    "limit": 10000,                    "remaining": 2616                }            },            "teamUsage": {                "onDemand": {                    "enabled": true,                    "used": 0,                    "limit": null,                    "remaining": null                },                "pooled": {                    "enabled": true,                    "used": 12725135,                    "limit": 28122000,                    "remaining": 15396865                }            }        }        """        let data = try #require(json.data(using: .utf8))        let summary = try JSONDecoder().decode(CursorUsageSummary.self, from: data)         #expect(summary.membershipType == "enterprise")        #expect(summary.limitType == "team")        #expect(summary.individualUsage?.plan == nil)        #expect(summary.individualUsage?.overall?.used == 7384)        #expect(summary.individualUsage?.overall?.limit == 10000)        #expect(summary.individualUsage?.overall?.remaining == 2616)        #expect(summary.teamUsage?.pooled?.used == 12_725_135)        #expect(summary.teamUsage?.pooled?.limit == 28_122_000)    }     @Test    func `enterprise overall drives headline percent and dollars`() throws {        // Regression: Cursor Enterprise/Team accounts ship `individualUsage.overall` instead of        // `individualUsage.plan`. Without a model for `overall`, the parser used to report 0%        // (i.e. the menu showed "100% remaining"). The personal cap must take precedence over        // any team pool, and USD figures must reflect the same source.        let snapshot = CursorStatusProbe(browserDetection: BrowserDetection(cacheTTL: 0))            .parseUsageSummary(                CursorUsageSummary(                    billingCycleStart: "2026-04-01T00:00:00.000Z",                    billingCycleEnd: "2026-05-01T00:00:00.000Z",                    membershipType: "enterprise",                    limitType: "team",                    isUnlimited: false,                    autoModelSelectedDisplayMessage: nil,                    namedModelSelectedDisplayMessage: nil,                    individualUsage: CursorIndividualUsage(                        plan: nil,                        onDemand: nil,                        overall: CursorOverallUsage(enabled: true, used: 7384, limit: 10000, remaining: 2616)),                    teamUsage: CursorTeamUsage(                        onDemand: CursorOnDemandUsage(enabled: true, used: 0, limit: nil, remaining: nil),                        pooled: CursorPooledUsage(                            enabled: true,                            used: 12_725_135,                            limit: 28_122_000,                            remaining: 15_396_865))),                userInfo: nil,                rawJSON: nil)         // Headline: $73.84 / $100 -> 73.84% (matches Cursor's own dashboard).        // Allow a tiny tolerance for floating-point division (7384/10000 * 100).        #expect(abs(snapshot.planPercentUsed - 73.84) < 0.0001)        #expect(snapshot.planUsedUSD == 73.84)        #expect(snapshot.planLimitUSD == 100.0)        #expect(snapshot.autoPercentUsed == nil)        #expect(snapshot.apiPercentUsed == nil)         let primaryPercent = try #require(snapshot.toUsageSnapshot().primary?.usedPercent)        #expect(abs(primaryPercent - 73.84) < 0.0001)    }     @Test    func `enterprise pooled fallback used when no individual data`() {        // When Cursor only reports a shared team pool (no `plan`, no `overall`) we should still surface        // a non-zero headline so the menu reflects pool consumption rather than appearing "all clear".        let snapshot = CursorStatusProbe(browserDetection: BrowserDetection(cacheTTL: 0))            .parseUsageSummary(                CursorUsageSummary(                    billingCycleStart: nil,                    billingCycleEnd: nil,                    membershipType: "enterprise",                    limitType: "team",                    isUnlimited: false,                    autoModelSelectedDisplayMessage: nil,                    namedModelSelectedDisplayMessage: nil,                    individualUsage: nil,                    teamUsage: CursorTeamUsage(                        onDemand: nil,                        pooled: CursorPooledUsage(                            enabled: true,                            used: 12_725_135,                            limit: 28_122_000,                            remaining: 15_396_865))),                userInfo: nil,                rawJSON: nil)         #expect(snapshot.planPercentUsed > 45.0)        #expect(snapshot.planPercentUsed < 45.5)        #expect(snapshot.planUsedUSD == 127_251.35)        #expect(snapshot.planLimitUSD == 281_220.0)    }     @Test    func `team on-demand pool is the budget and personal spend rides along`() {        // Live team-plan payload (sanitized): the user's own on-demand spend has no personal limit,        // so the team pool is the headline budget. The personal spend must still be surfaced.        let snapshot = CursorStatusProbe(browserDetection: BrowserDetection(cacheTTL: 0))            .parseUsageSummary(                CursorUsageSummary(                    billingCycleStart: "2026-06-01T00:00:00.000Z",                    billingCycleEnd: "2026-07-01T00:00:00.000Z",                    membershipType: "enterprise",                    limitType: "team",                    isUnlimited: false,                    autoModelSelectedDisplayMessage: nil,                    namedModelSelectedDisplayMessage: nil,                    individualUsage: CursorIndividualUsage(                        plan: CursorPlanUsage(                            enabled: true,                            used: 2000,                            limit: 2000,                            remaining: 0,                            breakdown: nil,                            autoPercentUsed: 0,                            apiPercentUsed: 100,                            totalPercentUsed: 100),                        onDemand: CursorOnDemandUsage(enabled: true, used: 4471, limit: nil, remaining: nil),                        overall: nil),                    teamUsage: CursorTeamUsage(                        onDemand: CursorOnDemandUsage(                            enabled: true,                            used: 1_311_125,                            limit: 2_000_000,                            remaining: 688_875),                        pooled: nil)),                userInfo: nil,                rawJSON: nil)         let cost = snapshot.toUsageSnapshot().providerCost        #expect(cost?.used == 13111.25) // team pool used        #expect(cost?.limit == 20000.0) // team pool limit        #expect(cost?.personalUsed == 44.71) // this account's own on-demand spend    }     @Test    func `personal on-demand limit keeps personal budget with no rider`() {        // When the user has their own on-demand limit, that is the budget and there is no separate rider.        let snapshot = CursorStatusProbe(browserDetection: BrowserDetection(cacheTTL: 0))            .parseUsageSummary(                CursorUsageSummary(                    billingCycleStart: nil,                    billingCycleEnd: nil,                    membershipType: "pro",                    limitType: "user",                    isUnlimited: false,                    autoModelSelectedDisplayMessage: nil,                    namedModelSelectedDisplayMessage: nil,                    individualUsage: CursorIndividualUsage(                        plan: nil,                        onDemand: CursorOnDemandUsage(enabled: true, used: 4471, limit: 10000, remaining: 5529),                        overall: nil),                    teamUsage: nil),                userInfo: nil,                rawJSON: nil)         let cost = snapshot.toUsageSnapshot().providerCost        #expect(cost?.used == 44.71)        #expect(cost?.limit == 100.0)        #expect(cost?.personalUsed == nil)    }     @Test    func `existing plan block still wins over overall and pooled`() {        // Guard against future drift: when Cursor sends both legacy `plan` and the newer `overall`        // blocks, the existing percent precedence must remain intact.        let snapshot = CursorStatusProbe(browserDetection: BrowserDetection(cacheTTL: 0))            .parseUsageSummary(                CursorUsageSummary(                    billingCycleStart: nil,                    billingCycleEnd: nil,                    membershipType: "pro",                    limitType: "user",                    isUnlimited: false,                    autoModelSelectedDisplayMessage: nil,                    namedModelSelectedDisplayMessage: nil,                    individualUsage: CursorIndividualUsage(                        plan: CursorPlanUsage(                            enabled: true,                            used: 1500,                            limit: 5000,                            remaining: 3500,                            breakdown: nil,                            autoPercentUsed: nil,                            apiPercentUsed: nil,                            totalPercentUsed: 30.0),                        onDemand: nil,                        overall: CursorOverallUsage(enabled: true, used: 7384, limit: 10000, remaining: 2616)),                    teamUsage: CursorTeamUsage(                        onDemand: nil,                        pooled: CursorPooledUsage(                            enabled: true,                            used: 12_725_135,                            limit: 28_122_000,                            remaining: 15_396_865))),                userInfo: nil,                rawJSON: nil)         #expect(snapshot.planPercentUsed == 30.0)        #expect(snapshot.planUsedUSD == 15.0)        #expect(snapshot.planLimitUSD == 50.0)    }}