chore: vault tests refactors and lint (#2374)
This commit is contained in:
@@ -112,7 +112,7 @@ describe('application instances', () => {
|
||||
await app.lock()
|
||||
})
|
||||
|
||||
describe.skip('signOut()', () => {
|
||||
describe('signOut()', () => {
|
||||
let testNote1
|
||||
let confirmAlert
|
||||
let deinit
|
||||
@@ -129,7 +129,7 @@ describe('application instances', () => {
|
||||
beforeEach(async () => {
|
||||
testSNApp = await Factory.createAndInitializeApplication('test-application')
|
||||
testNote1 = await Factory.createMappedNote(testSNApp, 'Note 1', 'This is a test note!', false)
|
||||
confirmAlert = sinon.spy(testSNApp.alertService, 'confirm')
|
||||
confirmAlert = sinon.spy(testSNApp.alerts, 'confirm')
|
||||
deinit = sinon.spy(testSNApp, 'deinit')
|
||||
})
|
||||
|
||||
@@ -164,7 +164,7 @@ describe('application instances', () => {
|
||||
|
||||
it('cancels sign out if confirmation dialog is rejected', async () => {
|
||||
confirmAlert.restore()
|
||||
confirmAlert = sinon.stub(testSNApp.alertService, 'confirm').callsFake((_message) => false)
|
||||
confirmAlert = sinon.stub(testSNApp.alerts, 'confirm').callsFake((_message) => false)
|
||||
|
||||
await testSNApp.mutator.setItemDirty(testNote1)
|
||||
await testSNApp.user.signOut()
|
||||
|
||||
@@ -207,7 +207,6 @@ describe('basic auth', function () {
|
||||
await specContext.launch()
|
||||
await specContext.register()
|
||||
await specContext.signout()
|
||||
await specContext.deinit()
|
||||
|
||||
specContext = await Factory.createAppContextWithFakeCrypto(Math.random(), uppercase, password)
|
||||
|
||||
@@ -217,6 +216,7 @@ describe('basic auth', function () {
|
||||
expect(response).to.be.ok
|
||||
expect(response.data.error).to.not.be.ok
|
||||
expect(await specContext.application.encryption.getRootKey()).to.be.ok
|
||||
await specContext.deinit()
|
||||
}).timeout(20000)
|
||||
|
||||
it('can sign into account regardless of whitespace', async function () {
|
||||
@@ -232,7 +232,6 @@ describe('basic auth', function () {
|
||||
await specContext.launch()
|
||||
await specContext.register()
|
||||
await specContext.signout()
|
||||
await specContext.deinit()
|
||||
|
||||
specContext = await Factory.createAppContextWithFakeCrypto(Math.random(), withspace, password)
|
||||
await specContext.launch()
|
||||
@@ -241,6 +240,7 @@ describe('basic auth', function () {
|
||||
expect(response).to.be.ok
|
||||
expect(response.data.error).to.not.be.ok
|
||||
expect(await specContext.application.encryption.getRootKey()).to.be.ok
|
||||
await specContext.deinit()
|
||||
}).timeout(20000)
|
||||
|
||||
it('fails login with wrong password', async function () {
|
||||
@@ -367,7 +367,7 @@ describe('basic auth', function () {
|
||||
|
||||
it('successfully changes password', changePassword).timeout(40000)
|
||||
|
||||
it.skip('successfully changes password when passcode is set', async function () {
|
||||
it('successfully changes password when passcode is set', async function () {
|
||||
const passcode = 'passcode'
|
||||
const promptValueReply = (prompts) => {
|
||||
const values = []
|
||||
@@ -393,7 +393,7 @@ describe('basic auth', function () {
|
||||
context.application.submitValuesForChallenge(challenge, initialValues)
|
||||
},
|
||||
})
|
||||
await context.application.setPasscode(passcode)
|
||||
await context.application.addPasscode(passcode)
|
||||
await changePassword.bind(this)()
|
||||
}).timeout(20000)
|
||||
|
||||
@@ -550,6 +550,8 @@ describe('basic auth', function () {
|
||||
|
||||
expect(response.status).to.equal(401)
|
||||
expect(response.data.error.message).to.equal('Operation not allowed.')
|
||||
|
||||
await secondContext.deinit()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -81,7 +81,6 @@ describe('backups', function () {
|
||||
})
|
||||
|
||||
it('passcode + account backup file should have correct number of items', async function () {
|
||||
this.timeout(10000)
|
||||
const passcode = 'passcode'
|
||||
await this.application.register(this.email, this.password)
|
||||
Factory.handlePasswordChallenges(this.application, this.password)
|
||||
@@ -101,7 +100,7 @@ describe('backups', function () {
|
||||
// Encrypted backup with authorization
|
||||
const authorizedEncryptedData = await this.application.createEncryptedBackupFileForAutomatedDesktopBackups()
|
||||
expect(authorizedEncryptedData.items.length).to.equal(BaseItemCounts.DefaultItemsWithAccount + 2)
|
||||
})
|
||||
}).timeout(10000)
|
||||
|
||||
it('backup file item should have correct fields', async function () {
|
||||
await Factory.createSyncedNote(this.application)
|
||||
|
||||
@@ -133,11 +133,12 @@ describe('features', () => {
|
||||
await promise
|
||||
})
|
||||
|
||||
it.skip('migrated ext repo should have property indicating it was migrated', async () => {
|
||||
it('migrated ext repo should have property indicating it was migrated', async () => {
|
||||
sinon.stub(application.legacyApi, 'isThirdPartyHostUsed').callsFake(() => {
|
||||
return false
|
||||
})
|
||||
expect(await application.settings.getDoesSensitiveSettingExist(SettingName.ExtensionKey)).to.equal(false)
|
||||
const setting = SettingName.create(SettingName.NAMES.ExtensionKey).getValue()
|
||||
expect(await application.settings.getDoesSensitiveSettingExist(setting)).to.equal(false)
|
||||
const extensionKey = UuidGenerator.GenerateUuid().split('-').join('')
|
||||
const promise = new Promise((resolve) => {
|
||||
application.streamItems(ContentType.TYPES.ExtensionRepo, ({ changed }) => {
|
||||
|
||||
@@ -404,7 +404,7 @@ describe('history manager', () => {
|
||||
|
||||
expect(noteHistory.length).to.equal(expectedRevisions)
|
||||
expect(dupeHistory.length).to.equal(expectedRevisions + 1)
|
||||
})
|
||||
}).timeout(Factory.ThirtySecondTimeout)
|
||||
|
||||
it('can decrypt revisions for duplicate_of items', async function () {
|
||||
const note = await Factory.createSyncedNote(this.application)
|
||||
|
||||
@@ -449,6 +449,7 @@ describe('keys', function () {
|
||||
},
|
||||
})
|
||||
expect(payload.items_key_id).to.equal(newDefaultItemsKey.uuid)
|
||||
await Factory.safeDeinit(application)
|
||||
})
|
||||
|
||||
it('compares root keys', async function () {
|
||||
@@ -642,7 +643,7 @@ describe('keys', function () {
|
||||
await contextB.deinit()
|
||||
})
|
||||
|
||||
describe('changing password on 003 client while signed into 004 client', function () {
|
||||
describe('changing password on 003 account while signed into 004 client', function () {
|
||||
/**
|
||||
* When an 004 client signs into 003 account, it creates a root key based items key.
|
||||
* Then, if the 003 client changes its account password, and the 004 client
|
||||
@@ -859,5 +860,6 @@ describe('keys', function () {
|
||||
const notePayload = rawPayloads.find((p) => p.content_type === ContentType.TYPES.Note)
|
||||
|
||||
expect(notePayload.items_key_id).to.equal(itemsKey.uuid)
|
||||
await otherClient.deinit()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -47,6 +47,30 @@ export class AppContext {
|
||||
this.host,
|
||||
this.crypto || new FakeWebCrypto(),
|
||||
)
|
||||
|
||||
this.application.dependencies.get(TYPES.Logger).setLevel('error')
|
||||
|
||||
this.disableSubscriptionFetching()
|
||||
}
|
||||
|
||||
disableSubscriptionFetching() {
|
||||
this.application.subscriptions.fetchAvailableSubscriptions = () => {}
|
||||
}
|
||||
|
||||
async launch({ awaitDatabaseLoad = true, receiveChallenge } = { awaitDatabaseLoad: true }) {
|
||||
await this.application.prepareForLaunch({
|
||||
receiveChallenge: receiveChallenge || this.handleChallenge,
|
||||
})
|
||||
|
||||
await this.application.launch(awaitDatabaseLoad)
|
||||
|
||||
await this.awaitUserPrefsSingletonCreation()
|
||||
|
||||
this.application.http.loggingEnabled = true
|
||||
}
|
||||
|
||||
async deinit() {
|
||||
await Utils.safeDeinit(this.application)
|
||||
}
|
||||
|
||||
get sessions() {
|
||||
@@ -253,10 +277,10 @@ export class AppContext {
|
||||
|
||||
awaitNextSucessfulSync() {
|
||||
return new Promise((resolve) => {
|
||||
const removeObserver = this.application.sync.addEventObserver((event) => {
|
||||
const removeObserver = this.application.sync.addEventObserver((event, data) => {
|
||||
if (event === SyncEvent.SyncCompletedWithAllItemsUploadedAndDownloaded) {
|
||||
removeObserver()
|
||||
resolve()
|
||||
resolve(data)
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -294,6 +318,16 @@ export class AppContext {
|
||||
})
|
||||
}
|
||||
|
||||
resolveWithSyncRetrievedPayloads() {
|
||||
return new Promise((resolve) => {
|
||||
this.application.sync.addEventObserver((event, data) => {
|
||||
if (event === SyncEvent.PaginatedSyncRequestCompleted) {
|
||||
resolve(data.retrievedPayloads)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
resolveWithConflicts() {
|
||||
return new Promise((resolve) => {
|
||||
this.application.sync.addEventObserver((event, response) => {
|
||||
@@ -359,10 +393,16 @@ export class AppContext {
|
||||
}
|
||||
|
||||
resolveWhenAsyncFunctionCompletes(object, functionName) {
|
||||
if (!object[functionName]) {
|
||||
throw new Error(`Object does not have function ${functionName}`)
|
||||
}
|
||||
|
||||
const originalFunction = object[functionName].bind(object)
|
||||
|
||||
return new Promise((resolve) => {
|
||||
sinon.stub(object, functionName).callsFake(async (params) => {
|
||||
object[functionName].restore()
|
||||
const result = await object[functionName](params)
|
||||
const result = await originalFunction(params)
|
||||
resolve()
|
||||
return result
|
||||
})
|
||||
@@ -370,23 +410,26 @@ export class AppContext {
|
||||
}
|
||||
|
||||
spyOnFunctionResult(object, functionName) {
|
||||
return new Promise((resolve) => {
|
||||
sinon.stub(object, functionName).callsFake(async (params) => {
|
||||
object[functionName].restore()
|
||||
const result = await object[functionName](params)
|
||||
resolve(result)
|
||||
return result
|
||||
})
|
||||
const originalFunction = object[functionName].bind(object)
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
sinon.stub(object, functionName).callsFake(async (params) => {
|
||||
const result = await originalFunction(params)
|
||||
object[functionName].restore()
|
||||
setTimeout(() => {
|
||||
resolve(result)
|
||||
}, 0)
|
||||
return result
|
||||
})
|
||||
} catch (err) {
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
resolveWhenAsymmetricMessageProcessingCompletes() {
|
||||
return this.resolveWhenAsyncFunctionCompletes(this.asymmetric, 'handleRemoteReceivedAsymmetricMessages')
|
||||
}
|
||||
|
||||
resolveWhenUserMessagesProcessingCompletes() {
|
||||
const objectToSpy = this.application.dependencies.get(TYPES.UserEventService)
|
||||
return this.resolveWhenAsyncFunctionCompletes(objectToSpy, 'handleReceivedUserEvents')
|
||||
const objectToSpy = this.application.dependencies.get(TYPES.NotificationService)
|
||||
return this.resolveWhenAsyncFunctionCompletes(objectToSpy, 'handleReceivedNotifications')
|
||||
}
|
||||
|
||||
resolveWhenAllInboundAsymmetricMessagesAreDeleted() {
|
||||
@@ -466,18 +509,6 @@ export class AppContext {
|
||||
})
|
||||
}
|
||||
|
||||
async launch({ awaitDatabaseLoad = true, receiveChallenge } = { awaitDatabaseLoad: true }) {
|
||||
await this.application.prepareForLaunch({
|
||||
receiveChallenge: receiveChallenge || this.handleChallenge,
|
||||
})
|
||||
await this.application.launch(awaitDatabaseLoad)
|
||||
await this.awaitUserPrefsSingletonCreation()
|
||||
}
|
||||
|
||||
async deinit() {
|
||||
await Utils.safeDeinit(this.application)
|
||||
}
|
||||
|
||||
async sync(options) {
|
||||
await this.application.sync.sync(options || { awaitAll: true })
|
||||
}
|
||||
@@ -628,6 +659,10 @@ export class AppContext {
|
||||
console.warn('Anticipating a console error with message:', message)
|
||||
}
|
||||
|
||||
awaitPromiseOrThrow(promise, maxWait = 2.0, reason = 'Awaiting promise timed out; No description provided') {
|
||||
return Utils.awaitPromiseOrThrow(promise, maxWait, reason)
|
||||
}
|
||||
|
||||
async activatePaidSubscriptionForUser(options = {}) {
|
||||
const dateInAnHour = new Date()
|
||||
dateInAnHour.setHours(dateInAnHour.getHours() + 1)
|
||||
@@ -655,17 +690,17 @@ export class AppContext {
|
||||
await Utils.sleep(2)
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
`Mock events service not available. You are probalby running a test suite for home server: ${error.message}`,
|
||||
`Mock events service not available. You are probably running a test suite for home server: ${error.message}`,
|
||||
)
|
||||
}
|
||||
|
||||
try {
|
||||
await HomeServer.activatePremiumFeatures(this.email, options.subscriptionPlanName, options.expiresAt)
|
||||
|
||||
await Utils.sleep(1)
|
||||
await Utils.sleep(1, 'Waiting for premium features to be activated')
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
`Home server not available. You are probalby running a test suite for self hosted setup: ${error.message}`,
|
||||
`Home server not available. You are probably running a test suite for self hosted setup: ${error.message}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import * as Factory from './factory.js'
|
||||
import * as Utils from './Utils.js'
|
||||
|
||||
export const createContactContext = async () => {
|
||||
const contactContext = await Factory.createAppContextWithRealCrypto()
|
||||
const contactContext = await Factory.createVaultsContextWithRealCrypto()
|
||||
await contactContext.launch()
|
||||
await contactContext.register()
|
||||
|
||||
@@ -15,6 +16,8 @@ export const createTrustedContactForUserOfContext = async (
|
||||
contextAddingNewContact,
|
||||
contextImportingContactInfoFrom,
|
||||
) => {
|
||||
const syncPromisme = contextAddingNewContact.awaitNextSucessfulSync()
|
||||
|
||||
const contact = await contextAddingNewContact.contacts.createOrEditTrustedContact({
|
||||
name: 'John Doe',
|
||||
publicKey: contextImportingContactInfoFrom.publicKey,
|
||||
@@ -22,6 +25,8 @@ export const createTrustedContactForUserOfContext = async (
|
||||
contactUuid: contextImportingContactInfoFrom.userUuid,
|
||||
})
|
||||
|
||||
await syncPromisme
|
||||
|
||||
return contact
|
||||
}
|
||||
|
||||
@@ -36,7 +41,35 @@ export const acceptAllInvites = async (context) => {
|
||||
}
|
||||
}
|
||||
|
||||
export const createSharedVaultWithAcceptedInvite = async (context, permission = SharedVaultUserPermission.PERMISSIONS.Write) => {
|
||||
const inviteContext = async (context, contactContext, sharedVault, contact, permission) => {
|
||||
contactContext.lockSyncing()
|
||||
|
||||
const inviteOrError = await context.vaultInvites.inviteContactToSharedVault(sharedVault, contact, permission)
|
||||
if (inviteOrError.isFailed()) {
|
||||
throw new Error(inviteOrError.getError())
|
||||
}
|
||||
|
||||
const invite = inviteOrError.getValue()
|
||||
|
||||
const promise = contactContext.resolveWhenAsyncFunctionCompletes(contactContext.vaultInvites, 'processInboundInvites')
|
||||
|
||||
contactContext.unlockSyncing()
|
||||
await contactContext.sync()
|
||||
|
||||
await Utils.awaitPromiseOrThrow(promise, 2.0, '[inviteContext] processInboundInvites was not called in time')
|
||||
|
||||
const inviteRecords = contactContext.vaultInvites.getCachedPendingInviteRecords()
|
||||
if (inviteRecords.length === 0) {
|
||||
throw new Error('Invite was not properly received')
|
||||
}
|
||||
|
||||
return invite
|
||||
}
|
||||
|
||||
export const createSharedVaultWithAcceptedInvite = async (
|
||||
context,
|
||||
permission = SharedVaultUserPermission.PERMISSIONS.Write,
|
||||
) => {
|
||||
const { sharedVault, contact, contactContext, deinitContactContext } =
|
||||
await createSharedVaultWithUnacceptedButTrustedInvite(context, permission)
|
||||
|
||||
@@ -44,7 +77,7 @@ export const createSharedVaultWithAcceptedInvite = async (context, permission =
|
||||
|
||||
await acceptAllInvites(contactContext)
|
||||
|
||||
await promise
|
||||
await Utils.awaitPromiseOrThrow(promise, 2.0, 'Waiting for vault to sync')
|
||||
|
||||
const contactVault = contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
|
||||
|
||||
@@ -61,7 +94,10 @@ export const createSharedVaultWithAcceptedInviteAndNote = async (
|
||||
)
|
||||
const note = await context.createSyncedNote('foo', 'bar')
|
||||
const updatedNote = await moveItemToVault(context, sharedVault, note)
|
||||
|
||||
const promise = contactContext.awaitNextSucessfulSync()
|
||||
await contactContext.sync()
|
||||
await Utils.awaitPromiseOrThrow(promise, 2.0, 'Waiting for contactContext to sync added note')
|
||||
|
||||
return { sharedVault, note: updatedNote, contact, contactContext, deinitContactContext }
|
||||
}
|
||||
@@ -76,34 +112,20 @@ export const createSharedVaultWithUnacceptedButTrustedInvite = async (
|
||||
const contact = await createTrustedContactForUserOfContext(context, contactContext)
|
||||
await createTrustedContactForUserOfContext(contactContext, context)
|
||||
|
||||
const inviteOrError = await context.vaultInvites.inviteContactToSharedVault(sharedVault, contact, permission)
|
||||
if (inviteOrError.isFailed()) {
|
||||
throw new Error(inviteOrError.getError())
|
||||
}
|
||||
const invite = inviteOrError.getValue()
|
||||
|
||||
await contactContext.sync()
|
||||
const invite = await inviteContext(context, contactContext, sharedVault, contact, permission)
|
||||
|
||||
return { sharedVault, contact, contactContext, deinitContactContext, invite }
|
||||
}
|
||||
|
||||
export const createSharedVaultAndInviteContact = async (
|
||||
createInContext,
|
||||
inviteContext,
|
||||
inviteContact,
|
||||
context,
|
||||
contactContext,
|
||||
contact,
|
||||
permission = SharedVaultUserPermission.PERMISSIONS.Write,
|
||||
) => {
|
||||
const sharedVault = await createSharedVault(createInContext)
|
||||
const sharedVault = await createSharedVault(context)
|
||||
|
||||
await createInContext.vaultInvites.inviteContactToSharedVault(sharedVault, inviteContact, permission)
|
||||
|
||||
const promise = inviteContext.awaitNextSyncSharedVaultFromScratchEvent()
|
||||
|
||||
await inviteContext.sync()
|
||||
|
||||
await acceptAllInvites(inviteContext)
|
||||
|
||||
await promise
|
||||
await inviteContext(context, contactContext, sharedVault, contact, permission)
|
||||
|
||||
return { sharedVault }
|
||||
}
|
||||
@@ -118,20 +140,33 @@ export const createSharedVaultWithUnacceptedAndUntrustedInvite = async (
|
||||
const contact = await createTrustedContactForUserOfContext(context, contactContext)
|
||||
|
||||
const invite = (await context.vaultInvites.inviteContactToSharedVault(sharedVault, contact, permission)).getValue()
|
||||
|
||||
const promise = contactContext.resolveWhenAsyncFunctionCompletes(contactContext.vaultInvites, 'processInboundInvites')
|
||||
|
||||
await contactContext.sync()
|
||||
|
||||
await Utils.awaitPromiseOrThrow(
|
||||
promise,
|
||||
2.0,
|
||||
'[createSharedVaultWithUnacceptedAndUntrustedInvite] Waiting to process invites',
|
||||
)
|
||||
|
||||
return { sharedVault, contact, contactContext, deinitContactContext, invite }
|
||||
}
|
||||
|
||||
export const inviteNewPartyToSharedVault = async (context, sharedVault, permission = SharedVaultUserPermission.PERMISSIONS.Write) => {
|
||||
export const inviteNewPartyToSharedVault = async (
|
||||
context,
|
||||
sharedVault,
|
||||
permission = SharedVaultUserPermission.PERMISSIONS.Write,
|
||||
) => {
|
||||
const { contactContext: thirdPartyContext, deinitContactContext: deinitThirdPartyContext } =
|
||||
await createContactContext()
|
||||
|
||||
const thirdPartyContact = await createTrustedContactForUserOfContext(context, thirdPartyContext)
|
||||
await createTrustedContactForUserOfContext(thirdPartyContext, context)
|
||||
await context.vaultInvites.inviteContactToSharedVault(sharedVault, thirdPartyContact, permission)
|
||||
|
||||
await thirdPartyContext.sync()
|
||||
await createTrustedContactForUserOfContext(thirdPartyContext, context)
|
||||
|
||||
await inviteContext(context, thirdPartyContext, sharedVault, thirdPartyContact, permission)
|
||||
|
||||
return { thirdPartyContext, thirdPartyContact, deinitThirdPartyContext }
|
||||
}
|
||||
|
||||
@@ -11,15 +11,15 @@ export async function safeDeinit(application) {
|
||||
await application.storage.awaitPersist()
|
||||
|
||||
/** Limit waiting to 1s */
|
||||
await Promise.race([sleep(1), application.sync?.awaitCurrentSyncs()])
|
||||
await Promise.race([sleep(1, 'Deinit'), application.sync?.awaitCurrentSyncs()])
|
||||
|
||||
await application.prepareForDeinit()
|
||||
|
||||
application.deinit(DeinitMode.Soft, DeinitSource.SignOut)
|
||||
}
|
||||
|
||||
export async function sleep(seconds) {
|
||||
console.warn(`Test sleeping for ${seconds}s`)
|
||||
export async function sleep(seconds, reason) {
|
||||
console.warn(`Test sleeping for ${seconds}s. Reason: ${reason}`)
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(function () {
|
||||
@@ -32,3 +32,22 @@ export function generateUuid() {
|
||||
const crypto = new FakeWebCrypto()
|
||||
return crypto.generateUUID()
|
||||
}
|
||||
|
||||
export async function awaitPromiseOrThrow(promise, maxWait, reason) {
|
||||
let timer = undefined
|
||||
|
||||
// Create a promise that rejects in <maxWait> milliseconds
|
||||
const timeout = new Promise((resolve, reject) => {
|
||||
timer = setTimeout(() => {
|
||||
clearTimeout(timer)
|
||||
console.error(reason)
|
||||
reject(new Error(reason || `Promise timed out after ${maxWait} milliseconds: ${reason}`))
|
||||
}, maxWait * 1000)
|
||||
})
|
||||
|
||||
// Returns a race between our timeout and the passed in promise
|
||||
return Promise.race([promise, timeout]).then((result) => {
|
||||
clearTimeout(timer)
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
54
packages/snjs/mocha/lib/VaultsContext.js
Normal file
54
packages/snjs/mocha/lib/VaultsContext.js
Normal file
@@ -0,0 +1,54 @@
|
||||
import { AppContext } from './AppContext.js'
|
||||
|
||||
export class VaultsContext extends AppContext {
|
||||
constructor(params) {
|
||||
super(params)
|
||||
}
|
||||
|
||||
async changeVaultName(vault, nameAndDesc) {
|
||||
const sendDataChangePromise = this.resolveWhenAsyncFunctionCompletes(
|
||||
this.sharedVaults._sendVaultDataChangeMessage,
|
||||
'execute',
|
||||
)
|
||||
|
||||
await this.vaults.changeVaultNameAndDescription(vault, {
|
||||
name: nameAndDesc.name,
|
||||
description: nameAndDesc.description,
|
||||
})
|
||||
|
||||
await this.awaitPromiseOrThrow(sendDataChangePromise, undefined, 'Waiting for vault data change message to process')
|
||||
}
|
||||
|
||||
async changePassword(password) {
|
||||
const promise = this.resolveWhenAsyncFunctionCompletes(this.sharedVaults._handleKeyPairChange, 'execute')
|
||||
|
||||
await super.changePassword(password)
|
||||
|
||||
await this.awaitPromiseOrThrow(promise, undefined, 'Waiting for keypair change message to process')
|
||||
}
|
||||
|
||||
async syncAndAwaitMessageProcessing() {
|
||||
const promise = this.resolveWhenAsyncFunctionCompletes(this.asymmetric, 'handleRemoteReceivedAsymmetricMessages')
|
||||
|
||||
await this.sync()
|
||||
|
||||
await this.awaitPromiseOrThrow(promise, undefined, 'Waiting for messages to process')
|
||||
}
|
||||
|
||||
async syncAndAwaitInviteProcessing() {
|
||||
const promise = this.resolveWhenAsyncFunctionCompletes(this.vaultInvites, 'processInboundInvites')
|
||||
|
||||
await this.sync()
|
||||
|
||||
await this.awaitPromiseOrThrow(promise, undefined, 'Waiting for invites to process')
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a request to keep refresh token from expiring due to long bouts of inactivity for contact context
|
||||
* while main context changes password. Tests have a refresh token age of 10s typically, and changing password
|
||||
* on CI environment may be time consuming.
|
||||
*/
|
||||
async runAnyRequestToPreventRefreshTokenFromExpiring() {
|
||||
await this.asymmetric.getInboundMessages()
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
/* eslint-disable no-undef */
|
||||
import FakeWebCrypto from './fake_web_crypto.js'
|
||||
import { AppContext } from './AppContext.js'
|
||||
import { VaultsContext } from './VaultsContext.js'
|
||||
import * as Applications from './Applications.js'
|
||||
import * as Defaults from './Defaults.js'
|
||||
import * as Utils from './Utils.js'
|
||||
@@ -57,6 +58,16 @@ export async function createAppContext({ identifier, crypto, email, password, ho
|
||||
return context
|
||||
}
|
||||
|
||||
export async function createVaultsContextWithRealCrypto(identifier) {
|
||||
return createVaultsContext({ identifier, crypto: new SNWebCrypto() })
|
||||
}
|
||||
|
||||
export async function createVaultsContext({ identifier, crypto, email, password, host } = {}) {
|
||||
const context = new VaultsContext({ identifier, crypto, email, password, host })
|
||||
await context.initialize()
|
||||
return context
|
||||
}
|
||||
|
||||
export function disableIntegrityAutoHeal(application) {
|
||||
application.sync.emitOutOfSyncRemotePayloads = () => {
|
||||
console.warn('Integrity self-healing is disabled for this test')
|
||||
@@ -288,7 +299,7 @@ export function tomorrow() {
|
||||
}
|
||||
|
||||
export async function sleep(seconds, reason) {
|
||||
console.log('Sleeping for reason', reason)
|
||||
console.log('[Factory] Sleeping for reason', reason)
|
||||
return Utils.sleep(seconds)
|
||||
}
|
||||
|
||||
|
||||
@@ -843,8 +843,6 @@ describe('importing', function () {
|
||||
})
|
||||
|
||||
it('importing another accounts notes/tags should correctly keep relationships', async function () {
|
||||
this.timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
await setup({ fakeCrypto: true })
|
||||
|
||||
await Factory.registerUserToApplication({
|
||||
@@ -881,5 +879,5 @@ describe('importing', function () {
|
||||
const importedTag = application.items.getDisplayableTags()[0]
|
||||
expect(application.items.referencesForItem(importedTag).length).to.equal(1)
|
||||
expect(application.items.itemsReferencingItem(importedNote).length).to.equal(1)
|
||||
})
|
||||
}).timeout(Factory.TwentySecondTimeout)
|
||||
})
|
||||
|
||||
@@ -4,7 +4,9 @@ const expect = chai.expect
|
||||
|
||||
describe('note display criteria', function () {
|
||||
beforeEach(async function () {
|
||||
this.payloadManager = new PayloadManager()
|
||||
const logger = new Logger('test')
|
||||
|
||||
this.payloadManager = new PayloadManager(logger)
|
||||
this.itemManager = new ItemManager(this.payloadManager)
|
||||
this.mutator = new MutatorService(this.itemManager, this.payloadManager)
|
||||
|
||||
@@ -616,7 +618,10 @@ describe('note display criteria', function () {
|
||||
describe.skip('multiple tags', function () {
|
||||
it('normal note', async function () {
|
||||
await this.createNote()
|
||||
|
||||
/**
|
||||
* This test presently fails because the compound predicate created
|
||||
* when using multiple views is an AND predicate instead of OR
|
||||
*/
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
|
||||
@@ -6,7 +6,8 @@ const expect = chai.expect
|
||||
|
||||
describe('payload manager', () => {
|
||||
beforeEach(async function () {
|
||||
this.payloadManager = new PayloadManager()
|
||||
const logger = new Logger('test')
|
||||
this.payloadManager = new PayloadManager(logger)
|
||||
this.createNotePayload = async () => {
|
||||
return new DecryptedPayload({
|
||||
uuid: Factory.generateUuidish(),
|
||||
|
||||
@@ -58,16 +58,15 @@ describe('preferences', function () {
|
||||
})
|
||||
|
||||
it('emits an event when preferences change', async function () {
|
||||
let callTimes = 0
|
||||
this.application.addEventObserver(() => {
|
||||
callTimes++
|
||||
}, ApplicationEvent.PreferencesChanged)
|
||||
callTimes += 1
|
||||
await Factory.sleep(0) /** Await next tick */
|
||||
expect(callTimes).to.equal(1) /** App start */
|
||||
await register.call(this)
|
||||
const promise = new Promise((resolve) => {
|
||||
this.application.addEventObserver(() => {
|
||||
resolve()
|
||||
}, ApplicationEvent.PreferencesChanged)
|
||||
})
|
||||
|
||||
await this.application.setPreference('editorLeft', 300)
|
||||
expect(callTimes).to.equal(2)
|
||||
await promise
|
||||
expect(promise).to.be.fulfilled
|
||||
})
|
||||
|
||||
it('discards existing preferences when signing in', async function () {
|
||||
|
||||
@@ -46,8 +46,6 @@ describe('server session', function () {
|
||||
}
|
||||
|
||||
it('should succeed when a sync request is perfomed with an expired access token', async function () {
|
||||
this.timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
@@ -59,7 +57,7 @@ describe('server session', function () {
|
||||
const response = await this.application.legacyApi.sync([])
|
||||
|
||||
expect(response.status).to.equal(200)
|
||||
})
|
||||
}).timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
it('should return the new session in the response when refreshed', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
@@ -78,8 +76,6 @@ describe('server session', function () {
|
||||
})
|
||||
|
||||
it('should be refreshed on any api call if access token is expired', async function () {
|
||||
this.timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
@@ -103,11 +99,9 @@ describe('server session', function () {
|
||||
expect(sessionBeforeSync.accessToken.expiresAt).to.be.lessThan(sessionAfterSync.accessToken.expiresAt)
|
||||
// New token should expire in the future.
|
||||
expect(sessionAfterSync.accessToken.expiresAt).to.be.greaterThan(Date.now())
|
||||
})
|
||||
}).timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
it('should not deadlock while renewing session', async function () {
|
||||
this.timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
@@ -125,7 +119,7 @@ describe('server session', function () {
|
||||
const sessionAfterSync = this.application.legacyApi.getSession()
|
||||
|
||||
expect(sessionAfterSync.accessToken.expiresAt).to.be.greaterThan(Date.now())
|
||||
})
|
||||
}).timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
it('should succeed when a sync request is perfomed after signing into an ephemeral session', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
@@ -199,8 +193,6 @@ describe('server session', function () {
|
||||
})
|
||||
|
||||
it('sign out request should be performed successfully and terminate session with expired access token', async function () {
|
||||
this.timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
@@ -218,11 +210,9 @@ describe('server session', function () {
|
||||
expect(syncResponse.status).to.equal(401)
|
||||
expect(syncResponse.data.error.tag).to.equal('invalid-auth')
|
||||
expect(syncResponse.data.error.message).to.equal('Invalid login credentials.')
|
||||
})
|
||||
}).timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
it('change email request should be successful with a valid access token', async function () {
|
||||
this.timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
let { application, password } = await Factory.createAndInitSimpleAppContext({
|
||||
registerUser: true,
|
||||
})
|
||||
@@ -241,11 +231,9 @@ describe('server session', function () {
|
||||
expect(loginResponse).to.be.ok
|
||||
expect(loginResponse.status).to.equal(200)
|
||||
await Factory.safeDeinit(application)
|
||||
})
|
||||
}).timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
it('change email request should fail with an invalid access token', async function () {
|
||||
this.timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
let { application, password } = await Factory.createAndInitSimpleAppContext({
|
||||
registerUser: true,
|
||||
})
|
||||
@@ -266,14 +254,13 @@ describe('server session', function () {
|
||||
expect(changeEmailResponse.error.message).to.equal('Invalid login credentials.')
|
||||
|
||||
await Factory.safeDeinit(application)
|
||||
})
|
||||
}).timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
it('change email request should fail with an expired refresh token', async function () {
|
||||
this.timeout(Factory.ThirtySecondTimeout)
|
||||
|
||||
let { application, email, password } = await Factory.createAndInitSimpleAppContext({
|
||||
let { application, password } = await Factory.createAndInitSimpleAppContext({
|
||||
registerUser: true,
|
||||
})
|
||||
application.sync.lockSyncing()
|
||||
/** Waiting for the refresh token to expire. */
|
||||
await sleepUntilSessionExpires(application, false)
|
||||
|
||||
@@ -285,11 +272,9 @@ describe('server session', function () {
|
||||
expect(changeEmailResponse.error.message).to.equal('Invalid login credentials.')
|
||||
|
||||
await Factory.safeDeinit(application)
|
||||
})
|
||||
}).timeout(Factory.ThirtySecondTimeout)
|
||||
|
||||
it('change password request should be successful with a valid access token', async function () {
|
||||
this.timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
@@ -309,11 +294,9 @@ describe('server session', function () {
|
||||
|
||||
expect(loginResponse).to.be.ok
|
||||
expect(loginResponse.status).to.be.equal(200)
|
||||
})
|
||||
}).timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
it('change password request should be successful after the expired access token is refreshed', async function () {
|
||||
this.timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
@@ -336,7 +319,7 @@ describe('server session', function () {
|
||||
|
||||
expect(loginResponse).to.be.ok
|
||||
expect(loginResponse.status).to.be.equal(200)
|
||||
})
|
||||
}).timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
it('change password request should fail with an invalid access token', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
@@ -360,14 +343,14 @@ describe('server session', function () {
|
||||
})
|
||||
|
||||
it('change password request should fail with an expired refresh token', async function () {
|
||||
this.timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
})
|
||||
|
||||
this.application.sync.lockSyncing()
|
||||
|
||||
/** Waiting for the refresh token to expire. */
|
||||
await sleepUntilSessionExpires(this.application, false)
|
||||
|
||||
@@ -399,14 +382,14 @@ describe('server session', function () {
|
||||
})
|
||||
|
||||
it('should fail when renewing a session with an expired refresh token', async function () {
|
||||
this.timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
})
|
||||
|
||||
this.application.sync.lockSyncing()
|
||||
|
||||
await sleepUntilSessionExpires(this.application, false)
|
||||
|
||||
const refreshSessionResponse = await this.application.legacyApi.refreshSession()
|
||||
@@ -424,7 +407,7 @@ describe('server session', function () {
|
||||
expect(syncResponse.status).to.equal(401)
|
||||
expect(syncResponse.data.error.tag).to.equal('invalid-auth')
|
||||
expect(syncResponse.data.error.message).to.equal('Invalid login credentials.')
|
||||
})
|
||||
}).timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
it('should fail when renewing a session with an invalid refresh token', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
@@ -474,8 +457,6 @@ describe('server session', function () {
|
||||
})
|
||||
|
||||
it('notes should be synced as expected after refreshing a session', async function () {
|
||||
this.timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
@@ -500,7 +481,7 @@ describe('server session', function () {
|
||||
const noteResult = await this.application.items.findItem(aNoteBeforeSync.uuid)
|
||||
expect(aNoteBeforeSync.isItemContentEqualWith(noteResult)).to.equal(true)
|
||||
}
|
||||
})
|
||||
}).timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
it('changing password on one client should not invalidate other sessions', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
@@ -635,8 +616,6 @@ describe('server session', function () {
|
||||
})
|
||||
|
||||
it('revoking a session should destroy local data', async function () {
|
||||
this.timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
Factory.handlePasswordChallenges(this.application, this.password)
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
@@ -662,11 +641,9 @@ describe('server session', function () {
|
||||
const deviceInterface = new WebDeviceInterface()
|
||||
const payloads = await deviceInterface.getAllDatabaseEntries(app2identifier)
|
||||
expect(payloads).to.be.empty
|
||||
})
|
||||
}).timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
it('revoking other sessions should destroy their local data', async function () {
|
||||
this.timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
Factory.handlePasswordChallenges(this.application, this.password)
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
@@ -690,7 +667,7 @@ describe('server session', function () {
|
||||
const deviceInterface = new WebDeviceInterface()
|
||||
const payloads = await deviceInterface.getAllDatabaseEntries(app2identifier)
|
||||
expect(payloads).to.be.empty
|
||||
})
|
||||
}).timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
it('signing out with invalid session token should still delete local data', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
|
||||
@@ -14,7 +14,6 @@ describe('settings service', function () {
|
||||
|
||||
let application
|
||||
let context
|
||||
let subscriptionId = 2001
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
@@ -7,17 +7,19 @@ const expect = chai.expect
|
||||
|
||||
describe('storage manager', function () {
|
||||
this.timeout(Factory.TenSecondTimeout)
|
||||
|
||||
/**
|
||||
* Items are saved in localStorage in tests.
|
||||
* Base keys are `storage`, `snjs_version`, and `keychain`
|
||||
*/
|
||||
const BASE_KEY_COUNT = 3
|
||||
const BASE_KEY_COUNT = ['storage', 'snjs_version', 'keychain'].length
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
this.expectedKeyCount = BASE_KEY_COUNT
|
||||
|
||||
this.context = await Factory.createAppContext()
|
||||
await this.context.launch()
|
||||
|
||||
this.application = this.context.application
|
||||
this.email = UuidGenerator.GenerateUuid()
|
||||
this.password = UuidGenerator.GenerateUuid()
|
||||
@@ -62,18 +64,19 @@ describe('storage manager', function () {
|
||||
expect(keychainValue.serverPassword).to.not.be.ok
|
||||
})
|
||||
|
||||
it.skip('regular session should persist data', async function () {
|
||||
it('regular session should persist data', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
ephemeral: false,
|
||||
})
|
||||
|
||||
const key = 'foo'
|
||||
const value = 'bar'
|
||||
await this.application.storage.setValue(key, value)
|
||||
/** Items are stored in local storage */
|
||||
expect(Object.keys(localStorage).length).to.equal(this.expectedKeyCount + BaseItemCounts.DefaultItems)
|
||||
|
||||
expect(Object.keys(localStorage).length).to.equal(this.expectedKeyCount + BaseItemCounts.DefaultItemsWithAccount)
|
||||
const retrievedValue = await this.application.storage.getValue(key)
|
||||
expect(retrievedValue).to.equal(value)
|
||||
})
|
||||
|
||||
@@ -479,8 +479,7 @@ describe('online conflict handling', function () {
|
||||
await this.sharedFinalAssertions()
|
||||
})
|
||||
|
||||
/** Temporarily skipping due to long run time */
|
||||
it.skip('handles stale data in bulk', async function () {
|
||||
it('handles stale data in bulk', async function () {
|
||||
/** This number must be greater than the pagination limit per sync request.
|
||||
* For example if the limit per request is 150 items sent/received, this number should
|
||||
* be something like 160. */
|
||||
@@ -728,43 +727,39 @@ describe('online conflict handling', function () {
|
||||
await this.sharedFinalAssertions()
|
||||
})
|
||||
|
||||
/** Temporarily skipping due to long run time */
|
||||
it.skip(
|
||||
'registering for account with bulk offline data belonging to another account should be error-free',
|
||||
async function () {
|
||||
/**
|
||||
* When performing a multi-page sync request where we are uploading data imported from a backup,
|
||||
* if the first page of the sync request returns conflicted items keys, we rotate their UUID.
|
||||
* The second page of sync waiting to be sent up is still encrypted with the old items key UUID.
|
||||
* This causes a problem because when that second page is returned as conflicts, we will be looking
|
||||
* for an items_key_id that no longer exists (has been rotated). Rather than modifying the entire
|
||||
* sync paradigm to allow multi-page requests to consider side-effects of each page, we will instead
|
||||
* take the approach of making sure the decryption function is liberal with regards to searching
|
||||
* for the right items key. It will now consider (as a result of this test) an items key as being
|
||||
* the correct key to decrypt an item if the itemskey.uuid == item.items_key_id OR if the itemsKey.duplicateOf
|
||||
* value is equal to item.items_key_id.
|
||||
*/
|
||||
it('registering for account with bulk offline data belonging to another account should be error-free', async function () {
|
||||
/**
|
||||
* When performing a multi-page sync request where we are uploading data imported from a backup,
|
||||
* if the first page of the sync request returns conflicted items keys, we rotate their UUID.
|
||||
* The second page of sync waiting to be sent up is still encrypted with the old items key UUID.
|
||||
* This causes a problem because when that second page is returned as conflicts, we will be looking
|
||||
* for an items_key_id that no longer exists (has been rotated). Rather than modifying the entire
|
||||
* sync paradigm to allow multi-page requests to consider side-effects of each page, we will instead
|
||||
* take the approach of making sure the decryption function is liberal with regards to searching
|
||||
* for the right items key. It will now consider (as a result of this test) an items key as being
|
||||
* the correct key to decrypt an item if the itemskey.uuid == item.items_key_id OR if the itemsKey.duplicateOf
|
||||
* value is equal to item.items_key_id.
|
||||
*/
|
||||
|
||||
/** Create bulk data belonging to another account and sync */
|
||||
const largeItemCount = SyncUpDownLimit + 10
|
||||
await Factory.createManyMappedNotes(this.application, largeItemCount)
|
||||
await this.application.sync.sync(syncOptions)
|
||||
const priorData = this.application.items.items
|
||||
/** Create bulk data belonging to another account and sync */
|
||||
const largeItemCount = SyncUpDownLimit + 10
|
||||
await Factory.createManyMappedNotes(this.application, largeItemCount)
|
||||
await this.application.sync.sync(syncOptions)
|
||||
const priorData = this.application.items.items
|
||||
|
||||
/** Register new account and import this same data */
|
||||
const newApp = await Factory.signOutApplicationAndReturnNew(this.application)
|
||||
await Factory.registerUserToApplication({
|
||||
application: newApp,
|
||||
email: Utils.generateUuid(),
|
||||
password: Utils.generateUuid(),
|
||||
})
|
||||
await newApp.mutator.emitItemsFromPayloads(priorData.map((i) => i.payload))
|
||||
await newApp.sync.markAllItemsAsNeedingSyncAndPersist()
|
||||
await newApp.sync.sync(syncOptions)
|
||||
expect(newApp.payloads.invalidPayloads.length).to.equal(0)
|
||||
await Factory.safeDeinit(newApp)
|
||||
},
|
||||
).timeout(80000)
|
||||
/** Register new account and import this same data */
|
||||
const newApp = await Factory.signOutApplicationAndReturnNew(this.application)
|
||||
await Factory.registerUserToApplication({
|
||||
application: newApp,
|
||||
email: Utils.generateUuid(),
|
||||
password: Utils.generateUuid(),
|
||||
})
|
||||
await newApp.mutator.emitItemsFromPayloads(priorData.map((i) => i.payload))
|
||||
await newApp.sync.markAllItemsAsNeedingSyncAndPersist()
|
||||
await newApp.sync.sync(syncOptions)
|
||||
expect(newApp.payloads.invalidPayloads.length).to.equal(0)
|
||||
await Factory.safeDeinit(newApp)
|
||||
}).timeout(80000)
|
||||
|
||||
it('importing data belonging to another account should not result in duplication', async function () {
|
||||
/** Create primary account and export data */
|
||||
|
||||
@@ -528,8 +528,7 @@ describe('online syncing', function () {
|
||||
await this.application.sync.sync(syncOptions)
|
||||
})
|
||||
|
||||
/** Temporarily skipping due to long run time */
|
||||
it.skip('should handle uploading with sync pagination', async function () {
|
||||
it('should handle uploading with sync pagination', async function () {
|
||||
const largeItemCount = SyncUpDownLimit + 10
|
||||
for (let i = 0; i < largeItemCount; i++) {
|
||||
const note = await Factory.createMappedNote(this.application)
|
||||
@@ -541,10 +540,9 @@ describe('online syncing', function () {
|
||||
await this.application.sync.sync(syncOptions)
|
||||
const rawPayloads = await this.application.storage.getAllRawPayloads()
|
||||
expect(rawPayloads.length).to.equal(this.expectedItemCount)
|
||||
}).timeout(15000)
|
||||
}).timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
/** Temporarily skipping due to long run time */
|
||||
it.skip('should handle downloading with sync pagination', async function () {
|
||||
it('should handle downloading with sync pagination', async function () {
|
||||
const largeItemCount = SyncUpDownLimit + 10
|
||||
for (let i = 0; i < largeItemCount; i++) {
|
||||
const note = await Factory.createMappedNote(this.application)
|
||||
|
||||
@@ -17,7 +17,7 @@ describe('asymmetric messages', function () {
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
context = await Factory.createAppContextWithRealCrypto()
|
||||
context = await Factory.createVaultsContextWithRealCrypto()
|
||||
|
||||
await context.launch()
|
||||
await context.register()
|
||||
@@ -29,7 +29,7 @@ describe('asymmetric messages', function () {
|
||||
|
||||
contactContext.lockSyncing()
|
||||
|
||||
await context.vaults.changeVaultNameAndDescription(sharedVault, {
|
||||
await context.changeVaultName(sharedVault, {
|
||||
name: 'new vault name',
|
||||
description: 'new vault description',
|
||||
})
|
||||
@@ -38,11 +38,8 @@ describe('asymmetric messages', function () {
|
||||
get: () => 'invalid user uuid',
|
||||
})
|
||||
|
||||
const completedProcessingMessagesPromise = contactContext.resolveWhenAsymmetricMessageProcessingCompletes()
|
||||
|
||||
contactContext.unlockSyncing()
|
||||
await contactContext.sync()
|
||||
await completedProcessingMessagesPromise
|
||||
await contactContext.syncAndAwaitMessageProcessing()
|
||||
|
||||
const updatedVault = contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
|
||||
|
||||
@@ -53,33 +50,22 @@ describe('asymmetric messages', function () {
|
||||
})
|
||||
|
||||
it('should delete message after processing it', async () => {
|
||||
const { contactContext, deinitContactContext } = await Collaboration.createSharedVaultWithAcceptedInvite(context)
|
||||
const { sharedVault, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInvite(context)
|
||||
|
||||
const eventData = {
|
||||
current: {
|
||||
encryption: context.encryption.getKeyPair(),
|
||||
signing: context.encryption.getSigningKeyPair(),
|
||||
},
|
||||
previous: {
|
||||
encryption: context.encryption.getKeyPair(),
|
||||
signing: context.encryption.getSigningKeyPair(),
|
||||
},
|
||||
}
|
||||
|
||||
await context.contacts.sendOwnContactChangeEventToAllContacts(eventData)
|
||||
await context.changeVaultName(sharedVault, {
|
||||
name: 'New Name',
|
||||
description: 'New Description',
|
||||
})
|
||||
|
||||
const deleteFunction = sinon.spy(contactContext.asymmetric, 'deleteMessageAfterProcessing')
|
||||
|
||||
const promise = contactContext.resolveWhenAsymmetricMessageProcessingCompletes()
|
||||
|
||||
await contactContext.sync()
|
||||
|
||||
await promise
|
||||
await contactContext.syncAndAwaitMessageProcessing()
|
||||
|
||||
expect(deleteFunction.callCount).to.equal(1)
|
||||
|
||||
const messages = await contactContext.asymmetric.getInboundMessages()
|
||||
expect(messages.length).to.equal(0)
|
||||
expect(messages.getValue().length).to.equal(0)
|
||||
|
||||
await deinitContactContext()
|
||||
})
|
||||
@@ -106,10 +92,7 @@ describe('asymmetric messages', function () {
|
||||
|
||||
await sendContactSharePromise
|
||||
|
||||
const completedProcessingMessagesPromise = contactContext.resolveWhenAsymmetricMessageProcessingCompletes()
|
||||
|
||||
await contactContext.sync()
|
||||
await completedProcessingMessagesPromise
|
||||
await contactContext.syncAndAwaitMessageProcessing()
|
||||
|
||||
const updatedContact = contactContext.contacts.findContact(thirdPartyContext.userUuid)
|
||||
expect(updatedContact.name).to.equal('Changed 3rd Party Name')
|
||||
@@ -122,7 +105,10 @@ describe('asymmetric messages', function () {
|
||||
const { sharedVault, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInvite(context)
|
||||
|
||||
const handleInitialContactShareMessage = contactContext.resolveWhenAsymmetricMessageProcessingCompletes()
|
||||
const handleInitialContactShareMessage = contactContext.resolveWhenAsyncFunctionCompletes(
|
||||
contactContext.asymmetric,
|
||||
'handleRemoteReceivedAsymmetricMessages',
|
||||
)
|
||||
|
||||
const { thirdPartyContext, deinitThirdPartyContext } = await Collaboration.inviteNewPartyToSharedVault(
|
||||
context,
|
||||
@@ -162,6 +148,7 @@ describe('asymmetric messages', function () {
|
||||
await Collaboration.acceptAllInvites(thirdPartyContext)
|
||||
|
||||
await contactContext.sync()
|
||||
contactContext.lockSyncing()
|
||||
|
||||
const sendContactSharePromise = context.resolveWhenSharedVaultServiceSendsContactShareMessage()
|
||||
|
||||
@@ -179,6 +166,7 @@ describe('asymmetric messages', function () {
|
||||
const thirdPartySpy = sinon.spy(thirdPartyContext.asymmetric, 'handleTrustedContactShareMessage')
|
||||
|
||||
await context.sync()
|
||||
contactContext.unlockSyncing()
|
||||
await contactContext.sync()
|
||||
await thirdPartyContext.sync()
|
||||
|
||||
@@ -194,12 +182,26 @@ describe('asymmetric messages', function () {
|
||||
const { sharedVault, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInvite(context)
|
||||
|
||||
contactContext.anticipateConsoleError(
|
||||
'(2x) Error decrypting contentKey from parameters',
|
||||
'Items keys are encrypted with new root key and are later decrypted in the test',
|
||||
)
|
||||
|
||||
contactContext.lockSyncing()
|
||||
|
||||
const promise = context.resolveWhenAsyncFunctionCompletes(
|
||||
context.sharedVaults._notifyVaultUsersOfKeyRotation,
|
||||
'execute',
|
||||
)
|
||||
await context.vaults.rotateVaultRootKey(sharedVault)
|
||||
await promise
|
||||
|
||||
const firstPartySpy = sinon.spy(context.asymmetric, 'handleTrustedSharedVaultRootKeyChangedMessage')
|
||||
const secondPartySpy = sinon.spy(contactContext.asymmetric, 'handleTrustedSharedVaultRootKeyChangedMessage')
|
||||
|
||||
await context.sync()
|
||||
|
||||
contactContext.unlockSyncing()
|
||||
await contactContext.sync()
|
||||
|
||||
expect(firstPartySpy.callCount).to.equal(0)
|
||||
@@ -212,7 +214,7 @@ describe('asymmetric messages', function () {
|
||||
const { sharedVault, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInvite(context)
|
||||
|
||||
await context.vaults.changeVaultNameAndDescription(sharedVault, {
|
||||
await context.changeVaultName(sharedVault, {
|
||||
name: 'New Name',
|
||||
description: 'New Description',
|
||||
})
|
||||
@@ -238,20 +240,13 @@ describe('asymmetric messages', function () {
|
||||
|
||||
contactContext.lockSyncing()
|
||||
|
||||
const sendPromise = context.resolveWhenAsyncFunctionCompletes(
|
||||
context.contacts,
|
||||
'sendOwnContactChangeEventToAllContacts',
|
||||
)
|
||||
await context.changePassword('new password')
|
||||
await sendPromise
|
||||
|
||||
const firstPartySpy = sinon.spy(context.asymmetric, 'handleTrustedSenderKeypairChangedMessage')
|
||||
const secondPartySpy = sinon.spy(contactContext.asymmetric, 'handleTrustedSenderKeypairChangedMessage')
|
||||
|
||||
const completedProcessingMessagesPromise = contactContext.resolveWhenAsymmetricMessageProcessingCompletes()
|
||||
contactContext.unlockSyncing()
|
||||
await contactContext.sync()
|
||||
await completedProcessingMessagesPromise
|
||||
await contactContext.syncAndAwaitMessageProcessing()
|
||||
|
||||
expect(firstPartySpy.callCount).to.equal(0)
|
||||
expect(secondPartySpy.callCount).to.equal(1)
|
||||
@@ -269,14 +264,12 @@ describe('asymmetric messages', function () {
|
||||
|
||||
await context.changePassword('new password')
|
||||
|
||||
await context.vaults.changeVaultNameAndDescription(sharedVault, {
|
||||
await context.changeVaultName(sharedVault, {
|
||||
name: 'New Name',
|
||||
description: 'New Description',
|
||||
})
|
||||
|
||||
const completedProcessingMessagesPromise = contactContext.resolveWhenAsymmetricMessageProcessingCompletes()
|
||||
await contactContext.sync()
|
||||
await completedProcessingMessagesPromise
|
||||
await contactContext.syncAndAwaitMessageProcessing()
|
||||
|
||||
const updatedVault = contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
|
||||
expect(updatedVault.name).to.equal('New Name')
|
||||
@@ -295,16 +288,14 @@ describe('asymmetric messages', function () {
|
||||
|
||||
await context.changePassword('new password')
|
||||
|
||||
await context.vaults.changeVaultNameAndDescription(sharedVault, {
|
||||
await context.changeVaultName(sharedVault, {
|
||||
name: 'New Name',
|
||||
description: 'New Description',
|
||||
})
|
||||
|
||||
context.lockSyncing()
|
||||
|
||||
const completedProcessingMessagesPromise = contactContext.resolveWhenAsymmetricMessageProcessingCompletes()
|
||||
await contactContext.sync()
|
||||
await completedProcessingMessagesPromise
|
||||
await contactContext.syncAndAwaitMessageProcessing()
|
||||
|
||||
/**
|
||||
* There's really no good way to await the exact call since
|
||||
@@ -313,12 +304,12 @@ describe('asymmetric messages', function () {
|
||||
await context.sleep(0.25)
|
||||
|
||||
const messages = await context.asymmetric.getInboundMessages()
|
||||
expect(messages.length).to.equal(0)
|
||||
expect(messages.getValue().length).to.equal(0)
|
||||
|
||||
await deinitContactContext()
|
||||
})
|
||||
|
||||
it.skip('should process sender keypair changed message', async () => {
|
||||
it('should process sender keypair changed message', async () => {
|
||||
const { contactContext, deinitContactContext } = await Collaboration.createContactContext()
|
||||
await Collaboration.createTrustedContactForUserOfContext(context, contactContext)
|
||||
await Collaboration.createTrustedContactForUserOfContext(contactContext, context)
|
||||
@@ -326,9 +317,7 @@ describe('asymmetric messages', function () {
|
||||
|
||||
await context.changePassword('new_password')
|
||||
|
||||
const completedProcessingMessagesPromise = contactContext.resolveWhenAsymmetricMessageProcessingCompletes()
|
||||
await contactContext.sync()
|
||||
await completedProcessingMessagesPromise
|
||||
await contactContext.syncAndAwaitMessageProcessing()
|
||||
|
||||
const updatedContact = contactContext.contacts.findContact(context.userUuid)
|
||||
|
||||
@@ -341,7 +330,7 @@ describe('asymmetric messages', function () {
|
||||
await deinitContactContext()
|
||||
})
|
||||
|
||||
it.skip('sender keypair changed message should be signed using old key pair', async () => {
|
||||
it('sender keypair changed message should be signed using old key pair', async () => {
|
||||
const { contactContext, deinitContactContext } = await Collaboration.createSharedVaultWithAcceptedInvite(context)
|
||||
|
||||
const oldKeyPair = context.encryption.getKeyPair()
|
||||
@@ -352,9 +341,7 @@ describe('asymmetric messages', function () {
|
||||
const secondPartySpy = sinon.spy(contactContext.asymmetric, 'handleTrustedSenderKeypairChangedMessage')
|
||||
|
||||
await context.sync()
|
||||
const completedProcessingMessagesPromise = contactContext.resolveWhenAsymmetricMessageProcessingCompletes()
|
||||
await contactContext.sync()
|
||||
await completedProcessingMessagesPromise
|
||||
await contactContext.syncAndAwaitMessageProcessing()
|
||||
|
||||
const message = secondPartySpy.args[0][0]
|
||||
const encryptedMessage = message.encrypted_message
|
||||
@@ -376,9 +363,7 @@ describe('asymmetric messages', function () {
|
||||
const newKeyPair = context.encryption.getKeyPair()
|
||||
const newSigningKeyPair = context.encryption.getSigningKeyPair()
|
||||
|
||||
const completedProcessingMessagesPromise = contactContext.resolveWhenAsymmetricMessageProcessingCompletes()
|
||||
await contactContext.sync()
|
||||
await completedProcessingMessagesPromise
|
||||
await contactContext.syncAndAwaitMessageProcessing()
|
||||
|
||||
const updatedContact = contactContext.contacts.findContact(context.userUuid)
|
||||
expect(updatedContact.publicKeySet.encryption).to.equal(newKeyPair.publicKey)
|
||||
@@ -395,7 +380,7 @@ describe('asymmetric messages', function () {
|
||||
|
||||
contactContext.lockSyncing()
|
||||
|
||||
await context.vaults.changeVaultNameAndDescription(sharedVault, {
|
||||
await context.changeVaultName(sharedVault, {
|
||||
name: 'New Name',
|
||||
description: 'New Description',
|
||||
})
|
||||
@@ -405,7 +390,7 @@ describe('asymmetric messages', function () {
|
||||
await promise
|
||||
|
||||
const messages = await contactContext.asymmetric.getInboundMessages()
|
||||
expect(messages.length).to.equal(0)
|
||||
expect(messages.getValue().length).to.equal(0)
|
||||
|
||||
const updatedVault = contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
|
||||
expect(updatedVault.name).to.not.equal('New Name')
|
||||
@@ -413,4 +398,85 @@ describe('asymmetric messages', function () {
|
||||
|
||||
await deinitContactContext()
|
||||
})
|
||||
|
||||
it('should be able to decrypt previously sent own messages', async () => {
|
||||
const { sharedVault, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInvite(context)
|
||||
|
||||
contactContext.lockSyncing()
|
||||
|
||||
await context.changeVaultName(sharedVault, {
|
||||
name: 'New Name',
|
||||
description: 'New Description',
|
||||
})
|
||||
|
||||
const usecase = context.application.dependencies.get(TYPES.ResendAllMessages)
|
||||
const result = await usecase.execute({
|
||||
keys: {
|
||||
encryption: context.encryption.getKeyPair(),
|
||||
signing: context.encryption.getSigningKeyPair(),
|
||||
},
|
||||
previousKeys: {
|
||||
encryption: context.encryption.getKeyPair(),
|
||||
signing: context.encryption.getSigningKeyPair(),
|
||||
},
|
||||
})
|
||||
|
||||
expect(result.isFailed()).to.be.false
|
||||
|
||||
await deinitContactContext()
|
||||
})
|
||||
|
||||
it('sending a new vault invite to a trusted contact then changing account password should still allow contact to trust invite', async () => {
|
||||
const { contactContext, contact, deinitContactContext } = await Collaboration.createSharedVaultWithAcceptedInvite(
|
||||
context,
|
||||
)
|
||||
|
||||
contactContext.lockSyncing()
|
||||
|
||||
const newVault = await Collaboration.createSharedVault(context)
|
||||
|
||||
await context.vaultInvites.inviteContactToSharedVault(
|
||||
newVault,
|
||||
contact,
|
||||
SharedVaultUserPermission.PERMISSIONS.Write,
|
||||
)
|
||||
|
||||
await contactContext.runAnyRequestToPreventRefreshTokenFromExpiring()
|
||||
|
||||
await context.changePassword('new password')
|
||||
|
||||
await contactContext.runAnyRequestToPreventRefreshTokenFromExpiring()
|
||||
|
||||
/**
|
||||
* When resending keypair changed messages here, we expect that one of their previous messages will fail to decrypt.
|
||||
* This is because the first contact keypair change message was encrypted using their keypair N (original), then after
|
||||
* the second password change, the reference to "previous" key will be N + 1 instead of N, so there is no longer a reference
|
||||
* to the original keypair. This is not a problem, and in fact even if the message were decryptable, it would be skipped
|
||||
* because we do not want to re-send keypair changed messages.
|
||||
*/
|
||||
|
||||
await context.changePassword('new password 2')
|
||||
|
||||
const messages = await contactContext.asymmetric.getInboundMessages()
|
||||
if (messages.isFailed()) {
|
||||
console.error(messages.getError())
|
||||
}
|
||||
|
||||
expect(messages.isFailed()).to.be.false
|
||||
expect(messages.getValue().length).to.equal(2)
|
||||
|
||||
contactContext.unlockSyncing()
|
||||
await contactContext.syncAndAwaitInviteProcessing()
|
||||
|
||||
const invites = contactContext.vaultInvites.getCachedPendingInviteRecords()
|
||||
expect(invites.length).to.equal(1)
|
||||
|
||||
const invite = invites[0]
|
||||
expect(invite.trusted).to.equal(true)
|
||||
|
||||
await contactContext.vaultInvites.acceptInvite(invite)
|
||||
|
||||
await deinitContactContext()
|
||||
}).timeout(Factory.ThirtySecondTimeout)
|
||||
})
|
||||
|
||||
@@ -17,7 +17,7 @@ describe('shared vault conflicts', function () {
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
context = await Factory.createAppContextWithRealCrypto()
|
||||
context = await Factory.createVaultsContextWithRealCrypto()
|
||||
|
||||
await context.launch()
|
||||
await context.register()
|
||||
|
||||
@@ -17,7 +17,7 @@ describe('contacts', function () {
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
context = await Factory.createAppContextWithRealCrypto()
|
||||
context = await Factory.createVaultsContextWithRealCrypto()
|
||||
|
||||
await context.launch()
|
||||
await context.register()
|
||||
@@ -101,6 +101,5 @@ describe('contacts', function () {
|
||||
await deinitContactContext()
|
||||
})
|
||||
|
||||
it.skip('should be able to refresh a contact using a collaborationID that includes full chain of previous public keys', async () => {
|
||||
})
|
||||
it.skip('should be able to refresh a contact using a collaborationID that includes full chain of previous public keys', async () => {})
|
||||
})
|
||||
|
||||
@@ -17,7 +17,7 @@ describe('shared vault crypto', function () {
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
context = await Factory.createAppContextWithRealCrypto()
|
||||
context = await Factory.createVaultsContextWithRealCrypto()
|
||||
|
||||
await context.launch()
|
||||
await context.register()
|
||||
@@ -28,11 +28,13 @@ describe('shared vault crypto', function () {
|
||||
const appIdentifier = context.identifier
|
||||
await context.deinit()
|
||||
|
||||
let recreatedContext = await Factory.createAppContextWithRealCrypto(appIdentifier)
|
||||
let recreatedContext = await Factory.createVaultsContextWithRealCrypto(appIdentifier)
|
||||
await recreatedContext.launch()
|
||||
|
||||
expect(recreatedContext.encryption.getKeyPair()).to.not.be.undefined
|
||||
expect(recreatedContext.encryption.getSigningKeyPair()).to.not.be.undefined
|
||||
|
||||
await recreatedContext.deinit()
|
||||
})
|
||||
|
||||
it('changing user password should re-encrypt all key system root keys and contacts with new user root key', async () => {
|
||||
@@ -90,11 +92,13 @@ describe('shared vault crypto', function () {
|
||||
await deinitContactContext()
|
||||
})
|
||||
|
||||
it.skip('encrypting an item into storage then loading it should verify authenticity of original content rather than most recent symmetric signature', async () => {
|
||||
it('encrypting an item into storage then loading it should verify authenticity of original content rather than most recent symmetric signature', async () => {
|
||||
const { note, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(context)
|
||||
|
||||
await contactContext.changeNoteTitleAndSync(note, 'new title')
|
||||
const contactNote = contactContext.items.findItem(note.uuid)
|
||||
|
||||
await contactContext.changeNoteTitleAndSync(contactNote, 'new title')
|
||||
|
||||
/** Override decrypt result to return failing signature */
|
||||
const objectToSpy = context.encryption
|
||||
@@ -103,6 +107,7 @@ describe('shared vault crypto', function () {
|
||||
|
||||
const decryptedPayloads = await objectToSpy.decryptSplit(split)
|
||||
expect(decryptedPayloads.length).to.equal(1)
|
||||
expect(decryptedPayloads[0].content_type).to.equal(ContentType.TYPES.Note)
|
||||
|
||||
const payload = decryptedPayloads[0]
|
||||
const mutatedPayload = new DecryptedPayload({
|
||||
@@ -118,6 +123,7 @@ describe('shared vault crypto', function () {
|
||||
|
||||
return [mutatedPayload]
|
||||
})
|
||||
|
||||
await context.sync()
|
||||
|
||||
let updatedNote = context.items.findItem(note.uuid)
|
||||
@@ -127,7 +133,7 @@ describe('shared vault crypto', function () {
|
||||
const appIdentifier = context.identifier
|
||||
await context.deinit()
|
||||
|
||||
let recreatedContext = await Factory.createAppContextWithRealCrypto(appIdentifier)
|
||||
let recreatedContext = await Factory.createVaultsContextWithRealCrypto(appIdentifier)
|
||||
await recreatedContext.launch()
|
||||
|
||||
updatedNote = recreatedContext.items.findItem(note.uuid)
|
||||
@@ -140,7 +146,7 @@ describe('shared vault crypto', function () {
|
||||
|
||||
await recreatedContext.deinit()
|
||||
|
||||
recreatedContext = await Factory.createAppContextWithRealCrypto(appIdentifier)
|
||||
recreatedContext = await Factory.createVaultsContextWithRealCrypto(appIdentifier)
|
||||
await recreatedContext.launch()
|
||||
|
||||
/** Decrypting from storage will now verify current user symmetric signature only */
|
||||
|
||||
@@ -18,7 +18,7 @@ describe('shared vault deletion', function () {
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
context = await Factory.createAppContextWithRealCrypto()
|
||||
context = await Factory.createVaultsContextWithRealCrypto()
|
||||
|
||||
await context.launch()
|
||||
await context.register()
|
||||
|
||||
@@ -5,7 +5,7 @@ import * as Collaboration from '../lib/Collaboration.js'
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe.skip('shared vault files', function () {
|
||||
describe('shared vault files', function () {
|
||||
this.timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
let context
|
||||
@@ -19,7 +19,7 @@ describe.skip('shared vault files', function () {
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
context = await Factory.createAppContextWithRealCrypto()
|
||||
context = await Factory.createVaultsContextWithRealCrypto()
|
||||
|
||||
await context.launch()
|
||||
await context.register()
|
||||
@@ -29,7 +29,7 @@ describe.skip('shared vault files', function () {
|
||||
})
|
||||
|
||||
describe('private vaults', () => {
|
||||
it('should be able to upload and download file to vault as owner', async () => {
|
||||
it('should be able to upload and download file to private vault as owner', async () => {
|
||||
const vault = await Collaboration.createPrivateVault(context)
|
||||
const response = await fetch('/mocha/assets/small_file.md')
|
||||
const buffer = new Uint8Array(await response.arrayBuffer())
|
||||
@@ -45,7 +45,7 @@ describe.skip('shared vault files', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should be able to upload and download file to vault as owner', async () => {
|
||||
it('should be able to upload and download file to shared vault as owner', async () => {
|
||||
const sharedVault = await Collaboration.createSharedVault(context)
|
||||
const response = await fetch('/mocha/assets/small_file.md')
|
||||
const buffer = new Uint8Array(await response.arrayBuffer())
|
||||
|
||||
@@ -17,7 +17,7 @@ describe.skip('vault importing', function () {
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
context = await Factory.createAppContextWithRealCrypto()
|
||||
context = await Factory.createVaultsContextWithRealCrypto()
|
||||
|
||||
await context.launch()
|
||||
await context.register()
|
||||
@@ -39,7 +39,7 @@ describe.skip('vault importing', function () {
|
||||
|
||||
const backupData = await context.application.createEncryptedBackupFileForAutomatedDesktopBackups()
|
||||
|
||||
const otherContext = await Factory.createAppContextWithRealCrypto()
|
||||
const otherContext = await Factory.createVaultsContextWithRealCrypto()
|
||||
await otherContext.launch()
|
||||
|
||||
await otherContext.application.importData(backupData)
|
||||
|
||||
@@ -17,7 +17,7 @@ describe('shared vault invites', function () {
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
context = await Factory.createAppContextWithRealCrypto()
|
||||
context = await Factory.createVaultsContextWithRealCrypto()
|
||||
await context.launch()
|
||||
await context.register()
|
||||
})
|
||||
|
||||
@@ -17,7 +17,7 @@ describe('shared vault items', function () {
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
context = await Factory.createAppContextWithRealCrypto()
|
||||
context = await Factory.createVaultsContextWithRealCrypto()
|
||||
|
||||
await context.launch()
|
||||
await context.register()
|
||||
@@ -38,13 +38,19 @@ describe('shared vault items', function () {
|
||||
it('should add item to shared vault with contact', async () => {
|
||||
const note = await context.createSyncedNote('foo', 'bar')
|
||||
|
||||
const { sharedVault, deinitContactContext } = await Collaboration.createSharedVaultWithAcceptedInvite(context)
|
||||
const { sharedVault, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInvite(context)
|
||||
|
||||
await Collaboration.moveItemToVault(context, sharedVault, note)
|
||||
|
||||
await contactContext.sync()
|
||||
|
||||
const updatedNote = context.items.findItem(note.uuid)
|
||||
expect(updatedNote.key_system_identifier).to.equal(sharedVault.systemIdentifier)
|
||||
|
||||
const contactNote = contactContext.items.findItem(note.uuid)
|
||||
expect(contactNote.key_system_identifier).to.equal(sharedVault.systemIdentifier)
|
||||
|
||||
await deinitContactContext()
|
||||
})
|
||||
|
||||
@@ -117,7 +123,7 @@ describe('shared vault items', function () {
|
||||
})
|
||||
|
||||
it('adding item to vault while belonging to other vault should move the item to new vault', async () => {
|
||||
const { note, sharedVault, contactContext, contact, deinitContactContext } =
|
||||
const { note, contactContext, contact, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(context)
|
||||
|
||||
const { sharedVault: otherSharedVault } = await Collaboration.createSharedVaultAndInviteContact(
|
||||
@@ -126,6 +132,8 @@ describe('shared vault items', function () {
|
||||
contact,
|
||||
)
|
||||
|
||||
await Collaboration.acceptAllInvites(contactContext)
|
||||
|
||||
const updatedNote = await Collaboration.moveItemToVault(context, otherSharedVault, note)
|
||||
|
||||
expect(updatedNote.key_system_identifier).to.equal(otherSharedVault.systemIdentifier)
|
||||
@@ -134,7 +142,6 @@ describe('shared vault items', function () {
|
||||
await contactContext.sync()
|
||||
|
||||
const receivedNote = contactContext.items.findItem(note.uuid)
|
||||
expect(receivedNote).to.not.be.undefined
|
||||
expect(receivedNote.title).to.equal(note.title)
|
||||
expect(receivedNote.key_system_identifier).to.equal(otherSharedVault.systemIdentifier)
|
||||
expect(receivedNote.shared_vault_uuid).to.equal(otherSharedVault.sharing.sharedVaultUuid)
|
||||
|
||||
@@ -16,7 +16,7 @@ describe('vault key management', function () {
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
context = await Factory.createAppContextWithRealCrypto()
|
||||
context = await Factory.createVaultsContextWithRealCrypto()
|
||||
|
||||
await context.launch()
|
||||
})
|
||||
|
||||
@@ -17,7 +17,7 @@ describe('vault key rotation', function () {
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
context = await Factory.createAppContextWithRealCrypto()
|
||||
context = await Factory.createVaultsContextWithRealCrypto()
|
||||
|
||||
await context.launch()
|
||||
await context.register()
|
||||
@@ -99,7 +99,7 @@ describe('vault key rotation', function () {
|
||||
await context.vaults.rotateVaultRootKey(sharedVault)
|
||||
await promise
|
||||
|
||||
const outboundMessages = await context.asymmetric.getOutboundMessages()
|
||||
const outboundMessages = (await context.asymmetric.getOutboundMessages()).getValue()
|
||||
const expectedMessages = ['root key change', 'vault metadata change']
|
||||
expect(outboundMessages.length).to.equal(expectedMessages.length)
|
||||
|
||||
@@ -154,10 +154,8 @@ describe('vault key rotation', function () {
|
||||
await context.vaults.rotateVaultRootKey(sharedVault)
|
||||
await promise
|
||||
|
||||
const contactPromise = contactContext.resolveWhenAsymmetricMessageProcessingCompletes()
|
||||
contactContext.unlockSyncing()
|
||||
await contactContext.sync()
|
||||
await contactPromise
|
||||
await contactContext.syncAndAwaitMessageProcessing()
|
||||
|
||||
const newPrimaryItemsKey = contactContext.keys.getPrimaryKeySystemItemsKey(sharedVault.systemIdentifier)
|
||||
expect(newPrimaryItemsKey).to.not.be.undefined
|
||||
@@ -217,7 +215,7 @@ describe('vault key rotation', function () {
|
||||
await context.vaults.rotateVaultRootKey(sharedVault)
|
||||
await firstPromise
|
||||
|
||||
const asymmetricMessageAfterFirstChange = await context.asymmetric.getOutboundMessages()
|
||||
const asymmetricMessageAfterFirstChange = (await context.asymmetric.getOutboundMessages()).getValue()
|
||||
const expectedMessages = ['root key change', 'vault metadata change']
|
||||
expect(asymmetricMessageAfterFirstChange.length).to.equal(expectedMessages.length)
|
||||
|
||||
@@ -227,7 +225,7 @@ describe('vault key rotation', function () {
|
||||
await context.vaults.rotateVaultRootKey(sharedVault)
|
||||
await secondPromise
|
||||
|
||||
const asymmetricMessageAfterSecondChange = await context.asymmetric.getOutboundMessages()
|
||||
const asymmetricMessageAfterSecondChange = (await context.asymmetric.getOutboundMessages()).getValue()
|
||||
expect(asymmetricMessageAfterSecondChange.length).to.equal(expectedMessages.length)
|
||||
|
||||
const messageAfterSecondChange = asymmetricMessageAfterSecondChange[0]
|
||||
|
||||
@@ -17,13 +17,13 @@ describe('keypair change', function () {
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
context = await Factory.createAppContextWithRealCrypto()
|
||||
context = await Factory.createVaultsContextWithRealCrypto()
|
||||
|
||||
await context.launch()
|
||||
await context.register()
|
||||
})
|
||||
|
||||
it.skip('contacts should be able to handle receiving multiple keypair changed messages and trust them in order', async () => {
|
||||
it('contacts should be able to handle receiving multiple keypair changed messages and trust them in order', async () => {
|
||||
const { note, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(context)
|
||||
|
||||
@@ -39,20 +39,24 @@ describe('keypair change', function () {
|
||||
publicKeyChain.push(context.publicKey)
|
||||
signingPublicKeyChain.push(context.signingPublicKey)
|
||||
|
||||
await contactContext.runAnyRequestToPreventRefreshTokenFromExpiring()
|
||||
|
||||
await context.changePassword('new_password-2')
|
||||
publicKeyChain.push(context.publicKey)
|
||||
signingPublicKeyChain.push(context.signingPublicKey)
|
||||
|
||||
await contactContext.runAnyRequestToPreventRefreshTokenFromExpiring()
|
||||
|
||||
await context.changePassword('new_password-3')
|
||||
publicKeyChain.push(context.publicKey)
|
||||
signingPublicKeyChain.push(context.signingPublicKey)
|
||||
|
||||
await contactContext.runAnyRequestToPreventRefreshTokenFromExpiring()
|
||||
|
||||
await context.changeNoteTitleAndSync(note, 'new title')
|
||||
|
||||
contactContext.unlockSyncing()
|
||||
const promise = contactContext.resolveWhenAsymmetricMessageProcessingCompletes()
|
||||
await contactContext.sync()
|
||||
await promise
|
||||
await contactContext.syncAndAwaitMessageProcessing()
|
||||
|
||||
const originatorContact = contactContext.contacts.findContact(context.userUuid)
|
||||
let currentKeySet = originatorContact.publicKeySet
|
||||
@@ -70,7 +74,7 @@ describe('keypair change', function () {
|
||||
expect(receivedNote.signatureData.result.passes).to.be.true
|
||||
|
||||
await deinitContactContext()
|
||||
})
|
||||
}).timeout(Factory.ThirtySecondTimeout)
|
||||
|
||||
it('should not trust messages sent with previous key pair', async () => {
|
||||
const { sharedVault, contactContext, deinitContactContext } =
|
||||
@@ -86,15 +90,13 @@ describe('keypair change', function () {
|
||||
sinon.stub(context.encryption, 'getKeyPair').returns(previousKeyPair)
|
||||
sinon.stub(context.encryption, 'getSigningKeyPair').returns(previousSigningKeyPair)
|
||||
|
||||
await context.vaults.changeVaultNameAndDescription(sharedVault, {
|
||||
await context.changeVaultName(sharedVault, {
|
||||
name: 'New Name',
|
||||
description: 'New Description',
|
||||
})
|
||||
|
||||
contactContext.unlockSyncing()
|
||||
const promise = contactContext.resolveWhenAsymmetricMessageProcessingCompletes()
|
||||
await contactContext.sync()
|
||||
await promise
|
||||
await contactContext.syncAndAwaitMessageProcessing()
|
||||
|
||||
const updatedVault = contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
|
||||
expect(updatedVault.name).to.equal(sharedVault.name)
|
||||
@@ -133,23 +135,18 @@ describe('keypair change', function () {
|
||||
|
||||
contactContext.lockSyncing()
|
||||
|
||||
await context.vaults.changeVaultNameAndDescription(sharedVault, {
|
||||
await context.changeVaultName(sharedVault, {
|
||||
name: 'New Name',
|
||||
description: 'New Description',
|
||||
})
|
||||
|
||||
const originalMessages = await contactContext.asymmetric.getInboundMessages()
|
||||
const originalMessages = (await contactContext.asymmetric.getInboundMessages()).getValue()
|
||||
expect(originalMessages.length).to.equal(1)
|
||||
const originalMessage = originalMessages[0]
|
||||
|
||||
const promise = context.resolveWhenAsyncFunctionCompletes(
|
||||
context.application.dependencies.get(TYPES.HandleKeyPairChange),
|
||||
'execute',
|
||||
)
|
||||
await context.changePassword('new_password')
|
||||
await promise
|
||||
|
||||
const updatedMessages = await contactContext.asymmetric.getInboundMessages()
|
||||
const updatedMessages = (await contactContext.asymmetric.getInboundMessages()).getValue()
|
||||
const expectedMessages = ['keypair-change', 'vault-change']
|
||||
expect(updatedMessages.length).to.equal(expectedMessages.length)
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ describe('shared vault permissions', function () {
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
context = await Factory.createAppContextWithRealCrypto()
|
||||
context = await Factory.createVaultsContextWithRealCrypto()
|
||||
|
||||
await context.launch()
|
||||
await context.register()
|
||||
|
||||
@@ -18,7 +18,7 @@ describe('public key cryptography', function () {
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
context = await Factory.createAppContextWithRealCrypto()
|
||||
context = await Factory.createVaultsContextWithRealCrypto()
|
||||
|
||||
await context.launch()
|
||||
await context.register()
|
||||
@@ -40,7 +40,7 @@ describe('public key cryptography', function () {
|
||||
const password = context.password
|
||||
await context.signout()
|
||||
|
||||
const recreatedContext = await Factory.createAppContextWithRealCrypto()
|
||||
const recreatedContext = await Factory.createVaultsContextWithRealCrypto()
|
||||
await recreatedContext.launch()
|
||||
recreatedContext.email = email
|
||||
recreatedContext.password = password
|
||||
@@ -51,6 +51,8 @@ describe('public key cryptography', function () {
|
||||
|
||||
expect(recreatedContext.sessions.getSigningPublicKey()).to.not.be.undefined
|
||||
expect(recreatedContext.encryption.getSigningKeyPair().privateKey).to.not.be.undefined
|
||||
|
||||
await recreatedContext.deinit()
|
||||
})
|
||||
|
||||
it('should rotate keypair during password change', async () => {
|
||||
@@ -74,7 +76,7 @@ describe('public key cryptography', function () {
|
||||
})
|
||||
|
||||
it('should allow option to enable collaboration for previously signed in accounts', async () => {
|
||||
const newContext = await Factory.createAppContextWithRealCrypto()
|
||||
const newContext = await Factory.createVaultsContextWithRealCrypto()
|
||||
await newContext.launch()
|
||||
|
||||
await newContext.register()
|
||||
@@ -94,5 +96,7 @@ describe('public key cryptography', function () {
|
||||
expect(result.error).to.be.undefined
|
||||
|
||||
expect(newContext.application.sessions.isUserMissingKeyPair()).to.be.false
|
||||
|
||||
await newContext.deinit()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -10,15 +10,10 @@ describe('shared vaults', function () {
|
||||
let context
|
||||
let vaults
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
context = await Factory.createAppContextWithRealCrypto()
|
||||
context = await Factory.createVaultsContextWithRealCrypto()
|
||||
|
||||
await context.launch()
|
||||
await context.register()
|
||||
@@ -26,11 +21,18 @@ describe('shared vaults', function () {
|
||||
vaults = context.vaults
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
it('should update vault name and description', async () => {
|
||||
const { sharedVault, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInvite(context)
|
||||
|
||||
await vaults.changeVaultNameAndDescription(sharedVault, {
|
||||
contactContext.lockSyncing()
|
||||
|
||||
await context.changeVaultName(sharedVault, {
|
||||
name: 'new vault name',
|
||||
description: 'new vault description',
|
||||
})
|
||||
@@ -39,9 +41,8 @@ describe('shared vaults', function () {
|
||||
expect(updatedVault.name).to.equal('new vault name')
|
||||
expect(updatedVault.description).to.equal('new vault description')
|
||||
|
||||
const promise = contactContext.resolveWhenAsymmetricMessageProcessingCompletes()
|
||||
await contactContext.sync()
|
||||
await promise
|
||||
contactContext.unlockSyncing()
|
||||
await contactContext.syncAndAwaitMessageProcessing()
|
||||
|
||||
const contactVault = contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
|
||||
expect(contactVault.name).to.equal('new vault name')
|
||||
@@ -65,7 +66,7 @@ describe('shared vaults', function () {
|
||||
expect(contactContext.keys.getPrimaryKeySystemRootKey(sharedVault.systemIdentifier)).to.be.undefined
|
||||
expect(contactContext.keys.getKeySystemItemsKeys(sharedVault.systemIdentifier)).to.be.empty
|
||||
|
||||
const recreatedContext = await Factory.createAppContextWithRealCrypto(contactContext.identifier)
|
||||
const recreatedContext = await Factory.createVaultsContextWithRealCrypto(contactContext.identifier)
|
||||
await recreatedContext.launch()
|
||||
|
||||
expect(recreatedContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })).to.be.undefined
|
||||
@@ -92,7 +93,7 @@ describe('shared vaults', function () {
|
||||
expect(contactContext.keys.getPrimaryKeySystemRootKey(sharedVault.systemIdentifier)).to.be.undefined
|
||||
expect(contactContext.keys.getKeySystemItemsKeys(sharedVault.systemIdentifier)).to.be.empty
|
||||
|
||||
const recreatedContext = await Factory.createAppContextWithRealCrypto(contactContext.identifier)
|
||||
const recreatedContext = await Factory.createVaultsContextWithRealCrypto(contactContext.identifier)
|
||||
await recreatedContext.launch()
|
||||
|
||||
expect(recreatedContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })).to.be.undefined
|
||||
@@ -127,4 +128,52 @@ describe('shared vaults', function () {
|
||||
|
||||
await deinitThirdPartyContext()
|
||||
})
|
||||
|
||||
it('syncing a shared vault exclusively should not retrieve non vault items', async () => {
|
||||
const { sharedVault, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInvite(context)
|
||||
|
||||
await contactContext.createSyncedNote('foo', 'bar')
|
||||
|
||||
const syncPromise = contactContext.awaitNextSyncSharedVaultFromScratchEvent()
|
||||
|
||||
await contactContext.application.sync.syncSharedVaultsFromScratch([sharedVault.sharing.sharedVaultUuid])
|
||||
|
||||
const syncResponse = await syncPromise
|
||||
|
||||
const expectedItems = ['key system items key']
|
||||
|
||||
expect(syncResponse.retrievedPayloads.length).to.equal(expectedItems.length)
|
||||
|
||||
await deinitContactContext()
|
||||
})
|
||||
|
||||
it('syncing a shared vault with note exclusively should retrieve note and items key', async () => {
|
||||
const { sharedVault, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(context)
|
||||
|
||||
const syncPromise = contactContext.awaitNextSyncSharedVaultFromScratchEvent()
|
||||
|
||||
await contactContext.application.sync.syncSharedVaultsFromScratch([sharedVault.sharing.sharedVaultUuid])
|
||||
|
||||
const syncResponse = await syncPromise
|
||||
|
||||
const expectedItems = ['key system items key', 'note']
|
||||
|
||||
expect(syncResponse.retrievedPayloads.length).to.equal(expectedItems.length)
|
||||
|
||||
await deinitContactContext()
|
||||
})
|
||||
|
||||
it('regular sync should not needlessly return vault items', async () => {
|
||||
await Collaboration.createSharedVault(context)
|
||||
|
||||
const promise = context.resolveWithSyncRetrievedPayloads()
|
||||
|
||||
await context.sync()
|
||||
|
||||
const retrievedPayloads = await promise
|
||||
|
||||
expect(retrievedPayloads.length).to.equal(0)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -17,7 +17,7 @@ describe('signatures', function () {
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
context = await Factory.createAppContextWithRealCrypto()
|
||||
context = await Factory.createVaultsContextWithRealCrypto()
|
||||
|
||||
await context.launch()
|
||||
await context.register()
|
||||
|
||||
@@ -17,7 +17,7 @@ describe('vaults', function () {
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
context = await Factory.createAppContextWithRealCrypto()
|
||||
context = await Factory.createVaultsContextWithRealCrypto()
|
||||
|
||||
await context.launch()
|
||||
|
||||
@@ -77,7 +77,7 @@ describe('vaults', function () {
|
||||
await vaults.moveItemToVault(vault, note)
|
||||
await context.deinit()
|
||||
|
||||
const recreatedContext = await Factory.createAppContextWithRealCrypto(appIdentifier)
|
||||
const recreatedContext = await Factory.createVaultsContextWithRealCrypto(appIdentifier)
|
||||
await recreatedContext.launch()
|
||||
|
||||
const updatedNote = recreatedContext.items.findItem(note.uuid)
|
||||
@@ -101,7 +101,7 @@ describe('vaults', function () {
|
||||
|
||||
await context.deinit()
|
||||
|
||||
const recreatedContext = await Factory.createAppContextWithRealCrypto(appIdentifier)
|
||||
const recreatedContext = await Factory.createVaultsContextWithRealCrypto(appIdentifier)
|
||||
await recreatedContext.launch()
|
||||
|
||||
const notes = recreatedContext.notes
|
||||
@@ -128,7 +128,7 @@ describe('vaults', function () {
|
||||
|
||||
await context.deinit()
|
||||
|
||||
const recreatedContext = await Factory.createAppContextWithRealCrypto(appIdentifier)
|
||||
const recreatedContext = await Factory.createVaultsContextWithRealCrypto(appIdentifier)
|
||||
await recreatedContext.launch()
|
||||
|
||||
const updatedNote = recreatedContext.items.findItem(note.uuid)
|
||||
@@ -181,7 +181,7 @@ describe('vaults', function () {
|
||||
await vaults.moveItemToVault(vault, note)
|
||||
await context.deinit()
|
||||
|
||||
const recreatedContext = await Factory.createAppContextWithRealCrypto(appIdentifier)
|
||||
const recreatedContext = await Factory.createVaultsContextWithRealCrypto(appIdentifier)
|
||||
await recreatedContext.launch()
|
||||
|
||||
const updatedNote = recreatedContext.items.findItem(note.uuid)
|
||||
|
||||
Reference in New Issue
Block a user