@@ -66,3 +66,124 @@ test("returns identity", async (t) => {
6666 expect ( res . body . identity ! . id ) . toEqual ( identity . id ) ;
6767 expect ( res . body . identity ! . externalId ) . toEqual ( identity . externalId ) ;
6868} ) ;
69+
70+ test ( "returns credits from new credits table" , async ( t ) => {
71+ const h = await IntegrationHarness . init ( t ) ;
72+ const root = await h . createRootKey ( [ "api.*.read_key" ] ) ;
73+
74+ const keyId = newId ( "test" ) ;
75+ await h . db . primary . insert ( schema . keys ) . values ( {
76+ id : keyId ,
77+ keyAuthId : h . resources . userKeyAuth . id ,
78+ workspaceId : h . resources . userWorkspace . id ,
79+ start : "test" ,
80+ hash : await sha256 ( new KeyV1 ( { byteLength : 16 } ) . toString ( ) ) ,
81+ createdAtM : Date . now ( ) ,
82+ } ) ;
83+
84+ // Create credits in new table with monthly refill
85+ const creditId = newId ( "credit" ) ;
86+ await h . db . primary . insert ( schema . credits ) . values ( {
87+ id : creditId ,
88+ keyId,
89+ workspaceId : h . resources . userWorkspace . id ,
90+ remaining : 5000 ,
91+ refillAmount : 2000 ,
92+ refillDay : 10 , // monthly refill
93+ createdAt : Date . now ( ) ,
94+ refilledAt : Date . now ( ) ,
95+ identityId : null ,
96+ updatedAt : null ,
97+ } ) ;
98+
99+ const res = await h . get < V1KeysGetKeyResponse > ( {
100+ url : `/v1/keys.getKey?keyId=${ keyId } ` ,
101+ headers : {
102+ Authorization : `Bearer ${ root . key } ` ,
103+ } ,
104+ } ) ;
105+
106+ expect ( res . status , `expected 200, received: ${ JSON . stringify ( res , null , 2 ) } ` ) . toBe ( 200 ) ;
107+ expect ( res . body . remaining ) . toBe ( 5000 ) ;
108+ expect ( res . body . refill ) . toBeDefined ( ) ;
109+ expect ( res . body . refill ! . interval ) . toBe ( "monthly" ) ;
110+ expect ( res . body . refill ! . amount ) . toBe ( 2000 ) ;
111+ expect ( res . body . refill ! . refillDay ) . toBe ( 10 ) ;
112+ } ) ;
113+
114+ test ( "returns credits from legacy remaining field when no credits table entry" , async ( t ) => {
115+ const h = await IntegrationHarness . init ( t ) ;
116+ const root = await h . createRootKey ( [ "api.*.read_key" ] ) ;
117+
118+ const keyId = newId ( "test" ) ;
119+ await h . db . primary . insert ( schema . keys ) . values ( {
120+ id : keyId ,
121+ keyAuthId : h . resources . userKeyAuth . id ,
122+ workspaceId : h . resources . userWorkspace . id ,
123+ start : "test" ,
124+ hash : await sha256 ( new KeyV1 ( { byteLength : 16 } ) . toString ( ) ) ,
125+ remaining : 800 ,
126+ refillAmount : 100 ,
127+ refillDay : null , // daily refill
128+ createdAtM : Date . now ( ) ,
129+ } ) ;
130+
131+ const res = await h . get < V1KeysGetKeyResponse > ( {
132+ url : `/v1/keys.getKey?keyId=${ keyId } ` ,
133+ headers : {
134+ Authorization : `Bearer ${ root . key } ` ,
135+ } ,
136+ } ) ;
137+
138+ expect ( res . status , `expected 200, received: ${ JSON . stringify ( res , null , 2 ) } ` ) . toBe ( 200 ) ;
139+ expect ( res . body . remaining ) . toBe ( 800 ) ;
140+ expect ( res . body . refill ) . toBeDefined ( ) ;
141+ expect ( res . body . refill ! . interval ) . toBe ( "daily" ) ;
142+ expect ( res . body . refill ! . amount ) . toBe ( 100 ) ;
143+ } ) ;
144+
145+ test ( "prefers new credits table over legacy remaining field" , async ( t ) => {
146+ const h = await IntegrationHarness . init ( t ) ;
147+ const root = await h . createRootKey ( [ "api.*.read_key" ] ) ;
148+
149+ const keyId = newId ( "test" ) ;
150+ // Create key with legacy credits (should be ignored)
151+ await h . db . primary . insert ( schema . keys ) . values ( {
152+ id : keyId ,
153+ keyAuthId : h . resources . userKeyAuth . id ,
154+ workspaceId : h . resources . userWorkspace . id ,
155+ start : "test" ,
156+ hash : await sha256 ( new KeyV1 ( { byteLength : 16 } ) . toString ( ) ) ,
157+ remaining : 123 , // This should be ignored
158+ refillAmount : 50 , // This should be ignored
159+ createdAtM : Date . now ( ) ,
160+ } ) ;
161+
162+ // Create credits in new table (should take precedence)
163+ const creditId = newId ( "credit" ) ;
164+ await h . db . primary . insert ( schema . credits ) . values ( {
165+ id : creditId ,
166+ keyId,
167+ workspaceId : h . resources . userWorkspace . id ,
168+ remaining : 7000 ,
169+ refillAmount : 3000 ,
170+ refillDay : null , // daily refill
171+ createdAt : Date . now ( ) ,
172+ refilledAt : Date . now ( ) ,
173+ identityId : null ,
174+ updatedAt : null ,
175+ } ) ;
176+
177+ const res = await h . get < V1KeysGetKeyResponse > ( {
178+ url : `/v1/keys.getKey?keyId=${ keyId } ` ,
179+ headers : {
180+ Authorization : `Bearer ${ root . key } ` ,
181+ } ,
182+ } ) ;
183+
184+ expect ( res . status , `expected 200, received: ${ JSON . stringify ( res , null , 2 ) } ` ) . toBe ( 200 ) ;
185+ // Should use values from credits table, not legacy fields
186+ expect ( res . body . remaining ) . toBe ( 7000 ) ;
187+ expect ( res . body . refill ) . toBeDefined ( ) ;
188+ expect ( res . body . refill ! . amount ) . toBe ( 3000 ) ;
189+ } ) ;
0 commit comments