@@ -20,6 +20,11 @@ const mocks = vi.hoisted(() => ({
2020 dispatchMemberSync : vi . fn ( ) ,
2121 memberAccessAvailable : vi . fn ( ) ,
2222 provision : vi . fn ( ) ,
23+ rewriteAcls : vi . fn ( ) ,
24+ } ) )
25+
26+ vi . mock ( '@/lib/knowledge/connectors/member-observations' , ( ) => ( {
27+ rewriteConnectorAcls : mocks . rewriteAcls ,
2328} ) )
2429
2530vi . mock ( '@sim/audit' , ( ) => ( {
@@ -242,6 +247,7 @@ describe('performUpdateKnowledgeConnectorAccess', () => {
242247 beforeEach ( ( ) => {
243248 vi . clearAllMocks ( )
244249 resetDbChainMock ( )
250+ mocks . rewriteAcls . mockResolvedValue ( true )
245251 mocks . grant . mockResolvedValue ( undefined )
246252 mocks . revoke . mockResolvedValue ( undefined )
247253 mocks . dispatchSync . mockResolvedValue ( { queued : true } )
@@ -277,14 +283,21 @@ describe('performUpdateKnowledgeConnectorAccess', () => {
277283 queueGroupRow ( 'option-1' )
278284 dbChainMockFns . returning
279285 . mockResolvedValueOnce ( [ { ...WORKSPACE_CONNECTOR , status : 'syncing' , syncLockToken : 's-1' } ] )
280- /** The rewrite finds nothing left, the flip lands under the lease, then the release. */
281- . mockResolvedValueOnce ( [ ] )
286+ /** The flip lands under the lease, then the release. */
282287 . mockResolvedValueOnce ( [ { id : 'c-1' } ] )
283288 . mockResolvedValueOnce ( [ { ...MEMBERS_CONNECTOR , nextMemberSyncAt : new Date ( ) } ] )
284289
285290 const outcome = await switchTo ( { accessMode : 'members' , binding : BINDING } )
286291
287292 expect ( outcome ) . toMatchObject ( { success : true , changed : true } )
293+ /** The rewrite hides every document and proves the switch lease inside each batch. */
294+ expect ( mocks . rewriteAcls ) . toHaveBeenCalledWith (
295+ 'c-1' ,
296+ [ ] ,
297+ expect . objectContaining ( {
298+ lease : expect . objectContaining ( { stillHeld : expect . any ( Function ) } ) ,
299+ } )
300+ )
288301 expect ( mocks . grant ) . toHaveBeenCalledWith (
289302 {
290303 workspaceId : 'ws-1' ,
@@ -294,7 +307,6 @@ describe('performUpdateKnowledgeConnectorAccess', () => {
294307 } ,
295308 'admin-1'
296309 )
297- expect ( dbChainMockFns . set ) . toHaveBeenCalledWith ( expect . objectContaining ( { acl : [ ] } ) )
298310 /** The flip is written inside the group's row lock. */
299311 expect ( dbChainMockFns . for ) . toHaveBeenCalledWith ( 'update' )
300312 expect ( dbChainMockFns . transaction ) . toHaveBeenCalledOnce ( )
@@ -325,9 +337,9 @@ describe('performUpdateKnowledgeConnectorAccess', () => {
325337 it ( 'refuses the flip, and undoes the grant, when the option is gone by the time the group is locked' , async ( ) => {
326338 queueTableRows ( schemaMock . knowledgeConnector , [ WORKSPACE_CONNECTOR ] )
327339 queueGroupRow ( )
328- dbChainMockFns . returning
329- . mockResolvedValueOnce ( [ { ...WORKSPACE_CONNECTOR , status : 'syncing' , syncLockToken : 's-1' } ] )
330- . mockResolvedValueOnce ( [ ] )
340+ dbChainMockFns . returning . mockResolvedValueOnce ( [
341+ { ...WORKSPACE_CONNECTOR , status : 'syncing' , syncLockToken : 's-1' } ,
342+ ] )
331343
332344 const outcome = await switchTo ( { accessMode : 'members' , binding : BINDING } )
333345
@@ -348,7 +360,6 @@ describe('performUpdateKnowledgeConnectorAccess', () => {
348360 queueGroupRow ( 'option-2' )
349361 dbChainMockFns . returning
350362 . mockResolvedValueOnce ( [ { ...MEMBERS_CONNECTOR , status : 'syncing' , syncLockToken : 's-1' } ] )
351- . mockResolvedValueOnce ( [ ] )
352363 . mockResolvedValueOnce ( [ { id : 'c-1' } ] )
353364 . mockResolvedValueOnce ( [
354365 { ...MEMBERS_CONNECTOR , credentialGroupId : 'group-2' , credentialGroupOptionId : 'option-2' } ,
@@ -384,16 +395,22 @@ describe('performUpdateKnowledgeConnectorAccess', () => {
384395 queueTableRows ( schemaMock . knowledgeConnector , [ MEMBERS_CONNECTOR ] )
385396 dbChainMockFns . returning
386397 . mockResolvedValueOnce ( [ { ...MEMBERS_CONNECTOR , status : 'syncing' , syncLockToken : 's-1' } ] )
387- /** The flip lands under the lease, then the rewrite finds nothing left, then the release. */
398+ /** The flip lands under the lease, then the release. */
388399 . mockResolvedValueOnce ( [ { id : 'c-1' } ] )
389- . mockResolvedValueOnce ( [ ] )
390400 . mockResolvedValueOnce ( [ { ...WORKSPACE_CONNECTOR , credentialId : 'cred-2' } ] )
391401
392402 const outcome = await switchTo ( { accessMode : 'workspace' , credentialId : 'cred-2' } )
393403
394404 expect ( outcome ) . toMatchObject ( { success : true , changed : true } )
395405 expect ( dbChainMockFns . delete ) . toHaveBeenCalled ( )
396- expect ( dbChainMockFns . set ) . toHaveBeenCalledWith ( expect . objectContaining ( { acl : [ 'ws' ] } ) )
406+ /** Workspace access is restored under the switch lease, proved inside each batch. */
407+ expect ( mocks . rewriteAcls ) . toHaveBeenCalledWith (
408+ 'c-1' ,
409+ [ 'ws' ] ,
410+ expect . objectContaining ( {
411+ lease : expect . objectContaining ( { stillHeld : expect . any ( Function ) } ) ,
412+ } )
413+ )
397414 expect ( mocks . revoke ) . toHaveBeenCalledWith (
398415 { workspaceId : 'ws-1' , credentialGroupId : 'group-1' , connectorId : 'c-1' } ,
399416 'admin-1'
@@ -509,7 +526,6 @@ describe('performUpdateKnowledgeConnectorAccess', () => {
509526 queueGroupRow ( 'option-1' )
510527 dbChainMockFns . returning
511528 . mockResolvedValueOnce ( [ { ...WORKSPACE_CONNECTOR , status : 'paused' } ] )
512- . mockResolvedValueOnce ( [ ] )
513529 . mockResolvedValueOnce ( [ { id : 'c-1' } ] )
514530 . mockResolvedValueOnce ( [ { ...MEMBERS_CONNECTOR , status : 'paused' } ] )
515531
0 commit comments