@@ -332,6 +332,78 @@ describe('atomic organization live Search MCP setup', () => {
332332 restoreSlackHttp = ( ) => spy . mockRestore ( )
333333 }
334334
335+ async function seedImplicitSlackApproval ( ) {
336+ const knowledgeBaseId = generateId ( )
337+ const connectorId = generateId ( )
338+ await db . insert ( knowledgeBase ) . values ( {
339+ id : knowledgeBaseId ,
340+ userId : ids . owner ,
341+ organizationId : ids . organization ,
342+ isSearchIndex : true ,
343+ name : 'Slack Search fixture' ,
344+ } )
345+ await db . insert ( knowledgeConnector ) . values ( {
346+ id : connectorId ,
347+ knowledgeBaseId,
348+ connectorType : 'slack' ,
349+ status : 'active' ,
350+ sourceConfig : { } ,
351+ } )
352+ return connectorId
353+ }
354+
355+ it . each ( [ false , true ] ) (
356+ 'verifies implicitly approved Search permissions unless explicitly disabled (disabled: %s)' ,
357+ async ( disabled ) => {
358+ const setup = await seedSlackAuthorization ( )
359+ await seedImplicitSlackApproval ( )
360+ if ( disabled )
361+ await approveSearchIntegration . execute ( {
362+ principal : createSessionPrincipal ( { userId : ids . owner , sessionId : generateId ( ) } ) ,
363+ input : { organizationId : ids . organization , connectorType : 'slack' , approved : false } ,
364+ } )
365+ expect ( await integrationStatus ( 'slack' ) ) . toMatchObject ( { approved : ! disabled } )
366+ const pending = await setup . start ( )
367+ const scopes = disabled
368+ ? [ ...SLACK_MANAGED_USER_SCOPES ]
369+ : [ ...new Set ( [ ...SLACK_MANAGED_USER_SCOPES , ...SLACK_SEARCH_USER_SCOPES ] ) ]
370+ expect ( new URL ( pending . authorizationUrl ) . searchParams . get ( 'user_scope' ) ! . split ( ',' ) ) . toEqual (
371+ expect . arrayContaining ( scopes )
372+ )
373+ if ( disabled )
374+ expect ( new URL ( pending . authorizationUrl ) . searchParams . get ( 'user_scope' ) ) . not . toContain (
375+ 'search:read.public'
376+ )
377+ provideSlackConsent ( scopes )
378+ await expect ( setup . complete ( pending . state ) ) . resolves . toMatchObject ( { ok : true } )
379+ const state = await snapshot ( )
380+ expect (
381+ state . groups [ 0 ] . options . find ( ( entry ) => entry . id === setup . optionId ) ?. requiredScopes
382+ ) . toEqual ( expect . arrayContaining ( scopes ) )
383+ if ( disabled )
384+ await expect ( setup . resolveToken ( ) ) . resolves . toMatchObject ( { accessToken : 'fixture-token' } )
385+ }
386+ )
387+
388+ it . each ( [ 'added' , 'removed' ] as const ) (
389+ 'rejects pending authorization when implicit Search approval is %s' ,
390+ async ( change ) => {
391+ const setup = await seedSlackAuthorization ( )
392+ const connectorId = change === 'removed' ? await seedImplicitSlackApproval ( ) : null
393+ const pending = await setup . start ( )
394+ if ( connectorId )
395+ await db
396+ . update ( knowledgeConnector )
397+ . set ( { archivedAt : new Date ( ) } )
398+ . where ( eq ( knowledgeConnector . id , connectorId ) )
399+ else await seedImplicitSlackApproval ( )
400+ provideSlackConsent ( [ ...SLACK_MANAGED_USER_SCOPES , ...SLACK_SEARCH_USER_SCOPES ] )
401+ await expect ( setup . complete ( pending . state ) ) . rejects . toThrow ( 'Search approval changed' )
402+ expect ( ( await snapshot ( ) ) . groups ) . toEqual ( setup . before . groups )
403+ await expect ( setup . resolveToken ( ) ) . resolves . toMatchObject ( { accessToken : 'fixture-token' } )
404+ }
405+ )
406+
335407 it . each ( [
336408 { name : 'workflow policy' , scopes : SLACK_MANAGED_USER_SCOPES } ,
337409 { name : 'custom policy' , scopes : [ 'chat:write' , 'users:read' , 'users:read.email' ] } ,
0 commit comments