@@ -4,6 +4,85 @@ import Testing
44@testable import CodexBarCLI
55
66struct DashboardSnapshotBuilderTests {
7+ @Test
8+ func `dashboard cost collection do es not fetch Cursor when cookie source is off`() async {
9+ let recorder = DashboardCostFetchRecorder ( )
10+ let config = CodexBarConfig ( providers: [
11+ ProviderConfig ( id: . cursor, enabled: true , cookieSource: . off) ,
12+ ] )
13+
14+ let payload = await CodexBarCLI . collectConfiguredCostPayloads (
15+ providers: [ . cursor] ,
16+ config: config,
17+ context: self . costCollectionContext ( ) )
18+ { provider, header in
19+ await recorder. record ( provider: provider, cursorCookieHeaderOverride: header)
20+ return CodexBarCLI . makeCostPayload ( provider: provider, snapshot: nil , error: nil )
21+ }
22+
23+ #expect( await recorder. calls( ) . isEmpty)
24+ #expect( payload. count == 1 )
25+ #expect( payload[ 0 ] . provider == " cursor" )
26+ #expect( payload [ 0 ] . error? . message. contains ( " cookie source is set to Off " ) == true )
27+ }
28+
29+ @Test
30+ func `dashboard cost collection forwards configured Cursor manual cookie`() async {
31+ let recorder = DashboardCostFetchRecorder ( )
32+ let config = CodexBarConfig ( providers: [
33+ ProviderConfig (
34+ id: . cursor,
35+ enabled: true ,
36+ cookieHeader: " session=manual " ,
37+ cookieSource: . manual) ,
38+ ] )
39+
40+ let payload = await CodexBarCLI . collectConfiguredCostPayloads (
41+ providers: [ . cursor] ,
42+ config: config,
43+ context: self . costCollectionContext ( ) )
44+ { provider, header in
45+ await recorder. record ( provider: provider, cursorCookieHeaderOverride: header)
46+ return CodexBarCLI . makeCostPayload ( provider: provider, snapshot: nil , error: nil )
47+ }
48+
49+ let calls = await recorder. calls ( )
50+ #expect( calls. count == 1 )
51+ #expect( calls [ 0 ] . provider == . cursor)
52+ #expect( calls [ 0 ] . cursorCookieHeaderOverride == " session=manual " )
53+ #expect( payload. count == 1 )
54+ #expect( payload [ 0 ] . error == nil )
55+ }
56+
57+ @Test
58+ func `dashboard producer forwards its config to cost collection`() async throws {
59+ let recorder = DashboardCostConfigRecorder ( )
60+ let config = CodexBarConfig ( providers: [
61+ ProviderConfig (
62+ id: . cursor,
63+ enabled: true ,
64+ cookieHeader: " session=manual " ,
65+ cookieSource: . manual) ,
66+ ] )
67+ let producer = DashboardSnapshotProducer (
68+ collectUsage: { _ in UsageCommandOutput ( ) } ,
69+ collectCost: { providers, config in
70+ await recorder. record ( providers: providers, config: config)
71+ return [ ]
72+ } ,
73+ now: { Date ( timeIntervalSince1970: 1_800_000_000 ) } )
74+
75+ _ = try await producer. collect (
76+ config: config,
77+ refreshInterval: 0 ,
78+ codexBarVersion: " 9.8.7 " )
79+
80+ let call = try #require( await recorder. call ( ) )
81+ #expect( call. providers == [ . cursor] )
82+ #expect( call. cookieSource == . manual)
83+ #expect( call. cookieHeader == " session=manual " )
84+ }
85+
786 @Test
887 func `producer keeps stable order redaction and partial errors`() async throws {
988 let generatedAt = Date ( timeIntervalSince1970: 1_800_000_000 )
@@ -27,7 +106,7 @@ struct DashboardSnapshotBuilderTests {
27106 output. exitCode = . failure
28107 return output
29108 } ,
30- collectCost: { _ in [ ] } ,
109+ collectCost: { _, _ in [ ] } ,
31110 now: { generatedAt } )
32111 let config = CodexBarConfig ( providers: [
33112 ProviderConfig ( id: . claude, enabled: true ) ,
@@ -53,6 +132,15 @@ struct DashboardSnapshotBuilderTests {
53132 #expect( object [ " generatedAt " ] as? String == " 2027-01-15T08:00:00Z " )
54133 }
55134
135+ private func costCollectionContext( ) -> ServeCostCollectionContext {
136+ ServeCostCollectionContext (
137+ configFingerprint: " dashboard-cost-policy " ,
138+ providerTimeout: nil ,
139+ requestDeadline: nil ,
140+ now: { ContinuousClock ( ) . now } ,
141+ providerOperations: CLIServeOperationCoordinator ( ) )
142+ }
143+
56144 @Test
57145 func `builds stable display- oriented dashboard snapshot`() throws {
58146 let generatedAt = Date ( timeIntervalSince1970: 1_800_000_000 )
@@ -496,3 +584,44 @@ struct DashboardSnapshotBuilderTests {
496584 return try #require( JSONSerialization . jsonObject ( with: data) as? [ String : Any ] )
497585 }
498586}
587+
588+ private actor DashboardCostFetchRecorder {
589+ struct Call : Sendable {
590+ let provider : UsageProvider
591+ let cursorCookieHeaderOverride : String ?
592+ }
593+
594+ private var recordedCalls : [ Call ] = [ ]
595+
596+ func record( provider: UsageProvider , cursorCookieHeaderOverride: String ? ) {
597+ self . recordedCalls. append ( Call (
598+ provider: provider,
599+ cursorCookieHeaderOverride: cursorCookieHeaderOverride) )
600+ }
601+
602+ func calls( ) -> [ Call ] {
603+ self . recordedCalls
604+ }
605+ }
606+
607+ private actor DashboardCostConfigRecorder {
608+ struct Call : Sendable {
609+ let providers : [ UsageProvider ]
610+ let cookieSource : ProviderCookieSource ?
611+ let cookieHeader : String ?
612+ }
613+
614+ private var recordedCall : Call ?
615+
616+ func record( providers: [ UsageProvider ] , config: CodexBarConfig ) {
617+ let cursor = config. providerConfig ( for: . cursor)
618+ self . recordedCall = Call (
619+ providers: providers,
620+ cookieSource: cursor? . cookieSource,
621+ cookieHeader: cursor? . cookieHeader)
622+ }
623+
624+ func call( ) -> Call ? {
625+ self . recordedCall
626+ }
627+ }
0 commit comments