oak-db/test/testcase/date.ts

811 lines
25 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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('[6.1]date expression $year $month $day', async () => {
const store = storeGetter();
const context = new TestContext(store);
await context.begin();
// 2024-06-15 14:30:45
const specificDate = new Date('2024-06-15T14:30:45.000Z').valueOf();
const id1 = v4();
await store.operate('token', {
id: v4(),
action: 'create',
data: {
id: id1,
env: { type: 'web' } as any,
applicationId: v4(),
userId: v4(),
playerId: v4(),
refreshedAt: specificDate,
value: v4(),
}
}, context, {});
await context.commit();
// 测试 $year
const r1 = await context.select('token', {
data: { id: 1 },
filter: {
id: id1,
$expr: { $eq: [{ $year: { '#attr': 'refreshedAt' } }, 2024] }
}
}, {});
assert(r1.length === 1, `$year failed`);
// 测试 $month
const r2 = await context.select('token', {
data: { id: 1 },
filter: {
id: id1,
$expr: { $eq: [{ $month: { '#attr': 'refreshedAt' } }, 6] }
}
}, {});
assert(r2.length === 1, `$month failed`);
// 测试 $day
const r3 = await context.select('token', {
data: { id: 1 },
filter: {
id: id1,
$expr: { $eq: [{ $day: { '#attr': 'refreshedAt' } }, 15] }
}
}, {});
assert(r3.length === 1, `$day failed`);
});
it('[6.2]date expression $dayOfMonth $dayOfWeek $dayOfYear', async () => {
const store = storeGetter();
const context = new TestContext(store);
await context.begin();
// 2024-06-15 是星期六是一年中的第167天
const specificDate = new Date('2024-06-15T14:30:45.000Z').valueOf();
const id1 = v4();
await store.operate('token', {
id: v4(),
action: 'create',
data: {
id: id1,
env: { type: 'web' } as any,
applicationId: v4(),
userId: v4(),
playerId: v4(),
refreshedAt: specificDate,
value: v4(),
}
}, context, {});
await context.commit();
// 测试 $dayOfMonth
const r1 = await context.select('token', {
data: { id: 1 },
filter: {
id: id1,
$expr: { $eq: [{ $dayOfMonth: { '#attr': 'refreshedAt' } }, 15] }
}
}, {});
assert(r1.length === 1, `$dayOfMonth failed`);
// 测试 $dayOfWeek (MySQL: 1=周日, 7=周六; 2024-06-15是周六)
const r2 = await context.select('token', {
data: { id: 1 },
filter: {
id: id1,
$expr: { $eq: [{ $dayOfWeek: { '#attr': 'refreshedAt' } }, 7] }
}
}, {});
assert(r2.length === 1, `$dayOfWeek failed`);
// 测试 $dayOfYear (2024-06-15 是第167天)
const r3 = await context.select('token', {
data: { id: 1 },
filter: {
id: id1,
$expr: { $eq: [{ $dayOfYear: { '#attr': 'refreshedAt' } }, 167] }
}
}, {});
assert(r3.length === 1, `$dayOfYear failed`);
});
it('[6.3]date expression $weekday $weekOfYear', async () => {
const store = storeGetter();
const context = new TestContext(store);
await context.begin();
// 2024-06-15 是第24周
const specificDate = new Date('2024-06-15T14:30:45.000Z').valueOf();
const id1 = v4();
await store.operate('token', {
id: v4(),
action: 'create',
data: {
id: id1,
env: { type: 'web' } as any,
applicationId: v4(),
userId: v4(),
playerId: v4(),
refreshedAt: specificDate,
value: v4(),
}
}, context, {});
await context.commit();
// 测试 $weekday (MySQL WEEKDAY: 0=周一, 6=周日; 2024-06-15是周六=5)
const r1 = await context.select('token', {
data: { id: 1 },
filter: {
id: id1,
$expr: { $eq: [{ $weekday: { '#attr': 'refreshedAt' } }, 5] }
}
}, {});
assert(r1.length === 1, `$weekday failed`);
// 测试 $weekOfYear
const r2 = await context.select('token', {
data: { id: 1 },
filter: {
id: id1,
$expr: { $eq: [{ $weekOfYear: { '#attr': 'refreshedAt' } }, 24] }
}
}, {});
assert(r2.length === 1, `$weekOfYear failed`);
});
it('[6.4]$dateDiff with different units', async () => {
const store = storeGetter();
const context = new TestContext(store);
await context.begin();
const now = Date.now();
const oneYearAgo = now - 365 * 24 * 3600 * 1000;
const id1 = v4();
await store.operate('token', {
id: v4(),
action: 'create',
data: {
id: id1,
env: { type: 'web' } as any,
applicationId: v4(),
userId: v4(),
playerId: v4(),
refreshedAt: oneYearAgo,
value: v4(),
}
}, context, {});
await context.commit();
// 测试年份差异
const r1 = await context.select('token', {
data: { id: 1 },
filter: {
id: id1,
$expr: {
$gte: [
{ $dateDiff: [now, { '#attr': 'refreshedAt' }, 'y'] },
1
]
}
}
}, {});
assert(r1.length === 1, `$dateDiff year failed`);
// 测试月份差异
const r2 = await context.select('token', {
data: { id: 1 },
filter: {
id: id1,
$expr: {
$gte: [
{ $dateDiff: [now, { '#attr': 'refreshedAt' }, 'M'] },
11
]
}
}
}, {});
assert(r2.length === 1, `$dateDiff month failed`);
});
it('[6.5]$dateFloor with different units', async () => {
const store = storeGetter();
const context = new TestContext(store);
await context.begin();
// 2024-06-15 14:30:45.123
const specificDate = new Date('2024-06-15T14:30:45.123Z').valueOf();
const id1 = v4();
await store.operate('token', {
id: v4(),
action: 'create',
data: {
id: id1,
env: { type: 'web' } as any,
applicationId: v4(),
userId: v4(),
playerId: v4(),
refreshedAt: specificDate,
value: v4(),
}
}, context, {});
await context.commit();
//TODO: 暂不支持
// // 测试向下取整到月 - 应该是 2024-06-01
const startOfMonth = new Date('2024-06-01T00:00:00.000Z').valueOf();
const r1 = await context.select('token', {
data: { id: 1 },
filter: {
id: id1,
$expr: {
$gte: [
{ $dateFloor: [{ '#attr': 'refreshedAt' }, 'M'] },
startOfMonth
]
}
}
}, {});
assert(r1.length === 1, `$dateFloor month failed: Expected 1, got ${r1.length}`);
// 测试向下取整到年 - 应该是 2024-01-01
const startOfYear = new Date('2024-01-01T00:00:00.000Z').valueOf();
const r2 = await context.select('token', {
data: { id: 1 },
filter: {
id: id1,
$expr: {
$gte: [
{ $dateFloor: [{ '#attr': 'refreshedAt' }, 'y'] },
startOfYear
]
}
}
}, {});
assert(r2.length === 1, `$dateFloor year failed`);
});
// TODO: 暂不支持
it('[6.6]$dateCeil with different units', async () => {
const store = storeGetter();
const context = new TestContext(store);
await context.begin();
// 2024-06-15 14:30:45.123
const specificDate = new Date('2024-06-15T14:30:45.123Z').valueOf();
const id1 = v4();
await store.operate('token', {
id: v4(),
action: 'create',
data: {
id: id1,
env: { type: 'web' } as any,
applicationId: v4(),
userId: v4(),
playerId: v4(),
refreshedAt: specificDate,
value: v4(),
}
}, context, {});
await context.commit();
// 测试向上取整到月 - 应该是 2024-07-01
const startOfNextMonth = new Date('2024-07-01T00:00:00.000Z').valueOf();
const r1 = await context.select('token', {
data: { id: 1 },
filter: {
id: id1,
$expr: {
$lte: [
{ $dateCeil: [{ '#attr': 'refreshedAt' }, 'M'] },
startOfNextMonth
]
}
}
}, {});
assert(r1.length === 1, `$dateCeil month failed`);
// 测试向上取整到年 - 应该是 2025-01-01
const startOfNextYear = new Date('2025-01-01T00:00:00.000Z').valueOf();
const r2 = await context.select('token', {
data: { id: 1 },
filter: {
id: id1,
$expr: {
$lte: [
{ $dateCeil: [{ '#attr': 'refreshedAt' }, 'y'] },
startOfNextYear
]
}
}
}, {});
assert(r2.length === 1, `$dateCeil year failed`);
});
it('[1.14]$dateDiff expression', async () => {
const store = storeGetter();
const context = new TestContext(store);
await context.begin();
const now = Date.now();
const oneHourAgo = now - 3600 * 1000; // 1小时前
const oneDayAgo = now - 24 * 3600 * 1000; // 1天前
const oneMonthAgo = now - 30 * 24 * 3600 * 1000; // 约30天前
const id1 = v4();
const id2 = v4();
const id3 = v4();
await store.operate('token', {
id: v4(),
action: 'create',
data: [
{
id: id1,
env: { type: 'web' } as any,
applicationId: v4(),
userId: v4(),
playerId: v4(),
refreshedAt: oneHourAgo,
value: v4(),
},
{
id: id2,
env: { type: 'web' } as any,
applicationId: v4(),
userId: v4(),
playerId: v4(),
refreshedAt: oneDayAgo,
value: v4(),
},
{
id: id3,
env: { type: 'web' } as any,
applicationId: v4(),
userId: v4(),
playerId: v4(),
refreshedAt: oneMonthAgo,
value: v4(),
}
]
}, context, {});
await context.commit();
// 测试秒级差异
const r1 = await context.select('token', {
data: {
id: 1,
refreshedAt: 1,
},
filter: {
id: id1,
$expr: {
$gte: [
{
$dateDiff: [
now,
{ '#attr': 'refreshedAt' },
's'
]
},
3500 // 约1小时 = 3600秒给一些误差
]
}
}
}, {});
assert(r1.length === 1, `Expected 1 row for seconds diff, got ${r1.length}`);
// 测试分钟级差异
const r2 = await context.select('token', {
data: {
id: 1,
refreshedAt: 1,
},
filter: {
id: id1,
$expr: {
$gte: [
{
$dateDiff: [
now,
{ '#attr': 'refreshedAt' },
'm'
]
},
55 // 约1小时 = 60分钟
]
}
}
}, {});
assert(r2.length === 1, `Expected 1 row for minutes diff, got ${r2.length}`);
// 测试小时级差异
const r3 = await context.select('token', {
data: {
id: 1,
refreshedAt: 1,
},
filter: {
id: id2,
$expr: {
$gte: [
{
$dateDiff: [
now,
{ '#attr': 'refreshedAt' },
'h'
]
},
23 // 约1天 = 24小时
]
}
}
}, {});
assert(r3.length === 1, `Expected 1 row for hours diff, got ${r3.length}`);
// 测试天级差异
const r4 = await context.select('token', {
data: {
id: 1,
refreshedAt: 1,
},
filter: {
id: id3,
$expr: {
$gte: [
{
$dateDiff: [
now,
{ '#attr': 'refreshedAt' },
'd'
]
},
25 // 约30天
]
}
}
}, {});
assert(r4.length === 1, `Expected 1 row for days diff, got ${r4.length}`);
});
it('[1.15]$dateFloor expression', async () => {
const store = storeGetter();
const context = new TestContext(store);
await context.begin();
// 创建一个特定时间的记录: 2024-06-15 14:30:45
const specificTime = new Date('2024-06-15T14:30:45.123Z').valueOf();
const id1 = v4();
await store.operate('token', {
id: v4(),
action: 'create',
data: {
id: id1,
env: { type: 'web' } as any,
applicationId: v4(),
userId: v4(),
playerId: v4(),
refreshedAt: specificTime,
value: v4(),
}
}, context, {});
await context.commit();
// 测试向下取整到分钟 - 应该得到 14:30:00
const startOfMinute = new Date('2024-06-15T14:30:00.000Z').valueOf();
const endOfMinute = new Date('2024-06-15T14:31:00.000Z').valueOf();
const r1 = await context.select('token', {
data: {
id: 1,
refreshedAt: 1,
},
filter: {
id: id1,
$expr: {
$and: [
{
$gte: [
{
$dateFloor: [
{ '#attr': 'refreshedAt' },
'm'
]
},
startOfMinute
]
},
{
$lt: [
{
$dateFloor: [
{ '#attr': 'refreshedAt' },
'm'
]
},
endOfMinute
]
}
]
}
}
}, {});
assert(r1.length === 1, `Expected 1 row for minute floor, got ${r1.length}`);
// 测试向下取整到小时 - 应该得到 14:00:00
const startOfHour = new Date('2024-06-15T14:00:00.000Z').valueOf();
const endOfHour = new Date('2024-06-15T15:00:00.000Z').valueOf();
const r2 = await context.select('token', {
data: {
id: 1,
refreshedAt: 1,
},
filter: {
id: id1,
$expr: {
$and: [
{
$gte: [
{
$dateFloor: [
{ '#attr': 'refreshedAt' },
'h'
]
},
startOfHour
]
},
{
$lt: [
{
$dateFloor: [
{ '#attr': 'refreshedAt' },
'h'
]
},
endOfHour
]
}
]
}
}
}, {});
assert(r2.length === 1, `Expected 1 row for hour floor, got ${r2.length}`);
// 测试向下取整到天 - 应该得到 2024-06-15 00:00:00
const startOfDay = new Date('2024-06-15T00:00:00.000Z').valueOf();
const endOfDay = new Date('2024-06-16T00:00:00.000Z').valueOf();
const r3 = await context.select('token', {
data: {
id: 1,
refreshedAt: 1,
},
filter: {
id: id1,
$expr: {
$and: [
{
$gte: [
{
$dateFloor: [
{ '#attr': 'refreshedAt' },
'd'
]
},
startOfDay
]
},
{
$lt: [
{
$dateFloor: [
{ '#attr': 'refreshedAt' },
'd'
]
},
endOfDay
]
}
]
}
}
}, {});
assert(r3.length === 1, `Expected 1 row for day floor, got ${r3.length}`);
});
it('[1.16]$dateCeil expression', async () => {
const store = storeGetter();
const context = new TestContext(store);
await context.begin();
// 创建一个特定时间的记录: 2024-06-15 14:30:45
const specificTime = new Date('2024-06-15T14:30:45.123Z').valueOf();
const id1 = v4();
await store.operate('token', {
id: v4(),
action: 'create',
data: {
id: id1,
env: { type: 'web' } as any,
applicationId: v4(),
userId: v4(),
playerId: v4(),
refreshedAt: specificTime,
value: v4(),
}
}, context, {});
await context.commit();
// 测试向上取整到分钟 - 应该得到 14:31:00
const ceilMinute = new Date('2024-06-15T14:31:00.000Z').valueOf();
const r1 = await context.select('token', {
data: {
id: 1,
refreshedAt: 1,
},
filter: {
id: id1,
$expr: {
$gte: [
{
$dateCeil: [
{ '#attr': 'refreshedAt' },
'm'
]
},
ceilMinute
]
}
}
}, {});
assert(r1.length === 1, `Expected 1 row for minute ceil, got ${r1.length}`);
// 测试向上取整到小时 - 应该得到 15:00:00
const ceilHour = new Date('2024-06-15T15:00:00.000Z').valueOf();
const r2 = await context.select('token', {
data: {
id: 1,
refreshedAt: 1,
},
filter: {
id: id1,
$expr: {
$gte: [
{
$dateCeil: [
{ '#attr': 'refreshedAt' },
'h'
]
},
ceilHour
]
}
}
}, {});
assert(r2.length === 1, `Expected 1 row for hour ceil, got ${r2.length}`);
// 测试向上取整到天 - 应该得到 2024-06-16 00:00:00
const ceilDay = new Date('2024-06-16T00:00:00.000Z').valueOf();
const r3 = await context.select('token', {
data: {
id: 1,
refreshedAt: 1,
},
filter: {
id: id1,
$expr: {
$gte: [
{
$dateCeil: [
{ '#attr': 'refreshedAt' },
'd'
]
},
ceilDay
]
}
}
}, {});
assert(r3.length === 1, `Expected 1 row for day ceil, got ${r3.length}`);
});
it('[1.18]date expression with constants', async () => {
const store = storeGetter();
const context = new TestContext(store);
await context.begin();
const now = Date.now();
const id1 = v4();
await store.operate('token', {
id: v4(),
action: 'create',
data: {
id: id1,
env: { type: 'web' } as any,
applicationId: v4(),
userId: v4(),
playerId: v4(),
refreshedAt: now,
value: v4(),
}
}, context, {});
await context.commit();
// 测试 $dateDiff 使用常量日期
const yesterday = now - 24 * 3600 * 1000;
const r1 = await context.select('token', {
data: {
id: 1,
},
filter: {
id: id1,
$expr: {
$eq: [
{
$dateDiff: [
now,
yesterday,
'd'
]
},
1
]
}
}
}, {});
assert(r1.length === 1, `Expected 1 row for constant date diff, got ${r1.length}`);
// 测试 $dateDiff 混合使用属性和常量
const r2 = await context.select('token', {
data: {
id: 1,
},
filter: {
id: id1,
$expr: {
$lt: [
{
$dateDiff: [
{ '#attr': 'refreshedAt' },
yesterday,
'h'
]
},
48 // 应该在48小时以内
]
}
}
}, {});
assert(r2.length === 1, `Expected 1 row for mixed date diff, got ${r2.length}`);
});
}