jest.mock('../../../src/modules/qqbot/core/qqbot-core.module', () => ({ QqbotCoreModule: class QqbotCoreModule {}, })); import { MODULE_METADATA } from '@nestjs/common/constants'; import { ConfigModule } from '@nestjs/config'; import { AppModule } from '../../../src/app.module'; import { AdminAuthGuardModule } from '../../../src/modules/admin/identity/auth/admin-auth-guard.module'; import { ASSET_CONTROLLERS, ASSET_DOMAIN_CONTRACT, ASSET_PROVIDERS, AssetModule, } from '../../../src/modules/asset/asset.module'; import { MinioClientService } from '../../../src/modules/asset/application/asset-minio.service'; import { MinioClientController } from '../../../src/modules/asset/contract/asset-minio.controller'; import { AdminPlatformConfigModule } from '../../../src/modules/admin/platform-config/admin-platform-config.module'; import { collectControllerRoutes, routeKey, } from '../../helpers/controller-route.helper'; import { readRefactorV3SqlSchema } from '../../helpers/sql-schema.helper'; const getModuleMetadata = (moduleClass: unknown, key: string): T[] => { return Reflect.getMetadata(key, moduleClass) || []; }; const expectNoModuleNamed = (modules: unknown[], moduleName: string) => { expect( modules.some( (moduleRef) => typeof moduleRef === 'function' && moduleRef.name === moduleName, ), ).toBe(false); }; describe('Asset module contract', () => { const schema = readRefactorV3SqlSchema(); it('keeps MinIO-backed asset routes compatible through the Asset boundary', () => { const routes = collectControllerRoutes(ASSET_CONTROLLERS); expect(routes.map(routeKey)).toEqual( expect.arrayContaining([ 'GET /minio/check', 'POST /minio/bucket', 'POST /minio/upload', 'GET /minio/list', 'GET /minio/url', 'GET /minio/resource-proxy', 'GET /minio/download', 'DELETE /minio/remove', ]), ); }); it('routes MinIO through the Asset module boundary without duplicate direct controllers', () => { expect(getModuleMetadata(AppModule, MODULE_METADATA.IMPORTS)).toEqual( expect.arrayContaining([AssetModule]), ); expect( getModuleMetadata(AdminPlatformConfigModule, MODULE_METADATA.IMPORTS), ).toEqual(expect.arrayContaining([AssetModule])); const appImports = getModuleMetadata(AppModule, MODULE_METADATA.IMPORTS); const platformImports = getModuleMetadata( AdminPlatformConfigModule, MODULE_METADATA.IMPORTS, ); const assetImports = getModuleMetadata(AssetModule, MODULE_METADATA.IMPORTS); const assetExports = getModuleMetadata(AssetModule, MODULE_METADATA.EXPORTS); expectNoModuleNamed(appImports, 'MinioClientModule'); expectNoModuleNamed(platformImports, 'MinioClientModule'); expectNoModuleNamed(assetImports, 'MinioClientModule'); expectNoModuleNamed(assetExports, 'MinioClientModule'); expect(assetImports).toEqual( expect.arrayContaining([AdminAuthGuardModule, ConfigModule]), ); expect( getModuleMetadata(AssetModule, MODULE_METADATA.CONTROLLERS), ).toEqual(expect.arrayContaining([MinioClientController])); expect( getModuleMetadata(AssetModule, MODULE_METADATA.PROVIDERS), ).toEqual(expect.arrayContaining([MinioClientService])); expect(assetExports).toEqual(expect.arrayContaining([MinioClientService])); expect(ASSET_CONTROLLERS).toEqual( expect.arrayContaining([MinioClientController]), ); expect(ASSET_PROVIDERS).toEqual( expect.arrayContaining([MinioClientService]), ); }); it('matches the real Batch 3 Asset SQL schema and boundary contract', () => { expect(ASSET_DOMAIN_CONTRACT.tables).toEqual([ 'asset_bucket', 'asset_object', 'asset_reference', 'asset_access_grant', ]); for (const table of ASSET_DOMAIN_CONTRACT.tables) { expect(schema.hasTable(table)).toBe(true); } schema.expectTableColumns('asset_bucket', [ 'id', 'bucket_key', 'bucket_name', 'provider', 'status', ]); schema.expectTableColumns('asset_object', [ 'id', 'bucket_id', 'object_key', 'source_module', 'mime_type', 'size_bytes', 'metadata_json', ]); schema.expectTableColumns('asset_reference', [ 'id', 'object_id', 'owner_module', 'owner_type', 'owner_id', ]); schema.expectTableColumns('asset_access_grant', [ 'id', 'object_id', 'grant_token', 'expires_at', 'status', ]); expect(ASSET_DOMAIN_CONTRACT.objectOwnership).toEqual({ objectTable: 'asset_object', bucketTable: 'asset_bucket', ownerModuleField: 'source_module', objectKeyField: 'object_key', }); expect(ASSET_DOMAIN_CONTRACT.mimeMetadata).toEqual({ objectTable: 'asset_object', mimeField: 'mime_type', sizeField: 'size_bytes', metadataField: 'metadata_json', }); expect(ASSET_DOMAIN_CONTRACT.reference).toEqual({ table: 'asset_reference', objectKey: 'object_id', ownerFields: ['owner_module', 'owner_type', 'owner_id'], }); expect(ASSET_DOMAIN_CONTRACT.accessGrant).toEqual({ table: 'asset_access_grant', objectKey: 'object_id', tokenField: 'grant_token', expiresAtField: 'expires_at', statusField: 'status', }); }); });