From 2bca3e2abf6c232c05ea0185abbb5924ea08e41a Mon Sep 17 00:00:00 2001 From: Fini Date: Wed, 22 Apr 2026 00:36:17 +0800 Subject: [PATCH] =?UTF-8?q?test(mcp):=20contract=20drift-guard=20=E2=80=94?= =?UTF-8?q?=20pen-core=20builder=20exists=20for=20every=20tool?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pairs with the apps/web shim drift-guard (SUPPORTED_EMBEDDED_ELEMENT_TOOLS ⇔ ELEMENT_TOOL_NAMES). This one fires from the server side: every add_*_v0 registered in pen-mcp must have a matching pen-core buildX export. Catches the case where a new handler ships without the canonical pen-core builder (which would mean the embedded shim + Nitro SERVER_BUILDERS can't cover it, silently degrading AI generation under the flag). Failure message maps each missing tool to the expected buildX name so the fix is mechanical. pen-mcp tests: 8 → 9. --- .../__tests__/element-tools-contract.test.ts | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/packages/pen-mcp/src/__tests__/element-tools-contract.test.ts b/packages/pen-mcp/src/__tests__/element-tools-contract.test.ts index ba9c3907c..d9dad4572 100644 --- a/packages/pen-mcp/src/__tests__/element-tools-contract.test.ts +++ b/packages/pen-mcp/src/__tests__/element-tools-contract.test.ts @@ -131,6 +131,33 @@ describe('element tools — v0-MUST contract', () => { expect(props?.variant, `${name} must NOT have variant union`).toBeUndefined(); } }); + + it('drift-guard: every add_*_v0 has a matching pen-core builder export', async () => { + // Pairs with the apps/web `SUPPORTED_EMBEDDED_ELEMENT_TOOLS` + // drift-guard — from the server side, verify each pen-mcp tool + // name maps to a pen-core `buildX` export. Catches the case + // where a handler is added/renamed but the pen-core builder + // (the drift-free source) isn't. + const penCore = await import('@zseven-w/pen-core'); + const missing: string[] = []; + for (const name of ELEMENT_TOOL_NAMES) { + // add_xxx_v0 → buildXxx (camelCase) + const stem = name.replace(/^add_/, '').replace(/_v\d+$/, ''); + const builderKey = `build${stem + .split('_') + .map((p) => p.charAt(0).toUpperCase() + p.slice(1)) + .join('')}`; + if (typeof (penCore as Record)[builderKey] !== 'function') { + missing.push(`${name} → ${builderKey}`); + } + } + expect( + missing, + `Element tools without a pen-core builder — add the buildX export to ` + + `packages/pen-core/src/element-builders/ (and wire it through the index.ts + ` + + `src/index.ts barrel): ${missing.join(', ')}`, + ).toEqual([]); + }); }); // Regression test for the post-insert verification path in