oak-db/test/testcase/complax.ts

340 lines
11 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('[8.1]nested math expressions', async () => {
const store = storeGetter();
const context = new TestContext(store);
await context.begin();
const id1 = v4();
await store.operate('house', {
id: v4(),
action: 'create',
data: {
id: id1,
areaId: v4(),
ownerId: v4(),
district: '杭州',
size: 100.0,
}
}, context, {});
await context.commit();
// 测试嵌套表达式: size * 2 + 50 / 5 = 210
const r1 = await context.select('house', {
data: { id: 1, size: 1 },
filter: {
id: id1,
$expr: {
$eq: [
{
$divide: [
{
$add: [
{ $multiply: [{ '#attr': 'size' }, 2] },
50
]
},
5
]
},
210
]
}
}
}, {});
assert(r1.length === 1, `Nested math expression failed`);
});
it('[8.2]complex logic with date and math', async () => {
const store = storeGetter();
const context = new TestContext(store);
await context.begin();
const now = Date.now();
const twoDaysAgo = now - 2 * 24 * 3600 * 1000;
const fiveDaysAgo = now - 5 * 24 * 3600 * 1000;
const id1 = v4();
const id2 = v4();
await store.operate('token', {
id: v4(),
action: 'create',
data: [
{
id: id1,
env: { type: 'web' } as any,
applicationId: v4(),
userId: v4(),
playerId: v4(),
refreshedAt: twoDaysAgo,
value: v4(),
},
{
id: id2,
env: { type: 'web' } as any,
applicationId: v4(),
userId: v4(),
playerId: v4(),
refreshedAt: fiveDaysAgo,
value: v4(),
}
]
}, context, {});
await context.commit();
// 查询: 刷新时间在3天内 OR 刷新时间超过4天
const r1 = await context.select('token', {
data: { id: 1 },
filter: {
id: { $in: [id1, id2] },
$expr: {
$or: [
{
$lt: [
{ $dateDiff: [now, { '#attr': 'refreshedAt' }, 'd'] },
3
]
},
{
$gt: [
{ $dateDiff: [now, { '#attr': 'refreshedAt' }, 'd'] },
4
]
}
]
}
}
}, {});
assert(r1.length === 2, `Complex logic failed, expected 2, got ${r1.length}`);
// 查询: 刷新时间在3天到4天之间
const r2 = await context.select('token', {
data: { id: 1 },
filter: {
id: { $in: [id1, id2] },
$expr: {
$and: [
{
$gte: [
{ $dateDiff: [now, { '#attr': 'refreshedAt' }, 'd'] },
3
]
},
{
$lte: [
{ $dateDiff: [now, { '#attr': 'refreshedAt' }, 'd'] },
4
]
}
]
}
}
}, {});
assert(r2.length === 0, `Complex logic failed, expected 0, got ${r2.length}`);
});
it('[8.3]expression with cross-entity reference', async () => {
const store = storeGetter();
const context = new TestContext(store);
await context.begin();
const systemId = v4();
const appId1 = v4();
const appId2 = v4();
await store.operate('system', {
id: v4(),
action: 'create',
data: {
id: systemId,
name: 'parent_system',
description: 'test',
folder: '/test',
application$system: [
{
id: v4(),
action: 'create',
data: {
id: appId1,
name: 'parent_system_app1',
description: 't1',
type: 'web',
}
},
{
id: v4(),
action: 'create',
data: {
id: appId2,
name: 'child_app',
description: 't2',
type: 'web',
}
}
]
}
} as EntityDict['system']['CreateSingle'], context, {});
await context.commit();
// // 查询: application.name 以 system.name 的前缀开头
const r1 = await context.select('application', {
data: {
id: 1,
name: 1,
system: { id: 1, name: 1 }
},
filter: {
systemId,
$expr: {
$startsWith: [
{ '#attr': 'name' },
{
'#refAttr': 'name',
'#refId': 'node-1'
}
]
},
system: {
'#id': 'node-1'
}
}
}, {});
assert(r1.length === 1, `Cross-entity expression failed, expected 1, got ${r1.length}`);
assert(r1[0].id === appId1, `Expected appId1, got ${r1[0].id}`);
});
// ==================== $mod 运算符测试 ====================
it('[10.1]math expression $mod', async () => {
const store = storeGetter();
const context = new TestContext(store);
await context.begin();
const id1 = v4();
const id2 = v4();
const id3 = v4();
await store.operate('user', {
id: v4(),
action: 'create',
data: [
{ id: id1, name: 'user1', nickname: 'n1', idState: 'unverified', userState: 'normal' },
{ id: id2, name: 'user2', nickname: 'n2', idState: 'unverified', userState: 'normal' },
{ id: id3, name: 'user3', nickname: 'n3', idState: 'unverified', userState: 'normal' },
]
}, context, {});
await context.commit();
// 测试 $mod: $$seq$$ % 2 = 0 (偶数序列号)
const r1 = await context.select('user', {
data: { id: 1, $$seq$$: 1 },
filter: {
id: { $in: [id1, id2, id3] },
$expr: {
$eq: [
{ $mod: [{ '#attr': '$$seq$$' }, 2] },
0
]
}
}
}, {});
// 偶数序列号的数量取决于插入顺序,这里只检查查询能正常执行
assert(r1.length >= 0, `$mod expression failed`);
// 测试 $mod: $$seq$$ % 3 = 1
const r2 = await context.select('user', {
data: { id: 1, $$seq$$: 1 },
filter: {
id: { $in: [id1, id2, id3] },
$expr: {
$eq: [
{ $mod: [{ '#attr': '$$seq$$' }, 3] },
1
]
}
}
}, {});
assert(r2.length >= 0, `$mod with 3 failed`);
});
// ==================== 聚合与普通查询组合测试 ====================
it('[20.2]select with nested aggregation', async () => {
const store = storeGetter();
const context = new TestContext(store);
await context.begin();
const systemId1 = v4();
const systemId2 = v4();
await store.operate('system', {
id: v4(),
action: 'create',
data: [
{
id: systemId1,
name: 'agg_system1',
description: 'test',
folder: '/test1',
platformId: 'p1',
application$system: [
{ id: v4(), action: 'create', data: { id: v4(), name: 'app1_1', description: 't', type: 'web' } },
{ id: v4(), action: 'create', data: { id: v4(), name: 'app1_2', description: 't', type: 'web' } },
{ id: v4(), action: 'create', data: { id: v4(), name: 'app1_3', description: 't', type: 'wechatMp' } },
]
},
{
id: systemId2,
name: 'agg_system2',
description: 'test',
folder: '/test2',
platformId: 'p1',
application$system: [
{ id: v4(), action: 'create', data: { id: v4(), name: 'app2_1', description: 't', type: 'web' } },
]
}
]
} as EntityDict['system']['CreateMulti'], context, {});
await context.commit();
// 查询 system 并附带 application 聚合统计
const r1 = await context.select('system', {
data: {
id: 1,
name: 1,
application$system$$aggr: {
$entity: 'application',
data: {
'#aggr': { type: 1 },
'#count-1': { id: 1 }
}
}
},
filter: {
id: { $in: [systemId1, systemId2] }
}
}, {});
assert(r1.length === 2, `Nested aggregation query failed`);
const sys1 = r1.find(s => s.id === systemId1);
assert(sys1, `System1 not found`);
assert(sys1.application$system$$aggr && sys1.application$system$$aggr.length === 2, `System1 should have 2 type groups`);
const webCount = sys1.application$system$$aggr.find((a: any) => a['#data']?.type === 'web');
assert(webCount && webCount['#count-1'] === 2, `System1 web count should be 2`);
});
}