forked from boos/BoosAPI
feat: add createdAt timestamps to check-in logs and improve i18n translations
- Added createdAt timestamps to various check-in log entries in checkinService.ts for better tracking. - Updated i18n translations in Models.tsx to improve user experience with accurate Chinese translations. - Reduced default page size in ProgramLogs.tsx and ProxyLogs.tsx from 80 and 100 to 50 for better performance. - Enhanced ProxyLogs.tsx with memoization for filtered logs and summary statistics. - Implemented support for parsing Unix timestamps and PostgreSQL-style timezone offsets in checkinLogTime.ts. - Added comprehensive tests for database proxy query wrapper in index.proxy-wrap.test.ts to ensure functionality.
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
import { mkdtempSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { beforeAll, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
type DbModule = typeof import('./index.js');
|
||||
|
||||
describe('db proxy query wrapper', () => {
|
||||
let testUtils: DbModule['__dbProxyTestUtils'];
|
||||
|
||||
beforeAll(async () => {
|
||||
process.env.DATA_DIR = mkdtempSync(join(tmpdir(), 'metapi-db-proxy-wrap-'));
|
||||
const dbModule = await import('./index.js');
|
||||
testUtils = dbModule.__dbProxyTestUtils;
|
||||
});
|
||||
|
||||
it('wraps thenable query builders and provides all/get shims', async () => {
|
||||
const execute = vi.fn(async () => [{ id: 1, name: 'demo' }]);
|
||||
const queryLike = {
|
||||
execute,
|
||||
where() {
|
||||
return this;
|
||||
},
|
||||
then(onFulfilled: (v: unknown) => unknown, onRejected?: (e: unknown) => unknown) {
|
||||
return Promise.resolve(execute()).then(onFulfilled, onRejected);
|
||||
},
|
||||
};
|
||||
|
||||
expect(testUtils.shouldWrapObject(queryLike)).toBe(true);
|
||||
const wrapped = testUtils.wrapQueryLike(queryLike as any);
|
||||
|
||||
const rows = await wrapped.where().all();
|
||||
const row = await wrapped.where().get();
|
||||
|
||||
expect(execute).toHaveBeenCalledTimes(2);
|
||||
expect(rows).toEqual([{ id: 1, name: 'demo' }]);
|
||||
expect(row).toEqual({ id: 1, name: 'demo' });
|
||||
});
|
||||
|
||||
it('provides run shim for thenable query builders', async () => {
|
||||
const execute = vi.fn(async () => [{ changes: 3, lastInsertRowid: 9 }]);
|
||||
const queryLike = {
|
||||
execute,
|
||||
then(onFulfilled: (v: unknown) => unknown, onRejected?: (e: unknown) => unknown) {
|
||||
return Promise.resolve(execute()).then(onFulfilled, onRejected);
|
||||
},
|
||||
};
|
||||
|
||||
const wrapped = testUtils.wrapQueryLike(queryLike as any);
|
||||
const runResult = await wrapped.run();
|
||||
|
||||
expect(execute).toHaveBeenCalledTimes(1);
|
||||
expect(runResult).toEqual({ changes: 3, lastInsertRowid: 9 });
|
||||
expect(testUtils.shouldWrapObject(Promise.resolve())).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
+16
-3
@@ -411,8 +411,16 @@ function normalizeRunResult(result: unknown): { changes: number; lastInsertRowid
|
||||
|
||||
const wrappedObjects = new WeakMap<object, unknown>();
|
||||
|
||||
function shouldWrapObject(value: unknown): value is object {
|
||||
if (!value || typeof value !== 'object') return false;
|
||||
// Drizzle query builders are thenable objects (QueryPromise) but are not native Promises.
|
||||
// They still need wrapping so we can provide sqlite-style `.all/.get/.run` shims.
|
||||
if (value instanceof Promise) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function wrapQueryLike<T>(value: T): T {
|
||||
if (!value || typeof value !== 'object') return value;
|
||||
if (!shouldWrapObject(value)) return value;
|
||||
const target = value as unknown as object;
|
||||
if (wrappedObjects.has(target)) {
|
||||
return wrappedObjects.get(target) as T;
|
||||
@@ -446,7 +454,7 @@ function wrapQueryLike<T>(value: T): T {
|
||||
|
||||
return (...args: unknown[]) => {
|
||||
const result = original.apply(innerTarget, args);
|
||||
if (result && typeof result === 'object' && typeof (result as Promise<unknown>).then !== 'function') {
|
||||
if (shouldWrapObject(result)) {
|
||||
return wrapQueryLike(result);
|
||||
}
|
||||
return result;
|
||||
@@ -483,7 +491,7 @@ function wrapDbClient<T extends object>(
|
||||
|
||||
return (...args: unknown[]) => {
|
||||
const result = original.apply(target, args);
|
||||
if (result && typeof result === 'object' && typeof (result as Promise<unknown>).then !== 'function') {
|
||||
if (shouldWrapObject(result)) {
|
||||
return wrapQueryLike(result);
|
||||
}
|
||||
return result;
|
||||
@@ -636,3 +644,8 @@ export async function switchRuntimeDatabase(nextDialect: RuntimeDbDialect, nextD
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export const __dbProxyTestUtils = {
|
||||
wrapQueryLike,
|
||||
shouldWrapObject,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user