/**
* Integration test for category-aware gate selection.
*
* Tests that:
* 1. getCategoryGates() returns gates based on YAML activation.prompt_categories
* 2. Framework gates (gate_type: 'framework') require BOTH category AND framework match
* 3. Regular gates activate based on category matching only
*/
import { describe, test, expect, beforeAll, afterAll, jest } from '@jest/globals';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { GateManager, createGateManager } from '../../../src/engine/gates/gate-manager.js';
import type { Logger } from '../../../src/infra/logging/index.js';
// Set up server root for gate loader to find resources
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const serverRoot = path.resolve(__dirname, '../../..');
describe('Gate Category Selection Integration', () => {
let gateManager: GateManager;
let originalServerRoot: string | undefined;
beforeAll(async () => {
// Save and set MCP_SERVER_ROOT for gate loader
originalServerRoot = process.env.MCP_SERVER_ROOT;
process.env.MCP_SERVER_ROOT = serverRoot;
const logger: Logger = {
info: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
debug: jest.fn(),
};
gateManager = await createGateManager(logger, { debug: false });
});
afterAll(() => {
// Restore original MCP_SERVER_ROOT
if (originalServerRoot !== undefined) {
process.env.MCP_SERVER_ROOT = originalServerRoot;
} else {
delete process.env.MCP_SERVER_ROOT;
}
});
describe('getCategoryGates() - YAML-driven gate selection', () => {
test('returns code-quality gate for development category', () => {
const gates = gateManager.getCategoryGates('development');
expect(gates).toContain('code-quality');
});
test('does NOT return code-quality gate for research category', () => {
const gates = gateManager.getCategoryGates('research');
expect(gates).not.toContain('code-quality');
});
test('excludes gates with explicit_request: true from auto-assignment', () => {
// technical-accuracy and research-quality have explicit_request: true
const devGates = gateManager.getCategoryGates('development');
expect(devGates).not.toContain('technical-accuracy');
const researchGates = gateManager.getCategoryGates('research');
expect(researchGates).not.toContain('research-quality');
});
test('returns security-awareness for development category', () => {
// security-awareness has development in categories and explicit_request: false
const gates = gateManager.getCategoryGates('development');
expect(gates).toContain('security-awareness');
});
test('excludes framework gates from category selection', () => {
// framework-compliance has gate_type: 'framework' and should be excluded
const devGates = gateManager.getCategoryGates('development');
expect(devGates).not.toContain('framework-compliance');
const researchGates = gateManager.getCategoryGates('research');
expect(researchGates).not.toContain('framework-compliance');
});
test('handles case-insensitive category matching', () => {
const upperGates = gateManager.getCategoryGates('DEVELOPMENT');
const lowerGates = gateManager.getCategoryGates('development');
expect(upperGates).toEqual(lowerGates);
});
test('returns empty for unknown category without matching gates', () => {
const gates = gateManager.getCategoryGates('unknown_category_xyz');
// Should return gates with no category restriction or matching 'general'
// Most gates have specific categories, so this may be empty or contain only unrestricted gates
expect(Array.isArray(gates)).toBe(true);
});
});
describe('selectGates() - framework gate AND logic', () => {
test('framework-compliance activates with BOTH category AND framework', () => {
const result = gateManager.selectGates({
promptCategory: 'development',
framework: 'CAGEERF',
enabledOnly: true,
});
expect(result.selectedIds).toContain('framework-compliance');
});
test('framework-compliance does NOT activate with category only (no framework)', () => {
const result = gateManager.selectGates({
promptCategory: 'development',
// No framework specified
enabledOnly: true,
});
expect(result.selectedIds).not.toContain('framework-compliance');
expect(result.skippedIds).toContain('framework-compliance');
});
test('framework-compliance does NOT activate with framework only (no matching category)', () => {
const result = gateManager.selectGates({
promptCategory: 'greeting', // Not in framework-compliance's prompt_categories
framework: 'CAGEERF',
enabledOnly: true,
});
expect(result.selectedIds).not.toContain('framework-compliance');
expect(result.skippedIds).toContain('framework-compliance');
});
test('framework-compliance requires matching framework from list', () => {
const result = gateManager.selectGates({
promptCategory: 'development',
framework: 'UNKNOWN_FRAMEWORK',
enabledOnly: true,
});
expect(result.selectedIds).not.toContain('framework-compliance');
});
test('framework-compliance accepts all configured frameworks', () => {
const frameworks = ['CAGEERF', 'ReACT', '5W1H', 'SCAMPER'];
for (const framework of frameworks) {
const result = gateManager.selectGates({
promptCategory: 'analysis', // In framework-compliance's categories
framework,
enabledOnly: true,
});
expect(result.selectedIds).toContain('framework-compliance');
}
});
});
describe('selectGates() - explicit gate requests', () => {
test('includes explicitly requested gates regardless of activation rules', () => {
const result = gateManager.selectGates({
promptCategory: 'greeting', // Doesn't match any gate categories
explicitGateIds: ['code-quality', 'research-quality'],
enabledOnly: true,
});
expect(result.selectedIds).toContain('code-quality');
expect(result.selectedIds).toContain('research-quality');
});
});
describe('selection metadata', () => {
test('tracks selection method for category-based selection', () => {
const result = gateManager.selectGates({
promptCategory: 'development',
enabledOnly: true,
});
expect(result.metadata.selectionMethod).toBe('category');
expect(typeof result.metadata.selectionTime).toBe('number');
});
test('tracks selection method for framework-based selection', () => {
const result = gateManager.selectGates({
framework: 'CAGEERF',
enabledOnly: true,
});
expect(result.metadata.selectionMethod).toBe('framework');
});
test('tracks selection method for combined selection', () => {
const result = gateManager.selectGates({
promptCategory: 'development',
framework: 'CAGEERF',
enabledOnly: true,
});
expect(result.metadata.selectionMethod).toBe('combined');
});
});
});