feat(GOV-009): 建立 NestJS 三进程基线
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
import { type AddressInfo } from 'node:net';
|
||||
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { bootstrapApi } from '../apps/api/src/main';
|
||||
import { bootstrapScheduler } from '../apps/scheduler/src/main';
|
||||
import { bootstrapWorker } from '../apps/worker/src/main';
|
||||
import type {
|
||||
ApiRuntimeConfig,
|
||||
SchedulerRuntimeConfig,
|
||||
WorkerRuntimeConfig,
|
||||
} from '../libs/config/src/runtime-config';
|
||||
|
||||
const redisConfig = Object.freeze({
|
||||
host: '127.0.0.1',
|
||||
port: 6379,
|
||||
username: '',
|
||||
password: '',
|
||||
database: 0,
|
||||
tls: false,
|
||||
});
|
||||
|
||||
const mysqlConfig = Object.freeze({
|
||||
enabled: false,
|
||||
host: '127.0.0.1',
|
||||
port: 3306,
|
||||
database: 'a_share_analysis_dev',
|
||||
username: '',
|
||||
password: '',
|
||||
charset: 'utf8mb4' as const,
|
||||
collation: 'utf8mb4_general_ci' as const,
|
||||
timezone: '+08:00' as const,
|
||||
});
|
||||
|
||||
const aiConfig = Object.freeze({
|
||||
enabled: false,
|
||||
baseUrl: '',
|
||||
apiKey: '',
|
||||
model: '',
|
||||
timeoutMs: 30_000,
|
||||
});
|
||||
|
||||
const rocketMqConfig = Object.freeze({
|
||||
enabled: false,
|
||||
nameServerAddress: '127.0.0.1:9876',
|
||||
endpoint: '127.0.0.1:8081',
|
||||
topicPrefix: 'a-share-analysis',
|
||||
});
|
||||
|
||||
describe('NestJS 三进程入口', () => {
|
||||
it('API 使用真实 HTTP 应用监听注入端口并可关闭', async () => {
|
||||
const config: ApiRuntimeConfig = {
|
||||
processName: 'api',
|
||||
appEnv: 'development',
|
||||
logLevel: 'debug',
|
||||
api: Object.freeze({ host: '127.0.0.1', port: 3000 }),
|
||||
redis: redisConfig,
|
||||
mysql: mysqlConfig,
|
||||
ai: aiConfig,
|
||||
};
|
||||
|
||||
const app = await bootstrapApi({ config, port: 0, logger: false });
|
||||
const address = app.getHttpServer().address() as AddressInfo;
|
||||
|
||||
expect(address.address).toBe('127.0.0.1');
|
||||
expect(address.port).toBeGreaterThan(0);
|
||||
|
||||
await app.close();
|
||||
expect(app.getHttpServer().listening).toBe(false);
|
||||
});
|
||||
|
||||
it('Scheduler 使用无 HTTP 端口的应用上下文并可关闭', async () => {
|
||||
const config: SchedulerRuntimeConfig = {
|
||||
processName: 'scheduler',
|
||||
appEnv: 'development',
|
||||
logLevel: 'debug',
|
||||
scheduler: Object.freeze({ enabled: true, timezone: 'Asia/Shanghai' }),
|
||||
redis: Object.freeze({ ...redisConfig, database: 1 }),
|
||||
rocketMq: Object.freeze({ ...rocketMqConfig, sendTimeoutMs: 3000 }),
|
||||
};
|
||||
|
||||
const app = await bootstrapScheduler({ config, logger: false });
|
||||
|
||||
expect('getHttpServer' in app).toBe(false);
|
||||
await expect(app.close()).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('Worker 使用无 HTTP 端口的应用上下文并可关闭', async () => {
|
||||
const config: WorkerRuntimeConfig = {
|
||||
processName: 'worker',
|
||||
appEnv: 'development',
|
||||
logLevel: 'debug',
|
||||
marketData: Object.freeze({
|
||||
mode: 'mock',
|
||||
provider: 'deterministic-fixture',
|
||||
baseUrl: '',
|
||||
apiKey: '',
|
||||
}),
|
||||
redis: Object.freeze({ ...redisConfig, database: 2 }),
|
||||
mysql: mysqlConfig,
|
||||
rocketMq: Object.freeze({
|
||||
...rocketMqConfig,
|
||||
consumerGroup: 'a-share-analysis-worker',
|
||||
consumerConcurrency: 4,
|
||||
}),
|
||||
ai: aiConfig,
|
||||
};
|
||||
|
||||
const app = await bootstrapWorker({ config, logger: false });
|
||||
|
||||
expect('getHttpServer' in app).toBe(false);
|
||||
await expect(app.close()).resolves.toBeUndefined();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user