@taicode/common-server 4.0.0-beta.1 → 4.0.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/output/app/index.d.ts +18 -15
- package/output/app/index.d.ts.map +1 -1
- package/output/app/index.js +330 -18
- package/output/auth/index.d.ts +73 -0
- package/output/auth/index.d.ts.map +1 -0
- package/output/auth/index.js +196 -0
- package/output/core/assemble.d.ts +64 -0
- package/output/core/assemble.d.ts.map +1 -0
- package/output/core/assemble.js +88 -0
- package/output/core/boot/context.d.ts +109 -0
- package/output/core/boot/context.d.ts.map +1 -0
- package/output/core/boot/context.js +145 -0
- package/output/core/boot/lifecycle.d.ts +206 -0
- package/output/core/boot/lifecycle.d.ts.map +1 -0
- package/output/core/boot/lifecycle.js +255 -0
- package/output/core/boot/shutdown.d.ts +34 -0
- package/output/core/boot/shutdown.d.ts.map +1 -0
- package/output/core/boot/shutdown.js +50 -0
- package/output/core/boot/signal.d.ts +13 -0
- package/output/core/boot/signal.d.ts.map +1 -0
- package/output/{app → core/boot}/signal.js +3 -2
- package/output/core/boot/steps.d.ts +138 -0
- package/output/core/boot/steps.d.ts.map +1 -0
- package/output/core/boot/steps.js +259 -0
- package/output/core/entries.d.ts +151 -0
- package/output/core/entries.d.ts.map +1 -0
- package/output/core/entries.js +242 -0
- package/output/core/entries.test.d.ts +2 -0
- package/output/core/entries.test.d.ts.map +1 -0
- package/output/core/entries.test.js +458 -0
- package/output/core/env.d.ts +83 -0
- package/output/core/env.d.ts.map +1 -0
- package/output/core/env.js +124 -0
- package/output/{app → core}/env.test.d.ts.map +1 -1
- package/output/core/env.test.js +105 -0
- package/output/core/modules/auth-provider/index.d.ts +10 -0
- package/output/core/modules/auth-provider/index.d.ts.map +1 -0
- package/output/core/modules/auth-provider/index.js +9 -0
- package/output/core/modules/auth-provider/types.d.ts +71 -0
- package/output/core/modules/auth-provider/types.d.ts.map +1 -0
- package/output/core/modules/context/index.d.ts +18 -0
- package/output/core/modules/context/index.d.ts.map +1 -0
- package/output/core/modules/context/index.js +23 -0
- package/output/core/modules/context/index.test.d.ts.map +1 -0
- package/output/core/modules/cookie/index.d.ts +21 -0
- package/output/core/modules/cookie/index.d.ts.map +1 -0
- package/output/{app/core/cookie.js → core/modules/cookie/index.js} +1 -6
- package/output/core/modules/identity/index.d.ts +4 -0
- package/output/core/modules/identity/index.d.ts.map +1 -0
- package/output/{app/core/identity.js → core/modules/identity/index.js} +1 -1
- package/output/core/modules/logging/index.d.ts +5 -0
- package/output/core/modules/logging/index.d.ts.map +1 -0
- package/output/{app/core/logging.js → core/modules/logging/index.js} +2 -2
- package/output/core/modules/openapi/index.d.ts +7 -0
- package/output/core/modules/openapi/index.d.ts.map +1 -0
- package/output/{app/core/openapi.js → core/modules/openapi/index.js} +11 -12
- package/output/core/modules/permission-operations/index.d.ts +26 -0
- package/output/core/modules/permission-operations/index.d.ts.map +1 -0
- package/output/core/modules/permission-operations/index.js +36 -0
- package/output/core/modules/permission-operations/index.test.d.ts +2 -0
- package/output/core/modules/permission-operations/index.test.d.ts.map +1 -0
- package/output/core/modules/permission-operations/index.test.js +45 -0
- package/output/{app/core/request-id.d.ts → core/modules/request-id/index.d.ts} +1 -4
- package/output/core/modules/request-id/index.d.ts.map +1 -0
- package/output/{app/core/request-id.js → core/modules/request-id/index.js} +2 -2
- package/output/core/modules/response/index.d.ts +50 -0
- package/output/core/modules/response/index.d.ts.map +1 -0
- package/output/{app/core/response.js → core/modules/response/index.js} +31 -18
- package/output/{schema/index.d.ts → core/modules/schema/builders.d.ts} +8 -1
- package/output/core/modules/schema/builders.d.ts.map +1 -0
- package/output/{schema/index.js → core/modules/schema/builders.js} +7 -4
- package/output/core/modules/schema/index.d.ts +34 -0
- package/output/core/modules/schema/index.d.ts.map +1 -0
- package/output/core/modules/schema/index.js +62 -0
- package/output/core/modules/schema/index.test.d.ts +2 -0
- package/output/core/modules/schema/index.test.d.ts.map +1 -0
- package/output/core/modules/schema/index.test.js +55 -0
- package/output/core/modules/system-routes/index.d.ts +15 -0
- package/output/core/modules/system-routes/index.d.ts.map +1 -0
- package/output/core/modules/system-routes/index.js +205 -0
- package/output/core/resource.d.ts +56 -0
- package/output/core/resource.d.ts.map +1 -0
- package/output/core/resource.js +56 -0
- package/output/core/test-support/database.d.ts +89 -0
- package/output/core/test-support/database.d.ts.map +1 -0
- package/output/core/test-support/database.js +131 -0
- package/output/core/test-support/database.test.d.ts +2 -0
- package/output/core/test-support/database.test.d.ts.map +1 -0
- package/output/core/test-support/database.test.js +111 -0
- package/output/core/test-support/environment.d.ts +38 -0
- package/output/core/test-support/environment.d.ts.map +1 -0
- package/output/core/test-support/environment.js +45 -0
- package/output/core/test-support/service-context.d.ts +64 -0
- package/output/core/test-support/service-context.d.ts.map +1 -0
- package/output/core/test-support/service-context.js +63 -0
- package/output/core/test-support/service-context.test.d.ts +2 -0
- package/output/core/test-support/service-context.test.d.ts.map +1 -0
- package/output/core/test-support/service-context.test.js +55 -0
- package/output/core/test-support/test-server.d.ts +154 -0
- package/output/core/test-support/test-server.d.ts.map +1 -0
- package/output/core/test-support/test-server.js +114 -0
- package/output/core/test-support/test-server.test.d.ts +2 -0
- package/output/core/test-support/test-server.test.d.ts.map +1 -0
- package/output/core/test-support/test-server.test.js +296 -0
- package/output/core/test-support/testdata.d.ts +255 -0
- package/output/core/test-support/testdata.d.ts.map +1 -0
- package/output/core/test-support/testdata.js +271 -0
- package/output/core/test-support/testdata.test.d.ts +2 -0
- package/output/core/test-support/testdata.test.d.ts.map +1 -0
- package/output/core/test-support/testdata.test.js +334 -0
- package/output/{app → core}/type-augment.d.ts +2 -2
- package/output/core/type-augment.d.ts.map +1 -0
- package/output/core/types.d.ts +582 -0
- package/output/core/types.d.ts.map +1 -0
- package/output/cors/index.d.ts +3 -0
- package/output/cors/index.d.ts.map +1 -0
- package/output/cors/index.js +28 -0
- package/output/index.d.ts +10 -4
- package/output/index.d.ts.map +1 -1
- package/output/index.js +25 -5
- package/output/rate-limit/index.d.ts +23 -0
- package/output/rate-limit/index.d.ts.map +1 -0
- package/output/rate-limit/index.js +43 -0
- package/output/{rate-limiter/rate-limiter.d.ts → rate-limit/limiter.d.ts} +1 -1
- package/output/rate-limit/limiter.d.ts.map +1 -0
- package/output/{rate-limiter → rate-limit}/memory-store.d.ts +6 -0
- package/output/rate-limit/memory-store.d.ts.map +1 -0
- package/output/{rate-limiter → rate-limit}/memory-store.js +8 -0
- package/output/{rate-limiter → rate-limit}/redis-store.d.ts +18 -0
- package/output/rate-limit/redis-store.d.ts.map +1 -0
- package/output/{rate-limiter → rate-limit}/redis-store.js +35 -0
- package/output/rate-limit/redis-store.test.d.ts.map +1 -0
- package/output/{rate-limiter → rate-limit}/redis-store.test.js +2 -2
- package/output/rate-limit/types.d.ts +45 -0
- package/output/rate-limit/types.d.ts.map +1 -0
- package/output/rate-limit/types.js +1 -0
- package/output/{redis-cache/redis-cache.d.ts → redis/cache.d.ts} +1 -1
- package/output/redis/cache.d.ts.map +1 -0
- package/output/redis/cache.test.d.ts +2 -0
- package/output/redis/cache.test.d.ts.map +1 -0
- package/output/{redis-cache/redis-cache.test.js → redis/cache.test.js} +1 -1
- package/output/{app/redis.d.ts → redis/index.d.ts} +12 -7
- package/output/redis/index.d.ts.map +1 -0
- package/output/{app/redis.js → redis/index.js} +15 -5
- package/output/redis/index.test.d.ts +2 -0
- package/output/{context → redis}/index.test.d.ts.map +1 -1
- package/output/redis/index.test.js +55 -0
- package/output/redis-queue/index.js +1 -1
- package/output/redis-queue/test-helpers.d.ts +1 -1
- package/output/redis-queue/test-helpers.js +1 -1
- package/output/redis-queue/types.d.ts +3 -12
- package/output/redis-queue/types.d.ts.map +1 -1
- package/package.json +5 -5
- package/output/app/auth.test.d.ts +0 -2
- package/output/app/auth.test.d.ts.map +0 -1
- package/output/app/auth.test.js +0 -148
- package/output/app/boot.d.ts +0 -48
- package/output/app/boot.d.ts.map +0 -1
- package/output/app/boot.js +0 -123
- package/output/app/config.test.d.ts +0 -2
- package/output/app/config.test.d.ts.map +0 -1
- package/output/app/config.test.js +0 -132
- package/output/app/context.d.ts +0 -68
- package/output/app/context.d.ts.map +0 -1
- package/output/app/context.js +0 -73
- package/output/app/core/cookie.d.ts +0 -25
- package/output/app/core/cookie.d.ts.map +0 -1
- package/output/app/core/identity.d.ts +0 -12
- package/output/app/core/identity.d.ts.map +0 -1
- package/output/app/core/index.d.ts +0 -44
- package/output/app/core/index.d.ts.map +0 -1
- package/output/app/core/index.js +0 -57
- package/output/app/core/logging.d.ts +0 -13
- package/output/app/core/logging.d.ts.map +0 -1
- package/output/app/core/openapi.d.ts +0 -17
- package/output/app/core/openapi.d.ts.map +0 -1
- package/output/app/core/request-id.d.ts.map +0 -1
- package/output/app/core/response.d.ts +0 -41
- package/output/app/core/response.d.ts.map +0 -1
- package/output/app/define-server.negative-cases.d.ts +0 -32
- package/output/app/define-server.negative-cases.d.ts.map +0 -1
- package/output/app/define-server.negative-cases.js +0 -209
- package/output/app/define-server.test.d.ts +0 -2
- package/output/app/define-server.test.d.ts.map +0 -1
- package/output/app/define-server.test.js +0 -97
- package/output/app/entries.d.ts +0 -29
- package/output/app/entries.d.ts.map +0 -1
- package/output/app/entries.js +0 -64
- package/output/app/env.d.ts +0 -59
- package/output/app/env.d.ts.map +0 -1
- package/output/app/env.js +0 -66
- package/output/app/env.test.js +0 -124
- package/output/app/error-contract.test.d.ts +0 -2
- package/output/app/error-contract.test.d.ts.map +0 -1
- package/output/app/error-contract.test.js +0 -125
- package/output/app/errors.d.ts +0 -20
- package/output/app/errors.d.ts.map +0 -1
- package/output/app/errors.js +0 -51
- package/output/app/generate-openapi.test.d.ts +0 -2
- package/output/app/generate-openapi.test.d.ts.map +0 -1
- package/output/app/generate-openapi.test.js +0 -123
- package/output/app/lifecycle-logging.test.d.ts +0 -2
- package/output/app/lifecycle-logging.test.d.ts.map +0 -1
- package/output/app/lifecycle-logging.test.js +0 -155
- package/output/app/lifecycle.d.ts +0 -70
- package/output/app/lifecycle.d.ts.map +0 -1
- package/output/app/lifecycle.js +0 -99
- package/output/app/lifecycle.test.d.ts +0 -2
- package/output/app/lifecycle.test.d.ts.map +0 -1
- package/output/app/lifecycle.test.js +0 -152
- package/output/app/middleware/auth.d.ts +0 -71
- package/output/app/middleware/auth.d.ts.map +0 -1
- package/output/app/middleware/auth.js +0 -190
- package/output/app/middleware/context.d.ts +0 -13
- package/output/app/middleware/context.d.ts.map +0 -1
- package/output/app/middleware/context.js +0 -15
- package/output/app/middleware/cors.d.ts +0 -14
- package/output/app/middleware/cors.d.ts.map +0 -1
- package/output/app/middleware/cors.js +0 -30
- package/output/app/middleware/index.d.ts +0 -17
- package/output/app/middleware/index.d.ts.map +0 -1
- package/output/app/middleware/index.js +0 -16
- package/output/app/middleware/rate-limit.d.ts +0 -24
- package/output/app/middleware/rate-limit.d.ts.map +0 -1
- package/output/app/middleware/rate-limit.js +0 -46
- package/output/app/middleware-order.test.d.ts +0 -2
- package/output/app/middleware-order.test.d.ts.map +0 -1
- package/output/app/middleware-order.test.js +0 -148
- package/output/app/permission-operations.d.ts +0 -48
- package/output/app/permission-operations.d.ts.map +0 -1
- package/output/app/permission-operations.js +0 -77
- package/output/app/permission-operations.test.d.ts +0 -2
- package/output/app/permission-operations.test.d.ts.map +0 -1
- package/output/app/permission-operations.test.js +0 -72
- package/output/app/permission-registration.test.d.ts +0 -2
- package/output/app/permission-registration.test.d.ts.map +0 -1
- package/output/app/permission-registration.test.js +0 -138
- package/output/app/redis.d.ts.map +0 -1
- package/output/app/registered-operations.test.d.ts +0 -2
- package/output/app/registered-operations.test.d.ts.map +0 -1
- package/output/app/registered-operations.test.js +0 -57
- package/output/app/request-id.test.d.ts +0 -2
- package/output/app/request-id.test.d.ts.map +0 -1
- package/output/app/request-id.test.js +0 -130
- package/output/app/resources.test.d.ts +0 -2
- package/output/app/resources.test.d.ts.map +0 -1
- package/output/app/resources.test.js +0 -197
- package/output/app/shutdown.d.ts +0 -22
- package/output/app/shutdown.d.ts.map +0 -1
- package/output/app/shutdown.js +0 -30
- package/output/app/shutdown.test.d.ts +0 -2
- package/output/app/shutdown.test.d.ts.map +0 -1
- package/output/app/shutdown.test.js +0 -103
- package/output/app/signal.d.ts +0 -12
- package/output/app/signal.d.ts.map +0 -1
- package/output/app/smoke.test.d.ts +0 -2
- package/output/app/smoke.test.d.ts.map +0 -1
- package/output/app/smoke.test.js +0 -210
- package/output/app/test-support.d.ts +0 -21
- package/output/app/test-support.d.ts.map +0 -1
- package/output/app/test-support.js +0 -24
- package/output/app/type-augment.d.ts.map +0 -1
- package/output/app/types.d.ts +0 -211
- package/output/app/types.d.ts.map +0 -1
- package/output/context/index.d.ts +0 -6
- package/output/context/index.d.ts.map +0 -1
- package/output/context/index.js +0 -14
- package/output/rate-limiter/index.d.ts +0 -5
- package/output/rate-limiter/index.d.ts.map +0 -1
- package/output/rate-limiter/index.js +0 -4
- package/output/rate-limiter/memory-store.d.ts.map +0 -1
- package/output/rate-limiter/rate-limiter.d.ts.map +0 -1
- package/output/rate-limiter/redis-store.d.ts.map +0 -1
- package/output/rate-limiter/redis-store.test.d.ts.map +0 -1
- package/output/rate-limiter/types.d.ts +0 -27
- package/output/rate-limiter/types.d.ts.map +0 -1
- package/output/redis-cache/index.d.ts +0 -4
- package/output/redis-cache/index.d.ts.map +0 -1
- package/output/redis-cache/index.js +0 -1
- package/output/redis-cache/redis-cache.d.ts.map +0 -1
- package/output/redis-cache/redis-cache.test.d.ts +0 -2
- package/output/redis-cache/redis-cache.test.d.ts.map +0 -1
- package/output/schema/index.d.ts.map +0 -1
- /package/output/{app → core}/env.test.d.ts +0 -0
- /package/output/{rate-limiter → core/modules/auth-provider}/types.js +0 -0
- /package/output/{context → core/modules/context}/index.test.d.ts +0 -0
- /package/output/{context → core/modules/context}/index.test.js +0 -0
- /package/output/{app → core}/type-augment.js +0 -0
- /package/output/{app → core}/types.js +0 -0
- /package/output/{rate-limiter/rate-limiter.js → rate-limit/limiter.js} +0 -0
- /package/output/{rate-limiter → rate-limit}/redis-store.test.d.ts +0 -0
- /package/output/{redis-cache/redis-cache.js → redis/cache.js} +0 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
5
|
+
import { cleanupTestData, defineTestData, discoverTestDataModules, loadTestData, resolveTestDataDirectory } from './testdata.js';
|
|
6
|
+
const ORIGINAL_ENV = { ...process.env };
|
|
7
|
+
const tempDirs = [];
|
|
8
|
+
afterEach(async () => {
|
|
9
|
+
process.env = { ...ORIGINAL_ENV };
|
|
10
|
+
await Promise.all(tempDirs.splice(0).map((dir) => rm(dir, { recursive: true, force: true })));
|
|
11
|
+
});
|
|
12
|
+
/** 造一个临时脚本目录(内容按 `文件名 → 模块源码` 写入) */
|
|
13
|
+
async function makeDirectory(files) {
|
|
14
|
+
const dir = await mkdtemp(join(tmpdir(), 'common-testdata-'));
|
|
15
|
+
tempDirs.push(dir);
|
|
16
|
+
await Promise.all(Object.entries(files).map(([name, source]) => writeFile(join(dir, name), source)));
|
|
17
|
+
return dir;
|
|
18
|
+
}
|
|
19
|
+
/** 造一个记录调用顺序的脚本源码(模块级数组挂在 globalThis 上,跨 import 共享) */
|
|
20
|
+
function scriptSource(name, recorder) {
|
|
21
|
+
return `
|
|
22
|
+
const record = (phase) => globalThis.${recorder}.push(\`\${phase}:${name}\`)
|
|
23
|
+
export function load() { record('load') }
|
|
24
|
+
export function cleanup() { record('cleanup') }
|
|
25
|
+
`;
|
|
26
|
+
}
|
|
27
|
+
describe('discoverTestDataModules', () => {
|
|
28
|
+
it('目录不存在时返回空数组(没写测试数据的服务是合法的)', async () => {
|
|
29
|
+
const dir = join(tmpdir(), `common-testdata-missing-${Date.now()}`);
|
|
30
|
+
await expect(discoverTestDataModules(dir)).resolves.toEqual([]);
|
|
31
|
+
});
|
|
32
|
+
it('按文件名升序装载,name 为去扩展名的文件名', async () => {
|
|
33
|
+
const dir = await makeDirectory({
|
|
34
|
+
'02-second.mjs': scriptSource('second', '__order02'),
|
|
35
|
+
'01-first.mjs': scriptSource('first', '__order01'),
|
|
36
|
+
});
|
|
37
|
+
const units = await discoverTestDataModules(dir);
|
|
38
|
+
expect(units.map((unit) => unit.name)).toEqual(['01-first', '02-second']);
|
|
39
|
+
expect(units.every((unit) => typeof unit.load === 'function' && typeof unit.cleanup === 'function')).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
it('`_` 开头的文件是共享 helper,不参与 load / cleanup', async () => {
|
|
42
|
+
const dir = await makeDirectory({
|
|
43
|
+
'_shared.mjs': 'export const helper = 1',
|
|
44
|
+
'01-real.mjs': scriptSource('real', '__orderShared'),
|
|
45
|
+
});
|
|
46
|
+
const units = await discoverTestDataModules(dir);
|
|
47
|
+
expect(units.map((unit) => unit.name)).toEqual(['01-real']);
|
|
48
|
+
});
|
|
49
|
+
it('非脚本文件(.md / .json / .d.ts)被忽略', async () => {
|
|
50
|
+
const dir = await makeDirectory({
|
|
51
|
+
'README.md': '# 说明',
|
|
52
|
+
'data.json': '{}',
|
|
53
|
+
'types.d.ts': 'export declare function load(): void',
|
|
54
|
+
'01-real.mjs': scriptSource('real', '__orderIgnore'),
|
|
55
|
+
});
|
|
56
|
+
const units = await discoverTestDataModules(dir);
|
|
57
|
+
expect(units.map((unit) => unit.name)).toEqual(['01-real']);
|
|
58
|
+
});
|
|
59
|
+
it('缺 cleanup 不报错(库里的行由资源单元的 clean() 兜底)', async () => {
|
|
60
|
+
const dir = await makeDirectory({
|
|
61
|
+
'01-plain.mjs': 'export function load() { globalThis.__orderNoCleanup.push(\'load\') }',
|
|
62
|
+
});
|
|
63
|
+
const order = globalThis.__orderNoCleanup = [];
|
|
64
|
+
const units = await discoverTestDataModules(dir);
|
|
65
|
+
expect(units.map((unit) => unit.name)).toEqual(['01-plain']);
|
|
66
|
+
expect(units[0]?.cleanup).toBeUndefined();
|
|
67
|
+
// `load` 照跑,`cleanup` 直接跳过(不是报错)
|
|
68
|
+
await loadTestData({ ctx: makeCtx('test'), units });
|
|
69
|
+
await cleanupTestData({ ctx: makeCtx('test'), units });
|
|
70
|
+
expect(order).toEqual(['load']);
|
|
71
|
+
});
|
|
72
|
+
it('缺 load 导出直接抛错', async () => {
|
|
73
|
+
const dir = await makeDirectory({
|
|
74
|
+
'01-bad.mjs': 'export function cleanup() {}',
|
|
75
|
+
});
|
|
76
|
+
await expect(discoverTestDataModules(dir)).rejects.toThrow(/缺少 load 导出/);
|
|
77
|
+
});
|
|
78
|
+
it('装载期抛错时带上文件名(定位不要靠猜)', async () => {
|
|
79
|
+
const dir = await makeDirectory({
|
|
80
|
+
'01-broken.mjs': 'throw new Error("boom")',
|
|
81
|
+
});
|
|
82
|
+
await expect(discoverTestDataModules(dir)).rejects.toThrow(/装载 "01-broken.mjs" 失败/);
|
|
83
|
+
});
|
|
84
|
+
it('dependsOn 缺省时按文件名升序', async () => {
|
|
85
|
+
const dir = await makeDirectory({
|
|
86
|
+
'02-b.mjs': scriptSource('b', '__orderNoDep'),
|
|
87
|
+
'01-a.mjs': scriptSource('a', '__orderNoDep'),
|
|
88
|
+
});
|
|
89
|
+
await expect(discoverTestDataModules(dir)).resolves.toMatchObject([{ name: '01-a' }, { name: '02-b' }]);
|
|
90
|
+
});
|
|
91
|
+
it('dependsOn 决定顺序:依赖在前,文件名管不了它', async () => {
|
|
92
|
+
const dir = await makeDirectory({
|
|
93
|
+
'01-late.mjs': `export const dependsOn = ['99-early']\n${scriptSource('late', '__orderDep')}`,
|
|
94
|
+
'99-early.mjs': scriptSource('early', '__orderDep'),
|
|
95
|
+
});
|
|
96
|
+
const units = await discoverTestDataModules(dir);
|
|
97
|
+
expect(units.map((unit) => unit.name)).toEqual(['99-early', '01-late']);
|
|
98
|
+
});
|
|
99
|
+
it('同层按文件名升序(无依赖关系的脚本可复现)', async () => {
|
|
100
|
+
const dir = await makeDirectory({
|
|
101
|
+
'03-c.mjs': `export const dependsOn = ['01-base']\n${scriptSource('c', '__orderTie')}`,
|
|
102
|
+
'02-b.mjs': `export const dependsOn = ['01-base']\n${scriptSource('b', '__orderTie')}`,
|
|
103
|
+
'01-base.mjs': scriptSource('base', '__orderTie'),
|
|
104
|
+
});
|
|
105
|
+
const units = await discoverTestDataModules(dir);
|
|
106
|
+
expect(units.map((unit) => unit.name)).toEqual(['01-base', '02-b', '03-c']);
|
|
107
|
+
});
|
|
108
|
+
it('依赖不存在的脚本直接抛错', async () => {
|
|
109
|
+
const dir = await makeDirectory({
|
|
110
|
+
'01-a.mjs': `export const dependsOn = ['99-missing']\n${scriptSource('a', '__orderMissing')}`,
|
|
111
|
+
});
|
|
112
|
+
await expect(discoverTestDataModules(dir)).rejects.toThrow(/依赖的 "99-missing" 不存在/);
|
|
113
|
+
});
|
|
114
|
+
it('依赖自己直接抛错', async () => {
|
|
115
|
+
const dir = await makeDirectory({
|
|
116
|
+
'01-a.mjs': `export const dependsOn = ['01-a']\n${scriptSource('a', '__orderSelf')}`,
|
|
117
|
+
});
|
|
118
|
+
await expect(discoverTestDataModules(dir)).rejects.toThrow(/依赖了自己/);
|
|
119
|
+
});
|
|
120
|
+
it('循环依赖直接抛错(不猜顺序)', async () => {
|
|
121
|
+
const dir = await makeDirectory({
|
|
122
|
+
'01-a.mjs': `export const dependsOn = ['02-b']\n${scriptSource('a', '__orderCycle')}`,
|
|
123
|
+
'02-b.mjs': `export const dependsOn = ['01-a']\n${scriptSource('b', '__orderCycle')}`,
|
|
124
|
+
});
|
|
125
|
+
await expect(discoverTestDataModules(dir)).rejects.toThrow(/循环依赖/);
|
|
126
|
+
});
|
|
127
|
+
it('dependsOn 不是字符串数组直接抛错', async () => {
|
|
128
|
+
const dir = await makeDirectory({
|
|
129
|
+
'01-a.mjs': `export const dependsOn = [1]\n${scriptSource('a', '__orderType')}`,
|
|
130
|
+
});
|
|
131
|
+
await expect(discoverTestDataModules(dir)).rejects.toThrow(/dependsOn 必须是字符串数组/);
|
|
132
|
+
});
|
|
133
|
+
it('`export default { load, cleanup }` 也是合法写法(定义对象直接可用)', async () => {
|
|
134
|
+
const dir = await makeDirectory({
|
|
135
|
+
'01-default.mjs': `
|
|
136
|
+
export default {
|
|
137
|
+
dependsOn: ['01-default'],
|
|
138
|
+
load() {},
|
|
139
|
+
cleanup() {},
|
|
140
|
+
}
|
|
141
|
+
`,
|
|
142
|
+
});
|
|
143
|
+
// 自依赖应被排序器拦下:证明默认导出的 dependsOn 确实被读到了
|
|
144
|
+
await expect(discoverTestDataModules(dir)).rejects.toThrow(/依赖了自己/);
|
|
145
|
+
});
|
|
146
|
+
it('默认导出优先于同名命名导出(不留「改了默认导出、旧导出还生效」的歧义)', async () => {
|
|
147
|
+
const dir = await makeDirectory({
|
|
148
|
+
'01-both.mjs': `
|
|
149
|
+
export default { load() { globalThis.__orderBoth.push('load:default') }, cleanup() {} }
|
|
150
|
+
export function load() { globalThis.__orderBoth.push('load:named') }
|
|
151
|
+
export function cleanup() {}
|
|
152
|
+
`,
|
|
153
|
+
});
|
|
154
|
+
const order = globalThis.__orderBoth = [];
|
|
155
|
+
const units = await discoverTestDataModules(dir);
|
|
156
|
+
await loadTestData({ ctx: makeCtx('test'), units });
|
|
157
|
+
expect(order).toEqual(['load:default']);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
describe('defineTestData', () => {
|
|
161
|
+
it('原样返回 load / cleanup,并保留 dependsOn', () => {
|
|
162
|
+
const load = () => { };
|
|
163
|
+
const cleanup = () => { };
|
|
164
|
+
const script = defineTestData({ dependsOn: ['01-database'], load, cleanup });
|
|
165
|
+
expect(script.load).toBe(load);
|
|
166
|
+
expect(script.cleanup).toBe(cleanup);
|
|
167
|
+
expect(script.dependsOn).toEqual(['01-database']);
|
|
168
|
+
});
|
|
169
|
+
it('没有 dependsOn 时结果里没有这个字段', () => {
|
|
170
|
+
expect(defineTestData({ load: () => { } })).not.toHaveProperty('dependsOn');
|
|
171
|
+
});
|
|
172
|
+
it('缺 load 直接抛错(定义期就发现,而不是装载期)', () => {
|
|
173
|
+
expect(() => defineTestData({ cleanup: () => { } })).toThrow(/缺少 load/);
|
|
174
|
+
});
|
|
175
|
+
it('不写 cleanup 是合法的(库里那半由资源单元划掉)', () => {
|
|
176
|
+
const script = defineTestData({ load: () => { } });
|
|
177
|
+
expect(script.cleanup).toBeUndefined();
|
|
178
|
+
});
|
|
179
|
+
it('cleanup 写了但不是函数则抛错(写错了不要静默跣过)', () => {
|
|
180
|
+
expect(() => defineTestData({ load: () => { }, cleanup: 'yes' })).toThrow(/cleanup 必须是函数/);
|
|
181
|
+
});
|
|
182
|
+
it('dependsOn 不是字符串数组直接抛错', () => {
|
|
183
|
+
expect(() => defineTestData({ dependsOn: [1], load: () => { }, cleanup: () => { } })).toThrow(/非空字符串数组/);
|
|
184
|
+
});
|
|
185
|
+
it('dependsOn 含空字符串直接抛错(空名指不到任何脚本)', () => {
|
|
186
|
+
expect(() => defineTestData({ dependsOn: [''], load: () => { }, cleanup: () => { } })).toThrow(/非空字符串数组/);
|
|
187
|
+
});
|
|
188
|
+
it('dependsOn 重复项直接抛错(重复声明没有意义,多半是写错)', () => {
|
|
189
|
+
expect(() => defineTestData({ dependsOn: ['01-a', '01-a'], load: () => { }, cleanup: () => { } })).toThrow(/重复项/);
|
|
190
|
+
});
|
|
191
|
+
it('结果冻结:外部改不动 load / cleanup / dependsOn', () => {
|
|
192
|
+
const script = defineTestData({ dependsOn: ['01-a'], load: () => { }, cleanup: () => { } });
|
|
193
|
+
expect(Object.isFrozen(script)).toBe(true);
|
|
194
|
+
expect(Object.isFrozen(script.dependsOn)).toBe(true);
|
|
195
|
+
});
|
|
196
|
+
it('泛型写 typeof server 时 ctx 带上全部槽位的类型', async () => {
|
|
197
|
+
const calls = [];
|
|
198
|
+
const script = defineTestData({
|
|
199
|
+
load(ctx) {
|
|
200
|
+
// 五个字段都有类型:写错(如 services.user.remove)会在 tsc 阶段报错
|
|
201
|
+
calls.push(ctx.resources.database.marker, String(ctx.config.feature), ctx.env.FOO ?? 'none');
|
|
202
|
+
ctx.clients.webhook.send();
|
|
203
|
+
ctx.services.user.create();
|
|
204
|
+
},
|
|
205
|
+
cleanup() { },
|
|
206
|
+
});
|
|
207
|
+
// 类型正确还不够:真跑一遍,确认运行时拿到的就是这些字段
|
|
208
|
+
await script.load({
|
|
209
|
+
env: { ENV: 'test' },
|
|
210
|
+
resources: { database: { marker: 'db' } },
|
|
211
|
+
config: { feature: true },
|
|
212
|
+
clients: { webhook: { send: () => { calls.push('webhook'); } } },
|
|
213
|
+
services: { user: { create: () => { calls.push('user'); } } },
|
|
214
|
+
log: { debug: () => { }, info: () => { }, warn: () => { }, error: () => { } },
|
|
215
|
+
});
|
|
216
|
+
expect(calls).toEqual(['db', 'true', 'none', 'webhook', 'user']);
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
/** 造一个最小 ctx(`resources` 用不着时给空对象;`log` 静默) */
|
|
220
|
+
function makeCtx(env, resources = {}) {
|
|
221
|
+
return {
|
|
222
|
+
env: { ENV: env },
|
|
223
|
+
resources: resources,
|
|
224
|
+
config: {},
|
|
225
|
+
clients: {},
|
|
226
|
+
services: {},
|
|
227
|
+
log: { debug: () => { }, info: () => { }, warn: () => { }, error: () => { } },
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
/** 造一批内存脚本单元(不经磁盘,专测执行语义) */
|
|
231
|
+
function unitsOf(order, names) {
|
|
232
|
+
return names.map((name) => ({
|
|
233
|
+
name,
|
|
234
|
+
load: () => {
|
|
235
|
+
order.push(`load:${name}`);
|
|
236
|
+
},
|
|
237
|
+
cleanup: () => {
|
|
238
|
+
order.push(`cleanup:${name}`);
|
|
239
|
+
},
|
|
240
|
+
}));
|
|
241
|
+
}
|
|
242
|
+
describe('testdata 执行语义', () => {
|
|
243
|
+
it('load 正序、cleanup 逆序', async () => {
|
|
244
|
+
const order = [];
|
|
245
|
+
const units = unitsOf(order, ['a', 'b', 'c']);
|
|
246
|
+
await loadTestData({ ctx: makeCtx('test'), units });
|
|
247
|
+
expect(order).toEqual(['load:a', 'load:b', 'load:c']);
|
|
248
|
+
order.length = 0;
|
|
249
|
+
await cleanupTestData({ ctx: makeCtx('test'), units });
|
|
250
|
+
expect(order).toEqual(['cleanup:c', 'cleanup:b', 'cleanup:a']);
|
|
251
|
+
});
|
|
252
|
+
it('先整体 cleanup(逆序)再整体 load(正序)就是一次干净基线', async () => {
|
|
253
|
+
const order = [];
|
|
254
|
+
const units = unitsOf(order, ['a', 'b']);
|
|
255
|
+
// `TestServer.reprovision()` 就是这两步的顺序:先拆干净,再重建
|
|
256
|
+
await cleanupTestData({ ctx: makeCtx('test'), units });
|
|
257
|
+
await loadTestData({ ctx: makeCtx('test'), units });
|
|
258
|
+
// 分两趟而不是「逐脚本 cleanup + load」:后者会让前一个脚本刚插入的数据
|
|
259
|
+
// 被后一个脚本的 cleanup 顺手删掉(顺序依赖)。
|
|
260
|
+
expect(order).toEqual(['cleanup:b', 'cleanup:a', 'load:a', 'load:b']);
|
|
261
|
+
});
|
|
262
|
+
it('每个脚本收到的就是同一个 ctx(env / resources / log 都是装配产物)', async () => {
|
|
263
|
+
const seen = [];
|
|
264
|
+
const resources = { database: { marker: 'live-db' } };
|
|
265
|
+
const ctx = makeCtx('test', resources);
|
|
266
|
+
const units = [
|
|
267
|
+
{ name: 'a', load: (received) => { seen.push(received); }, cleanup: () => { } },
|
|
268
|
+
];
|
|
269
|
+
await loadTestData({ ctx, units });
|
|
270
|
+
// 同一个对象:脚本里 `ctx.resources.database` 就是被测服务用的那个实例,不是副本
|
|
271
|
+
expect(seen[0]).toBe(ctx);
|
|
272
|
+
expect(seen[0].resources.database).toBe(resources.database);
|
|
273
|
+
});
|
|
274
|
+
it('执行器自己按 dependsOn 定序,不信任调用方传入的顺序', async () => {
|
|
275
|
+
const order = [];
|
|
276
|
+
const units = [
|
|
277
|
+
{ name: '01-late', dependsOn: ['99-early'], load: () => { order.push('load:late'); }, cleanup: () => { order.push('cleanup:late'); } },
|
|
278
|
+
{ name: '99-early', load: () => { order.push('load:early'); }, cleanup: () => { order.push('cleanup:early'); } },
|
|
279
|
+
];
|
|
280
|
+
await loadTestData({ ctx: makeCtx('test'), units });
|
|
281
|
+
expect(order).toEqual(['load:early', 'load:late']);
|
|
282
|
+
order.length = 0;
|
|
283
|
+
await cleanupTestData({ ctx: makeCtx('test'), units });
|
|
284
|
+
expect(order).toEqual(['cleanup:late', 'cleanup:early']);
|
|
285
|
+
});
|
|
286
|
+
it('没有脚本时两个入口都是空操作(但闸门照样生效)', async () => {
|
|
287
|
+
await expect(loadTestData({ ctx: makeCtx('test') })).resolves.toBeUndefined();
|
|
288
|
+
await expect(cleanupTestData({ ctx: makeCtx('development') })).resolves.toBeUndefined();
|
|
289
|
+
});
|
|
290
|
+
it.each([
|
|
291
|
+
['loadTestData', loadTestData],
|
|
292
|
+
['cleanupTestData', cleanupTestData],
|
|
293
|
+
])('%s:脚本失败直接冒泡(不降级成「跳过这条」)', async (_name, run) => {
|
|
294
|
+
const order = [];
|
|
295
|
+
const units = [
|
|
296
|
+
{ name: 'ok', load: () => { order.push('load:ok'); }, cleanup: () => { order.push('cleanup:ok'); } },
|
|
297
|
+
{ name: 'broken', load: () => { throw new Error('insert failed'); }, cleanup: () => { throw new Error('delete failed'); } },
|
|
298
|
+
];
|
|
299
|
+
await expect(run({ ctx: makeCtx('test'), units })).rejects.toThrow(/failed/);
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
describe('testdata 的环境闸门', () => {
|
|
303
|
+
const units = (order) => unitsOf(order, ['a']);
|
|
304
|
+
it.each([
|
|
305
|
+
['loadTestData', loadTestData],
|
|
306
|
+
['cleanupTestData', cleanupTestData],
|
|
307
|
+
])('%s:非可清理环境一个脚本都不跑', async (_name, run) => {
|
|
308
|
+
const order = [];
|
|
309
|
+
await expect(run({ ctx: makeCtx('production'), units: units(order) })).rejects.toThrow(/拒绝在 ENV=/);
|
|
310
|
+
expect(order).toEqual([]);
|
|
311
|
+
});
|
|
312
|
+
it.each(['production', 'staging', ''])('ENV=%s 时拒绝,报错带上当前值', async (env) => {
|
|
313
|
+
await expect(loadTestData({ ctx: makeCtx(env) })).rejects.toThrow(/ENV="/);
|
|
314
|
+
});
|
|
315
|
+
it.each(['development', 'test'])('ENV=%s 时放行', async (env) => {
|
|
316
|
+
const order = [];
|
|
317
|
+
await loadTestData({ ctx: makeCtx(env), units: units(order) });
|
|
318
|
+
expect(order).toEqual(['load:a']);
|
|
319
|
+
});
|
|
320
|
+
it('闸门先于一切:没有脚本也要在非可清理环境下失败', async () => {
|
|
321
|
+
await expect(cleanupTestData({ ctx: makeCtx('production') })).rejects.toThrow(/ENV="production"/);
|
|
322
|
+
});
|
|
323
|
+
it('判据是 ctx.env 快照,不是 process.env(事后改写不影响结论)', async () => {
|
|
324
|
+
const order = [];
|
|
325
|
+
process.env.ENV = 'production';
|
|
326
|
+
await loadTestData({ ctx: makeCtx('test'), units: units(order) });
|
|
327
|
+
expect(order).toEqual(['load:a']);
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
describe('resolveTestDataDirectory', () => {
|
|
331
|
+
it('默认是 <cwd>/testdata', () => {
|
|
332
|
+
expect(resolveTestDataDirectory()).toBe(join(process.cwd(), 'testdata'));
|
|
333
|
+
});
|
|
334
|
+
});
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* `request.user` / `request.token` / `request.id` 与 `routeOptions.config.accessControls`(以及
|
|
5
5
|
* 限流规则 `rateLimit`)的 `declare module 'fastify'` 由 common 统一提供,各仓删除自建声明。
|
|
6
6
|
*/
|
|
7
|
-
import type { RequestContext } from '
|
|
7
|
+
import type { RequestContext } from './modules/context/index.js';
|
|
8
8
|
/** 路由级访问控制声明(各服务在 `routeOptions.config.accessControls` 里写) */
|
|
9
9
|
export type AccessControl = 'NoLoginRequired' | 'IgnorePermission' | 'RequiredSecurityCode';
|
|
10
10
|
/** 路由级限流规则(`createRateLimitMiddleware` 消费,另有路由级 `config.rateLimit`) */
|
|
@@ -21,7 +21,7 @@ declare module 'fastify' {
|
|
|
21
21
|
user?: unknown;
|
|
22
22
|
/** 认证后由 `auth` 中间件填充 */
|
|
23
23
|
token?: unknown;
|
|
24
|
-
/**
|
|
24
|
+
/** 由 core 固定注入的请求级上下文(`registerRequestContext`) */
|
|
25
25
|
ctx?: RequestContext;
|
|
26
26
|
}
|
|
27
27
|
interface FastifyContextConfig {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type-augment.d.ts","sourceRoot":"","sources":["../../source/core/type-augment.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAA;AAEhE,8DAA8D;AAC9D,MAAM,MAAM,aAAa,GAAG,iBAAiB,GAAG,kBAAkB,GAAG,sBAAsB,CAAA;AAE3F,uEAAuE;AACvE,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,cAAc;IACd,MAAM,EAAE,MAAM,CAAA;IACd,wBAAwB;IACxB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;CAChB;AAED,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,cAAc;QACtB,wBAAwB;QACxB,IAAI,CAAC,EAAE,OAAO,CAAA;QACd,wBAAwB;QACxB,KAAK,CAAC,EAAE,OAAO,CAAA;QACf,mDAAmD;QACnD,GAAG,CAAC,EAAE,cAAc,CAAA;KACrB;IAED,UAAU,oBAAoB;QAC5B,+EAA+E;QAC/E,cAAc,CAAC,EAAE,aAAa,EAAE,CAAA;QAChC,cAAc;QACd,SAAS,CAAC,EAAE,aAAa,CAAA;KAC1B;CACF"}
|