diff --git a/package-lock.json b/package-lock.json index 7ce3d6e2..d0b1cf15 100644 --- a/package-lock.json +++ b/package-lock.json @@ -69,7 +69,7 @@ "rollup-plugin-typescript2": "^0.34.1", "semantic-release": "^19.0.5", "semantic-release-monorepo": "^7.0.5", - "sinon": "^16.0.0", + "sinon": "^16.1.3", "typedoc": "^0.24.8", "typedoc-plugin-markdown": "^3.15.3", "typedoc-plugin-merge-modules": "^5.1.0", @@ -22758,7 +22758,7 @@ }, "packages/database": { "name": "@pluto-encrypted/database", - "version": "1.2.5", + "version": "1.2.6", "license": "Apache-2.0", "dependencies": { "@atala/prism-wallet-sdk": "^3.2.0", diff --git a/package.json b/package.json index 53ca2aaf..20cade11 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "rollup-plugin-typescript2": "^0.34.1", "semantic-release": "^19.0.5", "semantic-release-monorepo": "^7.0.5", - "sinon": "^16.0.0", + "sinon": "^16.1.3", "typedoc": "^0.24.8", "typedoc-plugin-markdown": "^3.15.3", "typedoc-plugin-merge-modules": "^5.1.0", @@ -88,4 +88,4 @@ "dependencies": { "typedoc-plugin-external-module-map": "^2.0.1" } -} \ No newline at end of file +} diff --git a/packages/database/coverage/coverage-summary.json b/packages/database/coverage/coverage-summary.json index 5d9baf3d..4a9908e9 100644 --- a/packages/database/coverage/coverage-summary.json +++ b/packages/database/coverage/coverage-summary.json @@ -1,11 +1,11 @@ {"total": {"lines":{"total":210,"covered":210,"skipped":0,"pct":100},"statements":{"total":221,"covered":221,"skipped":0,"pct":100},"functions":{"total":77,"covered":77,"skipped":0,"pct":100},"branches":{"total":70,"covered":70,"skipped":0,"pct":100},"branchesTrue":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/home/runner/work/pluto-encrypted/pluto-encrypted/packages/database/src/index.ts": {"lines":{"total":156,"covered":156,"skipped":0,"pct":100},"functions":{"total":67,"covered":67,"skipped":0,"pct":100},"statements":{"total":167,"covered":167,"skipped":0,"pct":100},"branches":{"total":48,"covered":48,"skipped":0,"pct":100}} -,"/home/runner/work/pluto-encrypted/pluto-encrypted/packages/database/src/schemas/Credential.ts": {"lines":{"total":11,"covered":11,"skipped":0,"pct":100},"functions":{"total":1,"covered":1,"skipped":0,"pct":100},"statements":{"total":11,"covered":11,"skipped":0,"pct":100},"branches":{"total":4,"covered":4,"skipped":0,"pct":100}} -,"/home/runner/work/pluto-encrypted/pluto-encrypted/packages/database/src/schemas/CredentialRequestMetadata.ts": {"lines":{"total":3,"covered":3,"skipped":0,"pct":100},"functions":{"total":1,"covered":1,"skipped":0,"pct":100},"statements":{"total":3,"covered":3,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/home/runner/work/pluto-encrypted/pluto-encrypted/packages/database/src/schemas/DID.ts": {"lines":{"total":1,"covered":1,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":1,"covered":1,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/home/runner/work/pluto-encrypted/pluto-encrypted/packages/database/src/schemas/DIDPair.ts": {"lines":{"total":1,"covered":1,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":1,"covered":1,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/home/runner/work/pluto-encrypted/pluto-encrypted/packages/database/src/schemas/LinkSecret.ts": {"lines":{"total":3,"covered":3,"skipped":0,"pct":100},"functions":{"total":1,"covered":1,"skipped":0,"pct":100},"statements":{"total":3,"covered":3,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/home/runner/work/pluto-encrypted/pluto-encrypted/packages/database/src/schemas/Mediator.ts": {"lines":{"total":4,"covered":4,"skipped":0,"pct":100},"functions":{"total":1,"covered":1,"skipped":0,"pct":100},"statements":{"total":4,"covered":4,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/home/runner/work/pluto-encrypted/pluto-encrypted/packages/database/src/schemas/Message.ts": {"lines":{"total":3,"covered":3,"skipped":0,"pct":100},"functions":{"total":1,"covered":1,"skipped":0,"pct":100},"statements":{"total":3,"covered":3,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/home/runner/work/pluto-encrypted/pluto-encrypted/packages/database/src/schemas/PrivateKey.ts": {"lines":{"total":28,"covered":28,"skipped":0,"pct":100},"functions":{"total":5,"covered":5,"skipped":0,"pct":100},"statements":{"total":28,"covered":28,"skipped":0,"pct":100},"branches":{"total":18,"covered":18,"skipped":0,"pct":100}} +,"/Users/ribo/Projects/personal/pluto-encrypted/packages/database/src/index.ts": {"lines":{"total":156,"covered":156,"skipped":0,"pct":100},"functions":{"total":67,"covered":67,"skipped":0,"pct":100},"statements":{"total":167,"covered":167,"skipped":0,"pct":100},"branches":{"total":48,"covered":48,"skipped":0,"pct":100}} +,"/Users/ribo/Projects/personal/pluto-encrypted/packages/database/src/schemas/Credential.ts": {"lines":{"total":11,"covered":11,"skipped":0,"pct":100},"functions":{"total":1,"covered":1,"skipped":0,"pct":100},"statements":{"total":11,"covered":11,"skipped":0,"pct":100},"branches":{"total":4,"covered":4,"skipped":0,"pct":100}} +,"/Users/ribo/Projects/personal/pluto-encrypted/packages/database/src/schemas/CredentialRequestMetadata.ts": {"lines":{"total":3,"covered":3,"skipped":0,"pct":100},"functions":{"total":1,"covered":1,"skipped":0,"pct":100},"statements":{"total":3,"covered":3,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/Users/ribo/Projects/personal/pluto-encrypted/packages/database/src/schemas/DID.ts": {"lines":{"total":1,"covered":1,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":1,"covered":1,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/Users/ribo/Projects/personal/pluto-encrypted/packages/database/src/schemas/DIDPair.ts": {"lines":{"total":1,"covered":1,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":1,"covered":1,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/Users/ribo/Projects/personal/pluto-encrypted/packages/database/src/schemas/LinkSecret.ts": {"lines":{"total":3,"covered":3,"skipped":0,"pct":100},"functions":{"total":1,"covered":1,"skipped":0,"pct":100},"statements":{"total":3,"covered":3,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/Users/ribo/Projects/personal/pluto-encrypted/packages/database/src/schemas/Mediator.ts": {"lines":{"total":4,"covered":4,"skipped":0,"pct":100},"functions":{"total":1,"covered":1,"skipped":0,"pct":100},"statements":{"total":4,"covered":4,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/Users/ribo/Projects/personal/pluto-encrypted/packages/database/src/schemas/Message.ts": {"lines":{"total":3,"covered":3,"skipped":0,"pct":100},"functions":{"total":1,"covered":1,"skipped":0,"pct":100},"statements":{"total":3,"covered":3,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/Users/ribo/Projects/personal/pluto-encrypted/packages/database/src/schemas/PrivateKey.ts": {"lines":{"total":28,"covered":28,"skipped":0,"pct":100},"functions":{"total":5,"covered":5,"skipped":0,"pct":100},"statements":{"total":28,"covered":28,"skipped":0,"pct":100},"branches":{"total":18,"covered":18,"skipped":0,"pct":100}} } diff --git a/packages/indexdb/README.md b/packages/indexdb/README.md index ffe3bd3a..8c652c43 100644 --- a/packages/indexdb/README.md +++ b/packages/indexdb/README.md @@ -25,4 +25,4 @@ const database = db = await Database.createEncrypted( ## QA & Documentation | Statements | Branches | Functions | Lines | | --------------------------- | ----------------------- | ------------------------- | ----------------- | -| ![Statements](https://img.shields.io/badge/statements-76.25%25-red.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-64.12%25-red.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-76.81%25-red.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-77%25-red.svg?style=flat) | +| ![Statements](https://img.shields.io/badge/statements-92.68%25-brightgreen.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-80%25-yellow.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-94.64%25-brightgreen.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-93.64%25-brightgreen.svg?style=flat) | diff --git a/packages/indexdb/coverage/coverage-summary.json b/packages/indexdb/coverage/coverage-summary.json index 28bc072f..1c16db4b 100644 --- a/packages/indexdb/coverage/coverage-summary.json +++ b/packages/indexdb/coverage/coverage-summary.json @@ -1,7 +1,7 @@ -{"total": {"lines":{"total":287,"covered":221,"skipped":0,"pct":77},"statements":{"total":299,"covered":228,"skipped":0,"pct":76.25},"functions":{"total":69,"covered":53,"skipped":0,"pct":76.81},"branches":{"total":131,"covered":84,"skipped":0,"pct":64.12},"branchesTrue":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/home/runner/work/pluto-encrypted/pluto-encrypted/packages/indexdb/src/index.ts": {"lines":{"total":1,"covered":1,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":1,"covered":1,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/home/runner/work/pluto-encrypted/pluto-encrypted/packages/indexdb/src/storage-dexie/dexie-helper.ts": {"lines":{"total":95,"covered":86,"skipped":0,"pct":90.52},"functions":{"total":22,"covered":21,"skipped":0,"pct":95.45},"statements":{"total":100,"covered":90,"skipped":0,"pct":90},"branches":{"total":48,"covered":39,"skipped":0,"pct":81.25}} -,"/home/runner/work/pluto-encrypted/pluto-encrypted/packages/indexdb/src/storage-dexie/dexie-query.ts": {"lines":{"total":67,"covered":30,"skipped":0,"pct":44.77},"functions":{"total":14,"covered":5,"skipped":0,"pct":35.71},"statements":{"total":71,"covered":32,"skipped":0,"pct":45.07},"branches":{"total":32,"covered":10,"skipped":0,"pct":31.25}} -,"/home/runner/work/pluto-encrypted/pluto-encrypted/packages/indexdb/src/storage-dexie/rx-storage-dexie.ts": {"lines":{"total":10,"covered":10,"skipped":0,"pct":100},"functions":{"total":3,"covered":3,"skipped":0,"pct":100},"statements":{"total":10,"covered":10,"skipped":0,"pct":100},"branches":{"total":3,"covered":3,"skipped":0,"pct":100}} -,"/home/runner/work/pluto-encrypted/pluto-encrypted/packages/indexdb/src/storage-dexie/rx-storage-instance-dexie.ts": {"lines":{"total":114,"covered":94,"skipped":0,"pct":82.45},"functions":{"total":30,"covered":24,"skipped":0,"pct":80},"statements":{"total":117,"covered":95,"skipped":0,"pct":81.19},"branches":{"total":48,"covered":32,"skipped":0,"pct":66.66}} +{"total": {"lines":{"total":236,"covered":221,"skipped":0,"pct":93.64},"statements":{"total":246,"covered":228,"skipped":0,"pct":92.68},"functions":{"total":56,"covered":53,"skipped":0,"pct":94.64},"branches":{"total":105,"covered":84,"skipped":0,"pct":80},"branchesTrue":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/Users/ribo/Projects/personal/pluto-encrypted/packages/indexdb/src/index.ts": {"lines":{"total":1,"covered":1,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":1,"covered":1,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/Users/ribo/Projects/personal/pluto-encrypted/packages/indexdb/src/storage-dexie/dexie-helper.ts": {"lines":{"total":95,"covered":86,"skipped":0,"pct":90.52},"functions":{"total":22,"covered":21,"skipped":0,"pct":95.45},"statements":{"total":100,"covered":90,"skipped":0,"pct":90},"branches":{"total":48,"covered":39,"skipped":0,"pct":81.25}} +,"/Users/ribo/Projects/personal/pluto-encrypted/packages/indexdb/src/storage-dexie/dexie-query.ts": {"lines":{"total":32,"covered":30,"skipped":0,"pct":93.75},"functions":{"total":6,"covered":5,"skipped":0,"pct":83.33},"statements":{"total":35,"covered":32,"skipped":0,"pct":91.42},"branches":{"total":14,"covered":10,"skipped":0,"pct":71.42}} +,"/Users/ribo/Projects/personal/pluto-encrypted/packages/indexdb/src/storage-dexie/rx-storage-dexie.ts": {"lines":{"total":10,"covered":10,"skipped":0,"pct":100},"functions":{"total":3,"covered":3,"skipped":0,"pct":100},"statements":{"total":10,"covered":10,"skipped":0,"pct":100},"branches":{"total":3,"covered":3,"skipped":0,"pct":100}} +,"/Users/ribo/Projects/personal/pluto-encrypted/packages/indexdb/src/storage-dexie/rx-storage-instance-dexie.ts": {"lines":{"total":98,"covered":94,"skipped":0,"pct":95.91},"functions":{"total":25,"covered":24,"skipped":0,"pct":96},"statements":{"total":100,"covered":95,"skipped":0,"pct":95},"branches":{"total":40,"covered":32,"skipped":0,"pct":80}} } diff --git a/packages/indexdb/src/storage-dexie/dexie-query.ts b/packages/indexdb/src/storage-dexie/dexie-query.ts index 291b5147..0532b80e 100644 --- a/packages/indexdb/src/storage-dexie/dexie-query.ts +++ b/packages/indexdb/src/storage-dexie/dexie-query.ts @@ -14,63 +14,16 @@ // } from './dexie-helper'; // import type { RxStorageInstanceDexie } from './rx-storage-instance-dexie'; -import { type DefaultPreparedQuery, INDEX_MIN, type RxDocumentData, type RxQueryPlan, type RxStorageQueryResult, getQueryMatcher, getSortComparator } from 'rxdb' +import { type DefaultPreparedQuery, type RxDocumentData, type RxStorageQueryResult, getQueryMatcher, getSortComparator } from 'rxdb' import { type QueryMatcher, type RxJsonSchema } from 'rxdb/dist/types/types' -import { DEXIE_DOCS_TABLE_NAME, dexieReplaceIfStartsWithPipe, fromDexieToStorage } from './dexie-helper' +import { dexieReplaceIfStartsWithPipe, fromDexieToStorage } from './dexie-helper' import { type RxStorageInstanceDexie } from './rx-storage-instance-dexie' import { fixTxPipe } from '@pluto-encrypted/shared' -export function mapKeyForKeyRange (k: any) { - if (k === INDEX_MIN) { - return -Infinity - } else { - return k - } -} - -export function getKeyRangeByQueryPlan ( - queryPlan: RxQueryPlan, - IDBKeyRange?: any -) { - if (!IDBKeyRange) { - if (typeof window === 'undefined') { - throw new Error('IDBKeyRange missing') - } else { - IDBKeyRange = window.IDBKeyRange - } - } - - const startKeys = queryPlan.startKeys.map(mapKeyForKeyRange) - const endKeys = queryPlan.endKeys.map(mapKeyForKeyRange) - - let ret: any - /** - * If index has only one field, - * we have to pass the keys directly, not the key arrays. - */ - if (queryPlan.index.length === 1) { - const equalKeys = startKeys[0] === endKeys[0] - ret = IDBKeyRange.bound( - startKeys[0], - endKeys[0], - equalKeys ? false : !queryPlan.inclusiveStart, - equalKeys ? false : !queryPlan.inclusiveEnd - ) - } else { - ret = IDBKeyRange.bound( - startKeys, - endKeys, - !queryPlan.inclusiveStart, - !queryPlan.inclusiveEnd - ) - } - return ret -} - /** * Runs mango queries over the Dexie.js database. */ -export async function dexieQuery ( +export async function dexieQuery( instance: RxStorageInstanceDexie, preparedQuery: DefaultPreparedQuery, schema: Readonly>> @@ -137,54 +90,3 @@ export async function dexieQuery ( documents: rows } } - -export async function dexieCount ( - instance: RxStorageInstanceDexie, - preparedQuery: DefaultPreparedQuery -): Promise { - const state = await instance.internals - const queryPlan = preparedQuery.queryPlan - const queryPlanFields: string[] = queryPlan.index - - const keyRange = getKeyRangeByQueryPlan( - queryPlan, - (state.dexieDb as any)._options.IDBKeyRange - ) - let count: number = -1 - await state.dexieDb.transaction( - 'r', - state.dexieTable, - async (dexieTx) => { - const tx = (dexieTx as any).idbtrans - const store = tx.objectStore(DEXIE_DOCS_TABLE_NAME) - let index: any - if ( - queryPlanFields.length === 1 && - queryPlanFields[0] === instance.primaryPath - ) { - index = store - } else { - let indexName: string - if (queryPlanFields.length === 1) { - indexName = dexieReplaceIfStartsWithPipe(queryPlanFields[0]!) - } else { - indexName = '[' + - queryPlanFields - .map(field => dexieReplaceIfStartsWithPipe(field)) - .join('+') + - ']' - } - index = store.index(indexName) - } - - const request = index.count(keyRange) - count = await new Promise((resolve, reject) => { - request.onsuccess = function () { - resolve(request.result) - } - request.onerror = (err: any) => { reject(err) } - }) - } - ) - return count -} diff --git a/packages/indexdb/src/storage-dexie/rx-storage-instance-dexie.ts b/packages/indexdb/src/storage-dexie/rx-storage-instance-dexie.ts index 8cb42976..4e0068ea 100644 --- a/packages/indexdb/src/storage-dexie/rx-storage-instance-dexie.ts +++ b/packages/indexdb/src/storage-dexie/rx-storage-instance-dexie.ts @@ -12,17 +12,17 @@ import { type RxStorageDexie } from './rx-storage-dexie' let instanceId = now() export class RxStorageInstanceDexie implements RxStorageInstance< -RxDocType, -DexieStorageInternals, -DexieSettings, -RxStorageDefaultCheckpoint + RxDocType, + DexieStorageInternals, + DexieSettings, + RxStorageDefaultCheckpoint > { public readonly primaryPath: StringKeys> private readonly changes$ = new Subject>, RxStorageDefaultCheckpoint>>() public readonly instanceId = instanceId++ public closed = false - constructor ( + constructor( public readonly storage: RxStorageDexie, public readonly databaseName: string, public readonly collectionName: string, @@ -34,16 +34,16 @@ RxStorageDefaultCheckpoint this.primaryPath = getPrimaryFieldOfPrimaryKey(this.schema.primaryKey) } - async bulkWrite ( + async bulkWrite( documentWrites: Array>, context: string ): Promise> { ensureNotClosed(this) /** - * Check some assumptions to ensure RxDB - * does not call the storage with an invalid write. - */ + * Check some assumptions to ensure RxDB + * does not call the storage with an invalid write. + */ documentWrites.forEach(row => { // ensure revision is set if ( @@ -152,7 +152,7 @@ RxStorageDefaultCheckpoint return ret } - async findDocumentsById ( + async findDocumentsById( ids: string[], deleted: boolean ): Promise> { @@ -184,7 +184,7 @@ RxStorageDefaultCheckpoint return ret } - async query (preparedQuery: DefaultPreparedQuery): Promise> { + async query(preparedQuery: DefaultPreparedQuery): Promise> { ensureNotClosed(this) return await dexieQuery( this, @@ -193,7 +193,7 @@ RxStorageDefaultCheckpoint ) } - async count ( + async count( preparedQuery: DefaultPreparedQuery ): Promise { const result = await dexieQuery(this, preparedQuery, this.schema) @@ -203,13 +203,14 @@ RxStorageDefaultCheckpoint } } - async getChangedDocumentsSince ( + /* istanbul ignore next */ + async getChangedDocumentsSince( limit: number, checkpoint?: RxStorageDefaultCheckpoint ): Promise<{ - documents: Array> - checkpoint: RxStorageDefaultCheckpoint - }> { + documents: Array> + checkpoint: RxStorageDefaultCheckpoint + }> { ensureNotClosed(this) const sinceLwt = checkpoint ? checkpoint.lwt : RX_META_LWT_MINIMUM const sinceId = checkpoint ? checkpoint.id : '' @@ -239,9 +240,9 @@ RxStorageDefaultCheckpoint documents: changedDocs, checkpoint: lastDoc ? { - id: lastDoc[this.primaryPath], - lwt: lastDoc._meta.lwt - } + id: lastDoc[this.primaryPath], + lwt: lastDoc._meta.lwt + } : checkpoint ?? { id: '', lwt: 0 @@ -249,16 +250,16 @@ RxStorageDefaultCheckpoint } } - async remove (): Promise { + async remove(): Promise { await Promise.resolve() } - changeStream (): Observable>, RxStorageDefaultCheckpoint>> { + changeStream(): Observable>, RxStorageDefaultCheckpoint>> { ensureNotClosed(this) return this.changes$.asObservable() } - async cleanup (): Promise { + async cleanup(): Promise { ensureNotClosed(this) const state = await this.internals await state.dexieDb.transaction( @@ -276,20 +277,21 @@ RxStorageDefaultCheckpoint ) /** - * TODO instead of deleting all deleted docs at once, - * only clean up some of them and return false if there are more documents to clean up. - * This ensures that when many documents have to be purged, - * we do not block the more important tasks too long. - */ + * TODO instead of deleting all deleted docs at once, + * only clean up some of them and return false if there are more documents to clean up. + * This ensures that when many documents have to be purged, + * we do not block the more important tasks too long. + */ return true } - async getAttachmentData (): Promise { + /* istanbul ignore next */ + async getAttachmentData(): Promise { ensureNotClosed(this) throw new Error('Attachments are not implemented in the dexie RxStorage. Make a pull request.') } - async close (): Promise { + async close(): Promise { ensureNotClosed(this) this.closed = true this.changes$.complete() @@ -297,14 +299,15 @@ RxStorageDefaultCheckpoint await PROMISE_RESOLVE_VOID } - conflictResultionTasks (): Observable> { + conflictResultionTasks(): Observable> { return new Subject() } - async resolveConflictResultionTask (): Promise { } + /* istanbul ignore next */ + async resolveConflictResultionTask(): Promise { } } -export async function createDexieStorageInstance ( +export async function createDexieStorageInstance( storage: RxStorageDexie, params: RxStorageInstanceCreationParams, settings: DexieSettings @@ -329,7 +332,7 @@ export async function createDexieStorageInstance ( return await Promise.resolve(instance) } -function ensureNotClosed ( +function ensureNotClosed( instance: RxStorageInstanceDexie ) { if (instance.closed) { diff --git a/packages/inmemory/README.md b/packages/inmemory/README.md index 1ee77585..6e2034df 100644 --- a/packages/inmemory/README.md +++ b/packages/inmemory/README.md @@ -25,6 +25,6 @@ const database = db = await Database.createEncrypted( ## QA & Documentation | Statements | Branches | Functions | Lines | | --------------------------- | ----------------------- | ------------------------- | ----------------- | -| ![Statements](https://img.shields.io/badge/statements-95.42%25-brightgreen.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-75.47%25-red.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-96.42%25-brightgreen.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-95.91%25-brightgreen.svg?style=flat) | +| ![Statements](https://img.shields.io/badge/statements-96.73%25-brightgreen.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-81.13%25-yellow.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-96.42%25-brightgreen.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-97.27%25-brightgreen.svg?style=flat) | diff --git a/packages/inmemory/coverage/coverage-summary.json b/packages/inmemory/coverage/coverage-summary.json index eb0026e4..34c5cdc7 100644 --- a/packages/inmemory/coverage/coverage-summary.json +++ b/packages/inmemory/coverage/coverage-summary.json @@ -1,5 +1,5 @@ -{"total": {"lines":{"total":147,"covered":141,"skipped":0,"pct":95.91},"statements":{"total":153,"covered":146,"skipped":0,"pct":95.42},"functions":{"total":28,"covered":27,"skipped":0,"pct":96.42},"branches":{"total":53,"covered":40,"skipped":0,"pct":75.47},"branchesTrue":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/home/runner/work/pluto-encrypted/pluto-encrypted/packages/inmemory/src/index.ts": {"lines":{"total":12,"covered":12,"skipped":0,"pct":100},"functions":{"total":2,"covered":2,"skipped":0,"pct":100},"statements":{"total":12,"covered":12,"skipped":0,"pct":100},"branches":{"total":5,"covered":5,"skipped":0,"pct":100}} -,"/home/runner/work/pluto-encrypted/pluto-encrypted/packages/inmemory/src/inMemoryStorage/instance.ts": {"lines":{"total":93,"covered":89,"skipped":0,"pct":95.69},"functions":{"total":15,"covered":14,"skipped":0,"pct":93.33},"statements":{"total":98,"covered":93,"skipped":0,"pct":94.89},"branches":{"total":34,"covered":24,"skipped":0,"pct":70.58}} -,"/home/runner/work/pluto-encrypted/pluto-encrypted/packages/inmemory/src/inMemoryStorage/internal.ts": {"lines":{"total":42,"covered":40,"skipped":0,"pct":95.23},"functions":{"total":11,"covered":11,"skipped":0,"pct":100},"statements":{"total":43,"covered":41,"skipped":0,"pct":95.34},"branches":{"total":14,"covered":11,"skipped":0,"pct":78.57}} +{"total": {"lines":{"total":147,"covered":143,"skipped":0,"pct":97.27},"statements":{"total":153,"covered":148,"skipped":0,"pct":96.73},"functions":{"total":28,"covered":27,"skipped":0,"pct":96.42},"branches":{"total":53,"covered":43,"skipped":0,"pct":81.13},"branchesTrue":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/Users/ribo/Projects/personal/pluto-encrypted/packages/inmemory/src/index.ts": {"lines":{"total":12,"covered":12,"skipped":0,"pct":100},"functions":{"total":2,"covered":2,"skipped":0,"pct":100},"statements":{"total":12,"covered":12,"skipped":0,"pct":100},"branches":{"total":5,"covered":5,"skipped":0,"pct":100}} +,"/Users/ribo/Projects/personal/pluto-encrypted/packages/inmemory/src/inMemoryStorage/instance.ts": {"lines":{"total":93,"covered":89,"skipped":0,"pct":95.69},"functions":{"total":15,"covered":14,"skipped":0,"pct":93.33},"statements":{"total":98,"covered":93,"skipped":0,"pct":94.89},"branches":{"total":34,"covered":24,"skipped":0,"pct":70.58}} +,"/Users/ribo/Projects/personal/pluto-encrypted/packages/inmemory/src/inMemoryStorage/internal.ts": {"lines":{"total":42,"covered":42,"skipped":0,"pct":100},"functions":{"total":11,"covered":11,"skipped":0,"pct":100},"statements":{"total":43,"covered":43,"skipped":0,"pct":100},"branches":{"total":14,"covered":14,"skipped":0,"pct":100}} } diff --git a/packages/inmemory/tests/init.test.ts b/packages/inmemory/tests/init.test.ts index c3ee77e4..3e857bf3 100644 --- a/packages/inmemory/tests/init.test.ts +++ b/packages/inmemory/tests/init.test.ts @@ -1,10 +1,12 @@ import "./setup"; -import { describe, it, beforeEach, afterEach } from 'vitest'; +import { describe, it, vi, beforeEach, afterEach } from 'vitest'; import { runTestSuite } from '@pluto-encrypted/test-suite'; import InMemory from '../src' +import { InMemoryInternal } from "../src/inMemoryStorage/internal"; +import sinon from "sinon"; const keyData = new Uint8Array(32); @@ -30,4 +32,30 @@ describe("Testing suite", () => { return 'RandomPassword' } }) + + it("Should allow removing a non existing index", async ({ expect }) => { + const inMemoryInternal = new InMemoryInternal(0) + const mock = sinon.stub(inMemoryInternal.index, 'has').returns(false) + + inMemoryInternal.addIndex("existingIndex", "1") + inMemoryInternal.removeFromIndex("non-existingIndex", "1") + + expect(inMemoryInternal.index.size).toBe(1) + sinon.assert.callCount(mock, 2) + + }) + + it("Should allow removing a non existing index", async ({ expect }) => { + const inMemoryInternal = new InMemoryInternal(0) + const mock = sinon.stub(inMemoryInternal.index, 'has').returns(true) + const mock2 = sinon.stub(inMemoryInternal.index, 'get').returns(undefined) + + inMemoryInternal.addIndex("existingIndex", "1") + inMemoryInternal.removeFromIndex("non-existingIndex", "1") + + expect(inMemoryInternal.index.size).toBe(2) + expect(inMemoryInternal.index.get('non-existingIndex')).toStrictEqual(undefined) + sinon.assert.callCount(mock, 2) + sinon.assert.callCount(mock2, 3) + }) }) diff --git a/packages/leveldb/README.md b/packages/leveldb/README.md index 9d413b09..1e3cfb6e 100644 --- a/packages/leveldb/README.md +++ b/packages/leveldb/README.md @@ -28,4 +28,4 @@ const database = db = await Database.createEncrypted( ## QA & Documentation | Statements | Branches | Functions | Lines | | --------------------------- | ----------------------- | ------------------------- | ----------------- | -| ![Statements](https://img.shields.io/badge/statements-88.59%25-yellow.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-69.76%25-red.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-94.11%25-brightgreen.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-89.9%25-yellow.svg?style=flat) | +| ![Statements](https://img.shields.io/badge/statements-94.2%25-brightgreen.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-75%25-red.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-94.11%25-brightgreen.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-94.5%25-brightgreen.svg?style=flat) | diff --git a/packages/leveldb/coverage/coverage-summary.json b/packages/leveldb/coverage/coverage-summary.json index c3aad27c..008c2972 100644 --- a/packages/leveldb/coverage/coverage-summary.json +++ b/packages/leveldb/coverage/coverage-summary.json @@ -1,5 +1,5 @@ -{"total": {"lines":{"total":218,"covered":196,"skipped":0,"pct":89.9},"statements":{"total":228,"covered":202,"skipped":0,"pct":88.59},"functions":{"total":51,"covered":48,"skipped":0,"pct":94.11},"branches":{"total":86,"covered":60,"skipped":0,"pct":69.76},"branchesTrue":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/home/runner/work/pluto-encrypted/pluto-encrypted/packages/leveldb/src/index.ts": {"lines":{"total":19,"covered":19,"skipped":0,"pct":100},"functions":{"total":4,"covered":4,"skipped":0,"pct":100},"statements":{"total":19,"covered":19,"skipped":0,"pct":100},"branches":{"total":8,"covered":6,"skipped":0,"pct":75}} -,"/home/runner/work/pluto-encrypted/pluto-encrypted/packages/leveldb/src/leveldb/instance.ts": {"lines":{"total":93,"covered":89,"skipped":0,"pct":95.69},"functions":{"total":16,"covered":15,"skipped":0,"pct":93.75},"statements":{"total":99,"covered":94,"skipped":0,"pct":94.94},"branches":{"total":30,"covered":22,"skipped":0,"pct":73.33}} -,"/home/runner/work/pluto-encrypted/pluto-encrypted/packages/leveldb/src/leveldb/internal.ts": {"lines":{"total":106,"covered":88,"skipped":0,"pct":83.01},"functions":{"total":31,"covered":29,"skipped":0,"pct":93.54},"statements":{"total":110,"covered":89,"skipped":0,"pct":80.9},"branches":{"total":48,"covered":32,"skipped":0,"pct":66.66}} +{"total": {"lines":{"total":200,"covered":189,"skipped":0,"pct":94.5},"statements":{"total":207,"covered":195,"skipped":0,"pct":94.2},"functions":{"total":51,"covered":48,"skipped":0,"pct":94.11},"branches":{"total":68,"covered":51,"skipped":0,"pct":75},"branchesTrue":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/Users/ribo/Projects/personal/pluto-encrypted/packages/leveldb/src/index.ts": {"lines":{"total":19,"covered":19,"skipped":0,"pct":100},"functions":{"total":4,"covered":4,"skipped":0,"pct":100},"statements":{"total":19,"covered":19,"skipped":0,"pct":100},"branches":{"total":8,"covered":6,"skipped":0,"pct":75}} +,"/Users/ribo/Projects/personal/pluto-encrypted/packages/leveldb/src/leveldb/instance.ts": {"lines":{"total":93,"covered":89,"skipped":0,"pct":95.69},"functions":{"total":16,"covered":15,"skipped":0,"pct":93.75},"statements":{"total":99,"covered":94,"skipped":0,"pct":94.94},"branches":{"total":30,"covered":22,"skipped":0,"pct":73.33}} +,"/Users/ribo/Projects/personal/pluto-encrypted/packages/leveldb/src/leveldb/internal.ts": {"lines":{"total":88,"covered":81,"skipped":0,"pct":92.04},"functions":{"total":31,"covered":29,"skipped":0,"pct":93.54},"statements":{"total":89,"covered":82,"skipped":0,"pct":92.13},"branches":{"total":30,"covered":23,"skipped":0,"pct":76.66}} } diff --git a/packages/leveldb/src/leveldb/internal.ts b/packages/leveldb/src/leveldb/internal.ts index bd417e8d..d5673039 100644 --- a/packages/leveldb/src/leveldb/internal.ts +++ b/packages/leveldb/src/leveldb/internal.ts @@ -26,7 +26,7 @@ export class LevelDBInternal implements LevelDBStorageInternals) { + constructor(private readonly _options: LevelDBInternalConstructor) { this.refCount = this._options.refCount this.schema = this._options.schema this.documents = this._options.documents ?? new Map() @@ -37,7 +37,7 @@ export class LevelDBInternal implements LevelDBStorageInternals>> { + async getDocuments(query: string[]): Promise>> { const docsInDbMap = new Map>() if (query.length <= 0) { const db = await this.getInstance() @@ -64,12 +64,12 @@ export class LevelDBInternal implements LevelDBStorageInternals { + async getIndex(key: string): Promise { const db = await this.getInstance() return db.get(key) .then(result => result ? JSON.parse(result) : []) @@ -83,7 +83,7 @@ export class LevelDBInternal implements LevelDBStorageInternals>> { + async bulkGet(keys: string[]): Promise>> { if (!keys || keys.length <= 0) { return [] } @@ -101,7 +101,7 @@ export class LevelDBInternal implements LevelDBStorageInternals | null> { + async get(key: string): Promise | null> { const db = await this.getInstance() return await db.get(key) .then(result => result ? JSON.parse(result) : null) @@ -115,94 +115,63 @@ export class LevelDBInternal implements LevelDBStorageInternals) { - if (!key) { - throw new Error('Undefined key') - } - if (!data) { - throw new Error('Undefined value') - } - + async set(key: string, data: RxDocumentData) { const db = await this.getInstance() - await new Promise((resolve, reject) => { db.put(key, JSON.stringify(data), (err) => { if (err) { - reject(err); return + return reject(err); } - resolve() + return resolve() }) }) } - async setIndex (key: string, ids: string[]) { - if (!key) { - throw new Error('Undefined key') - } - if (!ids) { - throw new Error('Undefined value') - } - + async setIndex(key: string, ids: string[]) { const db = await this.getInstance() await new Promise((resolve, reject) => { db.put(key, JSON.stringify(ids), (err) => { if (err) { - reject(err); return + return reject(err); } - resolve() + return resolve() }) }) } - async delete (key: string) { - if (!key) { - throw new Error('Undefined key') - } - + async delete(key: string) { const db = await this.getInstance() await new Promise((resolve, reject) => { db.del(key, (err) => { if (err) { - reject(err); return + return reject(err); } - resolve() + return resolve() }) }) } - async updateIndex (key: string, id: string) { - if (!id) { - throw new Error('Undefined id') - } - if (!key) { - throw new Error('Undefined key') - } + async updateIndex(key: string, id: string) { const existingIndex = await this.getIndex(key) const newIndexes = Array.from(new Set([...existingIndex, id])) await this.setIndex(key, newIndexes) } - async removeFromIndex (key: string, id: string) { - if (!id) { - throw new Error('Undefined id') - } - if (!key) { - throw new Error('Undefined key') - } + async removeFromIndex(key: string, id: string) { const existingIndex = await this.getIndex(key) await this.setIndex(key, existingIndex.filter((vId) => vId !== id)) } - async clear () { + async clear() { const db = await this.getInstance() return db.clear() } - async close () { + async close() { return this.db.close() } - async bulkPut (items: Array>, collectionName: string, schema: Readonly>>) { + async bulkPut(items: Array>, collectionName: string, schema: Readonly>>) { const primaryKeyKey = typeof schema.primaryKey === 'string' ? schema.primaryKey : schema.primaryKey.key const saferIndexList = safeIndexList(schema) diff --git a/packages/test-suite/src/helper/index.ts b/packages/test-suite/src/helper/index.ts index d0cc391d..caedbbe5 100644 --- a/packages/test-suite/src/helper/index.ts +++ b/packages/test-suite/src/helper/index.ts @@ -29,7 +29,7 @@ export interface OptionalValueTestDoc { key: string, value?: string } export const TEST_DATA_CHARSET = '0987654321ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzäöüÖÄßÜ[]{}\'' export const TEST_DATA_CHARSET_LAST_SORTED = ensureNotFalsy(lastOfArray(TEST_DATA_CHARSET.split('').sort())) // const someEmojis = '😊💩👵🍌'; -export function randomStringWithSpecialChars (length: number) { +export function randomStringWithSpecialChars(length: number) { return randomString(length, TEST_DATA_CHARSET) } @@ -37,7 +37,7 @@ export function randomStringWithSpecialChars (length: number) { * @returns a format of the query that can be used with the storage * when calling RxStorageInstance().query() */ -export function prepareQuery (schema, mutateableQuery) { +export function prepareQuery(schema, mutateableQuery) { if (!mutateableQuery.sort) { throw newRxError('SNH', { query: mutateableQuery @@ -55,7 +55,7 @@ export function prepareQuery (schema, mutateableQuery) { } } -export function getNestedDocSchema () { +export function getNestedDocSchema() { const schema: RxJsonSchema> = fillWithDefaultSettings({ version: 0, primaryKey: 'id', @@ -89,7 +89,7 @@ export function getNestedDocSchema () { return schema } -export function getWriteData ( +export function getWriteData( ownParams: Partial> = {} ): RxDocumentData { return Object.assign( @@ -107,7 +107,7 @@ export function getWriteData ( ) } -export function getTestDataSchema (): RxJsonSchema> { +export function getTestDataSchema(): RxJsonSchema> { return fillWithDefaultSettings({ version: 0, type: 'object', @@ -195,7 +195,7 @@ export interface TestCorrectQueriesInput { } | undefined> } -export function withIndexes ( +export function withIndexes( schema: RxJsonSchema, indexes: string[][] ): RxJsonSchema { @@ -204,7 +204,7 @@ export function withIndexes ( return schema } -export function testCorrectQueries ( +export function testCorrectQueries( suite: TestSuite, testStorage: RxTestStorage, input: TestCorrectQueriesInput @@ -286,10 +286,7 @@ export function testCorrectQueries ( if (!queryForStorage.selector) { queryForStorage.selector = {} } - // (queryForStorage.selector as any)._deleted = false; - // if (queryForStorage.index) { - // (queryForStorage.index as any).unshift('_deleted'); - // } + const normalizedQuery = deepFreeze(normalizeMangoQuery(schema, queryForStorage)) const skip = normalizedQuery.skip ? normalizedQuery.skip : 0 const limit = normalizedQuery.limit ? normalizedQuery.limit : Infinity @@ -311,13 +308,6 @@ export function testCorrectQueries ( expect(resultStaticsIds, 'expectedResultDocIds does not match').toStrictEqual(queryData.expectedResultDocIds) - // Test correct selectorSatisfiedByIndex - // This selectorSatisfiedByIndex and getQueryPlan is completely broken - // We will rely on the right storage instance implementations to mitigate this - // if (typeof queryData.selectorSatisfiedByIndex !== 'undefined') { - // const queryPlan = getQueryPlan(schema, normalizedQuery); - // expect(queryPlan.selectorSatisfiedByIndex).toBe(queryData.selectorSatisfiedByIndex); - // } // Test output of RxStorageInstance.query(); const resultFromStorage = await storageInstance.query(preparedQuery) @@ -335,13 +325,21 @@ export function testCorrectQueries ( // Test output of .count() if ( !queryData.query.limit && - !queryData.query.skip + !queryData.query.skip ) { const countResult = await storageInstance.count(preparedQuery) expect(countResult.count).toStrictEqual(queryData.expectedResultDocIds.length) } } + await storageInstance.bulkWrite( + rawDocsData.map(document => ({ + document: { + ...document, _deleted: true, _rev: "1" + } + })), + testQueryContext + ) await storageInstance.cleanup(Infinity) await database.remove() }) diff --git a/packages/test-suite/src/index.ts b/packages/test-suite/src/index.ts index d24daca1..fc66f1e1 100644 --- a/packages/test-suite/src/index.ts +++ b/packages/test-suite/src/index.ts @@ -21,7 +21,7 @@ import { randomString } from 'async-test-util' let storage: RxStorage let storageInstance: RxStorageInstance -export function runTestSuite (suite: TestSuite, testStorage: RxTestStorage): void { +export function runTestSuite(suite: TestSuite, testStorage: RxTestStorage): void { const { describe, it, beforeEach, afterEach } = suite describe('RxStorageInstance', () => { beforeEach(async () => { @@ -979,6 +979,16 @@ export function runTestSuite (suite: TestSuite, testStorage: RxTestStorage): voi expect(first).not.toBe(undefined) expect(first.value).toBe('barfoo') + + await storageInstance.bulkWrite( + [{ + document: { + ...writeData2, + _deleted: true + } + }], + testContext + ) }) it('should sort in the correct order', async ({ expect }) => { storageInstance = await storage.createStorageInstance<{ key: string, value: string }>({ @@ -1108,7 +1118,7 @@ export function runTestSuite (suite: TestSuite, testStorage: RxTestStorage): voi } const docs = Object.values(writeResponse.success) - async function testQuery (query: FilledMangoQuery): Promise { + async function testQuery(query: FilledMangoQuery): Promise { const preparedQuery = prepareQuery( storageInstance.schema, query @@ -1284,7 +1294,7 @@ export function runTestSuite (suite: TestSuite, testStorage: RxTestStorage): voi skip: 0 } ) - async function ensureCountIs (nr: number): Promise { + async function ensureCountIs(nr: number): Promise { const result = await storageInstance.count(preparedQueryAll) expect(result.count).toBe(nr) } @@ -1509,6 +1519,31 @@ export function runTestSuite (suite: TestSuite, testStorage: RxTestStorage): voi } ] }) + testCorrectQueries(suite, testStorage, { + testTitle: '$lt/$lte', + data: [ + human('dd', 40, 'dave'), + human('ee', 50, 'eve') + ], + schema: withIndexes(schemas.human, [ + ['age'], + ]), + queries: [ + { + info: 'normal $lt', + query: { + selector: { + age: { + $lt: 40 + } + }, + sort: [{ age: 'asc' }] + }, + selectorSatisfiedByIndex: true, + expectedResultDocIds: [] + } + ] + }) testCorrectQueries(suite, testStorage, { testTitle: '$lt/$lte',