import { readFileSync } from 'fs'; import { join } from 'path'; const repoRoot = join(__dirname, '../../../..'); const readSource = (relativePath: string) => readFileSync(join(repoRoot, relativePath), 'utf8'); describe('QQBot plugin platform lifecycle runtime contract', () => { it('uses dedicated lifecycle use cases instead of direct status flips', () => { const controller = readSource( 'src/modules/qqbot/plugin-platform/plugin-platform.controller.ts', ); const service = readSource( 'src/modules/qqbot/plugin-platform/plugin-platform.service.ts', ); const bannedDirectStatusFlips = [ controller.includes('setInstallationStatus') ? 'controller.setInstallationStatus' : '', service.includes('setInstallationStatus') ? 'service.setInstallationStatus' : '', ].filter(Boolean); const missingLifecycleMethods = [ 'enableInstallation', 'disableInstallation', 'upgradeInstallation', 'uninstallInstallation', ].filter((methodName) => !service.includes(methodName)); expect(bannedDirectStatusFlips).toEqual([]); expect(missingLifecycleMethods).toEqual([]); }); it('activates workers and refreshes active registries during lifecycle transitions', () => { const source = [ readSource('src/modules/qqbot/plugin-platform/plugin-platform.service.ts'), readSource('src/modules/qqbot/plugin-platform/runtime/worker-runtime.ts'), ].join('\n'); const missingRuntimeSignals = [ 'QqbotPluginWorkerRuntime', 'activate', 'deactivate', 'dispose', 'refreshActive', 'activeOperation', 'activeEvent', ].filter((signal) => !source.includes(signal)); expect(missingRuntimeSignals).toEqual([]); }); it('exposes operation executor and event dispatcher through the platform', () => { const source = readSource( 'src/modules/qqbot/plugin-platform/plugin-platform.service.ts', ); const missingExecutorSignals = [ 'executeOperation', 'dispatchEvent', 'runtimeEventRepository', 'command log', ].filter((signal) => !source.includes(signal)); expect(missingExecutorSignals).toEqual([]); }); });