test(evm): verify static descriptor bindings before use (#2427)

Verify the static EVM descriptor operation, CLI command, factory and input/output schema bindings with compiler-backed negative assignability checks. Prove SDK duplicate registration and invalid target input fail before provider invocation, preserving named bindings and separate lazy owners.

Validated complete supported single-thread typecheck, focused12 SDK/composition cases plus a targeted2-case provider-spy rerun, static checks, checked pre-push, manual review and all exact-head CI checks. Production behavior/defaults and provider identity remain unchanged. Dynamic third-party module admission and ABI negotiation remain deferred.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
Ysopwnable 2026-10-13 09:07:40 +08:00 committed by GitHub
parent ee87111009
commit 4f4f6cd7b5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 104 additions and 1 deletions

View File

@ -179,3 +179,40 @@ interface question through an explicitly selected precise view, retain full
bytecode/original Evidence under the existing lifetime, and preserve complete
defaults. Discover support before sending new options to an older server. Include
discovery, follow-up, failures and retries in the complete task cost.
## Static contract admission verification
The follow-up from main `c462a4ebd3a10e59253bcf38353a0914b176738b` tests the
pilot's actual static boundary, without adding a dynamic module registry or
changing production code. Complete-project TypeScript checking now verifies
that the inferred descriptor type rejects a WASM operation contract, a WASM
CLI command, a WASM service factory, an unrelated input schema and an unrelated
output schema. These are negative assignability assertions: their guarantee
comes from the compiler, not from executing an incompatible descriptor.
The existing named contract and CLI identity are also checked at runtime;
separate factory calls produce distinct service/provider owners. Invalid target
input remains an application error without launching a worker.
The actual public in-memory MCP SDK test additionally spies on the provider:
duplicate EVM registration throws the named `already registered` error before
any provider call. Converted input/output JSON Schemas validate before the
first valid operation. Invalid client arguments are rejected before execution;
incompatible provider reply data is validated **after** execution and is not
presented as a pre-use contract guarantee. Existing healthy-peer, cancellation,
retained summary/default and cleanup-retry scenarios remain covered.
Full project typecheck passes with the installed compiler's supported single
thread scheduling; it includes all source/test files. The two exact focused
files pass 12 runtime cases. The resource scopes sample 2,057,961,472 bytes for
typecheck and 2,426,290,176 bytes for required build plus focused tests, below the
unchanged 3 GiB early-stop/4 GiB hard ceiling. Final transient-cgroup counters
remain unknown. No new real-provider or performance improvement is claimed by
these compile/SDK checks; the earlier pinned real EVMole/WABT/lifecycle evidence
retains its original scope and source identity.
This proves the one statically composed descriptor's named/schema/factory
compatibility and duplicate-registration guard. It does not implement runtime
admission of arbitrary JavaScript descriptors, third-party plugin ABI/version
negotiation or a general plugin SDK. Those remain deferred by the original plan.
Existing callers, full defaults, provider inputs, owned lifetimes and output
identities are unchanged.

View File

@ -28,8 +28,11 @@ import {
it("advertises exact valid schemas and records inline EVM interface evidence without a binary target", async () => {
const value = evmInterfaceFixture();
const inspect = vi.fn(() =>
Promise.resolve(ok(evmInterfaceExecution(value))),
);
const service = new EvmInterfaceService({
inspect: () => Promise.resolve(ok(evmInterfaceExecution(value))),
inspect,
});
const session = createTestBinarySession(() => {
throw new Error("deep provider must not start");
@ -71,6 +74,7 @@ it("advertises exact valid schemas and records inline EVM interface evidence wit
expect(() => registerEvmTools(server, service, silentLogger)).toThrow(
/inspect_evm_interface.*already registered/,
);
expect(inspect).not.toHaveBeenCalled();
expect(
(await client.listTools()).tools.filter(
(tool) => tool.name === "inspect_evm_interface",

View File

@ -0,0 +1,62 @@
import { expect, expectTypeOf, it, vi } from "vitest";
import { z } from "zod";
import { evmInterfaceModule } from "../../src/composition/evm.js";
import { wasmArtifactModule } from "../../src/composition/wasm.js";
import { CLI_COMMANDS } from "../../src/cliCommandNames.js";
import { INSPECT_EVM_INTERFACE_CONTRACT } from "../../src/contracts/evm/evmToolContracts.js";
it("rejects incompatible static bindings through the complete project typecheck", () => {
// expect-type's negative assertions are checked by tsc, not the runtime
// test runner. No malformed descriptor is registered or executed here.
expectTypeOf({
...evmInterfaceModule,
contract: wasmArtifactModule.contract,
}).not.toExtend<typeof evmInterfaceModule>();
expectTypeOf({
...evmInterfaceModule,
cliCommand: CLI_COMMANDS.inspectWasmArtifact,
}).not.toExtend<typeof evmInterfaceModule>();
expectTypeOf({
...evmInterfaceModule,
create: wasmArtifactModule.create,
}).not.toExtend<typeof evmInterfaceModule>();
expectTypeOf({
...evmInterfaceModule,
contract: {
...evmInterfaceModule.contract,
inputSchema: z.strictObject({ unrelated: z.string() }),
},
}).not.toExtend<typeof evmInterfaceModule>();
expectTypeOf({
...evmInterfaceModule,
contract: {
...evmInterfaceModule.contract,
outputSchema: z.strictObject({ unrelated: z.string() }),
},
}).not.toExtend<typeof evmInterfaceModule>();
});
it("keeps the named binding and gives each caller a separate lazy service owner", async () => {
expect(evmInterfaceModule.contract).toBe(INSPECT_EVM_INTERFACE_CONTRACT);
expect(evmInterfaceModule.cliCommand).toBe(CLI_COMMANDS.inspectEvmInterface);
const first = evmInterfaceModule.create({});
const second = evmInterfaceModule.create({});
try {
expect(first).not.toBe(second);
expect(first.provider).not.toBe(second.provider);
await first.close();
const inspect = vi.spyOn(second.provider, "inspect");
const invalid = await second.inspect({
path: "relative.hex",
encoding: "hex",
});
expect(invalid).toMatchObject({
ok: false,
error: { _tag: "AnalysisInputError" },
});
expect(inspect).not.toHaveBeenCalled();
} finally {
await first.close();
await second.close();
}
});