@@ -18,6 +18,7 @@ import {
18
18
mockOrgs ,
19
19
mockProjects ,
20
20
} from '../test/mocks.js' ;
21
+ import { BRANCH_COST_HOURLY , PROJECT_COST_MONTHLY } from './pricing.js' ;
21
22
import { createSupabaseMcpServer } from './server.js' ;
22
23
23
24
beforeEach ( ( ) => {
@@ -117,7 +118,10 @@ describe('tools', () => {
117
118
arguments : { } ,
118
119
} ) ;
119
120
120
- expect ( result ) . toEqual ( mockOrgs ) ;
121
+ expect ( result ) . toEqual ( [
122
+ { id : 'org-1' , name : 'Org 1' } ,
123
+ { id : 'org-2' , name : 'Org 2' } ,
124
+ ] ) ;
121
125
} ) ;
122
126
123
127
test ( 'get organization' , async ( ) => {
@@ -135,6 +139,108 @@ describe('tools', () => {
135
139
expect ( result ) . toEqual ( firstOrg ) ;
136
140
} ) ;
137
141
142
+ test ( 'get next project cost for free org' , async ( ) => {
143
+ const { callTool } = await setup ( ) ;
144
+
145
+ const freeOrg = mockOrgs . find ( ( org ) => org . plan === 'free' ) ! ;
146
+ const result = await callTool ( {
147
+ name : 'get_cost' ,
148
+ arguments : {
149
+ type : 'project' ,
150
+ organization_id : freeOrg . id ,
151
+ } ,
152
+ } ) ;
153
+
154
+ expect ( result ) . toEqual (
155
+ 'The new project will cost $0 monthly. You must repeat this to the user and confirm their understanding.'
156
+ ) ;
157
+ } ) ;
158
+
159
+ test ( 'get next project cost for paid org with 0 projects' , async ( ) => {
160
+ const { callTool } = await setup ( ) ;
161
+
162
+ const paidOrg = mockOrgs . find ( ( org ) => org . plan !== 'free' ) ! ;
163
+ const result = await callTool ( {
164
+ name : 'get_cost' ,
165
+ arguments : {
166
+ type : 'project' ,
167
+ organization_id : paidOrg . id ,
168
+ } ,
169
+ } ) ;
170
+
171
+ expect ( result ) . toEqual (
172
+ 'The new project will cost $0 monthly. You must repeat this to the user and confirm their understanding.'
173
+ ) ;
174
+ } ) ;
175
+
176
+ test ( 'get next project cost for paid org with > 0 ACTIVE_HEALTHY projects' , async ( ) => {
177
+ const { callTool } = await setup ( ) ;
178
+
179
+ const paidOrg = mockOrgs . find ( ( org ) => org . plan !== 'free' ) ! ;
180
+
181
+ const priorProject = await createProject ( {
182
+ name : 'Project 1' ,
183
+ region : 'us-east-1' ,
184
+ organization_id : paidOrg . id ,
185
+ } ) ;
186
+ priorProject . status = 'ACTIVE_HEALTHY' ;
187
+
188
+ const result = await callTool ( {
189
+ name : 'get_cost' ,
190
+ arguments : {
191
+ type : 'project' ,
192
+ organization_id : paidOrg . id ,
193
+ } ,
194
+ } ) ;
195
+
196
+ expect ( result ) . toEqual (
197
+ `The new project will cost $${ PROJECT_COST_MONTHLY } monthly. You must repeat this to the user and confirm their understanding.`
198
+ ) ;
199
+ } ) ;
200
+
201
+ test ( 'get next project cost for paid org with > 0 projects that are not ACTIVE_HEALTHY' , async ( ) => {
202
+ const { callTool } = await setup ( ) ;
203
+
204
+ const paidOrg = mockOrgs . find ( ( org ) => org . plan !== 'free' ) ! ;
205
+
206
+ const priorProject = await createProject ( {
207
+ name : 'Project 1' ,
208
+ region : 'us-east-1' ,
209
+ organization_id : paidOrg . id ,
210
+ } ) ;
211
+ priorProject . status = 'INACTIVE' ;
212
+
213
+ const result = await callTool ( {
214
+ name : 'get_cost' ,
215
+ arguments : {
216
+ type : 'project' ,
217
+ organization_id : paidOrg . id ,
218
+ } ,
219
+ } ) ;
220
+
221
+ expect ( result ) . toEqual (
222
+ `The new project will cost $0 monthly. You must repeat this to the user and confirm their understanding.`
223
+ ) ;
224
+ } ) ;
225
+
226
+ test ( 'get branch cost' , async ( ) => {
227
+ const { callTool } = await setup ( ) ;
228
+
229
+ const paidOrg = mockOrgs . find ( ( org ) => org . plan !== 'free' ) ! ;
230
+
231
+ const result = await callTool ( {
232
+ name : 'get_cost' ,
233
+ arguments : {
234
+ type : 'branch' ,
235
+ organization_id : paidOrg . id ,
236
+ } ,
237
+ } ) ;
238
+
239
+ expect ( result ) . toEqual (
240
+ `The new branch will cost $${ BRANCH_COST_HOURLY } hourly. You must repeat this to the user and confirm their understanding.`
241
+ ) ;
242
+ } ) ;
243
+
138
244
test ( 'list projects' , async ( ) => {
139
245
const { callTool } = await setup ( ) ;
140
246
@@ -164,19 +270,31 @@ describe('tools', () => {
164
270
test ( 'create project' , async ( ) => {
165
271
const { callTool } = await setup ( ) ;
166
272
273
+ const freeOrg = mockOrgs . find ( ( org ) => org . plan === 'free' ) ! ;
274
+
275
+ const confirm_cost_id = await callTool ( {
276
+ name : 'confirm_cost' ,
277
+ arguments : {
278
+ type : 'project' ,
279
+ recurrence : 'monthly' ,
280
+ amount : 0 ,
281
+ } ,
282
+ } ) ;
283
+
167
284
const newProject = {
168
285
name : 'New Project' ,
169
286
region : 'us-east-1' ,
170
- organization_id : mockOrgs [ 0 ] ! . id ,
287
+ organization_id : freeOrg . id ,
171
288
db_pass : 'dummy-password' ,
289
+ confirm_cost_id,
172
290
} ;
173
291
174
292
const result = await callTool ( {
175
293
name : 'create_project' ,
176
294
arguments : newProject ,
177
295
} ) ;
178
296
179
- const { db_pass, ...projectInfo } = newProject ;
297
+ const { db_pass, confirm_cost_id : _ , ...projectInfo } = newProject ;
180
298
181
299
expect ( result ) . toEqual ( {
182
300
...projectInfo ,
@@ -191,18 +309,28 @@ describe('tools', () => {
191
309
test ( 'create project chooses closest region when undefined' , async ( ) => {
192
310
const { callTool } = await setup ( ) ;
193
311
312
+ const confirm_cost_id = await callTool ( {
313
+ name : 'confirm_cost' ,
314
+ arguments : {
315
+ type : 'project' ,
316
+ recurrence : 'monthly' ,
317
+ amount : 0 ,
318
+ } ,
319
+ } ) ;
320
+
194
321
const newProject = {
195
322
name : 'New Project' ,
196
323
organization_id : mockOrgs [ 0 ] ! . id ,
197
324
db_pass : 'dummy-password' ,
325
+ confirm_cost_id,
198
326
} ;
199
327
200
328
const result = await callTool ( {
201
329
name : 'create_project' ,
202
330
arguments : newProject ,
203
331
} ) ;
204
332
205
- const { db_pass, ...projectInfo } = newProject ;
333
+ const { db_pass, confirm_cost_id : _ , ...projectInfo } = newProject ;
206
334
207
335
expect ( result ) . toEqual ( {
208
336
...projectInfo ,
@@ -215,6 +343,28 @@ describe('tools', () => {
215
343
} ) ;
216
344
} ) ;
217
345
346
+ test ( 'create project without cost confirmation fails' , async ( ) => {
347
+ const { callTool } = await setup ( ) ;
348
+
349
+ const org = mockOrgs [ 0 ] ! ;
350
+
351
+ const newProject = {
352
+ name : 'New Project' ,
353
+ region : 'us-east-1' ,
354
+ organization_id : org . id ,
355
+ db_pass : 'dummy-password' ,
356
+ } ;
357
+
358
+ const createProjectPromise = callTool ( {
359
+ name : 'create_project' ,
360
+ arguments : newProject ,
361
+ } ) ;
362
+
363
+ await expect ( createProjectPromise ) . rejects . toThrow (
364
+ 'User must confirm understanding of costs before creating a project.'
365
+ ) ;
366
+ } ) ;
367
+
218
368
test ( 'pause project' , async ( ) => {
219
369
const { callTool } = await setup ( ) ;
220
370
const project = mockProjects . values ( ) . next ( ) . value ! ;
@@ -497,12 +647,22 @@ describe('tools', () => {
497
647
const { callTool } = await setup ( ) ;
498
648
const project = mockProjects . values ( ) . next ( ) . value ! ;
499
649
650
+ const confirm_cost_id = await callTool ( {
651
+ name : 'confirm_cost' ,
652
+ arguments : {
653
+ type : 'branch' ,
654
+ recurrence : 'hourly' ,
655
+ amount : BRANCH_COST_HOURLY ,
656
+ } ,
657
+ } ) ;
658
+
500
659
const branchName = 'test-branch' ;
501
660
const result = await callTool ( {
502
661
name : 'create_branch' ,
503
662
arguments : {
504
663
project_id : project . id ,
505
664
name : branchName ,
665
+ confirm_cost_id,
506
666
} ,
507
667
} ) ;
508
668
@@ -523,15 +683,44 @@ describe('tools', () => {
523
683
} ) ;
524
684
} ) ;
525
685
686
+ test ( 'create branch without cost confirmation fails' , async ( ) => {
687
+ const { callTool } = await setup ( ) ;
688
+
689
+ const project = mockProjects . values ( ) . next ( ) . value ! ;
690
+
691
+ const branchName = 'test-branch' ;
692
+ const createBranchPromise = callTool ( {
693
+ name : 'create_branch' ,
694
+ arguments : {
695
+ project_id : project . id ,
696
+ name : branchName ,
697
+ } ,
698
+ } ) ;
699
+
700
+ await expect ( createBranchPromise ) . rejects . toThrow (
701
+ 'User must confirm understanding of costs before creating a branch.'
702
+ ) ;
703
+ } ) ;
704
+
526
705
test ( 'delete branch' , async ( ) => {
527
706
const { callTool } = await setup ( ) ;
528
707
const project = mockProjects . values ( ) . next ( ) . value ! ;
529
708
709
+ const confirm_cost_id = await callTool ( {
710
+ name : 'confirm_cost' ,
711
+ arguments : {
712
+ type : 'branch' ,
713
+ recurrence : 'hourly' ,
714
+ amount : BRANCH_COST_HOURLY ,
715
+ } ,
716
+ } ) ;
717
+
530
718
const branch = await callTool ( {
531
719
name : 'create_branch' ,
532
720
arguments : {
533
721
project_id : project . id ,
534
722
name : 'test-branch' ,
723
+ confirm_cost_id,
535
724
} ,
536
725
} ) ;
537
726
@@ -598,11 +787,21 @@ describe('tools', () => {
598
787
const { callTool } = await setup ( ) ;
599
788
const project = mockProjects . values ( ) . next ( ) . value ! ;
600
789
790
+ const confirm_cost_id = await callTool ( {
791
+ name : 'confirm_cost' ,
792
+ arguments : {
793
+ type : 'branch' ,
794
+ recurrence : 'hourly' ,
795
+ amount : BRANCH_COST_HOURLY ,
796
+ } ,
797
+ } ) ;
798
+
601
799
const branch = await callTool ( {
602
800
name : 'create_branch' ,
603
801
arguments : {
604
802
project_id : project . id ,
605
803
name : 'test-branch' ,
804
+ confirm_cost_id,
606
805
} ,
607
806
} ) ;
608
807
@@ -647,11 +846,21 @@ describe('tools', () => {
647
846
const { callTool } = await setup ( ) ;
648
847
const project = mockProjects . values ( ) . next ( ) . value ! ;
649
848
849
+ const confirm_cost_id = await callTool ( {
850
+ name : 'confirm_cost' ,
851
+ arguments : {
852
+ type : 'branch' ,
853
+ recurrence : 'hourly' ,
854
+ amount : BRANCH_COST_HOURLY ,
855
+ } ,
856
+ } ) ;
857
+
650
858
const branch = await callTool ( {
651
859
name : 'create_branch' ,
652
860
arguments : {
653
861
project_id : project . id ,
654
862
name : 'test-branch' ,
863
+ confirm_cost_id,
655
864
} ,
656
865
} ) ;
657
866
@@ -701,11 +910,21 @@ describe('tools', () => {
701
910
const { callTool } = await setup ( ) ;
702
911
const project = mockProjects . values ( ) . next ( ) . value ! ;
703
912
913
+ const confirm_cost_id = await callTool ( {
914
+ name : 'confirm_cost' ,
915
+ arguments : {
916
+ type : 'branch' ,
917
+ recurrence : 'hourly' ,
918
+ amount : BRANCH_COST_HOURLY ,
919
+ } ,
920
+ } ) ;
921
+
704
922
const branch = await callTool ( {
705
923
name : 'create_branch' ,
706
924
arguments : {
707
925
project_id : project . id ,
708
926
name : 'test-branch' ,
927
+ confirm_cost_id,
709
928
} ,
710
929
} ) ;
711
930
@@ -779,11 +998,21 @@ describe('tools', () => {
779
998
const { callTool } = await setup ( ) ;
780
999
const project = mockProjects . values ( ) . next ( ) . value ! ;
781
1000
1001
+ const confirm_cost_id = await callTool ( {
1002
+ name : 'confirm_cost' ,
1003
+ arguments : {
1004
+ type : 'branch' ,
1005
+ recurrence : 'hourly' ,
1006
+ amount : BRANCH_COST_HOURLY ,
1007
+ } ,
1008
+ } ) ;
1009
+
782
1010
const branch = await callTool ( {
783
1011
name : 'create_branch' ,
784
1012
arguments : {
785
1013
project_id : project . id ,
786
1014
name : 'test-branch' ,
1015
+ confirm_cost_id,
787
1016
} ,
788
1017
} ) ;
789
1018
0 commit comments