mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
test(mcp): align completion checks with provider readiness
Exercise cold and warm completion through ordinary provider operations. Bind fixture observations to the selected analysis profile so the warm-up call satisfies session validation, and make package completion candidates explicit with useful failure diagnostics.
This commit is contained in:
1 parent
abc8c6dadf
commit
fe4b94a661
3 files changed
+48
-31
No files matched your search
@@ -20,7 +20,7 @@ const execute = promisify(execFile);
|
||||
const verifyMcpToolsAndPrompts = async (client, mcpOptions) => {
|
||||
await verifyCompleteToolCatalog(client, mcpOptions);
|
||||
await prompts.verifyPromptCatalog(client, mcpOptions, prompts.names);
|
||||
await prompts.verifyPromptCompletion(client, mcpOptions, false);
|
||||
await prompts.verifyPromptCompletion(client, mcpOptions, []);
|
||||
};
|
||||
|
||||
const verifyMcpTargetFree = async (client, mcpOptions) => {
|
||||
@@ -122,7 +122,7 @@ const verifyMcpNonLinuxCurrentDocument = async (
|
||||
const verifyMcpBinaryLifecycle = async (client, mcpOptions) => {
|
||||
await verifyMcpOpenAndBind(client, mcpOptions);
|
||||
// Completion never starts a provider; only an ordinary operation readies it.
|
||||
await prompts.verifyPromptCompletion(client, mcpOptions, false);
|
||||
await prompts.verifyPromptCompletion(client, mcpOptions, []);
|
||||
if (process.platform === "linux") {
|
||||
await verifyMcpLinuxToolAvailability(client, mcpOptions);
|
||||
} else {
|
||||
@@ -130,8 +130,8 @@ const verifyMcpBinaryLifecycle = async (client, mcpOptions) => {
|
||||
{ name: "current_document", arguments: {} },
|
||||
mcpOptions,
|
||||
);
|
||||
await prompts.verifyPromptCompletion(client, mcpOptions, true);
|
||||
await verifyMcpNonLinuxCurrentDocument(client, mcpOptions, current);
|
||||
await prompts.verifyPromptCompletion(client, mcpOptions, ["fixture"]);
|
||||
}
|
||||
const closed = await client.callTool(
|
||||
{ name: "close_binary", arguments: {} },
|
||||
@@ -139,7 +139,7 @@ const verifyMcpBinaryLifecycle = async (client, mcpOptions) => {
|
||||
);
|
||||
if (closed.isError === true)
|
||||
throw new Error("packaged MCP could not close its binary");
|
||||
await prompts.verifyPromptCompletion(client, mcpOptions, false);
|
||||
await prompts.verifyPromptCompletion(client, mcpOptions, []);
|
||||
};
|
||||
|
||||
const verifyMcpEvidenceBundle = async (client, mcpOptions, evidenceRoot) => {
|
||||
|
||||
@@ -36,9 +36,8 @@ export const verifyPromptCatalog = async (client, options, expectedNames) => {
|
||||
throw new Error("packaged MCP guided prompt rendering failed");
|
||||
};
|
||||
|
||||
/** Verify live completion through each installed stdio target state. */
|
||||
export const verifyPromptCompletion = async (client, options, targetOpen) => {
|
||||
const expected = targetOpen ? ["fixture"] : [];
|
||||
/** Verify completion candidates for the installed provider readiness state. */
|
||||
export const verifyPromptCompletion = async (client, options, expected) => {
|
||||
const requests = [
|
||||
{
|
||||
ref: { type: "ref/prompt", name: "investigate_feature" },
|
||||
@@ -57,6 +56,8 @@ export const verifyPromptCompletion = async (client, options, targetOpen) => {
|
||||
result.completion.total !== expected.length ||
|
||||
result.completion.hasMore !== false
|
||||
)
|
||||
throw new Error("packaged MCP prompt completion lifecycle failed");
|
||||
throw new Error(
|
||||
`packaged MCP ${request.argument.name} completion mismatch: expected ${JSON.stringify(expected)}, received ${JSON.stringify(result.completion)}`,
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -5,7 +5,11 @@ import { afterEach, describe, expect, it } from "vitest";
|
||||
|
||||
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
||||
|
||||
import type { AnalysisClient } from "../../../src/application/AnalysisProvider.js";
|
||||
import {
|
||||
createAnalysisExecution,
|
||||
type AnalysisClient,
|
||||
} from "../../../src/application/AnalysisProvider.js";
|
||||
import type { AnalysisProfileCommitment } from "../../../src/domain/analysisProfile.js";
|
||||
import { createTestBinarySession } from "../../fixtures/binarySession.js";
|
||||
import { ARTIFACT_COMPARISON_EXAMPLE } from "../../../src/contracts/artifactComparisonExample.js";
|
||||
import { PROCESS_CAPTURE_REFERENCE } from "../../../src/contracts/investigationExamples.js";
|
||||
@@ -29,30 +33,34 @@ afterEach(async () => {
|
||||
describe("guided prompt completion from live analysis", () => {
|
||||
it("reads live documents and complete procedures without ambiguous names", async () => {
|
||||
const requests: Array<Readonly<Record<string, unknown>>> = [];
|
||||
const session = createTestBinarySession(() => client(requests), {
|
||||
resolveAnalysisProfile: () =>
|
||||
Promise.resolve(
|
||||
resultOk({
|
||||
profile: createAnalysisProfile(
|
||||
{
|
||||
id: "fixture",
|
||||
name: "Fixture analysis provider",
|
||||
version: "1",
|
||||
},
|
||||
{ fixture: true },
|
||||
),
|
||||
}),
|
||||
),
|
||||
});
|
||||
const session = createTestBinarySession(
|
||||
(_target, profile) => client(requests, profile),
|
||||
{
|
||||
resolveAnalysisProfile: () =>
|
||||
Promise.resolve(
|
||||
resultOk({
|
||||
profile: createAnalysisProfile(
|
||||
{
|
||||
id: "fixture",
|
||||
name: "Fixture analysis provider",
|
||||
version: "1",
|
||||
},
|
||||
{ fixture: true },
|
||||
),
|
||||
}),
|
||||
),
|
||||
},
|
||||
);
|
||||
directory = await createTestTempDirectory("rea-prompt-completion-");
|
||||
const target = join(directory, "fixture.hop");
|
||||
await writeFile(target, "fixture");
|
||||
expect((await session.open(target)).ok).toBe(true);
|
||||
const completion = createPromptCompletionSource(session, session);
|
||||
|
||||
// Completion never starts a provider; an ordinary operation readies it.
|
||||
expect(await completion.complete("document", "app")).toEqual([]);
|
||||
expect((await session.execute("list_documents", {})).ok).toBe(true);
|
||||
expect(await completion.complete("procedure", "")).toEqual([]);
|
||||
const warmed = await session.execute("list_documents", {});
|
||||
if (!warmed.ok) throw warmed.error;
|
||||
|
||||
expect(await completion.complete("document", "app")).toEqual([
|
||||
"App",
|
||||
"AppTests",
|
||||
@@ -272,14 +280,23 @@ const fixtureEnvironment = {
|
||||
|
||||
const client = (
|
||||
requests: Array<Readonly<Record<string, unknown>>>,
|
||||
analysisProfile?: AnalysisProfileCommitment,
|
||||
): AnalysisClient => {
|
||||
const observe = (value: unknown) =>
|
||||
resultOk(
|
||||
createAnalysisExecution(
|
||||
value,
|
||||
analysisProfile?.provider ?? fixtureProvider,
|
||||
analysisProfile === undefined ? {} : { analysisProfile },
|
||||
),
|
||||
);
|
||||
const execute: AnalysisClient["execute"] = (operation, parameters) => {
|
||||
if (operation === "list_documents")
|
||||
return Promise.resolve(observed(["AppTests", "App", "App"]));
|
||||
return Promise.resolve(observe(["AppTests", "App", "App"]));
|
||||
if (operation === "list_procedures") {
|
||||
requests.push(parameters);
|
||||
return Promise.resolve(
|
||||
observed([
|
||||
observe([
|
||||
{ address: "0x1000", value: "duplicate" },
|
||||
{ address: "0x2000", value: "unique" },
|
||||
{ address: "0x3000", value: "duplicate" },
|
||||
@@ -287,9 +304,8 @@ const client = (
|
||||
]),
|
||||
);
|
||||
}
|
||||
return Promise.resolve(observed(null));
|
||||
return Promise.resolve(observe(null));
|
||||
};
|
||||
// An in-process fixture client is ready as soon as the session opens.
|
||||
return {
|
||||
execute,
|
||||
executeIfReady: execute,
|
||||
|
||||
Reference in new issue
Block a user