Files
mcpctl/src/mcpd/tests/resource-rules.test.ts
Michal 5d859ca7d8 feat: audit console TUI, system prompt management, and CLI improvements
Audit Console Phase 1: tool_call_trace emission from mcplocal router,
session_bind/rbac_decision event kinds, GET /audit/sessions endpoint,
full Ink TUI with session sidebar, event timeline, and detail view
(mcpctl console --audit).

System prompts: move 6 hardcoded LLM prompts to mcpctl-system project
with extensible ResourceRuleRegistry validation framework, template
variable enforcement ({{maxTokens}}, {{pageCount}}), and delete-resets-
to-default behavior. All consumers fetch via SystemPromptFetcher with
hardcoded fallbacks.

CLI: -p shorthand for --project across get/create/delete/config commands,
console auto-scroll improvements, shell completions regenerated.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 23:50:54 +00:00

89 lines
3.5 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { ResourceRuleRegistry } from '../src/validation/resource-rules.js';
import type { ResourceRule, RuleMeta, RuleContext, ValidationResult } from '../src/validation/resource-rules.js';
const noopCtx: RuleContext = {
findResource: async () => null,
};
function makeRule(
name: string,
resource: string,
matchFn: (data: unknown, meta: RuleMeta) => boolean,
validateFn: () => Promise<ValidationResult>,
): ResourceRule {
return { name, resource, match: matchFn, validate: validateFn };
}
describe('ResourceRuleRegistry', () => {
it('returns valid when no rules are registered', async () => {
const registry = new ResourceRuleRegistry();
const result = await registry.validate('prompt', { name: 'x' }, { operation: 'create' }, noopCtx);
expect(result.valid).toBe(true);
});
it('returns valid when no rules match the resource type', async () => {
const registry = new ResourceRuleRegistry();
registry.register(makeRule('server-rule', 'server', () => true, async () => ({ valid: false, errors: ['fail'] })));
const result = await registry.validate('prompt', { name: 'x' }, { operation: 'create' }, noopCtx);
expect(result.valid).toBe(true);
});
it('skips rules that do not match the predicate', async () => {
const registry = new ResourceRuleRegistry();
registry.register(makeRule('skip-rule', 'prompt', () => false, async () => ({ valid: false, errors: ['fail'] })));
const result = await registry.validate('prompt', { name: 'x' }, { operation: 'create' }, noopCtx);
expect(result.valid).toBe(true);
});
it('runs matching rules and returns errors', async () => {
const registry = new ResourceRuleRegistry();
registry.register(makeRule('bad-rule', 'prompt', () => true, async () => ({
valid: false,
errors: ['missing template var'],
})));
const result = await registry.validate('prompt', { name: 'x' }, { operation: 'update' }, noopCtx);
expect(result.valid).toBe(false);
expect(result.errors).toEqual(['missing template var']);
});
it('aggregates errors from multiple matching rules', async () => {
const registry = new ResourceRuleRegistry();
registry.register(makeRule('rule-1', 'prompt', () => true, async () => ({
valid: false,
errors: ['error A'],
})));
registry.register(makeRule('rule-2', 'prompt', () => true, async () => ({
valid: false,
errors: ['error B', 'error C'],
})));
const result = await registry.validate('prompt', { name: 'x' }, { operation: 'create' }, noopCtx);
expect(result.valid).toBe(false);
expect(result.errors).toEqual(['error A', 'error B', 'error C']);
});
it('returns valid when all matching rules pass', async () => {
const registry = new ResourceRuleRegistry();
registry.register(makeRule('ok-rule', 'prompt', () => true, async () => ({ valid: true })));
const result = await registry.validate('prompt', { name: 'x' }, { operation: 'update' }, noopCtx);
expect(result.valid).toBe(true);
});
it('passes meta to match function', async () => {
const registry = new ResourceRuleRegistry();
let receivedMeta: RuleMeta | null = null;
registry.register(makeRule('meta-check', 'prompt', (_data, meta) => {
receivedMeta = meta;
return false;
}, async () => ({ valid: true })));
await registry.validate('prompt', { name: 'x' }, { operation: 'update', isSystemResource: true, projectName: 'sys' }, noopCtx);
expect(receivedMeta).toEqual({ operation: 'update', isSystemResource: true, projectName: 'sys' });
});
});