378 lines
15 KiB
JavaScript
378 lines
15 KiB
JavaScript
/* eslint-disable camelcase */
|
|
/* eslint-disable no-unused-expressions */
|
|
/* eslint-disable no-undef */
|
|
import { BaseItemCounts } from '../lib/BaseItemCounts.js'
|
|
import * as Factory from '../lib/factory.js'
|
|
chai.use(chaiAsPromised)
|
|
const expect = chai.expect
|
|
|
|
describe('app models', () => {
|
|
const sharedApplication = Factory.createApplicationWithFakeCrypto()
|
|
|
|
before(async function () {
|
|
localStorage.clear()
|
|
await Factory.initializeApplication(sharedApplication)
|
|
})
|
|
|
|
after(async function () {
|
|
localStorage.clear()
|
|
await Factory.safeDeinit(sharedApplication)
|
|
})
|
|
|
|
beforeEach(async function () {
|
|
this.expectedItemCount = BaseItemCounts.DefaultItems
|
|
this.context = await Factory.createAppContext()
|
|
this.application = this.context.application
|
|
await this.context.launch()
|
|
})
|
|
|
|
afterEach(async function () {
|
|
await Factory.safeDeinit(this.application)
|
|
})
|
|
|
|
it('payloads should be defined', () => {
|
|
expect(sharedApplication.payloads).to.be.ok
|
|
})
|
|
|
|
it('item should be defined', () => {
|
|
expect(GenericItem).to.be.ok
|
|
})
|
|
|
|
it('item content should be assigned', () => {
|
|
const params = Factory.createNotePayload()
|
|
const item = CreateDecryptedItemFromPayload(params)
|
|
expect(item.content.title).to.equal(params.content.title)
|
|
})
|
|
|
|
it('should default updated_at to 1970 and created_at to the present', () => {
|
|
const params = Factory.createNotePayload()
|
|
const item = CreateDecryptedItemFromPayload(params)
|
|
const epoch = new Date(0)
|
|
expect(item.serverUpdatedAt - epoch).to.equal(0)
|
|
expect(item.created_at - epoch).to.be.above(0)
|
|
|
|
const presentThresholdMs = 10
|
|
expect(new Date() - item.created_at).to.be.below(presentThresholdMs)
|
|
})
|
|
|
|
it('handles delayed mapping', async function () {
|
|
const params1 = Factory.createNotePayload()
|
|
const params2 = Factory.createNotePayload()
|
|
|
|
const mutated = new DecryptedPayload({
|
|
...params1,
|
|
content: {
|
|
...params1.content,
|
|
references: [
|
|
{
|
|
uuid: params2.uuid,
|
|
content_type: params2.content_type,
|
|
},
|
|
],
|
|
},
|
|
})
|
|
|
|
await this.application.mutator.emitItemsFromPayloads([mutated], PayloadEmitSource.LocalChanged)
|
|
await this.application.mutator.emitItemsFromPayloads([params2], PayloadEmitSource.LocalChanged)
|
|
|
|
const item1 = this.application.items.findItem(params1.uuid)
|
|
const item2 = this.application.items.findItem(params2.uuid)
|
|
|
|
expect(item1.content.references.length).to.equal(1)
|
|
expect(item2.content.references.length).to.equal(0)
|
|
|
|
expect(this.application.items.itemsReferencingItem(item1).length).to.equal(0)
|
|
expect(this.application.items.itemsReferencingItem(item2).length).to.equal(1)
|
|
})
|
|
|
|
it('mapping an item twice shouldnt cause problems', async function () {
|
|
const payload = Factory.createNotePayload()
|
|
const mutated = new DecryptedPayload({
|
|
...payload,
|
|
content: {
|
|
...payload.content,
|
|
foo: 'bar',
|
|
},
|
|
})
|
|
|
|
let items = await this.application.mutator.emitItemsFromPayloads([mutated], PayloadEmitSource.LocalChanged)
|
|
let item = items[0]
|
|
expect(item).to.be.ok
|
|
|
|
items = await this.application.mutator.emitItemsFromPayloads([mutated], PayloadEmitSource.LocalChanged)
|
|
item = items[0]
|
|
|
|
expect(item.content.foo).to.equal('bar')
|
|
expect(this.application.items.getDisplayableNotes().length).to.equal(1)
|
|
})
|
|
|
|
it('mapping item twice should preserve references', async function () {
|
|
const item1 = await Factory.createMappedNote(this.application)
|
|
const item2 = await Factory.createMappedNote(this.application)
|
|
|
|
await this.application.mutator.changeItem(item1, (mutator) => {
|
|
mutator.e2ePendingRefactor_addItemAsRelationship(item2)
|
|
})
|
|
await this.application.mutator.changeItem(item2, (mutator) => {
|
|
mutator.e2ePendingRefactor_addItemAsRelationship(item1)
|
|
})
|
|
|
|
const refreshedItem = this.application.items.findItem(item1.uuid)
|
|
expect(refreshedItem.content.references.length).to.equal(1)
|
|
})
|
|
|
|
it('fixes relationship integrity', async function () {
|
|
var item1 = await Factory.createMappedNote(this.application)
|
|
var item2 = await Factory.createMappedNote(this.application)
|
|
|
|
await this.application.mutator.changeItem(item1, (mutator) => {
|
|
mutator.e2ePendingRefactor_addItemAsRelationship(item2)
|
|
})
|
|
await this.application.mutator.changeItem(item2, (mutator) => {
|
|
mutator.e2ePendingRefactor_addItemAsRelationship(item1)
|
|
})
|
|
|
|
const refreshedItem1 = this.application.items.findItem(item1.uuid)
|
|
const refreshedItem2 = this.application.items.findItem(item2.uuid)
|
|
|
|
expect(refreshedItem1.content.references.length).to.equal(1)
|
|
expect(refreshedItem2.content.references.length).to.equal(1)
|
|
|
|
const damagedPayload = refreshedItem1.payload.copy({
|
|
content: {
|
|
...refreshedItem1.content,
|
|
// damage references of one object
|
|
references: [],
|
|
},
|
|
})
|
|
await this.application.mutator.emitItemsFromPayloads([damagedPayload], PayloadEmitSource.LocalChanged)
|
|
|
|
const refreshedItem1_2 = this.application.items.findItem(item1.uuid)
|
|
const refreshedItem2_2 = this.application.items.findItem(item2.uuid)
|
|
|
|
expect(refreshedItem1_2.content.references.length).to.equal(0)
|
|
expect(refreshedItem2_2.content.references.length).to.equal(1)
|
|
})
|
|
|
|
it('creating and removing relationships between two items should have valid references', async function () {
|
|
var item1 = await Factory.createMappedNote(this.application)
|
|
var item2 = await Factory.createMappedNote(this.application)
|
|
await this.application.mutator.changeItem(item1, (mutator) => {
|
|
mutator.e2ePendingRefactor_addItemAsRelationship(item2)
|
|
})
|
|
await this.application.mutator.changeItem(item2, (mutator) => {
|
|
mutator.e2ePendingRefactor_addItemAsRelationship(item1)
|
|
})
|
|
|
|
const refreshedItem1 = this.application.items.findItem(item1.uuid)
|
|
const refreshedItem2 = this.application.items.findItem(item2.uuid)
|
|
|
|
expect(refreshedItem1.content.references.length).to.equal(1)
|
|
expect(refreshedItem2.content.references.length).to.equal(1)
|
|
|
|
expect(this.application.items.itemsReferencingItem(item1)).to.include(refreshedItem2)
|
|
expect(this.application.items.itemsReferencingItem(item2)).to.include(refreshedItem1)
|
|
|
|
await this.application.mutator.changeItem(item1, (mutator) => {
|
|
mutator.removeItemAsRelationship(item2)
|
|
})
|
|
await this.application.mutator.changeItem(item2, (mutator) => {
|
|
mutator.removeItemAsRelationship(item1)
|
|
})
|
|
|
|
const refreshedItem1_2 = this.application.items.findItem(item1.uuid)
|
|
const refreshedItem2_2 = this.application.items.findItem(item2.uuid)
|
|
|
|
expect(refreshedItem1_2.content.references.length).to.equal(0)
|
|
expect(refreshedItem2_2.content.references.length).to.equal(0)
|
|
|
|
expect(this.application.items.itemsReferencingItem(item1).length).to.equal(0)
|
|
expect(this.application.items.itemsReferencingItem(item2).length).to.equal(0)
|
|
})
|
|
|
|
it('properly duplicates item with no relationships', async function () {
|
|
const item = await Factory.createMappedNote(this.application)
|
|
const duplicate = await this.application.mutator.duplicateItem(item)
|
|
expect(duplicate.uuid).to.not.equal(item.uuid)
|
|
expect(item.isItemContentEqualWith(duplicate)).to.equal(true)
|
|
expect(item.created_at.toISOString()).to.equal(duplicate.created_at.toISOString())
|
|
expect(item.content_type).to.equal(duplicate.content_type)
|
|
})
|
|
|
|
it('properly duplicates item with relationships', async function () {
|
|
const item1 = await Factory.createMappedNote(this.application)
|
|
const item2 = await Factory.createMappedNote(this.application)
|
|
|
|
const refreshedItem1 = await this.application.mutator.changeItem(item1, (mutator) => {
|
|
mutator.e2ePendingRefactor_addItemAsRelationship(item2)
|
|
})
|
|
|
|
expect(refreshedItem1.content.references.length).to.equal(1)
|
|
|
|
const duplicate = await this.application.mutator.duplicateItem(item1)
|
|
expect(duplicate.uuid).to.not.equal(item1.uuid)
|
|
expect(duplicate.content.references.length).to.equal(1)
|
|
|
|
expect(this.application.items.itemsReferencingItem(item1).length).to.equal(0)
|
|
expect(this.application.items.itemsReferencingItem(item2).length).to.equal(2)
|
|
|
|
const refreshedItem1_2 = this.application.items.findItem(item1.uuid)
|
|
expect(refreshedItem1_2.isItemContentEqualWith(duplicate)).to.equal(true)
|
|
expect(refreshedItem1_2.created_at.toISOString()).to.equal(duplicate.created_at.toISOString())
|
|
expect(refreshedItem1_2.content_type).to.equal(duplicate.content_type)
|
|
})
|
|
|
|
it('removing references should update cross-refs', async function () {
|
|
const item1 = await Factory.createMappedNote(this.application)
|
|
const item2 = await Factory.createMappedNote(this.application)
|
|
const refreshedItem1 = await this.application.mutator.changeItem(item1, (mutator) => {
|
|
mutator.e2ePendingRefactor_addItemAsRelationship(item2)
|
|
})
|
|
|
|
const refreshedItem1_2 = await this.application.mutator.emitItemFromPayload(
|
|
refreshedItem1.payloadRepresentation({
|
|
deleted: true,
|
|
content: {
|
|
...refreshedItem1.payload.content,
|
|
references: [],
|
|
},
|
|
}),
|
|
PayloadEmitSource.LocalChanged,
|
|
)
|
|
|
|
expect(this.application.items.itemsReferencingItem(item2).length).to.equal(0)
|
|
expect(this.application.items.itemsReferencingItem(item1).length).to.equal(0)
|
|
expect(refreshedItem1_2.content.references.length).to.equal(0)
|
|
})
|
|
|
|
it('properly handles single item uuid alternation', async function () {
|
|
const item1 = await Factory.createMappedNote(this.application)
|
|
const item2 = await Factory.createMappedNote(this.application)
|
|
|
|
const refreshedItem1 = await this.application.mutator.changeItem(item1, (mutator) => {
|
|
mutator.e2ePendingRefactor_addItemAsRelationship(item2)
|
|
})
|
|
|
|
expect(refreshedItem1.content.references.length).to.equal(1)
|
|
expect(this.application.items.itemsReferencingItem(item2).length).to.equal(1)
|
|
|
|
const alternatedItem = await Factory.alternateUuidForItem(this.application, item1.uuid)
|
|
const refreshedItem1_2 = this.application.items.findItem(item1.uuid)
|
|
expect(refreshedItem1_2).to.not.be.ok
|
|
|
|
expect(this.application.items.getDisplayableNotes().length).to.equal(2)
|
|
|
|
expect(alternatedItem.content.references.length).to.equal(1)
|
|
expect(this.application.items.itemsReferencingItem(alternatedItem.uuid).length).to.equal(0)
|
|
|
|
expect(this.application.items.itemsReferencingItem(item2).length).to.equal(1)
|
|
|
|
expect(alternatedItem.isReferencingItem(item2)).to.equal(true)
|
|
expect(alternatedItem.dirty).to.equal(true)
|
|
})
|
|
|
|
it('alterating uuid of item should fill its duplicateOf value', async function () {
|
|
const item1 = await Factory.createMappedNote(this.application)
|
|
const alternatedItem = await Factory.alternateUuidForItem(this.application, item1.uuid)
|
|
expect(alternatedItem.duplicateOf).to.equal(item1.uuid)
|
|
})
|
|
|
|
it('alterating itemskey uuid should update errored items encrypted with that key', async function () {
|
|
const item1 = await Factory.createMappedNote(this.application)
|
|
const itemsKey = this.application.items.getDisplayableItemsKeys()[0]
|
|
|
|
/** Encrypt item1 and emit as errored so it persists with items_key_id */
|
|
const encrypted = await this.application.encryption.encryptSplitSingle({
|
|
usesItemsKeyWithKeyLookup: {
|
|
items: [item1.payload],
|
|
},
|
|
})
|
|
const errored = encrypted.copy({
|
|
errorDecrypting: true,
|
|
waitingForKey: true,
|
|
})
|
|
|
|
await this.application.payloads.emitPayload(errored)
|
|
|
|
expect(this.application.payloads.findOne(item1.uuid).errorDecrypting).to.equal(true)
|
|
expect(this.application.payloads.findOne(item1.uuid).items_key_id).to.equal(itemsKey.uuid)
|
|
|
|
sinon.stub(this.application.encryption.itemsEncryption, 'decryptErroredItemPayloads').callsFake(() => {
|
|
// prevent auto decryption
|
|
})
|
|
|
|
const alternatedKey = await Factory.alternateUuidForItem(this.application, itemsKey.uuid)
|
|
const updatedPayload = this.application.payloads.findOne(item1.uuid)
|
|
|
|
expect(updatedPayload.items_key_id).to.equal(alternatedKey.uuid)
|
|
})
|
|
|
|
it('properly handles mutli item uuid alternation', async function () {
|
|
const item1 = await Factory.createMappedNote(this.application)
|
|
const item2 = await Factory.createMappedNote(this.application)
|
|
this.expectedItemCount += 2
|
|
|
|
await this.application.mutator.changeItem(item1, (mutator) => {
|
|
mutator.e2ePendingRefactor_addItemAsRelationship(item2)
|
|
})
|
|
|
|
expect(this.application.items.itemsReferencingItem(item2).length).to.equal(1)
|
|
|
|
const alternatedItem1 = await Factory.alternateUuidForItem(this.application, item1.uuid)
|
|
const alternatedItem2 = await Factory.alternateUuidForItem(this.application, item2.uuid)
|
|
|
|
expect(this.application.items.items.length).to.equal(this.expectedItemCount)
|
|
|
|
expect(item1.uuid).to.not.equal(alternatedItem1.uuid)
|
|
expect(item2.uuid).to.not.equal(alternatedItem2.uuid)
|
|
|
|
const refreshedAltItem1 = this.application.items.findItem(alternatedItem1.uuid)
|
|
expect(refreshedAltItem1.content.references.length).to.equal(1)
|
|
expect(refreshedAltItem1.content.references[0].uuid).to.equal(alternatedItem2.uuid)
|
|
expect(alternatedItem2.content.references.length).to.equal(0)
|
|
|
|
expect(this.application.items.itemsReferencingItem(alternatedItem2).length).to.equal(1)
|
|
|
|
expect(refreshedAltItem1.isReferencingItem(alternatedItem2)).to.equal(true)
|
|
expect(alternatedItem2.isReferencingItem(refreshedAltItem1)).to.equal(false)
|
|
expect(refreshedAltItem1.dirty).to.equal(true)
|
|
})
|
|
|
|
it('maintains referencing relationships when duplicating', async function () {
|
|
const tag = await Factory.createMappedTag(this.application)
|
|
const note = await Factory.createMappedNote(this.application)
|
|
const refreshedTag = await this.application.mutator.changeItem(tag, (mutator) => {
|
|
mutator.e2ePendingRefactor_addItemAsRelationship(note)
|
|
})
|
|
|
|
expect(refreshedTag.content.references.length).to.equal(1)
|
|
|
|
const noteCopy = await this.application.mutator.duplicateItem(note)
|
|
expect(note.uuid).to.not.equal(noteCopy.uuid)
|
|
|
|
expect(this.application.items.getDisplayableNotes().length).to.equal(2)
|
|
expect(this.application.items.getDisplayableTags().length).to.equal(1)
|
|
|
|
expect(note.content.references.length).to.equal(0)
|
|
expect(noteCopy.content.references.length).to.equal(0)
|
|
const refreshedTag_2 = this.application.items.findItem(tag.uuid)
|
|
expect(refreshedTag_2.content.references.length).to.equal(2)
|
|
})
|
|
|
|
it('maintains editor reference when duplicating note', async function () {
|
|
const component = await this.application.mutator.createItem(
|
|
ContentType.TYPES.Component,
|
|
{ area: ComponentArea.Editor, package_info: { identifier: 'foo-editor' } },
|
|
true,
|
|
)
|
|
const note = await Factory.insertItemWithOverride(this.application, ContentType.TYPES.Note, {
|
|
editorIdentifier: 'foo-editor',
|
|
})
|
|
|
|
expect(this.application.componentManager.editorForNote(note).uniqueIdentifier).to.equal(component.uuid)
|
|
|
|
const duplicate = await this.application.mutator.duplicateItem(note, true)
|
|
expect(this.application.componentManager.editorForNote(duplicate).uniqueIdentifier).to.equal(component.uuid)
|
|
})
|
|
})
|