@@ -38,6 +38,10 @@ import {
3838 CUMULATIVE_COST_EPSILON ,
3939 CumulativeUsageContextMismatchError ,
4040 getBillingPeriodUsageCost ,
41+ getBillingPeriodUsageCostByUser ,
42+ getBillingPeriodUsageCostWithSourceSubset ,
43+ getBillingPeriodWorkflowRunCount ,
44+ getStampedPeriodRangeUsageCostByUser ,
4145 getUserUsageLogs ,
4246 getWorkspaceUsageLogs ,
4347 recordCumulativeUsage ,
@@ -557,34 +561,83 @@ describe('usage-log query scopes', () => {
557561 } )
558562} )
559563
560- describe ( 'getBillingPeriodUsageCost' , ( ) => {
564+ describe ( 'ledger aggregates' , ( ) => {
565+ const billingEntity = { type : 'organization' as const , id : 'org-1' }
566+ const billingPeriod = {
567+ start : new Date ( '2026-05-01T00:00:00Z' ) ,
568+ end : new Date ( '2027-05-01T00:00:00Z' ) ,
569+ }
570+ /** Every aggregate over the ledger, with the row the mocked read hands back and the value it yields. */
571+ const aggregates : Array < {
572+ name : string
573+ read : ( ) => Promise < unknown >
574+ rows : unknown [ ]
575+ expected : unknown
576+ } > = [
577+ {
578+ name : 'getBillingPeriodUsageCost' ,
579+ read : ( ) => getBillingPeriodUsageCost ( billingEntity , billingPeriod ) ,
580+ rows : [ { cost : '12.5' } ] ,
581+ expected : 12.5 ,
582+ } ,
583+ {
584+ name : 'getBillingPeriodWorkflowRunCount' ,
585+ read : ( ) => getBillingPeriodWorkflowRunCount ( billingEntity , billingPeriod ) ,
586+ rows : [ { workflowRuns : 7 } ] ,
587+ expected : 7 ,
588+ } ,
589+ {
590+ name : 'getBillingPeriodUsageCostWithSourceSubset' ,
591+ read : ( ) =>
592+ getBillingPeriodUsageCostWithSourceSubset ( billingEntity , billingPeriod , [ 'workflow' ] ) ,
593+ rows : [ { total : '20' , subset : '5' } ] ,
594+ expected : { total : 20 , subset : 5 } ,
595+ } ,
596+ {
597+ name : 'getBillingPeriodUsageCostByUser' ,
598+ read : ( ) => getBillingPeriodUsageCostByUser ( billingEntity , billingPeriod ) ,
599+ rows : [ { userId : 'user-1' , cost : '3' } ] ,
600+ expected : new Map ( [ [ 'user-1' , 3 ] ] ) ,
601+ } ,
602+ {
603+ name : 'getStampedPeriodRangeUsageCostByUser' ,
604+ read : ( ) =>
605+ getStampedPeriodRangeUsageCostByUser ( billingEntity , {
606+ from : billingPeriod . start ,
607+ to : billingPeriod . end ,
608+ } ) ,
609+ rows : [ { userId : 'user-2' , cost : '4' } ] ,
610+ expected : new Map ( [ [ 'user-2' , 4 ] ] ) ,
611+ } ,
612+ ]
613+
561614 beforeEach ( ( ) => {
562615 vi . clearAllMocks ( )
563616 installSharedDbMocks ( )
564617 } )
565618
566- it ( 'bounds the ledger sum with its own statement timeout inside one transaction' , async ( ) => {
567- const execute = vi . fn ( ) . mockResolvedValue ( [ ] )
568- const where = vi . fn ( ) . mockResolvedValue ( [ { cost : '12.5' } ] )
569- const tx = { execute , select : vi . fn ( ( ) => ( { from : vi . fn ( ( ) => ( { where } ) ) } ) ) }
570- mockTransaction . mockImplementation ( ( callback : ( client : typeof tx ) => Promise < unknown > ) =>
571- callback ( tx )
572- )
573-
574- const cost = await getBillingPeriodUsageCost (
575- { type : 'organization' , id : 'org-1' } ,
576- { start : new Date ( '2026-05-01T00:00:00Z' ) , end : new Date ( '2027-05-01T00:00:00Z' ) }
577- )
578-
579- expect ( cost ) . toBe ( 12.5 )
580- expect ( mockTransaction ) . toHaveBeenCalledTimes ( 1 )
581- const executed = execute . mock . calls . map (
582- ( [ statement ] ) => ( statement as { toSQL : ( ) => { sql : string } } ) . toSQL ( ) . sql
583- )
584- expect ( executed ) . toContain (
585- `SET LOCAL statement_timeout = '${ USAGE_LEDGER_STATEMENT_TIMEOUT_MS } ms'`
586- )
587- /** The bound is set before the sum runs, not after. */
588- expect ( execute . mock . invocationCallOrder [ 0 ] ) . toBeLessThan ( where . mock . invocationCallOrder [ 0 ] )
589- } )
619+ for ( const aggregate of aggregates ) {
620+ it ( ` ${ aggregate . name } reads through the bounded ledger transaction` , async ( ) => {
621+ const execute = vi . fn ( ) . mockResolvedValue ( [ ] )
622+ const terminal = vi . fn ( ) . mockResolvedValue ( aggregate . rows )
623+ const chain : Record < string , unknown > = { }
624+ for ( const step of [ 'select' , 'from' , 'where' , 'leftJoin' ] ) chain [ step ] = vi . fn ( ( ) => chain )
625+ chain . groupBy = terminal
626+ chain . then = ( resolve : ( rows : unknown [ ] ) => unknown ) => terminal ( ) . then ( resolve )
627+ const tx = { execute , select : chain . select }
628+ mockTransaction . mockImplementation ( ( callback : ( client : typeof tx ) => Promise < unknown > ) =>
629+ callback ( tx )
630+ )
631+
632+ await expect ( aggregate . read ( ) ) . resolves . toEqual ( aggregate . expected )
633+ expect ( mockTransaction ) . toHaveBeenCalledTimes ( 1 )
634+ expect (
635+ execute . mock . calls . map (
636+ ( [ statement ] ) => ( statement as { toSQL : ( ) => { sql : string } } ) . toSQL ( ) . sql
637+ )
638+ ) . toEqual ( [ `SET LOCAL statement_timeout = '${ USAGE_LEDGER_STATEMENT_TIMEOUT_MS } ms'` ] )
639+ /** The bound is set before the aggregate runs, not after. */
640+ expect ( execute . mock . invocationCallOrder [ 0 ] ) . toBeLessThan ( terminal . mock . invocationCallOrder [ 0 ] )
641+ } )
642+ }
590643} )
0 commit comments