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('[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`); }); }