import { TestContext } from "../Context"; import { EntityDict } from "../test-app-domain"; import { v4 } from 'uuid'; import assert from 'assert'; import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid'; import { describe, it, before, after } from 'mocha'; import { DbStore } from "../../lib/types/dbStore"; export default (storeGetter: () => DbStore) => { it('[1.10]json insert/select', async () => { const store = storeGetter(); const context = new TestContext(store); await context.begin(); const id = await generateNewIdAsync(); await context.operate('house', { id: await generateNewIdAsync(), action: 'create', data: { id, areaId: 'area1', district: 'district1', ownerId: 'owner1', size: 123.4567, data: { rooms: 3, features: ['garden', 'garage'], address: { street: '123 Main St', city: 'Metropolis', zip: '12345' } } } }, {}); const result = await context.select('house', { data: { id: 1, // TODO: 在projection中支持json字段的部分展开,但是展开后返回值都为字符串!!! data: 1, }, filter: { id, }, }, {}); assert(result.length === 1, `Expected 1 result, got ${result.length}`); assert((result[0] as any).data.rooms === 3, `Expected rooms to be 3, got ${(result[0] as any).data.rooms}`); assert.deepStrictEqual((result[0] as any).data.features, ['garden', 'garage'], `Expected features to match`); assert.deepStrictEqual((result[0] as any).data.address, { street: '123 Main St', city: 'Metropolis', zip: '12345', }, `Expected address to match`); }); it('[1.11]json as filter', async () => { const store = storeGetter(); const context = new TestContext(store); await context.begin(); const id = await generateNewIdAsync(); await store.operate('oper', { id: await generateNewIdAsync(), action: 'create', data: { id, action: 'test', data: { name: 'xc', books: [{ title: 'mathmatics', price: 1, }, { title: 'english', price: 2, }], }, targetEntity: 'bbb', bornAt: 111, iState: 'normal', } }, context, {}); const row = await store.select('oper', { data: { id: 1, data: { name: 1, books: [undefined, { title: 1, price: 1, }], }, }, filter: { id, data: { name: 'xc', } } }, context, {}); const row2 = await store.select('oper', { data: { id: 1, data: { name: 1, books: [undefined, { title: 1, price: 1, }], }, }, filter: { id, data: { name: 'xc2', } } }, context, {}); await context.commit(); // console.log(JSON.stringify(row)); assert(row.length === 1, JSON.stringify(row)); assert(row2.length === 0, JSON.stringify(row2)); }); it('[1.11.2]json filter on top level', async () => { const store = storeGetter(); const context = new TestContext(store); await context.begin(); const id = await generateNewIdAsync(); await store.operate('actionAuth', { id: await generateNewIdAsync(), action: 'create', data: { id, pathId: await generateNewIdAsync(), deActions: ['1.12'], } }, context, {}); await context.commit(); const row = await store.select('actionAuth', { data: { id: 1, deActions: 1, }, filter: { id, deActions: { $overlaps: '1.12', } } }, context, {}); const row2 = await store.select('actionAuth', { data: { id: 1, deActions: 1, }, filter: { id, deActions: { $contains: ['1.13333'], } } }, context, {}); // console.log(JSON.stringify(row)); assert(row.length === 1, JSON.stringify(row)); console.log(JSON.stringify(row)); assert(row2.length === 0, JSON.stringify(row2)); const row3 = await store.select('actionAuth', { data: { id: 1, deActions: 1, }, filter: { id, deActions: { $exists: true, } } }, context, {}); assert(row3.length === 1); const row4 = await store.select('actionAuth', { data: { id: 1, deActions: 1, }, filter: { id, deActions: { $exists: false, } } }, context, {}); assert(row4.length === 0); }); it('[1.11.3]json escape', async () => { const store = storeGetter(); const context = new TestContext(store); await context.begin(); const id = await generateNewIdAsync(); await store.operate('oper', { id: await generateNewIdAsync(), action: 'create', data: { id, action: 'test', data: { $or: [{ name: 'xc', }, { name: { $includes: 'xc', } }], }, targetEntity: 'bbb', bornAt: 123, iState: 'normal', } }, context, {}); const row = await store.select('oper', { data: { id: 1, data: { name: 1, price: 1, }, }, filter: { id, data: { '.$or': { $contains: { name: 'xc', }, }, }, } }, context, {}); process.env.NODE_ENV = 'development'; const row2 = await store.select('oper', { data: { id: 1, data: { name: 1, price: 1, }, }, filter: { id, data: { '.$or': [ { name: 'xc', }, { name: { '.$includes': 'xc', } } ], }, } }, context, {}); await context.commit(); assert(row.length === 1); assert(row2.length === 1); }); it('[1.12]complicated json filter', async () => { const store = storeGetter(); const context = new TestContext(store); await context.begin(); const id = await generateNewIdAsync(); await store.operate('oper', { id: await generateNewIdAsync(), action: 'create', data: { id, action: 'test', data: { name: 'xcc', price: [100, 400, 1000], }, targetEntity: 'bbb', bornAt: 123, iState: 'normal', } }, context, {}); const row = await store.select('oper', { data: { id: 1, data: { name: 1, price: 1, }, }, filter: { data: { price: [undefined, 400], } } }, context, {}); const row2 = await store.select('oper', { data: { id: 1, data: { name: 1, price: 1, }, }, filter: { data: { price: [undefined, 200], } } }, context, {}); const row3 = await store.select('oper', { data: { id: 1, data: { name: 1, price: 1, }, }, filter: { data: { price: [undefined, { $gt: 300, }], } } }, context, {}); const row4 = await store.select('oper', { data: { id: 1, data: { name: 1, price: 1, }, }, filter: { data: { price: { $contains: [200, 500], }, } } }, context, {}); const row5 = await store.select('oper', { data: { id: 1, data: { name: 1, price: 1, }, }, filter: { data: { price: { $contains: [100, 400], }, } } }, context, {}); const row6 = await store.select('oper', { data: { id: 1, data: { name: 1, price: 1, }, }, filter: { data: { price: { $contains: ['xc'], }, } } }, context, {}); const row7 = await store.select('oper', { data: { id: 1, data: { name: 1, price: 1, }, }, filter: { data: { name: { $includes: 'xc', }, price: { $overlaps: [200, 400, 800], }, } } }, context, {}); /** * 带$or的查询 */ process.env.NODE_ENV = 'development'; const row8 = await store.select('oper', { data: { id: 1, data: { name: 1, price: 1, }, }, filter: { data: { $or: [ { name: { $includes: 'xc', }, }, { name: { $includes: 'xzw', } } ], price: { $overlaps: [200, 400, 800], }, } } }, context, {}); const row9 = await store.select('oper', { data: { id: 1, data: { name: 1, price: 1, }, }, filter: { data: { price: { $length: 3, }, } } }, context, {}); const row10 = await store.select('oper', { data: { id: 1, data: { name: 1, price: 1, }, }, filter: { data: { price: { $length: { $gt: 3, }, }, } } }, context, {}); await context.commit(); assert(row.length === 1); assert(row2.length === 0); assert(row3.length === 1); assert(row4.length === 0); assert(row5.length === 1); assert(row6.length === 0); assert(row7.length === 1); assert(row8.length === 1); assert(row9.length === 1); assert(row10.length === 0); // console.log(JSON.stringify(row7)); }); // ==================== JSON 操作符补充测试 ==================== it('[9.1]json $exists operator', async () => { const store = storeGetter(); const context = new TestContext(store); await context.begin(); const id1 = v4(); const id2 = v4(); await store.operate('oper', { id: v4(), action: 'create', data: [ { id: id1, action: 'test1', data: { name: 'xc', age: 25 }, targetEntity: 'test', bornAt: Date.now(), iState: 'normal', }, { id: id2, action: 'test2', data: { name: 'zz' }, // 没有 age 字段 targetEntity: 'test', bornAt: Date.now(), iState: 'normal', } ] }, context, {}); await context.commit(); // 测试 $exists: true - 检查 age 字段存在 const r1 = await context.select('oper', { data: { id: 1 }, filter: { id: { $in: [id1, id2] }, data: { age: { $exists: true } } } }, {}); assert(r1.length === 1 && r1[0].id === id1, `$exists true failed`); // 测试 $exists: false - 检查 age 字段不存在 const r2 = await context.select('oper', { data: { id: 1 }, filter: { id: { $in: [id1, id2] }, data: { age: { $exists: false } } } }, {}); assert(r2.length === 1 && r2[0].id === id2, `$exists false failed`); // 暂不支持这种写法 // 测试 $exists: 'keyName' - 检查 JSON 对象是否包含指定键 // const r3 = await context.select('oper', { // data: { id: 1 }, // filter: { // id: { $in: [id1, id2] }, // data: { $exists: 'age' } // } // }, {}); // assert(r3.length === 1 && r3[0].id === id1, `$exists keyName failed`); }); it('[9.2]json nested $and $or in filter', async () => { const store = storeGetter(); const context = new TestContext(store); await context.begin(); const id1 = v4(); const id2 = v4(); const id3 = v4(); await store.operate('oper', { id: v4(), action: 'create', data: [ { id: id1, action: 'test1', data: { name: 'alice', role: 'admin', level: 10 }, targetEntity: 'test', bornAt: Date.now(), iState: 'normal', }, { id: id2, action: 'test2', data: { name: 'bob', role: 'user', level: 5 }, targetEntity: 'test', bornAt: Date.now(), iState: 'normal', }, { id: id3, action: 'test3', data: { name: 'charlie', role: 'admin', level: 3 }, targetEntity: 'test', bornAt: Date.now(), iState: 'normal', } ] }, context, {}); await context.commit(); // 测试: (role = 'admin' AND level > 5) OR (role = 'user') const r1 = await context.select('oper', { data: { id: 1 }, filter: { id: { $in: [id1, id2, id3] }, data: { $or: [ { $and: [ { role: 'admin' }, { level: { $gt: 5 } } ] }, { role: 'user' } ] } } }, {}); assert(r1.length === 2, `Nested $and $or failed, expected 2, got ${r1.length}`); const ids = r1.map(r => r.id); assert(ids.includes(id1) && ids.includes(id2), `Expected id1 and id2`); }); it('[9.3]json array index access in filter', async () => { const store = storeGetter(); const context = new TestContext(store); await context.begin(); const id1 = v4(); const id2 = v4(); await store.operate('oper', { id: v4(), action: 'create', data: [ { id: id1, action: 'test1', data: { scores: [90, 85, 95] }, targetEntity: 'test', bornAt: Date.now(), iState: 'normal', }, { id: id2, action: 'test2', data: { scores: [70, 75, 80] }, targetEntity: 'test', bornAt: Date.now(), iState: 'normal', } ] }, context, {}); await context.commit(); // 测试数组第一个元素 > 80 const r1 = await context.select('oper', { data: { id: 1 }, filter: { id: { $in: [id1, id2] }, data: { scores: [{ $gt: 80 }] } } }, {}); assert(r1.length === 1 && r1[0].id === id1, `Array index filter failed`); // 测试数组第三个元素 = 95 const r2 = await context.select('oper', { data: { id: 1 }, filter: { id: { $in: [id1, id2] }, data: { scores: [undefined, undefined, 95] } } }, {}); assert(r2.length === 1 && r2[0].id === id1, `Array specific index filter failed`); }); }