kt-template-online-api/test/modules/qqbot/napcat/napcat-module-ownership.spec.ts

207 lines
7.2 KiB
TypeScript
Raw 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 { existsSync, readFileSync } from 'fs';
import { join } from 'path';
import { MODULE_METADATA } from '@nestjs/common/constants';
import { ConfigModule } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { getMetadataArgsStorage } from 'typeorm';
import { AdminAuthGuardModule } from '../../../../src/modules/admin/identity/auth/admin-auth-guard.module';
import { AppModule } from '../../../../src/app.module';
import { QQBOT_ACCOUNT_NAPCAT_RUNTIME_PORT } from '../../../../src/modules/qqbot/core/application/account/qqbot-account-napcat-runtime.port';
import { QqbotCoreModule } from '../../../../src/modules/qqbot/core/qqbot-core.module';
import {
QQBOT_NAPCAT_CONTROLLERS,
QQBOT_NAPCAT_ENTITIES,
QQBOT_NAPCAT_EXPORTS,
QQBOT_NAPCAT_PROVIDERS,
QqbotNapcatModule,
} from '../../../../src/modules/qqbot/napcat/qqbot-napcat.module';
import { QqbotNapcatAccountRuntimeService } from '../../../../src/modules/qqbot/napcat/application/account-runtime/qqbot-napcat-account-runtime.service';
import { QqbotNapcatLoginController } from '../../../../src/modules/qqbot/napcat/contract/qqbot-napcat-login.controller';
import { QqbotNapcatContainerService } from '../../../../src/modules/qqbot/napcat/infrastructure/integration/container/qqbot-napcat-container.service';
import {
collectControllerRoutes,
routeKey,
} from '../../../helpers/controller-route.helper';
import { readRefactorV3SqlSchema } from '../../../helpers/sql-schema.helper';
/**
* 查询 NapCat 登录运行态数据。
* @param moduleClass - Nest 模块类;读取装饰器 metadata。
* @param key - 键名;读取装饰器 metadata。
* @returns NapCat 登录运行态查询结果。
*/
const getModuleMetadata = <T>(moduleClass: unknown, key: string): T[] => {
return Reflect.getMetadata(key, moduleClass) || [];
};
/**
* 查询 NapCat 登录运行态数据。
* @param items - NapCat列表转换 NapCat列表项。
*/
const getNames = (items: unknown[]) =>
items.map((item) => {
if (typeof item === 'symbol') return item.description || item.toString();
return (item as { name?: string }).name || `${item}`;
});
/**
* 执行 NapCat 登录运行态流程。
* @param item - item 输入;驱动 `maybeForwardRef.forwardRef()` 的 NapCat步骤。
*/
const unwrapForwardRef = (item: unknown) => {
const maybeForwardRef = item as { forwardRef?: () => unknown };
return typeof maybeForwardRef.forwardRef === 'function'
? maybeForwardRef.forwardRef()
: item;
};
type EntityClass = new (...args: never[]) => unknown;
/**
* 查询 NapCat 登录运行态数据。
* @param entity - entity 输入;驱动 `getMetadataArgsStorage()` 的 NapCat步骤。
*/
const getEntityTableName = (entity: EntityClass) => {
return getMetadataArgsStorage().tables.find(
(table) => table.target === entity,
)?.name;
};
describe('QQBot NapCat module ownership', () => {
const schema = readRefactorV3SqlSchema();
it('owns scan, captcha, QR refresh and SSE routes under the NapCat boundary', () => {
const routes = collectControllerRoutes(QQBOT_NAPCAT_CONTROLLERS);
expect(QQBOT_NAPCAT_CONTROLLERS).toEqual(
expect.arrayContaining([QqbotNapcatLoginController]),
);
expect(routes.map(routeKey)).toEqual(
expect.arrayContaining([
'POST /qqbot/account/scan/create',
'POST /qqbot/account/scan/refresh',
'GET /qqbot/account/scan/status',
'GET /qqbot/account/scan/events',
'POST /qqbot/account/scan/qrcode/refresh',
'POST /qqbot/account/scan/captcha/submit',
'POST /qqbot/account/scan/cancel',
]),
);
});
it('is imported as its own Nest module and keeps contract files in napcat/contract', () => {
const contractControllerPath = join(
process.cwd(),
'src',
'modules',
'qqbot',
'napcat',
'contract',
'qqbot-napcat-login.controller.ts',
);
const appImports = getModuleMetadata(AppModule, MODULE_METADATA.IMPORTS);
const napcatImports = getModuleMetadata(
QqbotNapcatModule,
MODULE_METADATA.IMPORTS,
).map(unwrapForwardRef);
expect(existsSync(contractControllerPath)).toBe(true);
expect(appImports).toEqual(expect.arrayContaining([QqbotNapcatModule]));
expect(napcatImports).toEqual(
expect.arrayContaining([
ConfigModule,
AdminAuthGuardModule,
QqbotCoreModule,
]),
);
expect(
napcatImports.some(
(item) => (item as { module?: unknown }).module === TypeOrmModule,
),
).toBe(true);
});
it('registers NapCat runtime providers and entities outside QQBot core', () => {
expect(getNames(QQBOT_NAPCAT_PROVIDERS)).toEqual(
expect.arrayContaining([
'NapcatDeviceIdentityService',
'NapcatLoginStateStoreService',
'QqbotNapcatAccountRuntimeService',
'QqbotNapcatContainerService',
'QqbotNapcatLoginService',
'QqbotNapcatWatchdogService',
]),
);
expect(getNames(QQBOT_NAPCAT_ENTITIES)).toEqual(
expect.arrayContaining([
'NapcatAccountBinding',
'NapcatContainer',
'NapcatDeviceIdentity',
'NapcatLoginChallengeEntity',
'NapcatLoginSession',
'NapcatRuntimeCleanup',
]),
);
expect(getNames(QQBOT_NAPCAT_ENTITIES)).not.toEqual(
expect.arrayContaining(['QqbotAccountNapcat', 'QqbotNapcatContainer']),
);
expect(getNames(QQBOT_NAPCAT_EXPORTS)).toEqual(
expect.arrayContaining([
'NapcatDeviceIdentityService',
'NapcatLoginStateStoreService',
'QQBOT_ACCOUNT_NAPCAT_RUNTIME_PORT',
'QqbotNapcatLoginService',
]),
);
expect(QQBOT_NAPCAT_EXPORTS).not.toContain(QqbotNapcatContainerService);
expect(QQBOT_NAPCAT_PROVIDERS).toEqual(
expect.arrayContaining([
expect.objectContaining({
provide: QQBOT_ACCOUNT_NAPCAT_RUNTIME_PORT,
useExisting: QqbotNapcatAccountRuntimeService,
}),
]),
);
});
it('keeps every registered NapCat entity mapped to the refactor-v3 schema', () => {
for (const entity of QQBOT_NAPCAT_ENTITIES) {
const tableName = getEntityTableName(entity);
expect(tableName).toBeTruthy();
expect(schema.hasTable(tableName || '')).toBe(true);
}
});
it('does not keep legacy qqbot_napcat tables as executable schema truth', () => {
const schemaSql = readFileSync(
join(process.cwd(), 'sql', 'refactor-v3', '00-full-schema.sql'),
'utf8',
);
const qqbotInitSql = readFileSync(
join(process.cwd(), 'sql', 'qqbot-init.sql'),
'utf8',
);
expect(schemaSql).not.toContain(
'CREATE TABLE IF NOT EXISTS qqbot_account_napcat',
);
expect(schemaSql).not.toContain(
'CREATE TABLE IF NOT EXISTS qqbot_napcat_container',
);
expect(qqbotInitSql).not.toContain('qqbot_account_napcat');
expect(qqbotInitSql).not.toContain('qqbot_napcat_container');
expect(qqbotInitSql).toContain(
'CREATE TABLE IF NOT EXISTS `napcat_account_binding`',
);
expect(qqbotInitSql).toContain(
'CREATE TABLE IF NOT EXISTS `napcat_container`',
);
expect(
getNames(QQBOT_NAPCAT_ENTITIES).filter((name) =>
name.startsWith('Qqbot'),
),
).toEqual([]);
});
});