698 lines
20 KiB
TypeScript
698 lines
20 KiB
TypeScript
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<EntityDict, TestContext>) => {
|
||
|
||
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`);
|
||
});
|
||
|
||
} |