@@ -4,15 +4,16 @@ import { drizzle } from 'drizzle-orm/postgres-js'
44import postgres from 'postgres'
55import { afterAll , beforeAll , describe , expect , it , vi } from 'vitest'
66
7- const { select } = vi . hoisted ( ( ) => ( { select : vi . fn ( ) } ) )
7+ const { select, transaction } = vi . hoisted ( ( ) => ( { select : vi . fn ( ) , transaction : vi . fn ( ) } ) )
88const databaseUrl = readTestDatabaseUrl ( )
9- vi . mock ( '@sim/db' , ( ) => ( { db : { select } } ) )
9+ vi . mock ( '@sim/db' , ( ) => ( { db : { select, transaction } } ) )
1010
1111import {
12- type DashboardLedgerWindow ,
13- readDashboardHistoricalActorUsage ,
14- readDashboardLedgerUsage ,
15- readDashboardLedgerUsageByUser ,
12+ organizationWindow ,
13+ readDashboardHistoricalActorSpend ,
14+ readDashboardLedgerSpend ,
15+ readDashboardLedgerSpendByUser ,
16+ readDashboardWorkflowRuns ,
1617} from '@/lib/admin/dashboard-usage'
1718import type { ResolvedUsagePeriod } from '@/lib/billing/core/reporting-period'
1819
@@ -30,11 +31,6 @@ const reportingPeriod: ResolvedUsagePeriod = {
3031 anchorDate : '2026-01-15' ,
3132 interval : 'month' ,
3233}
33- const organizationWindow = ( id : string , period : ResolvedUsagePeriod ) : DashboardLedgerWindow => ( {
34- entity : { type : 'organization' , id } ,
35- period,
36- } )
37-
3834const P = `'2026-08-01', '2026-09-01'`
3935const PREVIOUS = `'2026-07-01', '2026-08-01'`
4036
@@ -106,6 +102,7 @@ beforeAll(async () => {
106102 ` )
107103 const database = drizzle ( connection )
108104 select . mockImplementation ( ( fields ) => database . select ( fields ) )
105+ transaction . mockImplementation ( ( run , config ) => database . transaction ( run , config ) )
109106} )
110107
111108afterAll ( async ( ) => {
@@ -114,63 +111,89 @@ afterAll(async () => {
114111} )
115112
116113describe ( 'dashboard ledger usage' , ( ) => {
117- it ( 'sums spend and counts distinct workflow runs per entity over each period' , async ( ) => {
118- const usage = await readDashboardLedgerUsage ( [
114+ it ( 'sums spend per entity over each period' , async ( ) => {
115+ const spend = await readDashboardLedgerSpend ( [
119116 organizationWindow ( 'org-a' , stampedPeriod ) ,
120117 organizationWindow ( 'org-b' , reportingPeriod ) ,
121118 organizationWindow ( 'org-c' , stampedPeriod ) ,
122119 { entity : { type : 'user' , id : 'p1' } , period : stampedPeriod } ,
123120 ] )
124121
125- expect ( Object . fromEntries ( usage ) ) . toEqual ( {
126- 'org-a' : { cost : 3.38 , workflowRuns : 5 } ,
127- 'org-b' : { cost : 1.75 , workflowRuns : 3 } ,
128- p1 : { cost : 0.6 , workflowRuns : 1 } ,
129- } )
122+ expect ( Object . fromEntries ( spend ) ) . toEqual ( { 'org-a' : 3.38 , 'org-b' : 1.75 , p1 : 0.6 } )
130123 } )
131124
132125 it ( 'reads an entity once however many times it is asked for' , async ( ) => {
133- const usage = await readDashboardLedgerUsage ( [
126+ const spend = await readDashboardLedgerSpend ( [
134127 organizationWindow ( 'org-a' , stampedPeriod ) ,
135128 organizationWindow ( 'org-a' , stampedPeriod ) ,
136129 ] )
137- expect ( usage . get ( 'org-a' ) ) . toEqual ( { cost : 3.38 , workflowRuns : 5 } )
130+ expect ( spend . get ( 'org-a' ) ) . toBe ( 3.38 )
138131 } )
139132
140- it ( 'splits spend and runs by acting user, counting a shared run for each actor ' , async ( ) => {
141- const usage = await readDashboardLedgerUsageByUser ( [
133+ it ( 'splits spend by acting user' , async ( ) => {
134+ const spend = await readDashboardLedgerSpendByUser ( [
142135 organizationWindow ( 'org-a' , stampedPeriod ) ,
143136 organizationWindow ( 'org-b' , reportingPeriod ) ,
144137 ] )
145138
146- expect ( Object . fromEntries ( usage . get ( 'org-a' ) ?? [ ] ) ) . toEqual ( {
147- u1 : { cost : 1.51 , workflowRuns : 2 } ,
148- u2 : { cost : 0.37 , workflowRuns : 2 } ,
149- u3 : { cost : 1 , workflowRuns : 1 } ,
150- u4 : { cost : 0.2 , workflowRuns : 0 } ,
151- u5 : { cost : 0.3 , workflowRuns : 1 } ,
152- } )
153- expect ( Object . fromEntries ( usage . get ( 'org-b' ) ?? [ ] ) ) . toEqual ( {
154- u6 : { cost : 1.75 , workflowRuns : 3 } ,
139+ expect ( Object . fromEntries ( spend . get ( 'org-a' ) ?? [ ] ) ) . toEqual ( {
140+ u1 : 1.51 ,
141+ u2 : 0.37 ,
142+ u3 : 1 ,
143+ u4 : 0.2 ,
144+ u5 : 0.3 ,
155145 } )
146+ expect ( Object . fromEntries ( spend . get ( 'org-b' ) ?? [ ] ) ) . toEqual ( { u6 : 1.75 } )
156147 } )
157148
158149 it ( 'narrows the per-user split to the requested users' , async ( ) => {
159150 const window = organizationWindow ( 'org-a' , stampedPeriod )
160151
161- const usage = await readDashboardLedgerUsageByUser ( [ window ] , [ 'u2' , 'u4' ] )
162- expect ( Object . fromEntries ( usage . get ( 'org-a' ) ?? [ ] ) ) . toEqual ( {
163- u2 : { cost : 0.37 , workflowRuns : 2 } ,
164- u4 : { cost : 0.2 , workflowRuns : 0 } ,
165- } )
166- expect ( ( await readDashboardLedgerUsageByUser ( [ window ] , [ ] ) ) . size ) . toBe ( 0 )
152+ const spend = await readDashboardLedgerSpendByUser ( [ window ] , [ 'u2' , 'u4' ] )
153+ expect ( Object . fromEntries ( spend . get ( 'org-a' ) ?? [ ] ) ) . toEqual ( { u2 : 0.37 , u4 : 0.2 } )
154+ expect ( ( await readDashboardLedgerSpendByUser ( [ window ] , [ ] ) ) . size ) . toBe ( 0 )
167155 } )
168156
169- it ( 'counts only actors who are neither members nor collaborators of the organization' , async ( ) => {
170- expect ( await readDashboardHistoricalActorUsage ( 'org-a' , stampedPeriod ) ) . toEqual ( {
171- usedDollars : 1.5 ,
172- workflowRuns : 2 ,
173- actorCount : 3 ,
157+ it ( 'counts distinct workflow runs in total and for each actor of a shared run' , async ( ) => {
158+ const orgA = await readDashboardWorkflowRuns ( organizationWindow ( 'org-a' , stampedPeriod ) )
159+ expect ( orgA ?. total ) . toBe ( 5 )
160+ expect ( Object . fromEntries ( orgA ?. byUser ?? [ ] ) ) . toEqual ( { u1 : 2 , u2 : 2 , u3 : 1 , u5 : 1 } )
161+
162+ const orgB = await readDashboardWorkflowRuns ( organizationWindow ( 'org-b' , reportingPeriod ) )
163+ expect ( orgB ?. total ) . toBe ( 3 )
164+ expect ( Object . fromEntries ( orgB ?. byUser ?? [ ] ) ) . toEqual ( { u6 : 3 } )
165+
166+ const empty = await readDashboardWorkflowRuns ( organizationWindow ( 'org-c' , stampedPeriod ) )
167+ expect ( empty ?. total ) . toBe ( 0 )
168+ expect ( empty ?. byUser . size ) . toBe ( 0 )
169+ } )
170+
171+ it ( 'reports runs as unknown when the count outlasts its budget' , async ( ) => {
172+ const locker = postgres ( databaseUrl , {
173+ max : 1 ,
174+ prepare : false ,
175+ connection : { search_path : schemaName } ,
176+ onnotice : ( ) => undefined ,
177+ } )
178+ const released = Promise . withResolvers < void > ( )
179+ const lockHeld = Promise . withResolvers < void > ( )
180+ const holder = locker . begin ( async ( tx ) => {
181+ await tx . unsafe ( 'LOCK TABLE usage_log IN ACCESS EXCLUSIVE MODE' )
182+ lockHeld . resolve ( )
183+ await released . promise
174184 } )
185+ try {
186+ await lockHeld . promise
187+ expect ( await readDashboardWorkflowRuns ( organizationWindow ( 'org-a' , stampedPeriod ) ) ) . toBeNull ( )
188+ } finally {
189+ released . resolve ( )
190+ await holder
191+ await locker . end ( )
192+ }
193+ } , 20_000 )
194+
195+ it ( 'spends only for actors who are neither members nor collaborators of the organization' , async ( ) => {
196+ const spend = await readDashboardHistoricalActorSpend ( 'org-a' , stampedPeriod )
197+ expect ( Object . fromEntries ( spend ) ) . toEqual ( { u3 : 1 , u4 : 0.2 , u5 : 0.3 } )
175198 } )
176199} )
0 commit comments