feat(GOV-009): 建立后端契约与健康检查
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { MysqlHealthClient, MYSQL_HEALTH_QUERY } from './mysql-health.client';
|
||||
import { RedisHealthClient } from './redis-health.client';
|
||||
import { buildTypeOrmOptions } from './typeorm-options';
|
||||
|
||||
describe('基础设施只读健康客户端', () => {
|
||||
it('Redis 仅执行 PING 并返回健康状态', async () => {
|
||||
const commands: string[] = [];
|
||||
const client = new RedisHealthClient({
|
||||
status: 'wait',
|
||||
connect: async () => {
|
||||
commands.push('CONNECT');
|
||||
},
|
||||
ping: async () => {
|
||||
commands.push('PING');
|
||||
return 'PONG';
|
||||
},
|
||||
quit: async () => 'OK',
|
||||
});
|
||||
|
||||
await expect(client.check()).resolves.toEqual({ status: 'healthy', detail: 'PONG' });
|
||||
expect(commands).toEqual(['CONNECT', 'PING']);
|
||||
});
|
||||
|
||||
it('MySQL 使用固定元数据查询并校验 5.7 与数据库口径', async () => {
|
||||
const queries: string[] = [];
|
||||
const client = new MysqlHealthClient(
|
||||
{
|
||||
query: async (sql: string) => {
|
||||
queries.push(sql);
|
||||
return [
|
||||
[
|
||||
{
|
||||
version: '5.7.42-log',
|
||||
databaseName: 'a_share_analysis_dev',
|
||||
charset: 'utf8mb4',
|
||||
collation: 'utf8mb4_general_ci',
|
||||
timezone: '+08:00',
|
||||
},
|
||||
],
|
||||
[],
|
||||
];
|
||||
},
|
||||
end: async () => undefined,
|
||||
},
|
||||
'a_share_analysis_dev',
|
||||
);
|
||||
|
||||
await expect(client.check()).resolves.toEqual({
|
||||
status: 'healthy',
|
||||
detail: 'MySQL 5.7 / a_share_analysis_dev / +08:00',
|
||||
});
|
||||
expect(queries).toEqual([MYSQL_HEALTH_QUERY]);
|
||||
});
|
||||
|
||||
it('MySQL 拒绝版本、库名或连接口径漂移', async () => {
|
||||
const createClient = (override: Record<string, string>) =>
|
||||
new MysqlHealthClient(
|
||||
{
|
||||
query: async () => [
|
||||
[
|
||||
{
|
||||
version: '5.7.42',
|
||||
databaseName: 'a_share_analysis_dev',
|
||||
charset: 'utf8mb4',
|
||||
collation: 'utf8mb4_general_ci',
|
||||
timezone: '+08:00',
|
||||
...override,
|
||||
},
|
||||
],
|
||||
[],
|
||||
],
|
||||
end: async () => undefined,
|
||||
},
|
||||
'a_share_analysis_dev',
|
||||
);
|
||||
|
||||
await expect(createClient({ version: '8.0.40' }).check()).rejects.toThrow(/MySQL 5.7/);
|
||||
await expect(createClient({ databaseName: 'a_share_analysis' }).check()).rejects.toThrow(
|
||||
/a_share_analysis_dev/,
|
||||
);
|
||||
await expect(createClient({ collation: 'utf8mb4_0900_ai_ci' }).check()).rejects.toThrow(
|
||||
/utf8mb4_general_ci/,
|
||||
);
|
||||
});
|
||||
|
||||
it('MySQL 按当前配置校验生产库名', async () => {
|
||||
const client = new MysqlHealthClient(
|
||||
{
|
||||
query: async () => [
|
||||
[
|
||||
{
|
||||
version: '5.7.42',
|
||||
databaseName: 'a_share_analysis',
|
||||
charset: 'utf8mb4',
|
||||
collation: 'utf8mb4_general_ci',
|
||||
timezone: '+08:00',
|
||||
},
|
||||
],
|
||||
[],
|
||||
],
|
||||
end: async () => undefined,
|
||||
},
|
||||
'a_share_analysis',
|
||||
);
|
||||
|
||||
await expect(client.check()).resolves.toMatchObject({ status: 'healthy' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('TypeORM MySQL 5.7 选项', () => {
|
||||
it('禁止自动同步并固定字符集、排序规则和东八区', () => {
|
||||
const options = buildTypeOrmOptions({
|
||||
enabled: true,
|
||||
host: '127.0.0.1',
|
||||
port: 3306,
|
||||
database: 'a_share_analysis_dev',
|
||||
username: 'dev-user',
|
||||
password: 'TEST_SENSITIVE_VALUE',
|
||||
charset: 'utf8mb4',
|
||||
collation: 'utf8mb4_general_ci',
|
||||
timezone: '+08:00',
|
||||
});
|
||||
|
||||
expect(options).toMatchObject({
|
||||
type: 'mysql',
|
||||
charset: 'utf8mb4',
|
||||
timezone: '+08:00',
|
||||
synchronize: false,
|
||||
migrationsRun: false,
|
||||
});
|
||||
expect(options.extra).toMatchObject({ collation: 'utf8mb4_general_ci' });
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user