From 9de994bcba1276aeea4214e9a58c37a1c6be44c3 Mon Sep 17 00:00:00 2001 From: Aman Harwara Date: Tue, 26 Sep 2023 18:37:17 +0530 Subject: [PATCH] chore: add tests for moving items into vault (#2539) --- .../src/Domain/Vault/VaultService.spec.ts | 238 ++++++++++++++++++ 1 file changed, 238 insertions(+) create mode 100644 packages/services/src/Domain/Vault/VaultService.spec.ts diff --git a/packages/services/src/Domain/Vault/VaultService.spec.ts b/packages/services/src/Domain/Vault/VaultService.spec.ts new file mode 100644 index 000000000..0426dfaa4 --- /dev/null +++ b/packages/services/src/Domain/Vault/VaultService.spec.ts @@ -0,0 +1,238 @@ +import { + DecryptedItemInterface, + DecryptedPayload, + FileContent, + FileItem, + FillItemContent, + NoteContent, + PayloadSource, + PayloadTimestampDefaults, + SNNote, + SNTag, + TagContent, + VaultListingInterface, +} from '@standardnotes/models' +import { + SyncServiceInterface, + ItemManagerInterface, + InternalEventBusInterface, + VaultService, + AlertService, + ChangeVaultKeyOptions, + CreateVault, + DeleteVault, + GetVault, + GetVaults, + IsVaultOwner, + MoveItemsToVault, + MutatorClientInterface, + RemoveItemFromVault, + RotateVaultKey, + SendVaultDataChangedMessage, + ValidateVaultPassword, + VaultLockServiceInterface, +} from '../..' +import { AuthorizeVaultDeletion } from './UseCase/AuthorizeVaultDeletion' +import { ContentType } from '@standardnotes/domain-core' + +let currentId = 0 + +const mockUuid = () => { + return `${currentId++}` +} + +const createNoteWithContent = (content: Partial, createdAt?: Date): SNNote => { + return new SNNote( + new DecryptedPayload( + { + uuid: mockUuid(), + content_type: ContentType.TYPES.Note, + content: FillItemContent(content), + ...PayloadTimestampDefaults(), + created_at: createdAt || new Date(), + }, + PayloadSource.Constructor, + ), + ) +} + +const createFile = (name: string) => { + return new FileItem( + new DecryptedPayload({ + uuid: mockUuid(), + content_type: ContentType.TYPES.File, + content: FillItemContent({ + name: name, + }), + ...PayloadTimestampDefaults(), + }), + ) +} + +const createTagWithContent = (content: Partial, key_system_identifier?: string): SNTag => { + return new SNTag( + new DecryptedPayload( + { + uuid: mockUuid(), + content_type: ContentType.TYPES.Tag, + content: FillItemContent(content), + key_system_identifier, + ...PayloadTimestampDefaults(), + }, + PayloadSource.Constructor, + ), + ) +} + +describe('VaultService', () => { + let sync: SyncServiceInterface + let items: ItemManagerInterface + let mutator: MutatorClientInterface + let vaultLocks: VaultLockServiceInterface + let alerts: AlertService + let _getVault: GetVault + let _getVaults: GetVaults + let _changeVaultKeyOptions: ChangeVaultKeyOptions + let _moveItemsToVault: MoveItemsToVault + let _createVault: CreateVault + let _removeItemFromVault: RemoveItemFromVault + let _deleteVault: DeleteVault + let _rotateVaultKey: RotateVaultKey + let _sendVaultDataChangeMessage: SendVaultDataChangedMessage + let _isVaultOwner: IsVaultOwner + let _validateVaultPassword: ValidateVaultPassword + let _authorizeVaultDeletion: AuthorizeVaultDeletion + let eventBus: InternalEventBusInterface + let service: VaultService + + beforeEach(() => { + sync = {} as jest.Mocked + items = {} as jest.Mocked + mutator = {} as jest.Mocked + + vaultLocks = {} as jest.Mocked + vaultLocks.isVaultLocked = jest.fn() + + alerts = {} as jest.Mocked + alerts.alertV2 = jest.fn().mockResolvedValue({}) + + eventBus = {} as jest.Mocked + + _getVault = {} as jest.Mocked + _getVaults = {} as jest.Mocked + _changeVaultKeyOptions = {} as jest.Mocked + _moveItemsToVault = {} as jest.Mocked + _moveItemsToVault.execute = jest.fn().mockResolvedValue({}) + _createVault = {} as jest.Mocked + _removeItemFromVault = {} as jest.Mocked + _deleteVault = {} as jest.Mocked + _rotateVaultKey = {} as jest.Mocked + _sendVaultDataChangeMessage = {} as jest.Mocked + _isVaultOwner = {} as jest.Mocked + _validateVaultPassword = {} as jest.Mocked + _authorizeVaultDeletion = {} as jest.Mocked + + service = new VaultService( + sync, + items, + mutator, + vaultLocks, + alerts, + _getVault, + _getVaults, + _changeVaultKeyOptions, + _moveItemsToVault, + _createVault, + _removeItemFromVault, + _deleteVault, + _rotateVaultKey, + _sendVaultDataChangeMessage, + _isVaultOwner, + _validateVaultPassword, + _authorizeVaultDeletion, + eventBus, + ) + }) + + describe('moveItemToVault', () => { + it('should throw error if vault is locked', async () => { + vaultLocks.isVaultLocked = jest.fn().mockReturnValue(true) + + const vault = { + uuid: '123', + } as VaultListingInterface + const item = { + uuid: '456', + } as DecryptedItemInterface + + await expect(service.moveItemToVault(vault, item)).rejects.toThrow('Attempting to add item to locked vault') + }) + + describe('moving note/file into vault', () => { + it('should not move note/file if any item linked to it is already in another vault', async () => { + const vault = { + uuid: '123', + systemIdentifier: '456', + } as VaultListingInterface + + const note = createNoteWithContent({ title: 'a' }) + const file = createFile('b') + + const returnValue = [ + { + uuid: '789', + key_system_identifier: '234', + }, + { + uuid: '012', + }, + ] as DecryptedItemInterface[] + + items.getItemLinkedFiles = jest.fn().mockReturnValue(returnValue) + items.getItemLinkedNotes = jest.fn().mockReturnValue(returnValue) + items.getUnsortedTagsForItem = jest.fn().mockReturnValue(returnValue) + + const result = await service.moveItemToVault(vault, note) + expect(result.isFailed()).toBe(true) + + const result2 = await service.moveItemToVault(vault, file) + expect(result2.isFailed()).toBe(true) + }) + }) + + describe('moving tag into vault', () => { + it('should not move tag if any deeply nested subtag is already in another vault', async () => { + const vault = { + uuid: '123', + systemIdentifier: '456', + } as VaultListingInterface + + const parentTag = createTagWithContent({ title: 'a' }) + const subTag = createTagWithContent({ title: 'b' }, '123') + const anotherSubTag = createTagWithContent({ title: 'c' }) + + items.getDeepTagChildren = jest.fn().mockReturnValue([subTag, anotherSubTag]) + + const result = await service.moveItemToVault(vault, parentTag) + expect(result.isFailed()).toBe(true) + }) + + it('should move parent tag and all deeply nested subtags into vault', async () => { + const vault = { + uuid: '123', + systemIdentifier: '456', + } as VaultListingInterface + + const parentTag = createTagWithContent({ title: 'a' }) + const subTag = createTagWithContent({ title: 'b' }) + const anotherSubTag = createTagWithContent({ title: 'c' }) + + items.getDeepTagChildren = jest.fn().mockReturnValue([subTag, anotherSubTag]) + items.findSureItem = jest.fn().mockReturnValue(parentTag.uuid) + + const result = await service.moveItemToVault(vault, parentTag) + expect(result.isFailed()).toBe(false) + }) + }) + }) +})