@@ -10,7 +10,11 @@ import pDefer from 'p-defer'
1010import { pEvent } from 'p-event'
1111import RAM from 'random-access-memory'
1212import { map } from 'iterpal'
13- import { BLOCKED_ROLE_ID , MEMBER_ROLE_ID } from '../src/roles.js'
13+ import {
14+ BLOCKED_ROLE_ID ,
15+ COORDINATOR_ROLE_ID ,
16+ MEMBER_ROLE_ID ,
17+ } from '../src/roles.js'
1418import comapeoServer from '@comapeo/cloud'
1519import {
1620 connectPeers ,
@@ -503,6 +507,69 @@ test('connecting and then immediately disconnecting (and then immediately connec
503507 )
504508} )
505509
510+ test ( 'duplicate add server from more than one device' , async ( t ) => {
511+ const [ managers , { serverBaseUrl } ] = await Promise . all ( [
512+ createManagers ( 2 , t , 'mobile' ) ,
513+ createTestServer ( t ) ,
514+ ] )
515+ const [ managerA , managerB ] = managers
516+
517+ // Manager A: create project and add the server to it
518+ const projectId = await managerA . createProject ( { name : 'foo' } )
519+ const managerAProject = await managerA . getProject ( projectId )
520+ t . after ( ( ) => managerAProject . $sync . disconnectServers ( ) )
521+
522+ // Add Manager B to project
523+ const disconnectManagers1 = connectPeers ( managers )
524+ await waitForPeers ( managers )
525+ await invite ( {
526+ invitor : managerA ,
527+ invitees : [ managerB ] ,
528+ projectId,
529+ roleId : COORDINATOR_ROLE_ID ,
530+ } )
531+ const managerBProject = await managerB . getProject ( projectId )
532+ t . after ( ( ) => managerBProject . $sync . disconnectServers ( ) )
533+ await disconnectManagers1 ( )
534+
535+ // Both managers add the same server without syncing with each other
536+ await managerAProject . $member . addServerPeer ( serverBaseUrl , {
537+ dangerouslyAllowInsecureConnections : true ,
538+ } )
539+
540+ await managerBProject . $member . addServerPeer ( serverBaseUrl , {
541+ dangerouslyAllowInsecureConnections : true ,
542+ } )
543+
544+ // Sync managers so that duplicate server entries sync
545+ const disconnectManagers2 = connectPeers ( managers )
546+ t . after ( disconnectManagers2 )
547+ await waitForPeers ( managers )
548+ const projects = [ managerAProject , managerBProject ]
549+ await waitForSync ( projects , 'initial' )
550+
551+ // manager A tries to remove server peer
552+ const serverPeerA = await findServerPeer ( managerAProject )
553+ assert ( serverPeerA , 'Manager A must have a server peer' )
554+ await managerAProject . $member . removeServerPeer ( serverPeerA . deviceId , {
555+ dangerouslyAllowInsecureConnections : true ,
556+ } )
557+
558+ // Sync managers so that removal syncs
559+ await waitForSync ( projects , 'initial' )
560+
561+ const serverPeerInA = await findServerPeer ( managerAProject )
562+ assert (
563+ serverPeerInA ?. role . roleId === BLOCKED_ROLE_ID ,
564+ 'server in A is blocked'
565+ )
566+ const serverPeerInB = await findServerPeer ( managerBProject )
567+ assert (
568+ serverPeerInB ?. role . roleId === BLOCKED_ROLE_ID ,
569+ 'server in B is blocked'
570+ )
571+ } )
572+
506573/**
507574 * @typedef {object } LocalTestServer
508575 * @prop {'local' } type
0 commit comments