123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354 |
- // @ts-nocheck
- import async from "async";
- import chai, { expect } from "chai";
- import sinon from "sinon";
- import sinonChai from "sinon-chai";
- import chaiAsPromised from "chai-as-promised";
- import { ObjectId } from "mongodb";
- import JobContext from "../JobContext";
- import JobQueue from "../JobQueue";
- import LogBook from "../LogBook";
- import ModuleManager from "../ModuleManager";
- import DataModule from "./DataModule";
- chai.should();
- chai.use(sinonChai);
- chai.use(chaiAsPromised);
- describe("Data Module", function () {
- const moduleManager = Object.getPrototypeOf(
- sinon.createStubInstance(ModuleManager)
- );
- moduleManager.logBook = sinon.createStubInstance(LogBook);
- moduleManager.jobQueue = sinon.createStubInstance(JobQueue);
- const dataModule = new DataModule(moduleManager);
- const jobContext = sinon.createStubInstance(JobContext);
- const testData = { abc: [] };
- before(async function () {
- await dataModule.startup();
- dataModule.redisClient = sinon.spy(dataModule.redisClient);
- });
- beforeEach(async function () {
- testData.abc = await async.map(Array(10), async () => {
- const result =
- await dataModule.collections?.abc.collection.insertOne({
- _id: new ObjectId(),
- name: `Test${Math.round(Math.random() * 1000)}`,
- autofill: {
- enabled: !!Math.floor(Math.random())
- },
- someNumbers: Array.from({
- length: Math.max(1, Math.round(Math.random() * 50))
- }).map(() => Math.round(Math.random() * 10000)),
- songs: Array.from({
- length: Math.max(1, Math.round(Math.random() * 10))
- }).map(() => ({
- _id: new ObjectId()
- })),
- createdAt: Date.now(),
- updatedAt: Date.now(),
- testData: true
- });
- return dataModule.collections?.abc.collection.findOne({
- _id: result?.insertedId
- });
- });
- });
- it("module loaded and started", function () {
- moduleManager.logBook.log.should.have.been.called;
- dataModule.getName().should.equal("data");
- dataModule.getStatus().should.equal("STARTED");
- });
- describe("find job", function () {
- // Run cache test twice to validate mongo and redis sourced data
- [false, true, true].forEach(useCache => {
- it(`filter by one _id string ${
- useCache ? "with" : "without"
- } cache`, async function () {
- const [document] = testData.abc;
- const find = await dataModule.find(jobContext, {
- collection: "abc",
- filter: { _id: document._id },
- limit: 1,
- useCache
- });
- find.should.be.an("object");
- find._id.should.deep.equal(document._id);
- find.createdAt.should.deep.equal(new Date(document.createdAt));
- if (useCache) {
- dataModule.redisClient?.GET.should.have.been.called;
- }
- });
- });
- it(`filter by name string without cache`, async function () {
- const [document] = testData.abc;
- const find = await dataModule.find(jobContext, {
- collection: "abc",
- filter: { name: document.name },
- limit: 1,
- useCache: false
- });
- find.should.be.an("object");
- find._id.should.deep.equal(document._id);
- find.should.have.keys([
- "_id",
- "createdAt",
- "updatedAt",
- // "name", - Name is restricted, so it won't be returned
- "autofill",
- "someNumbers",
- "songs"
- ]);
- });
- it(`filter by normal array item`, async function () {
- const [document] = testData.abc;
- const resultDocument = await dataModule.find(jobContext, {
- collection: "abc",
- filter: { someNumbers: document.someNumbers[0] },
- limit: 1,
- useCache: false
- });
- resultDocument.should.be.an("object");
- resultDocument._id.should.deep.equal(document._id);
- });
- it(`filter by normal array item that doesn't exist`, async function () {
- const resultDocument = await dataModule.find(jobContext, {
- collection: "abc",
- filter: { someNumbers: -1 },
- limit: 1,
- useCache: false
- });
- expect(resultDocument).to.be.null;
- });
- it(`filter by schema array item`, async function () {
- const [document] = testData.abc;
- const resultDocument = await dataModule.find(jobContext, {
- collection: "abc",
- filter: { songs: { _id: document.songs[0]._id } },
- limit: 1,
- useCache: false
- });
- resultDocument.should.be.an("object");
- resultDocument._id.should.deep.equal(document._id);
- });
- it(`filter by schema array item, invalid`, async function () {
- const jobPromise = dataModule.find(jobContext, {
- collection: "abc",
- filter: { songs: { randomProperty: "Value" } },
- limit: 1,
- useCache: false
- });
- await expect(jobPromise).to.be.rejectedWith(
- `Key "randomProperty" does not exist in the schema.`
- );
- });
- it(`filter by schema array item with dot notation`, async function () {
- const [document] = testData.abc;
- const resultDocument = await dataModule.find(jobContext, {
- collection: "abc",
- filter: { "songs._id": document.songs[0]._id },
- limit: 1,
- useCache: false
- });
- resultDocument.should.be.an("object");
- resultDocument._id.should.deep.equal(document._id);
- });
- it(`filter by schema array item with dot notation, invalid`, async function () {
- const jobPromise = dataModule.find(jobContext, {
- collection: "abc",
- filter: { "songs.randomProperty": "Value" },
- limit: 1,
- useCache: false
- });
- await expect(jobPromise).to.be.rejectedWith(
- `Key "randomProperty" does not exist in the schema.`
- );
- });
- });
- describe("normalize projection", function () {
- const dataModuleProjection = Object.getPrototypeOf(dataModule);
- it(`basics`, function () {
- dataModuleProjection.normalizeProjection.should.be.a("function");
- });
- it(`empty object/array projection`, function () {
- const expectedResult = { projection: [], mode: "includeAllBut" };
- const resultWithArray = dataModuleProjection.normalizeProjection(
- []
- );
- const resultWithObject = dataModuleProjection.normalizeProjection(
- {}
- );
- resultWithArray.should.deep.equal(expectedResult);
- resultWithObject.should.deep.equal(expectedResult);
- });
- it(`null/undefined projection`, function () {
- const expectedResult = { projection: [], mode: "includeAllBut" };
- const resultWithNull =
- dataModuleProjection.normalizeProjection(null);
- const resultWithUndefined =
- dataModuleProjection.normalizeProjection(undefined);
- const resultWithNothing =
- dataModuleProjection.normalizeProjection();
- resultWithNull.should.deep.equal(expectedResult);
- resultWithUndefined.should.deep.equal(expectedResult);
- resultWithNothing.should.deep.equal(expectedResult);
- });
- it(`simple exclude projection`, function () {
- const expectedResult = {
- projection: [["name", false]],
- mode: "includeAllBut"
- };
- const resultWithBoolean = dataModuleProjection.normalizeProjection({
- name: false
- });
- const resultWithNumber = dataModuleProjection.normalizeProjection({
- name: 0
- });
- resultWithBoolean.should.deep.equal(expectedResult);
- resultWithNumber.should.deep.equal(expectedResult);
- });
- it(`simple include projection`, function () {
- const expectedResult = {
- projection: [["name", true]],
- mode: "excludeAllBut"
- };
- const resultWithObject = dataModuleProjection.normalizeProjection({
- name: true
- });
- const resultWithArray = dataModuleProjection.normalizeProjection([
- "name"
- ]);
- resultWithObject.should.deep.equal(expectedResult);
- resultWithArray.should.deep.equal(expectedResult);
- });
- it(`simple include/exclude projection`, function () {
- const expectedResult = {
- projection: [
- ["color", false],
- ["name", true]
- ],
- mode: "excludeAllBut"
- };
- const result = dataModuleProjection.normalizeProjection({
- color: false,
- name: true
- });
- result.should.deep.equal(expectedResult);
- });
- it(`simple nested include projection`, function () {
- const expectedResult = {
- projection: [["location.city", true]],
- mode: "excludeAllBut"
- };
- const resultWithObject = dataModuleProjection.normalizeProjection({
- location: {
- city: true
- }
- });
- const resultWithArray = dataModuleProjection.normalizeProjection([
- "location.city"
- ]);
- resultWithObject.should.deep.equal(expectedResult);
- resultWithArray.should.deep.equal(expectedResult);
- });
- it(`simple nested exclude projection`, function () {
- const expectedResult = {
- projection: [["location.city", false]],
- mode: "includeAllBut"
- };
- const result = dataModuleProjection.normalizeProjection({
- location: {
- city: false
- }
- });
- result.should.deep.equal(expectedResult);
- });
- it(`path collision`, function () {
- expect(() => {
- dataModuleProjection.normalizeProjection({
- location: {
- city: false
- },
- "location.city": true
- });
- }).to.throw("Path collision, non-unique key");
- });
- it(`path collision 2`, function () {
- expect(() => {
- dataModuleProjection.normalizeProjection({
- location: {
- city: {
- extra: false
- }
- },
- "location.city": true
- });
- }).to.throw(
- "Path collision! location.city.extra collides with location.city"
- );
- });
- // TODO add more test cases
- });
- afterEach(async function () {
- sinon.reset();
- await dataModule.collections?.abc.collection.deleteMany({
- testData: true
- });
- });
- after(async function () {
- await dataModule.shutdown();
- });
- });
|