chore: designated survivor test suite (#2540)
* chore: designated survivor test suite * add more tests * fix: tests exclusivness * fix removing account from shared vaults test
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import * as Factory from '../lib/factory.js'
|
||||
import * as Utils from '../lib/Utils.js'
|
||||
import * as Collaboration from '../lib/Collaboration.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
@@ -167,4 +168,47 @@ describe('shared vault deletion', function () {
|
||||
|
||||
await deinitContactContext()
|
||||
})
|
||||
|
||||
it('should remove a user from all shared vaults upon account removal', async () => {
|
||||
const secondContext = await Factory.createVaultsContextWithRealCrypto()
|
||||
await secondContext.launch()
|
||||
await secondContext.register()
|
||||
|
||||
const thirdContext = await Factory.createVaultsContextWithRealCrypto()
|
||||
await thirdContext.launch()
|
||||
await thirdContext.register()
|
||||
|
||||
const firstVault = await Collaboration.createSharedVault(context)
|
||||
const secondVault = await Collaboration.createSharedVault(thirdContext)
|
||||
|
||||
const firstToSecondContact = await Collaboration.createTrustedContactForUserOfContext(context, secondContext)
|
||||
await Collaboration.createTrustedContactForUserOfContext(secondContext, context)
|
||||
|
||||
const thirdToSecondContact = await Collaboration.createTrustedContactForUserOfContext(thirdContext, secondContext)
|
||||
await Collaboration.createTrustedContactForUserOfContext(thirdContext, context)
|
||||
|
||||
await Collaboration.inviteContext(context, secondContext, firstVault, firstToSecondContact, SharedVaultUserPermission.PERMISSIONS.Write)
|
||||
await Collaboration.inviteContext(thirdContext, secondContext, secondVault, thirdToSecondContact, SharedVaultUserPermission.PERMISSIONS.Write)
|
||||
|
||||
const promise = secondContext.awaitNextSyncSharedVaultFromScratchEvent()
|
||||
|
||||
await Collaboration.acceptAllInvites(secondContext)
|
||||
|
||||
await Utils.awaitPromiseOrThrow(promise, 2.0, 'Waiting for vault to sync')
|
||||
|
||||
Factory.handlePasswordChallenges(secondContext.application, secondContext.password)
|
||||
await secondContext.application.user.deleteAccount()
|
||||
|
||||
await context.syncAndAwaitNotificationsProcessing()
|
||||
await thirdContext.syncAndAwaitNotificationsProcessing()
|
||||
|
||||
const sharedVaultUsersInFirstVault = await context.vaultUsers.getSharedVaultUsersFromServer(firstVault)
|
||||
expect(sharedVaultUsersInFirstVault.length).to.equal(1)
|
||||
|
||||
const sharedVaultUsersInSecondVault = await thirdContext.vaultUsers.getSharedVaultUsersFromServer(secondVault)
|
||||
expect(sharedVaultUsersInSecondVault.length).to.equal(1)
|
||||
|
||||
await secondContext.deinit()
|
||||
await thirdContext.deinit()
|
||||
})
|
||||
})
|
||||
|
||||
363
packages/snjs/mocha/vaults/surviving.test.js
Normal file
363
packages/snjs/mocha/vaults/surviving.test.js
Normal file
@@ -0,0 +1,363 @@
|
||||
import * as Factory from '../lib/factory.js'
|
||||
import * as Files from '../lib/Files.js'
|
||||
import * as Collaboration from '../lib/Collaboration.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('designated survival', function () {
|
||||
this.timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
let context
|
||||
let secondContext
|
||||
let thirdContext
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
context = await Factory.createVaultsContextWithRealCrypto()
|
||||
|
||||
await context.launch()
|
||||
await context.register()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
sinon.restore()
|
||||
context = undefined
|
||||
if (secondContext) {
|
||||
await secondContext.deinit()
|
||||
secondContext = undefined
|
||||
}
|
||||
if (thirdContext) {
|
||||
await thirdContext.deinit()
|
||||
thirdContext = undefined
|
||||
}
|
||||
})
|
||||
|
||||
it('should indicate that a vault has a designated survivor', async () => {
|
||||
const { sharedVault, contactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInvite(context)
|
||||
secondContext = contactContext
|
||||
|
||||
let vault = context.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
|
||||
expect(vault.sharing.designatedSurvivor).to.be.null
|
||||
|
||||
await Collaboration.designateSharedVaultSurvior(context, sharedVault, contactContext.userUuid)
|
||||
|
||||
await context.syncAndAwaitNotificationsProcessing()
|
||||
|
||||
vault = context.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
|
||||
expect(vault.sharing.designatedSurvivor).to.equal(contactContext.userUuid)
|
||||
})
|
||||
|
||||
describe('owner of a shared vault with a designated survivor removing the vault', () => {
|
||||
it('should not remove all users from the vault upon shared vault removal', async () => {
|
||||
const { sharedVault, contactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInvite(context)
|
||||
secondContext = contactContext
|
||||
|
||||
await Collaboration.designateSharedVaultSurvior(context, sharedVault, contactContext.userUuid)
|
||||
|
||||
const { thirdPartyContext } = await Collaboration.inviteNewPartyToSharedVault(
|
||||
context,
|
||||
sharedVault,
|
||||
)
|
||||
thirdContext = thirdPartyContext
|
||||
|
||||
await Collaboration.acceptAllInvites(thirdContext)
|
||||
|
||||
await context.sharedVaults.deleteSharedVault(sharedVault)
|
||||
|
||||
await context.syncAndAwaitNotificationsProcessing()
|
||||
await secondContext.syncAndAwaitNotificationsProcessing()
|
||||
await thirdContext.syncAndAwaitNotificationsProcessing()
|
||||
|
||||
const sharedVaultUsers = await secondContext.vaultUsers.getSharedVaultUsersFromServer(sharedVault)
|
||||
expect(sharedVaultUsers.length).to.equal(2)
|
||||
|
||||
expect(context.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })).to.be.undefined
|
||||
expect(context.keys.getPrimaryKeySystemRootKey(sharedVault.systemIdentifier)).to.be.undefined
|
||||
expect(context.keys.getKeySystemItemsKeys(sharedVault.systemIdentifier)).to.be.empty
|
||||
|
||||
expect(secondContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })).to.not.be.undefined
|
||||
expect(secondContext.keys.getPrimaryKeySystemRootKey(sharedVault.systemIdentifier)).to.not.be.undefined
|
||||
expect(secondContext.keys.getKeySystemItemsKeys(sharedVault.systemIdentifier)).to.not.be.empty
|
||||
|
||||
expect(thirdContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })).to.not.be.undefined
|
||||
expect(thirdContext.keys.getPrimaryKeySystemRootKey(sharedVault.systemIdentifier)).to.not.be.undefined
|
||||
expect(thirdContext.keys.getKeySystemItemsKeys(sharedVault.systemIdentifier)).to.not.be.empty
|
||||
})
|
||||
|
||||
it('should transition items of the owner in the vault to the designated survivor', async () => {
|
||||
const { sharedVault, contactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInvite(context)
|
||||
secondContext = contactContext
|
||||
|
||||
await Collaboration.designateSharedVaultSurvior(context, sharedVault, contactContext.userUuid)
|
||||
|
||||
const { thirdPartyContext } = await Collaboration.inviteNewPartyToSharedVault(
|
||||
context,
|
||||
sharedVault,
|
||||
)
|
||||
thirdContext = thirdPartyContext
|
||||
|
||||
await Collaboration.acceptAllInvites(thirdContext)
|
||||
|
||||
const note = await context.createSyncedNote('foo', 'bar')
|
||||
await Collaboration.moveItemToVault(context, sharedVault, note)
|
||||
|
||||
await context.sharedVaults.deleteSharedVault(sharedVault)
|
||||
|
||||
await secondContext.syncAndAwaitNotificationsProcessing()
|
||||
await thirdContext.syncAndAwaitNotificationsProcessing()
|
||||
|
||||
const sharedVaultUsers = await secondContext.vaultUsers.getSharedVaultUsersFromServer(sharedVault)
|
||||
expect(sharedVaultUsers.length).to.equal(2)
|
||||
|
||||
const contactNote = secondContext.items.findItem(note.uuid)
|
||||
expect(contactNote.key_system_identifier).to.equal(sharedVault.systemIdentifier)
|
||||
expect(contactNote.user_uuid).to.equal(secondContext.userUuid)
|
||||
|
||||
const thirdPartyNote = thirdContext.items.findItem(note.uuid)
|
||||
expect(thirdPartyNote.key_system_identifier).to.equal(sharedVault.systemIdentifier)
|
||||
expect(thirdPartyNote.user_uuid).to.equal(secondContext.userUuid)
|
||||
})
|
||||
|
||||
it('should still allow to download files of the owner in the vault', async () => {
|
||||
await context.activatePaidSubscriptionForUser()
|
||||
|
||||
const { sharedVault, contactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInvite(context)
|
||||
secondContext = contactContext
|
||||
|
||||
await Collaboration.designateSharedVaultSurvior(context, sharedVault, contactContext.userUuid)
|
||||
|
||||
const { thirdPartyContext } = await Collaboration.inviteNewPartyToSharedVault(
|
||||
context,
|
||||
sharedVault,
|
||||
)
|
||||
thirdContext = thirdPartyContext
|
||||
|
||||
await Collaboration.acceptAllInvites(thirdContext)
|
||||
|
||||
const response = await fetch('/mocha/assets/small_file.md')
|
||||
const buffer = new Uint8Array(await response.arrayBuffer())
|
||||
const uploadedFile = await Files.uploadFile(context.files, buffer, 'my-file', 'md', 1000, sharedVault)
|
||||
|
||||
await secondContext.syncAndAwaitNotificationsProcessing()
|
||||
|
||||
const sharedFileBefore = secondContext.items.findItem(uploadedFile.uuid)
|
||||
expect(sharedFileBefore).to.not.be.undefined
|
||||
expect(sharedFileBefore.remoteIdentifier).to.equal(uploadedFile.remoteIdentifier)
|
||||
|
||||
await context.sharedVaults.deleteSharedVault(sharedVault)
|
||||
|
||||
await secondContext.syncAndAwaitNotificationsProcessing()
|
||||
|
||||
const sharedFileAfter = secondContext.items.findItem(uploadedFile.uuid)
|
||||
expect(sharedFileAfter).to.not.be.undefined
|
||||
expect(sharedFileAfter.remoteIdentifier).to.equal(uploadedFile.remoteIdentifier)
|
||||
|
||||
const downloadedBytes = await Files.downloadFile(secondContext.files, sharedFileAfter)
|
||||
expect(downloadedBytes).to.eql(buffer)
|
||||
})
|
||||
|
||||
it('should transition vault ownership to the designated survivor', async () => {
|
||||
const { sharedVault, contactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInvite(context)
|
||||
secondContext = contactContext
|
||||
|
||||
await Collaboration.designateSharedVaultSurvior(context, sharedVault, contactContext.userUuid)
|
||||
|
||||
const { thirdPartyContext } = await Collaboration.inviteNewPartyToSharedVault(
|
||||
context,
|
||||
sharedVault,
|
||||
)
|
||||
thirdContext = thirdPartyContext
|
||||
|
||||
await Collaboration.acceptAllInvites(thirdContext)
|
||||
|
||||
await context.sharedVaults.deleteSharedVault(sharedVault)
|
||||
|
||||
await secondContext.syncAndAwaitNotificationsProcessing()
|
||||
await thirdContext.syncAndAwaitNotificationsProcessing()
|
||||
|
||||
const contactVault = secondContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
|
||||
expect(contactVault.sharing.ownerUserUuid).to.not.equal(context.userUuid)
|
||||
expect(contactVault.sharing.ownerUserUuid).to.equal(secondContext.userUuid)
|
||||
|
||||
const thirdPartyVault = thirdContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
|
||||
expect(thirdPartyVault.sharing.ownerUserUuid).to.not.equal(context.userUuid)
|
||||
expect(thirdPartyVault.sharing.ownerUserUuid).to.equal(secondContext.userUuid)
|
||||
})
|
||||
})
|
||||
|
||||
describe('owner of a shared vault with a designated survivor deleting their account', () => {
|
||||
it('should not remove all users from the vault upon account removal', async () => {
|
||||
const { sharedVault, contactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInvite(context)
|
||||
secondContext = contactContext
|
||||
|
||||
await Collaboration.designateSharedVaultSurvior(context, sharedVault, contactContext.userUuid)
|
||||
|
||||
const { thirdPartyContext } = await Collaboration.inviteNewPartyToSharedVault(
|
||||
context,
|
||||
sharedVault,
|
||||
)
|
||||
thirdContext = thirdPartyContext
|
||||
|
||||
await Collaboration.acceptAllInvites(thirdContext)
|
||||
|
||||
Factory.handlePasswordChallenges(context.application, context.password)
|
||||
await context.application.user.deleteAccount()
|
||||
|
||||
await secondContext.syncAndAwaitNotificationsProcessing()
|
||||
await thirdContext.syncAndAwaitNotificationsProcessing()
|
||||
|
||||
const sharedVaultUsers = await secondContext.vaultUsers.getSharedVaultUsersFromServer(sharedVault)
|
||||
expect(sharedVaultUsers.length).to.equal(2)
|
||||
|
||||
expect(secondContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })).to.not.be.undefined
|
||||
expect(secondContext.keys.getPrimaryKeySystemRootKey(sharedVault.systemIdentifier)).to.not.be.undefined
|
||||
expect(secondContext.keys.getKeySystemItemsKeys(sharedVault.systemIdentifier)).to.not.be.empty
|
||||
|
||||
expect(thirdContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })).to.not.be.undefined
|
||||
expect(thirdContext.keys.getPrimaryKeySystemRootKey(sharedVault.systemIdentifier)).to.not.be.undefined
|
||||
expect(thirdContext.keys.getKeySystemItemsKeys(sharedVault.systemIdentifier)).to.not.be.empty
|
||||
})
|
||||
|
||||
it('should transition items of the owner in the vault to the designated survivor', async () => {
|
||||
const { sharedVault, contactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInvite(context)
|
||||
secondContext = contactContext
|
||||
|
||||
await Collaboration.designateSharedVaultSurvior(context, sharedVault, contactContext.userUuid)
|
||||
|
||||
const { thirdPartyContext } = await Collaboration.inviteNewPartyToSharedVault(
|
||||
context,
|
||||
sharedVault,
|
||||
)
|
||||
thirdContext = thirdPartyContext
|
||||
|
||||
await Collaboration.acceptAllInvites(thirdContext)
|
||||
|
||||
const note = await context.createSyncedNote('foo', 'bar')
|
||||
await Collaboration.moveItemToVault(context, sharedVault, note)
|
||||
|
||||
Factory.handlePasswordChallenges(context.application, context.password)
|
||||
await context.application.user.deleteAccount()
|
||||
|
||||
await secondContext.syncAndAwaitNotificationsProcessing()
|
||||
await thirdContext.syncAndAwaitNotificationsProcessing()
|
||||
|
||||
const sharedVaultUsers = await secondContext.vaultUsers.getSharedVaultUsersFromServer(sharedVault)
|
||||
expect(sharedVaultUsers.length).to.equal(2)
|
||||
|
||||
const contactNote = secondContext.items.findItem(note.uuid)
|
||||
expect(contactNote.key_system_identifier).to.equal(sharedVault.systemIdentifier)
|
||||
expect(contactNote.user_uuid).to.equal(secondContext.userUuid)
|
||||
|
||||
const thirdPartyNote = thirdContext.items.findItem(note.uuid)
|
||||
expect(thirdPartyNote.key_system_identifier).to.equal(sharedVault.systemIdentifier)
|
||||
expect(thirdPartyNote.user_uuid).to.equal(secondContext.userUuid)
|
||||
})
|
||||
|
||||
it('should still allow to download files of the owner in the vault', async () => {
|
||||
await context.activatePaidSubscriptionForUser()
|
||||
|
||||
const { sharedVault, contactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInvite(context)
|
||||
secondContext = contactContext
|
||||
|
||||
await Collaboration.designateSharedVaultSurvior(context, sharedVault, contactContext.userUuid)
|
||||
|
||||
const { thirdPartyContext } = await Collaboration.inviteNewPartyToSharedVault(
|
||||
context,
|
||||
sharedVault,
|
||||
)
|
||||
thirdContext = thirdPartyContext
|
||||
|
||||
await Collaboration.acceptAllInvites(thirdContext)
|
||||
|
||||
const response = await fetch('/mocha/assets/small_file.md')
|
||||
const buffer = new Uint8Array(await response.arrayBuffer())
|
||||
const uploadedFile = await Files.uploadFile(context.files, buffer, 'my-file', 'md', 1000, sharedVault)
|
||||
|
||||
await secondContext.syncAndAwaitNotificationsProcessing()
|
||||
|
||||
const sharedFileBefore = secondContext.items.findItem(uploadedFile.uuid)
|
||||
expect(sharedFileBefore).to.not.be.undefined
|
||||
expect(sharedFileBefore.remoteIdentifier).to.equal(uploadedFile.remoteIdentifier)
|
||||
|
||||
Factory.handlePasswordChallenges(context.application, context.password)
|
||||
await context.application.user.deleteAccount()
|
||||
|
||||
await secondContext.syncAndAwaitNotificationsProcessing()
|
||||
|
||||
const sharedFileAfter = secondContext.items.findItem(uploadedFile.uuid)
|
||||
expect(sharedFileAfter).to.not.be.undefined
|
||||
expect(sharedFileAfter.remoteIdentifier).to.equal(uploadedFile.remoteIdentifier)
|
||||
|
||||
const downloadedBytes = await Files.downloadFile(secondContext.files, sharedFileAfter)
|
||||
expect(downloadedBytes).to.eql(buffer)
|
||||
})
|
||||
|
||||
it('should transition vault ownership to the designated survivor', async () => {
|
||||
const { sharedVault, contactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInvite(context)
|
||||
secondContext = contactContext
|
||||
|
||||
await Collaboration.designateSharedVaultSurvior(context, sharedVault, contactContext.userUuid)
|
||||
|
||||
const { thirdPartyContext } = await Collaboration.inviteNewPartyToSharedVault(
|
||||
context,
|
||||
sharedVault,
|
||||
)
|
||||
thirdContext = thirdPartyContext
|
||||
|
||||
await Collaboration.acceptAllInvites(thirdContext)
|
||||
|
||||
Factory.handlePasswordChallenges(context.application, context.password)
|
||||
await context.application.user.deleteAccount()
|
||||
|
||||
await secondContext.syncAndAwaitNotificationsProcessing()
|
||||
await thirdContext.syncAndAwaitNotificationsProcessing()
|
||||
|
||||
const contactVault = secondContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
|
||||
expect(contactVault.sharing.ownerUserUuid).to.equal(secondContext.userUuid)
|
||||
|
||||
const thirdPartyVault = thirdContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
|
||||
expect(thirdPartyVault.sharing.ownerUserUuid).to.equal(secondContext.userUuid)
|
||||
})
|
||||
})
|
||||
|
||||
describe('owner of a shared vault without a designated survivor deleting their account', () => {
|
||||
it('should remove all users from all shared vaults upon account removal', async () => {
|
||||
await context.activatePaidSubscriptionForUser()
|
||||
|
||||
const { sharedVault, contactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInvite(context)
|
||||
secondContext = contactContext
|
||||
|
||||
const result = await Collaboration.createSharedVaultWithAcceptedInvite(context)
|
||||
thirdContext = result.contactContext
|
||||
const secondSharedVault = result.sharedVault
|
||||
|
||||
Factory.handlePasswordChallenges(context.application, context.password)
|
||||
await context.application.user.deleteAccount()
|
||||
|
||||
await secondContext.syncAndAwaitNotificationsProcessing()
|
||||
await thirdContext.syncAndAwaitNotificationsProcessing()
|
||||
|
||||
expect(secondContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })).to.be.undefined
|
||||
expect(secondContext.keys.getPrimaryKeySystemRootKey(sharedVault.systemIdentifier)).to.be.undefined
|
||||
expect(secondContext.keys.getKeySystemItemsKeys(sharedVault.systemIdentifier)).to.be.empty
|
||||
|
||||
expect(thirdContext.vaults.getVault({ keySystemIdentifier: secondSharedVault.systemIdentifier })).to.be.undefined
|
||||
expect(thirdContext.keys.getPrimaryKeySystemRootKey(secondSharedVault.systemIdentifier)).to.be.undefined
|
||||
expect(thirdContext.keys.getKeySystemItemsKeys(secondSharedVault.systemIdentifier)).to.be.empty
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user