UNPKG

@webda/gcp

Version:

Webda GCP Services implementation

193 lines 8.5 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; import { Firestore } from "@google-cloud/firestore"; import { suite, test } from "@testdeck/mocha"; import { Ident, WebdaQL } from "@webda/core"; import { StoreTest } from "@webda/core/lib/stores/store.spec.js"; import * as assert from "assert"; import * as sinon from "sinon"; import { v4 as uuidv4 } from "uuid"; import { FireStore } from "./firestore.js"; let FireStoreTest = class FireStoreTest extends StoreTest { async before() { this.unitUuid = uuidv4(); this.firestore = new Firestore(); await super.before(); } getIdentStore() { let ident = this.getService("fireidents"); ident.getParameters().collection += "_" + this.unitUuid; return ident; } getUserStore() { let user = this.getService("fireusers"); user.getParameters().collection += "_" + this.unitUuid; return user; } getModelClass() { return Ident; } /** * Delete a full collection */ async deleteCollection(collectionPath, batchSize) { const collectionRef = this.firestore.collection(collectionPath); const query = collectionRef.orderBy("__name__").limit(batchSize); return new Promise((resolve, reject) => { this.deleteQueryBatch(this.firestore, query, resolve).catch(reject); }); } async cleanCollection() { let collections = await this.firestore.listCollections(); await Promise.all(collections.map(c => { return this.deleteCollection(c.id, 500); })); } async fillForQuery() { // Create a new store let store = new FireStore(this.webda, "queryStore", { collection: "webda-query", compoundIndexes: [{ state: "asc", "team.id": "asc" }], expose: { url: "/query" } }); store.resolve(); await store.init(); let res = await this.firestore.collection("webda-query").where("order", "==", 1).get(); if (!res.docs.length) { console.log("Init the query collection"); await Promise.all(this.getQueryDocuments().map(d => store.save(d))); } return store; } async query() { let store = await super.query(); let exp = new WebdaQL.QueryValidator('state = "CA" AND role = 4').getExpression(); let res = await store.find({ filter: exp, limit: 1000 }); assert.strictEqual(res.filter, true, `Should not have any post filter ${res.filter.toString()}`); assert.strictEqual(res.results.length, 50); exp = new WebdaQL.QueryValidator('state = "CA" AND team.id < 5').getExpression(); res = await store.find({ filter: exp, limit: 1000 }); assert.strictEqual(res.filter, true, `Should not have post filter ${res.filter.toString()}`); assert.strictEqual(res.results.length, 100); exp = new WebdaQL.QueryValidator('state = "CA" AND team.id < 5 AND role >= 4').getExpression(); res = await store.find({ filter: exp, limit: 1000 }); assert.notStrictEqual(res.filter, true, `Should have post filter ${res.filter.toString()}`); assert.strictEqual(res.results.length, 100); assert.strictEqual(res.results.filter(c => res.filter.eval(c)).length, 50); exp = new WebdaQL.QueryValidator('state = "CA" AND role <= 4').getExpression(); res = await store.find({ filter: exp, limit: 1000 }); assert.notStrictEqual(res.filter, true, "Should have post filter"); assert.strictEqual(res.results.length, 250); let items = ["CA", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]; res = await store.find({ filter: new WebdaQL.QueryValidator(`state IN [${items.map(i => `"${i}"`).join(",")}]`).getExpression(), limit: 1000 }); // Should be post filtered assert.strictEqual(res.results.length, 1000); res = await store.find({ filter: new WebdaQL.QueryValidator(`state IN ["CA", "OR"] AND team.id IN [4,8]`).getExpression(), limit: 1000 }); assert.strictEqual(res.results.length, 500); res = await store.find({ filter: new WebdaQL.QueryValidator(`team.id < 5 AND state > "CA"`).getExpression(), limit: 1000 }); assert.strictEqual(res.results.length, 250); // Test double CONTAINS exp = new WebdaQL.QueryValidator('states CONTAINS "CA" AND states CONTAINS "OR"').getExpression(); res = await store.find({ filter: exp, limit: 1000 }); assert.notStrictEqual(res.filter, true, `Should have post filter as CONTAINS cannot be used twice`); return store; } async queryOrder() { // Disable default ordering query as it is not possible with Dynamo let store = await this.fillForQuery(); let res = await store.query("order > 900 ORDER BY order DESC LIMIT 10"); assert.strictEqual(res.results.shift().order, 999); res = await store.query("ORDER BY order ASC LIMIT 10"); assert.strictEqual(res.results.shift().order, 0); res = await store.query("ORDER BY order DESC LIMIT 10"); assert.strictEqual(res.results.shift().order, 999); res = await store.query("ORDER BY state ASC, team.id ASC LIMIT 10"); res.results .map(c => this.mapQueryModel(c)) .forEach(c => { assert.deepStrictEqual({ teamId: 0, state: "CA" }, { teamId: c.teamId, state: c.state }); }); // It should not fail even if index is not found res = await store.query("ORDER BY state ASC, team.id DESC LIMIT 10"); res = await store.query("order > 900 ORDER BY team.id ASC LIMIT 10"); res = await store.query("ORDER BY team.id ASC, order DESC LIMIT 10"); } /** * Query batch * * Code from Google Documentation * */ async deleteQueryBatch(db, query, resolve) { const snapshot = await query.get(); const batchSize = snapshot.size; if (batchSize === 0) { // When there are no documents left, we are done resolve(); return; } // Delete documents in a batch const batch = db.batch(); snapshot.docs.forEach(doc => { batch.delete(doc.ref); }); await batch.commit(); // Recurse on the next process tick, to avoid // exploding the stack. process.nextTick(() => { this.deleteQueryBatch(db, query, resolve); }); } async after() { await this.deleteCollection(`idents_${this.unitUuid}`, 10000); await this.deleteCollection(`users_${this.unitUuid}`, 10000); } async deleteCondition() { let idents = this.getIdentStore(); let obj = await idents.save({ plop: 3 }); await assert.rejects(() => idents._delete(obj.getUuid(), 2, "plop"), /UpdateCondition not met/); await idents._delete(obj.getUuid(), 3, "plop"); } async cov() { const store = this.getIdentStore(); await assert.rejects(() => store.upsertItemToCollection("inexisting", "plop", {}, 0), /Item not found inexisting/); sinon.stub(store, "getDocumentRef").callsFake(() => { throw new Error("FAKE"); }); await assert.rejects(() => store.upsertItemToCollection("inexisting", "plop", {}), /FAKE/); await assert.rejects(() => store.incrementAttribute("inexisting", "plop", 1), /FAKE/); } }; __decorate([ test ], FireStoreTest.prototype, "query", null); __decorate([ test ], FireStoreTest.prototype, "queryOrder", null); __decorate([ test ], FireStoreTest.prototype, "deleteCondition", null); __decorate([ test ], FireStoreTest.prototype, "cov", null); FireStoreTest = __decorate([ suite ], FireStoreTest); //# sourceMappingURL=firestore.spec.js.map