feat: add snjs package
This commit is contained in:
1004
packages/snjs/mocha/sync_tests/conflicting.test.js
Normal file
1004
packages/snjs/mocha/sync_tests/conflicting.test.js
Normal file
File diff suppressed because it is too large
Load Diff
80
packages/snjs/mocha/sync_tests/integrity.test.js
Normal file
80
packages/snjs/mocha/sync_tests/integrity.test.js
Normal file
@@ -0,0 +1,80 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import * as Factory from '../lib/factory.js'
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('sync integrity', () => {
|
||||
const BASE_ITEM_COUNT = 2 /** Default items key, user preferences */
|
||||
|
||||
before(function () {
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
after(function () {
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
this.expectedItemCount = BASE_ITEM_COUNT
|
||||
this.application = await Factory.createInitAppWithFakeCrypto()
|
||||
this.email = UuidGenerator.GenerateUuid()
|
||||
this.password = UuidGenerator.GenerateUuid()
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
})
|
||||
})
|
||||
|
||||
const awaitSyncEventPromise = (application, targetEvent) => {
|
||||
return new Promise((resolve) => {
|
||||
application.syncService.addEventObserver((event) => {
|
||||
if (event === targetEvent) {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
afterEach(async function () {
|
||||
expect(this.application.syncService.isOutOfSync()).to.equal(false)
|
||||
const rawPayloads = await this.application.diskStorageService.getAllRawPayloads()
|
||||
expect(rawPayloads.length).to.equal(this.expectedItemCount)
|
||||
await Factory.safeDeinit(this.application)
|
||||
})
|
||||
|
||||
it('should detect when out of sync', async function () {
|
||||
const item = await this.application.itemManager.emitItemFromPayload(
|
||||
Factory.createNotePayload(),
|
||||
PayloadEmitSource.LocalChanged,
|
||||
)
|
||||
this.expectedItemCount++
|
||||
|
||||
const didEnterOutOfSync = awaitSyncEventPromise(this.application, SyncEvent.EnterOutOfSync)
|
||||
await this.application.syncService.sync({ checkIntegrity: true })
|
||||
|
||||
await this.application.itemManager.removeItemLocally(item)
|
||||
await this.application.syncService.sync({ checkIntegrity: true, awaitAll: true })
|
||||
|
||||
await didEnterOutOfSync
|
||||
})
|
||||
|
||||
it('should self heal after out of sync', async function () {
|
||||
const item = await this.application.itemManager.emitItemFromPayload(
|
||||
Factory.createNotePayload(),
|
||||
PayloadEmitSource.LocalChanged,
|
||||
)
|
||||
this.expectedItemCount++
|
||||
|
||||
const didEnterOutOfSync = awaitSyncEventPromise(this.application, SyncEvent.EnterOutOfSync)
|
||||
const didExitOutOfSync = awaitSyncEventPromise(this.application, SyncEvent.ExitOutOfSync)
|
||||
|
||||
await this.application.syncService.sync({ checkIntegrity: true })
|
||||
await this.application.itemManager.removeItemLocally(item)
|
||||
await this.application.syncService.sync({ checkIntegrity: true, awaitAll: true })
|
||||
|
||||
await Promise.all([didEnterOutOfSync, didExitOutOfSync])
|
||||
expect(this.application.syncService.isOutOfSync()).to.equal(false)
|
||||
})
|
||||
})
|
||||
168
packages/snjs/mocha/sync_tests/notes_tags.test.js
Normal file
168
packages/snjs/mocha/sync_tests/notes_tags.test.js
Normal file
@@ -0,0 +1,168 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import * as Factory from '../lib/factory.js'
|
||||
import { createRelatedNoteTagPairPayload } from '../lib/Items.js'
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('notes + tags syncing', function () {
|
||||
const syncOptions = {
|
||||
checkIntegrity: true,
|
||||
awaitAll: true,
|
||||
}
|
||||
|
||||
after(function () {
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
this.application = await Factory.createInitAppWithFakeCrypto()
|
||||
Factory.disableIntegrityAutoHeal(this.application)
|
||||
const email = UuidGenerator.GenerateUuid()
|
||||
const password = UuidGenerator.GenerateUuid()
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email,
|
||||
password,
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await Factory.safeDeinit(this.application)
|
||||
})
|
||||
|
||||
it('syncing an item then downloading it should include items_key_id', async function () {
|
||||
const note = await Factory.createMappedNote(this.application)
|
||||
await this.application.itemManager.setItemDirty(note)
|
||||
await this.application.syncService.sync(syncOptions)
|
||||
await this.application.payloadManager.resetState()
|
||||
await this.application.itemManager.resetState()
|
||||
await this.application.syncService.clearSyncPositionTokens()
|
||||
await this.application.syncService.sync(syncOptions)
|
||||
const downloadedNote = this.application.itemManager.getDisplayableNotes()[0]
|
||||
expect(downloadedNote.items_key_id).to.not.be.ok
|
||||
// Allow time for waitingForKey
|
||||
await Factory.sleep(0.1)
|
||||
expect(downloadedNote.title).to.be.ok
|
||||
expect(downloadedNote.content.text).to.be.ok
|
||||
})
|
||||
|
||||
it('syncing a note many times does not cause duplication', async function () {
|
||||
const pair = createRelatedNoteTagPairPayload()
|
||||
const notePayload = pair[0]
|
||||
const tagPayload = pair[1]
|
||||
|
||||
await this.application.itemManager.emitItemsFromPayloads([notePayload, tagPayload], PayloadEmitSource.LocalChanged)
|
||||
const note = this.application.itemManager.getItems([ContentType.Note])[0]
|
||||
const tag = this.application.itemManager.getItems([ContentType.Tag])[0]
|
||||
expect(this.application.itemManager.getDisplayableNotes().length).to.equal(1)
|
||||
expect(this.application.itemManager.getDisplayableTags().length).to.equal(1)
|
||||
|
||||
for (let i = 0; i < 9; i++) {
|
||||
await this.application.itemManager.setItemsDirty([note, tag])
|
||||
await this.application.syncService.sync(syncOptions)
|
||||
this.application.syncService.clearSyncPositionTokens()
|
||||
expect(tag.content.references.length).to.equal(1)
|
||||
expect(this.application.itemManager.itemsReferencingItem(note).length).to.equal(1)
|
||||
expect(tag.noteCount).to.equal(1)
|
||||
expect(this.application.itemManager.getDisplayableNotes().length).to.equal(1)
|
||||
expect(this.application.itemManager.getDisplayableTags().length).to.equal(1)
|
||||
console.warn('Waiting 0.1s...')
|
||||
await Factory.sleep(0.1)
|
||||
}
|
||||
}).timeout(20000)
|
||||
|
||||
it('handles signing in and merging data', async function () {
|
||||
const pair = createRelatedNoteTagPairPayload()
|
||||
const notePayload = pair[0]
|
||||
const tagPayload = pair[1]
|
||||
await this.application.itemManager.emitItemsFromPayloads([notePayload, tagPayload], PayloadEmitSource.LocalChanged)
|
||||
const originalNote = this.application.itemManager.getDisplayableNotes()[0]
|
||||
const originalTag = this.application.itemManager.getDisplayableTags()[0]
|
||||
await this.application.itemManager.setItemsDirty([originalNote, originalTag])
|
||||
|
||||
await this.application.syncService.sync(syncOptions)
|
||||
|
||||
expect(originalTag.content.references.length).to.equal(1)
|
||||
expect(originalTag.noteCount).to.equal(1)
|
||||
expect(this.application.itemManager.itemsReferencingItem(originalNote).length).to.equal(1)
|
||||
|
||||
// when signing in, all local items are cleared from storage (but kept in memory; to clear desktop logs),
|
||||
// then resaved with alternated uuids.
|
||||
await this.application.diskStorageService.clearAllPayloads()
|
||||
await this.application.syncService.markAllItemsAsNeedingSyncAndPersist()
|
||||
|
||||
expect(this.application.itemManager.getDisplayableNotes().length).to.equal(1)
|
||||
expect(this.application.itemManager.getDisplayableTags().length).to.equal(1)
|
||||
|
||||
const note = this.application.itemManager.getDisplayableNotes()[0]
|
||||
const tag = this.application.itemManager.getDisplayableTags()[0]
|
||||
|
||||
expect(tag.content.references.length).to.equal(1)
|
||||
expect(note.content.references.length).to.equal(0)
|
||||
|
||||
expect(tag.noteCount).to.equal(1)
|
||||
expect(this.application.itemManager.itemsReferencingItem(note).length).to.equal(1)
|
||||
})
|
||||
|
||||
it('duplicating a tag should maintian its relationships', async function () {
|
||||
const pair = createRelatedNoteTagPairPayload()
|
||||
const notePayload = pair[0]
|
||||
const tagPayload = pair[1]
|
||||
await this.application.itemManager.emitItemsFromPayloads([notePayload, tagPayload], PayloadEmitSource.LocalChanged)
|
||||
let note = this.application.itemManager.getDisplayableNotes()[0]
|
||||
let tag = this.application.itemManager.getDisplayableTags()[0]
|
||||
expect(this.application.itemManager.itemsReferencingItem(note).length).to.equal(1)
|
||||
|
||||
await this.application.itemManager.setItemsDirty([note, tag])
|
||||
await this.application.syncService.sync(syncOptions)
|
||||
await this.application.syncService.clearSyncPositionTokens()
|
||||
|
||||
note = this.application.itemManager.findItem(note.uuid)
|
||||
tag = this.application.itemManager.findItem(tag.uuid)
|
||||
|
||||
expect(note.dirty).to.equal(false)
|
||||
expect(tag.dirty).to.equal(false)
|
||||
|
||||
expect(this.application.itemManager.getDisplayableNotes().length).to.equal(1)
|
||||
expect(this.application.itemManager.getDisplayableTags().length).to.equal(1)
|
||||
|
||||
await Factory.changePayloadTimeStampAndSync(
|
||||
this.application,
|
||||
tag.payload,
|
||||
Factory.dateToMicroseconds(Factory.yesterday()),
|
||||
{
|
||||
title: `${Math.random()}`,
|
||||
},
|
||||
syncOptions,
|
||||
)
|
||||
|
||||
tag = this.application.itemManager.findItem(tag.uuid)
|
||||
|
||||
// tag should now be conflicted and a copy created
|
||||
expect(this.application.itemManager.getDisplayableNotes().length).to.equal(1)
|
||||
expect(this.application.itemManager.getDisplayableTags().length).to.equal(2)
|
||||
|
||||
const tags = this.application.itemManager.getDisplayableTags()
|
||||
const conflictedTag = tags.find((tag) => {
|
||||
return !!tag.content.conflict_of
|
||||
})
|
||||
const originalTag = tags.find((tag) => {
|
||||
return tag !== conflictedTag
|
||||
})
|
||||
|
||||
expect(conflictedTag.uuid).to.not.equal(originalTag.uuid)
|
||||
|
||||
expect(originalTag.uuid).to.equal(tag.uuid)
|
||||
expect(conflictedTag.content.conflict_of).to.equal(originalTag.uuid)
|
||||
expect(conflictedTag.noteCount).to.equal(originalTag.noteCount)
|
||||
|
||||
expect(this.application.itemManager.itemsReferencingItem(conflictedTag).length).to.equal(0)
|
||||
expect(this.application.itemManager.itemsReferencingItem(originalTag).length).to.equal(0)
|
||||
|
||||
// Two tags now link to this note
|
||||
const referencingItems = this.application.itemManager.itemsReferencingItem(note)
|
||||
expect(referencingItems.length).to.equal(2)
|
||||
expect(referencingItems[0]).to.not.equal(referencingItems[1])
|
||||
}).timeout(10000)
|
||||
})
|
||||
91
packages/snjs/mocha/sync_tests/offline.test.js
Normal file
91
packages/snjs/mocha/sync_tests/offline.test.js
Normal file
@@ -0,0 +1,91 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import * as Factory from '../lib/factory.js'
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('offline syncing', () => {
|
||||
const BASE_ITEM_COUNT = 2 /** Default items key, user preferences */
|
||||
|
||||
const syncOptions = {
|
||||
checkIntegrity: true,
|
||||
awaitAll: true,
|
||||
}
|
||||
|
||||
beforeEach(async function () {
|
||||
this.expectedItemCount = BASE_ITEM_COUNT
|
||||
this.application = await Factory.createInitAppWithFakeCrypto()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
expect(this.application.syncService.isOutOfSync()).to.equal(false)
|
||||
await Factory.safeDeinit(this.application)
|
||||
})
|
||||
|
||||
before(async function () {
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
after(async function () {
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
it('should sync item with no passcode', async function () {
|
||||
let note = await Factory.createMappedNote(this.application)
|
||||
expect(this.application.itemManager.getDirtyItems().length).to.equal(1)
|
||||
|
||||
const rawPayloads1 = await this.application.diskStorageService.getAllRawPayloads()
|
||||
expect(rawPayloads1.length).to.equal(this.expectedItemCount)
|
||||
|
||||
await this.application.syncService.sync(syncOptions)
|
||||
|
||||
note = this.application.items.findItem(note.uuid)
|
||||
|
||||
/** In rare cases a sync can complete so fast that the dates are equal; this is ok. */
|
||||
expect(note.lastSyncEnd).to.be.at.least(note.lastSyncBegan)
|
||||
|
||||
this.expectedItemCount++
|
||||
|
||||
expect(this.application.itemManager.getDirtyItems().length).to.equal(0)
|
||||
|
||||
const rawPayloads2 = await this.application.diskStorageService.getAllRawPayloads()
|
||||
expect(rawPayloads2.length).to.equal(this.expectedItemCount)
|
||||
|
||||
const itemsKeyRaw = (await Factory.getStoragePayloadsOfType(this.application, ContentType.ItemsKey))[0]
|
||||
const noteRaw = (await Factory.getStoragePayloadsOfType(this.application, ContentType.Note))[0]
|
||||
|
||||
/** Encrypts with default items key */
|
||||
expect(typeof noteRaw.content).to.equal('string')
|
||||
|
||||
/** Not encrypted as no passcode/root key */
|
||||
expect(typeof itemsKeyRaw.content).to.equal('object')
|
||||
})
|
||||
|
||||
it('should sync item encrypted with passcode', async function () {
|
||||
await this.application.addPasscode('foobar')
|
||||
await Factory.createMappedNote(this.application)
|
||||
expect(this.application.itemManager.getDirtyItems().length).to.equal(1)
|
||||
const rawPayloads1 = await this.application.diskStorageService.getAllRawPayloads()
|
||||
expect(rawPayloads1.length).to.equal(this.expectedItemCount)
|
||||
|
||||
await this.application.syncService.sync(syncOptions)
|
||||
this.expectedItemCount++
|
||||
|
||||
expect(this.application.itemManager.getDirtyItems().length).to.equal(0)
|
||||
const rawPayloads2 = await this.application.diskStorageService.getAllRawPayloads()
|
||||
expect(rawPayloads2.length).to.equal(this.expectedItemCount)
|
||||
|
||||
const payload = rawPayloads2[0]
|
||||
expect(typeof payload.content).to.equal('string')
|
||||
expect(payload.content.startsWith(this.application.protocolService.getLatestVersion())).to.equal(true)
|
||||
})
|
||||
|
||||
it('signing out while offline should succeed', async function () {
|
||||
await Factory.createMappedNote(this.application)
|
||||
this.expectedItemCount++
|
||||
await this.application.syncService.sync(syncOptions)
|
||||
this.application = await Factory.signOutApplicationAndReturnNew(this.application)
|
||||
expect(this.application.noAccount()).to.equal(true)
|
||||
expect(this.application.getUser()).to.not.be.ok
|
||||
})
|
||||
})
|
||||
1052
packages/snjs/mocha/sync_tests/online.test.js
Normal file
1052
packages/snjs/mocha/sync_tests/online.test.js
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user