test(ai): drift-guard shim registry ⇔ pen-mcp ELEMENT_TOOL_NAMES

Two assertions that fire if the embedded shim registry drifts out
of sync with the pen-mcp catalog:

  1. Every add_*_v0 name pen-mcp exposes must have a shim —
     catches "added a pen-mcp tool, forgot the builder / shim /
     Nitro SERVER_BUILDERS update" triple-edit drift.
  2. Every shim key must exist in pen-mcp — catches stale shims
     for removed or renamed tools.

Failure message names exactly which tools are missing on each
side so the fix is a copy-paste, not a search. Pairs with the
existing short-circuit test — together they lock the invariant
that elements.md catalog = shim set = Nitro registry.
This commit is contained in:
Fini 2026-04-22 00:20:50 +08:00
parent 9f7c8f7b54
commit 681f7f41e1

View file

@ -1,10 +1,44 @@
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { ELEMENT_TOOL_NAMES } from '@zseven-w/pen-mcp';
import { dispatchElementToolCall } from '../element-tools-dispatcher';
import { SUPPORTED_EMBEDDED_ELEMENT_TOOLS } from '../element-tool-shims';
import { useHistoryStore } from '@/stores/history-store';
import { useDocumentStore } from '@/stores/document-store';
import { useCanvasStore } from '@/stores/canvas-store';
import type { DesignOutputShape } from '../design-parser';
/**
* Drift-guard: the embedded shim registry MUST cover every tool
* name pen-mcp exposes. Adding a pen-mcp element tool without
* adding a matching pen-core builder + shim + Nitro SERVER_BUILDERS
* entry silently breaks AI generation under the flag (tool gets
* short-circuited to `unsupported` and the AI sees
* advertised-but-not-executable output).
*/
describe('SUPPORTED_EMBEDDED_ELEMENT_TOOLS drift guard', () => {
it('covers every add_*_v0 name pen-mcp exposes', () => {
const penMcpAddNames = [...ELEMENT_TOOL_NAMES].filter((n) => /^add_.+_v0$/.test(n));
const shimSet = new Set(SUPPORTED_EMBEDDED_ELEMENT_TOOLS);
const missing = penMcpAddNames.filter((n) => !shimSet.has(n));
expect(
missing,
`Embedded shim registry is missing the following pen-mcp tools — add pen-core builder + ` +
`shim + Nitro SERVER_BUILDERS entry together: ${missing.join(', ')}`,
).toEqual([]);
});
it('does not register shims for names pen-mcp does not expose', () => {
// The other direction: a stale shim for a removed tool would
// silently send stale output. Keep shim ⊆ pen-mcp exactly.
const penMcpSet = new Set(ELEMENT_TOOL_NAMES);
const orphaned = SUPPORTED_EMBEDDED_ELEMENT_TOOLS.filter((n) => !penMcpSet.has(n));
expect(
orphaned,
`Shim registry has entries pen-mcp does not expose (stale / renamed tool): ${orphaned.join(', ')}`,
).toEqual([]);
});
});
/**
* Phase 2 M2/M3 invariants under test:
* 1. Every dispatch runs inside exactly one startBatch/endBatch pair