wopee-mcp 1.30.0 → 1.30.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/tools/shared/gql-queries.js +5 -0
- package/build/tools/shared/helpers.js +7 -6
- package/build/tools/wopee_fetch_recent_executions/index.js +7 -9
- package/build/tools/wopee_fetch_test_inventory/index.js +12 -5
- package/build/tools/wopee_fetch_test_inventory/logic.js +13 -2
- package/package.json +1 -1
|
@@ -192,6 +192,9 @@ export const FetchExecutedTestCases = `
|
|
|
192
192
|
agentReportStatus
|
|
193
193
|
codeReport
|
|
194
194
|
codeReportStatus
|
|
195
|
+
verdictIntegrity
|
|
196
|
+
integrityFlagged
|
|
197
|
+
finishedAt
|
|
195
198
|
createdAt
|
|
196
199
|
updatedAt
|
|
197
200
|
}
|
|
@@ -284,6 +287,8 @@ export const FetchRecentExecutedTestCases = `
|
|
|
284
287
|
agentReportStatus
|
|
285
288
|
codeReport
|
|
286
289
|
codeReportStatus
|
|
290
|
+
verdictIntegrity
|
|
291
|
+
integrityFlagged
|
|
287
292
|
createdAt
|
|
288
293
|
updatedAt
|
|
289
294
|
}
|
|
@@ -69,16 +69,17 @@ export function _parseGenerateArtifactType(type) {
|
|
|
69
69
|
};
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
|
+
/** One-line description of a failed request, e.g. "[GRAPHQL_ERROR] Not Authorised!". */
|
|
73
|
+
export function _describeError(error) {
|
|
74
|
+
if (error instanceof RequestError)
|
|
75
|
+
return `[${error.code}] ${error.message}${error.retryable ? " (retryable)" : ""}`;
|
|
76
|
+
return error instanceof Error ? error.message : String(error);
|
|
77
|
+
}
|
|
72
78
|
export function _parseError(error) {
|
|
73
79
|
console.error(error instanceof z.ZodError ? error.issues : error);
|
|
74
80
|
if (error instanceof RequestError) {
|
|
75
81
|
return {
|
|
76
|
-
content: [
|
|
77
|
-
{
|
|
78
|
-
type: "text",
|
|
79
|
-
text: `[${error.code}] ${error.message}${error.retryable ? " (retryable)" : ""}`,
|
|
80
|
-
},
|
|
81
|
-
],
|
|
82
|
+
content: [{ type: "text", text: _describeError(error) }],
|
|
82
83
|
};
|
|
83
84
|
}
|
|
84
85
|
return {
|
|
@@ -7,7 +7,7 @@ export const wopeeFetchRecentExecutions = {
|
|
|
7
7
|
name: ToolName.WOPEE_FETCH_RECENT_EXECUTIONS,
|
|
8
8
|
config: {
|
|
9
9
|
title: "Fetch recent test executions",
|
|
10
|
-
description: "Fetch the most recent test case executions for the current project (up to 20, newest first). Use this to check the status of recently dispatched tests without needing to remember specific suite UUIDs. Returns execution status (
|
|
10
|
+
description: "Fetch the most recent test case executions for the current project (up to 20, newest first). Use this to check the status of recently dispatched tests without needing to remember specific suite UUIDs. Returns each run's verdict (PASSED, FAILED, or INCOMPLETE — the run never established a result, e.g. an infrastructure error, and says nothing about the application) or, for a run with no verdict yet, its execution status (IN_QUEUE, IN_PROGRESS, FINISHED, FAILED, STOPPED), plus agent reports. Takes no input; uses WOPEE_PROJECT_UUID from environment. Prefer this tool when the user asks 'what's the status?' or 'how did the tests go?' and you don't have the specific suite UUID handy.",
|
|
11
11
|
},
|
|
12
12
|
handler: async () => {
|
|
13
13
|
try {
|
|
@@ -48,15 +48,13 @@ export const wopeeFetchRecentExecutions = {
|
|
|
48
48
|
"",
|
|
49
49
|
];
|
|
50
50
|
for (const tc of executions) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
status = tc.executionStatus;
|
|
57
|
-
}
|
|
51
|
+
// The verdict first (backlog#4453). A run whose agent threw is FAILED underneath but
|
|
52
|
+
// INCOMPLETE on top, and reporting it as FAILED tells the user their app is broken.
|
|
53
|
+
const status = tc.agentReportStatus ?? tc.executionStatus;
|
|
58
54
|
lines.push(`- ${tc.userStoryId}:${tc.testCaseId} [${tc.analysisIdentifier}] → ${status} (${tc.updatedAt})`);
|
|
59
|
-
|
|
55
|
+
// Any report, not only a FINISHED run's: an incomplete run's report is what names the
|
|
56
|
+
// infrastructure error behind it.
|
|
57
|
+
if (tc.agentReport) {
|
|
60
58
|
const shortReport = tc.agentReport.slice(0, 200);
|
|
61
59
|
lines.push(` Report: ${shortReport}${tc.agentReport.length > 200 ? "..." : ""}`);
|
|
62
60
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getConfig } from "../../utils/getConfig.js";
|
|
2
2
|
import { requestClient } from "../../utils/requestClient.js";
|
|
3
|
-
import { _parseError } from "../shared/helpers.js";
|
|
3
|
+
import { _describeError, _parseError } from "../shared/helpers.js";
|
|
4
4
|
import { ArtifactType, ToolName, } from "../shared/types.js";
|
|
5
5
|
import { FetchAnalysisSuites, FetchArtifact, FetchExecutedTestCases, } from "../shared/gql-queries.js";
|
|
6
6
|
import { WopeeFetchTestInventoryInputSchema, } from "./schema.js";
|
|
@@ -9,7 +9,7 @@ export const wopeeFetchTestInventory = {
|
|
|
9
9
|
name: ToolName.WOPEE_FETCH_TEST_INVENTORY,
|
|
10
10
|
config: {
|
|
11
11
|
title: "Fetch test inventory (counts + statuses)",
|
|
12
|
-
description: "The authoritative tool for how many tests exist and their latest status. Returns, per analysis, the FULL list of authored test cases joined with their latest execution status — including never-run ones as NOT_RUN. Use this for questions like 'how many tests do I have', 'list the scenarios/test cases in A001', or 'show executed and not-run tests in one table'. Terminology: a 'scenario' is a test case; test cases are grouped under user stories (US001) and identified as US001:TC001. Reusable blocks (user story R001) are counted separately (reusableBlockCount) and are building blocks, not runnable, so they never carry an execution status. Regular tests are all non-R001 test cases. Read-only. Takes an optional analysisIdentifier (e.g. A001) to scope to one analysis; omit to cover every analysis in the project. Prefer this over wopee_fetch_recent_executions / wopee_fetch_executed_test_cases when the user asks about totals or the complete list — those return only test cases that have already run.",
|
|
12
|
+
description: "The authoritative tool for how many tests exist and their latest status. Returns, per analysis, the FULL list of authored test cases joined with their latest execution status — including never-run ones as NOT_RUN. Use this for questions like 'how many tests do I have', 'list the scenarios/test cases in A001', or 'show executed and not-run tests in one table'. Terminology: a 'scenario' is a test case; test cases are grouped under user stories (US001) and identified as US001:TC001. Reusable blocks (user story R001) are counted separately (reusableBlockCount) and are building blocks, not runnable, so they never carry an execution status. Regular tests are all non-R001 test cases. Read-only. Takes an optional analysisIdentifier (e.g. A001) to scope to one analysis; omit to cover every analysis in the project. Prefer this over wopee_fetch_recent_executions / wopee_fetch_executed_test_cases when the user asks about totals or the complete list — those return only test cases that have already run. Each test case carries executedTestCaseUuid (the latest run's uuid, the same one wopee_dispatch_agent returns) and verdictGateDisagreed (true when a verdict gate disagreed with a PASSED verdict — report it alongside the pass). Status UNKNOWN means the execution history could not be read (the reason is in the analysis's executionHistoryError); do not report such tests as not run.",
|
|
13
13
|
inputSchema: WopeeFetchTestInventoryInputSchema.shape,
|
|
14
14
|
},
|
|
15
15
|
handler: async (input) => {
|
|
@@ -38,9 +38,11 @@ export const wopeeFetchTestInventory = {
|
|
|
38
38
|
],
|
|
39
39
|
};
|
|
40
40
|
// Fetch the authored test cases (USER_STORIES artifact) and execution
|
|
41
|
-
// history for every analysis in parallel. A
|
|
42
|
-
//
|
|
43
|
-
//
|
|
41
|
+
// history for every analysis in parallel. A missing artifact (an analysis
|
|
42
|
+
// with no generated tests yet) degrades to an empty list rather than
|
|
43
|
+
// failing the whole inventory. A failed execution fetch does NOT degrade to
|
|
44
|
+
// "no runs": an analysis with no runs returns [], so a rejection is a real
|
|
45
|
+
// error (e.g. Not Authorised!) and is reported as UNKNOWN.
|
|
44
46
|
const inputs = await Promise.all(suites.map(async (suite) => {
|
|
45
47
|
const [artifactRes, execRes] = await Promise.allSettled([
|
|
46
48
|
requestClient(FetchArtifact, {
|
|
@@ -59,6 +61,8 @@ export const wopeeFetchTestInventory = {
|
|
|
59
61
|
},
|
|
60
62
|
}),
|
|
61
63
|
]);
|
|
64
|
+
if (execRes.status === "rejected")
|
|
65
|
+
console.error(execRes.reason);
|
|
62
66
|
return {
|
|
63
67
|
suite,
|
|
64
68
|
artifactContent: artifactRes.status === "fulfilled"
|
|
@@ -67,6 +71,9 @@ export const wopeeFetchTestInventory = {
|
|
|
67
71
|
execGroups: execRes.status === "fulfilled"
|
|
68
72
|
? (execRes.value?.fetchExecutedTestCases ?? [])
|
|
69
73
|
: [],
|
|
74
|
+
execError: execRes.status === "rejected"
|
|
75
|
+
? _describeError(execRes.reason)
|
|
76
|
+
: null,
|
|
70
77
|
};
|
|
71
78
|
}));
|
|
72
79
|
const inventory = buildTestInventory(inputs);
|
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
// test cases at generation time (api generateReusableTestCases.ts).
|
|
4
4
|
export const REUSABLE_USER_STORY_ID = "R001";
|
|
5
5
|
export const NOT_RUN = "NOT_RUN";
|
|
6
|
+
// The execution history could not be read (e.g. the credential is not allowed to
|
|
7
|
+
// read executions), so whether a test ran is unknown. Never collapsed into
|
|
8
|
+
// NOT_RUN: "never ran" and "could not ask" must not look the same.
|
|
9
|
+
export const UNKNOWN = "UNKNOWN";
|
|
6
10
|
/** Parse the USER_STORIES artifact JSON; tolerant of null/empty/malformed. */
|
|
7
11
|
export function parseUserStories(content) {
|
|
8
12
|
if (!content)
|
|
@@ -50,7 +54,11 @@ export function resolveStatus(exec) {
|
|
|
50
54
|
return report;
|
|
51
55
|
return exec.executionStatus ?? NOT_RUN;
|
|
52
56
|
}
|
|
53
|
-
|
|
57
|
+
/** Mirrors CMD rowVerdict: only a pass is split, a flagged fail is already red. */
|
|
58
|
+
export function verdictGateDisagreed(exec, status) {
|
|
59
|
+
return !!exec?.integrityFlagged && status.toUpperCase() === "PASSED";
|
|
60
|
+
}
|
|
61
|
+
function buildAnalysisInventory({ suite, artifactContent, execGroups, execError = null, }) {
|
|
54
62
|
const userStories = parseUserStories(artifactContent);
|
|
55
63
|
const execIndex = indexExecutions(execGroups);
|
|
56
64
|
const testCases = [];
|
|
@@ -69,7 +77,7 @@ function buildAnalysisInventory({ suite, artifactContent, execGroups, }) {
|
|
|
69
77
|
const exec = isReusable
|
|
70
78
|
? undefined
|
|
71
79
|
: execIndex.get(`${story.userStoryId}::${tc.testCaseId}`)?.[0];
|
|
72
|
-
const status = resolveStatus(exec);
|
|
80
|
+
const status = execError && !isReusable ? UNKNOWN : resolveStatus(exec);
|
|
73
81
|
if (!isReusable)
|
|
74
82
|
statusSummary[status] = (statusSummary[status] ?? 0) + 1;
|
|
75
83
|
testCases.push({
|
|
@@ -82,6 +90,8 @@ function buildAnalysisInventory({ suite, artifactContent, execGroups, }) {
|
|
|
82
90
|
kind: isReusable ? "REUSABLE" : "REGULAR",
|
|
83
91
|
status,
|
|
84
92
|
executedAt: exec?.updatedAt ?? exec?.createdAt ?? null,
|
|
93
|
+
executedTestCaseUuid: exec?.uuid ?? null,
|
|
94
|
+
verdictGateDisagreed: verdictGateDisagreed(exec, status),
|
|
85
95
|
});
|
|
86
96
|
}
|
|
87
97
|
}
|
|
@@ -93,6 +103,7 @@ function buildAnalysisInventory({ suite, artifactContent, execGroups, }) {
|
|
|
93
103
|
regularTestCount,
|
|
94
104
|
reusableBlockCount,
|
|
95
105
|
statusSummary,
|
|
106
|
+
executionHistoryError: execError,
|
|
96
107
|
testCases,
|
|
97
108
|
};
|
|
98
109
|
}
|
package/package.json
CHANGED