@@ -18,12 +18,10 @@ import { TokenServiceAccountValidationError } from '@/lib/credentials/token-serv
1818
1919const {
2020 mockCheckWorkspaceAccess,
21- mockGetCredentialActorContext,
2221 mockGetCredentialCreationWorkspaceContext,
2322 mockVerifyAndBuildServiceAccountSecret,
2423} = vi . hoisted ( ( ) => ( {
2524 mockCheckWorkspaceAccess : vi . fn ( ) ,
26- mockGetCredentialActorContext : vi . fn ( ) ,
2725 mockGetCredentialCreationWorkspaceContext : vi . fn ( ) ,
2826 mockVerifyAndBuildServiceAccountSecret : vi . fn ( ) ,
2927} ) )
@@ -43,10 +41,6 @@ vi.mock('@/lib/credentials/oauth', () => ({
4341 syncWorkspaceOAuthCredentialsForUser : vi . fn ( ) ,
4442} ) )
4543
46- vi . mock ( '@/lib/credentials/access' , ( ) => ( {
47- getCredentialActorContext : mockGetCredentialActorContext ,
48- } ) )
49-
5044vi . mock ( '@/lib/oauth' , ( ) => ( {
5145 getServiceConfigByProviderId : vi . fn ( ) ,
5246} ) )
@@ -138,15 +132,9 @@ describe('POST /api/credentials', () => {
138132 memberUserIds : [ 'user-1' ] ,
139133 canWrite : true ,
140134 } )
141- mockGetCredentialActorContext . mockResolvedValue ( {
142- member : null ,
143- hasWorkspaceAccess : true ,
144- canWriteWorkspace : true ,
145- isAdmin : true ,
146- } )
147135 } )
148136
149- describe ( 'service-account credentials ' , ( ) => {
137+ describe ( 'client-credential service accounts ' , ( ) => {
150138 it ( 'forwards clientId, clientSecret, and orgId to the secret builder on create' , async ( ) => {
151139 mockVerifyAndBuildServiceAccountSecret . mockResolvedValueOnce ( {
152140 providerId : 'zoom-service-account' ,
@@ -252,136 +240,6 @@ describe('POST /api/credentials', () => {
252240 )
253241 } )
254242
255- it . each ( [
256- {
257- family : 'client-credential' ,
258- providerId : 'zoom-service-account' ,
259- fields : {
260- orgId : 'zoom-account' ,
261- clientId : 'new-client-id' ,
262- clientSecret : 'new-client-secret' ,
263- } ,
264- } ,
265- {
266- family : 'token-paste' ,
267- providerId : 'snowflake-service-account' ,
268- fields : {
269- domain : 'acme.snowflakecomputing.com' ,
270- apiToken : 'new-programmatic-access-token' ,
271- } ,
272- } ,
273- {
274- family : 'Google JSON-key' ,
275- providerId : 'google-service-account' ,
276- fields : {
277- serviceAccountJson : JSON . stringify ( {
278- type : 'service_account' ,
279- client_email : 'automation@example.iam.gserviceaccount.com' ,
280- private_key : 'new-private-key' ,
281- project_id : 'example-project' ,
282- } ) ,
283- } ,
284- } ,
285- {
286- family : 'Atlassian' ,
287- providerId : 'atlassian-service-account' ,
288- fields : { domain : 'example.atlassian.net' , apiToken : 'new-atlassian-token' } ,
289- } ,
290- {
291- family : 'Slack different-ID' ,
292- providerId : 'slack-custom-bot' ,
293- fields : {
294- id : '33333333-4444-4555-8666-777777777777' ,
295- signingSecret : 'new-slack-signing-secret' ,
296- botToken : 'xoxb-new-slack-bot-token' ,
297- } ,
298- } ,
299- ] ) (
300- 'conflicts for an existing $family service account instead of dropping new secrets' ,
301- async ( { providerId, fields } ) => {
302- const displayName = 'Production automation'
303- mockVerifyAndBuildServiceAccountSecret . mockResolvedValueOnce ( {
304- providerId,
305- encryptedServiceAccountKey : 'new-secret-ciphertext' ,
306- displayName,
307- auditMetadata : { } ,
308- principal : null ,
309- } )
310- queueTableRows ( credential , [
311- {
312- id : 'existing-credential' ,
313- workspaceId : WORKSPACE_ID ,
314- type : 'service_account' ,
315- displayName,
316- providerId,
317- } ,
318- ] )
319-
320- const response = await POST (
321- createMockRequest ( 'POST' , {
322- workspaceId : WORKSPACE_ID ,
323- type : 'service_account' ,
324- providerId,
325- displayName,
326- ...fields ,
327- } )
328- )
329- const body = await response . json ( )
330-
331- expect ( response . status ) . toBe ( 409 )
332- expect ( body . code ) . toBe ( 'duplicate_display_name' )
333- expect ( dbChainMockFns . update ) . not . toHaveBeenCalled ( )
334- expect ( dbChainMockFns . insert ) . not . toHaveBeenCalled ( )
335- }
336- )
337-
338- it ( 'preserves an exact-ID Slack custom-bot create replay' , async ( ) => {
339- const credentialId = '22222222-3333-4444-8555-666666666666'
340- const existing = {
341- id : credentialId ,
342- workspaceId : WORKSPACE_ID ,
343- type : 'service_account' ,
344- displayName : 'Production Slack Bot' ,
345- description : null ,
346- providerId : 'slack-custom-bot' ,
347- accountId : null ,
348- envKey : null ,
349- envOwnerUserId : null ,
350- encryptedServiceAccountKey : 'existing-secret-ciphertext' ,
351- createdBy : 'user-1' ,
352- createdAt : new Date ( '2026-08-11T00:00:00.000Z' ) ,
353- updatedAt : new Date ( '2026-08-11T00:00:00.000Z' ) ,
354- }
355- mockVerifyAndBuildServiceAccountSecret . mockResolvedValueOnce ( {
356- providerId : 'slack-custom-bot' ,
357- encryptedServiceAccountKey : 'replayed-secret-ciphertext' ,
358- displayName : existing . displayName ,
359- auditMetadata : { } ,
360- principal : null ,
361- } )
362- queueTableRows ( credential , [ existing ] )
363-
364- const response = await POST (
365- createMockRequest ( 'POST' , {
366- id : credentialId ,
367- workspaceId : WORKSPACE_ID ,
368- type : 'service_account' ,
369- providerId : 'slack-custom-bot' ,
370- displayName : existing . displayName ,
371- signingSecret : 'slack-signing-secret' ,
372- botToken : 'xoxb-slack-bot-token' ,
373- } )
374- )
375-
376- const body = await response . json ( )
377- expect ( { status : response . status , body } ) . toMatchObject ( {
378- status : 200 ,
379- body : { credential : { id : credentialId } } ,
380- } )
381- expect ( dbChainMockFns . update ) . not . toHaveBeenCalled ( )
382- expect ( dbChainMockFns . insert ) . not . toHaveBeenCalled ( )
383- } )
384-
385243 it ( 'maps a verification failure to a 400 with the validation code' , async ( ) => {
386244 mockVerifyAndBuildServiceAccountSecret . mockRejectedValueOnce (
387245 new TokenServiceAccountValidationError ( 'invalid_credentials' , 400 , {
0 commit comments