@skyramp/mcp 0.4.4 → 0.5.0-rc.10
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/entitlements/entitlementGate.d.ts +8 -0
- package/build/entitlements/entitlementGate.js +57 -0
- package/build/entitlements/runCodes.d.ts +18 -0
- package/build/entitlements/runCodes.js +16 -0
- package/build/index.js +32 -6
- package/build/playwright/PlaywrightTraceService.d.ts +17 -0
- package/build/playwright/PlaywrightTraceService.js +29 -1
- package/build/playwright/dataSafetyConfig.d.ts +74 -0
- package/build/playwright/dataSafetyConfig.js +109 -0
- package/build/playwright/essentialTools.d.ts +1 -0
- package/build/playwright/essentialTools.js +44 -0
- package/build/playwright/registerPlaywrightTools.d.ts +36 -1
- package/build/playwright/registerPlaywrightTools.js +87 -43
- package/build/playwright/safeResolveDataSafety.d.ts +24 -0
- package/build/playwright/safeResolveDataSafety.js +33 -0
- package/build/prompts/initialize-workspace/initializeWorkspacePrompt.d.ts +17 -0
- package/build/prompts/initialize-workspace/initializeWorkspacePrompt.js +46 -177
- package/build/prompts/modularization/ui-test-modularization.d.ts +1 -0
- package/build/prompts/modularization/ui-test-modularization.js +9 -2
- package/build/prompts/test-maintenance/driftAnalysisShared.js +1 -1
- package/build/services/FleetExecutionService.d.ts +87 -16
- package/build/services/FleetExecutionService.js +487 -48
- package/build/services/FleetRunRegistry.d.ts +49 -0
- package/build/services/FleetRunRegistry.js +178 -0
- package/build/services/containerEnv.js +8 -2
- package/build/services/fleetLimitConfirmation.d.ts +19 -0
- package/build/services/fleetLimitConfirmation.js +126 -0
- package/build/tool-phases.d.ts +14 -8
- package/build/tool-phases.js +88 -43
- package/build/tools/code-refactor/assertion-state.js +3 -1
- package/build/tools/code-refactor/codeReuseTool.js +2 -1
- package/build/tools/code-refactor/utils-verify-gates.js +1 -1
- package/build/tools/executeFleetTool.d.ts +6 -4
- package/build/tools/executeFleetTool.js +67 -23
- package/build/tools/fixErrorTool.js +2 -1
- package/build/tools/fleetLifecycleTools.d.ts +20 -0
- package/build/tools/fleetLifecycleTools.js +124 -0
- package/build/tools/generate-tests/generateIntegrationRestTool.d.ts +38 -0
- package/build/tools/generate-tests/generateIntegrationRestTool.js +7 -1
- package/build/tools/generate-tests/generateUILoadRestTool.d.ts +19 -1
- package/build/tools/generate-tests/generateUILoadRestTool.js +316 -50
- package/build/tools/submitReportTool.js +4 -122
- package/build/tools/test-management/registerTestPlanTool.js +22 -6
- package/build/tools/workspace/initScanWorkspaceTool.d.ts +11 -1
- package/build/tools/workspace/initScanWorkspaceTool.js +111 -9
- package/build/tools/workspace/initializeWorkspaceTool.js +40 -4
- package/build/types/AssertionOutcome.d.ts +3 -0
- package/build/types/TestbotReport.d.ts +0 -37
- package/build/types/TestbotReport.js +0 -22
- package/build/types/index.d.ts +1 -1
- package/build/types/index.js +1 -1
- package/build/utils/assertion-verify/created-add-guard.d.ts +12 -0
- package/build/utils/assertion-verify/created-add-guard.js +173 -0
- package/build/utils/assertion-verify/index.d.ts +1 -0
- package/build/utils/assertion-verify/index.js +1 -0
- package/build/utils/assertion-verify/verify.d.ts +7 -3
- package/build/utils/assertion-verify/verify.js +9 -3
- package/build/utils/featureFlags.js +6 -3
- package/build/utils/repositorySlug.d.ts +14 -0
- package/build/utils/repositorySlug.js +46 -1
- package/build/utils/telemetry.d.ts +2 -0
- package/build/utils/telemetry.js +12 -0
- package/build/utils/utils-verify/action-key.d.ts +59 -2
- package/build/utils/utils-verify/action-key.js +162 -6
- package/build/utils/utils-verify/action-sites.d.ts +7 -0
- package/build/utils/utils-verify/action-sites.js +277 -35
- package/build/utils/utils-verify/call-sites.d.ts +8 -2
- package/build/utils/utils-verify/language-spec.js +1 -1
- package/build/utils/utils-verify/parse.d.ts +4 -0
- package/build/utils/utils-verify/parse.js +2 -1
- package/build/utils/versions.d.ts +3 -3
- package/build/utils/versions.js +1 -1
- package/build/workspace/workspace.d.ts +63 -121
- package/build/workspace/workspace.js +45 -48
- package/build/workspace/workspaceVerifier.d.ts +22 -0
- package/build/workspace/workspaceVerifier.js +65 -0
- package/node_modules/playwright/lib/dom-analyzer/blueprint.js +7 -6
- package/node_modules/playwright/lib/dom-analyzer/dynamicId.js +5 -4
- package/node_modules/playwright/lib/dom-analyzer/dynamicId.test.js +12 -0
- package/node_modules/playwright/lib/mcp/skyramp/dataSafetyGuard.js +822 -0
- package/node_modules/playwright/lib/mcp/skyramp/dataSafetySession.js +372 -0
- package/node_modules/playwright/lib/mcp/skyramp/exportTool.js +101 -8
- package/node_modules/playwright/lib/mcp/skyramp/finishTestSetupTool.js +45 -0
- package/node_modules/playwright/lib/mcp/skyramp/index.js +16 -0
- package/node_modules/playwright/lib/mcp/skyramp/recordingPhase.js +65 -0
- package/node_modules/playwright/lib/mcp/skyramp/skyRampImport.js +1 -0
- package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +598 -127
- package/node_modules/playwright/lib/mcp/test/skyRampExport.js +85 -31
- package/node_modules/playwright/lib/mcp/test/skyRampExport.test.js +132 -5
- package/node_modules/playwright/node_modules/playwright-core/lib/cli/program.js +2 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/client/browserContext.js +4 -2
- package/node_modules/playwright/node_modules/playwright-core/lib/generated/injectedScriptSource.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/generated/pollingRecorderSource.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/protocol/validator.js +2 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/server/browserContext.js +17 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/server/codegen/skyramp/jsonlReader.js +1 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/server/har/harRecorder.js +1 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/server/har/harTracer.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/recorderRunner.js +2 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/recorderSignalProcessor.js +2 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/skyramp/clickExpandedState.js +75 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder.js +4 -2
- package/node_modules/playwright/node_modules/playwright-core/lib/utils/isomorphic/volatileDate.js +85 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{codeMirrorModule-Bou8kKzE.js → codeMirrorModule-BuhAdckO.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-COAHN430.js +809 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{index.0MJW0uG7.js → index.7AU8h0PZ.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.html +2 -2
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{uiMode.GLqsx5cI.js → uiMode.Dl7smBJn.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +2 -2
- package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/cli/program.ts +5 -0
- package/node_modules/playwright/node_modules/playwright-core/src/client/browserContext.ts +3 -1
- package/node_modules/playwright/node_modules/playwright-core/src/client/types.ts +2 -0
- package/node_modules/playwright/node_modules/playwright-core/src/generated/injectedScriptSource.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/generated/pollingRecorderSource.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/protocol/validator.ts +1 -0
- package/node_modules/playwright/node_modules/playwright-core/src/server/browserContext.ts +25 -1
- package/node_modules/playwright/node_modules/playwright-core/src/server/codegen/skyramp/jsonlReader.ts +2 -0
- package/node_modules/playwright/node_modules/playwright-core/src/server/har/harRecorder.ts +1 -0
- package/node_modules/playwright/node_modules/playwright-core/src/server/har/harTracer.ts +3 -1
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/recorderRunner.ts +3 -1
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/recorderSignalProcessor.ts +1 -0
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/skyramp/clickExpandedState.ts +82 -0
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder.ts +2 -0
- package/node_modules/playwright/node_modules/playwright-core/src/utils/isomorphic/volatileDate.ts +137 -1
- package/node_modules/playwright/package.json +1 -1
- package/package.json +4 -4
- package/plugin/prompts/plan-tests.md +2 -2
- package/plugin/prompts/testbot-task1.md +1 -1
- package/plugin/skills/enhance-assertions/reference/integration.md +1 -0
- package/plugin/skills/setup-testbot-target-sut/SKILL.md +60 -0
- package/plugin/skills/setup-testbot-target-sut/reference/ci-workflow.md +26 -0
- package/plugin/skills/setup-testbot-target-sut/reference/deployed-environment.md +105 -0
- package/plugin/skills/setup-testbot-target-sut/reference/existing-tests.md +14 -0
- package/plugin/skills/setup-testbot-target-sut/reference/troubleshooting.md +20 -0
- package/build/prompts/sut-setup/modes/adaptWorkflowPrompt.d.ts +0 -2
- package/build/prompts/sut-setup/modes/adaptWorkflowPrompt.js +0 -178
- package/build/prompts/sut-setup/modes/dockerComposePrompt.d.ts +0 -2
- package/build/prompts/sut-setup/modes/dockerComposePrompt.js +0 -311
- package/build/prompts/sut-setup/shared.d.ts +0 -62
- package/build/prompts/sut-setup/shared.js +0 -196
- package/build/resources/sutSetupResource.d.ts +0 -2
- package/build/resources/sutSetupResource.js +0 -63
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-DTp2-8Si.js +0 -809
|
@@ -9,7 +9,7 @@ import { ProgrammingLanguage, TESTBOT_SUPPORTED_TEST_TYPES, TestType, } from "..
|
|
|
9
9
|
import { STEP_METHOD_DESCRIPTION, stepMethodSchema, } from "../types/StepMethod.js";
|
|
10
10
|
import { DriftAction } from "../types/TestAnalysis.js";
|
|
11
11
|
import { isResult, TestExecutionStatus } from "../types/TestExecution.js";
|
|
12
|
-
import {
|
|
12
|
+
import { IssueFoundCategory } from "../types/TestbotReport.js";
|
|
13
13
|
import { StateManager, runArtifactDir, getTestsRepoDir, resolveOwnRunStatePath, } from "../utils/AnalysisStateManager.js";
|
|
14
14
|
import { toolError, testFileMatches } from "../utils/utils.js";
|
|
15
15
|
import { isTestbotEnabled, isUtilsReuseEnabled, } from "../utils/featureFlags.js";
|
|
@@ -115,46 +115,17 @@ const repositoryField = z
|
|
|
115
115
|
.trim()
|
|
116
116
|
.min(1)
|
|
117
117
|
.describe("The 'owner/repo' this item pertains to (e.g. 'letsramp/api-insight'). Set it on every item — the primary's owner/repo for primary-repo objections, or a related repo's owner/repo for objections derived from that repository's diff.");
|
|
118
|
-
// One home for the family meanings: a `.describe()` on the base enum is replaced
|
|
119
|
-
// by the `.optional().describe()` at each use site, so the meanings go there.
|
|
120
|
-
const ENVIRONMENT_FAMILY_MEANINGS = "auth_token = an API token is missing or expired. " +
|
|
121
|
-
"secret = another secret or credential is missing. " +
|
|
122
|
-
"data_connection = the database or data store is not reachable. " +
|
|
123
|
-
"app_not_started = the app under test did not start or is not reachable. " +
|
|
124
|
-
"login = the sign-in step failed. " +
|
|
125
|
-
"external_dependency = an external service is not configured. " +
|
|
126
|
-
"recording_replay = recording or replay of the trace failed.";
|
|
127
|
-
const environmentFamilyField = z.enum(EnvironmentFamily);
|
|
128
|
-
// SKYR-4555. Why a Fail or Skipped row did not pass; the row-level refinement on
|
|
129
|
-
// testResultSchema decides when it is required and which shapes are consistent.
|
|
130
|
-
const failureCauseSchema = z
|
|
131
|
-
.object({
|
|
132
|
-
kind: z
|
|
133
|
-
.enum(FailureCauseKind)
|
|
134
|
-
.describe("application = the code under test misbehaved, or a test written to prove a defect failed as intended. " +
|
|
135
|
-
"environment = the test could not exercise the behaviour because of the test environment: a missing token or secret, no data connection, the app did not start, sign-in failed, an external service is not configured, or recording or replay failed. Name the gap in `family`. " +
|
|
136
|
-
"test_defect = the generated test itself is wrong, not the app: an ambiguous locator, a strict-mode clash, a bad assertion setup."),
|
|
137
|
-
family: environmentFamilyField
|
|
138
|
-
.optional()
|
|
139
|
-
.describe("The test-environment gap. Required when kind is 'environment'. Not allowed for the other kinds. " +
|
|
140
|
-
ENVIRONMENT_FAMILY_MEANINGS),
|
|
141
|
-
})
|
|
142
|
-
.describe("Why this test did not pass. REQUIRED when status is 'Fail' or 'Skipped'. Not allowed when status is 'Pass'. " +
|
|
143
|
-
"It must agree with reachedBehavior: 'application' needs reachedBehavior true or omitted; 'environment' needs reachedBehavior false or omitted.");
|
|
144
118
|
// videoPath is deliberately absent from this input contract: it is attached server-side
|
|
145
119
|
// from the run's execution records (see attachVideoPath), and zod strips any the model
|
|
146
120
|
// supplies anyway. SKYR-4156 is what happens when the agent owns that field instead.
|
|
147
|
-
const testResultSchema = z
|
|
148
|
-
.object({
|
|
121
|
+
const testResultSchema = z.object({
|
|
149
122
|
testType: z
|
|
150
123
|
.enum(TestType)
|
|
151
124
|
.describe("Type of test. Do not include priority or other metadata in this field."),
|
|
152
125
|
endpoint: z
|
|
153
126
|
.string()
|
|
154
127
|
.describe("For contract and integration tests, the HTTP verb and path, e.g. 'GET /api/v1/products'. For ui and e2e tests, the page path the test opens, e.g. '/notifications'."),
|
|
155
|
-
status: z
|
|
156
|
-
.enum(["Pass", "Fail", "Skipped"])
|
|
157
|
-
.describe("Test execution result"),
|
|
128
|
+
status: z.enum(["Pass", "Fail", "Skipped"]).describe("Test execution result"),
|
|
158
129
|
details: z
|
|
159
130
|
.string()
|
|
160
131
|
.describe("One sentence — no embedded newlines, no markdown. e.g. '10.8s, products_contract_test.py' or 'failed: <one-line error summary>, products_contract_test.py'"),
|
|
@@ -174,76 +145,7 @@ const testResultSchema = z
|
|
|
174
145
|
.describe("For a failing test only. `true` when the request that carries the behaviour under test was sent and answered by the code under test, and the assertion that failed is about that behaviour. " +
|
|
175
146
|
"`false` when the failure came earlier: a 404, 401, 403 or 5xx on a route the change adds; an expired token or a sign-in page; a control or setup step that failed; a generator or SDK error. " +
|
|
176
147
|
"If your own `details` text says the request was not sent or an earlier step tripped, this is `false`. Omit only if you cannot tell."),
|
|
177
|
-
failureCause: failureCauseSchema.optional(),
|
|
178
148
|
repository: repositoryField,
|
|
179
|
-
})
|
|
180
|
-
.superRefine((row, ctx) => {
|
|
181
|
-
// SKYR-4555: the business-user report sorts failures by cause, so every row
|
|
182
|
-
// that did not pass must name one and a passing row must not. The kind must
|
|
183
|
-
// agree with reachedBehavior where the agent set it: the two answer the same
|
|
184
|
-
// question — did the test reach the behaviour under test — from two sides.
|
|
185
|
-
const cause = row.failureCause;
|
|
186
|
-
if (row.status === "Pass") {
|
|
187
|
-
if (cause === undefined)
|
|
188
|
-
return;
|
|
189
|
-
ctx.addIssue({
|
|
190
|
-
code: "custom",
|
|
191
|
-
path: ["failureCause"],
|
|
192
|
-
message: `failureCause is not allowed when status is 'Pass'. A passing test has no failure cause. ` +
|
|
193
|
-
`Remove failureCause from this row.`,
|
|
194
|
-
});
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
if (cause === undefined) {
|
|
198
|
-
ctx.addIssue({
|
|
199
|
-
code: "custom",
|
|
200
|
-
path: ["failureCause"],
|
|
201
|
-
message: `failureCause is required when status is '${row.status}'. Set failureCause.kind to one of: ` +
|
|
202
|
-
`'application' (the code under test misbehaved, or a test written to prove a defect failed as intended); ` +
|
|
203
|
-
`'environment' (the test environment stopped the test before it reached the behaviour — also set failureCause.family); ` +
|
|
204
|
-
`'test_defect' (the generated test itself is wrong, not the app).`,
|
|
205
|
-
});
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
if (cause.kind === FailureCauseKind.Environment) {
|
|
209
|
-
if (cause.family === undefined) {
|
|
210
|
-
ctx.addIssue({
|
|
211
|
-
code: "custom",
|
|
212
|
-
path: ["failureCause", "family"],
|
|
213
|
-
message: `failureCause.family is required when failureCause.kind is 'environment'. ` +
|
|
214
|
-
`Set it to the gap that stopped the test: ${Object.values(EnvironmentFamily).join(", ")}.`,
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
else if (cause.family !== undefined) {
|
|
219
|
-
ctx.addIssue({
|
|
220
|
-
code: "custom",
|
|
221
|
-
path: ["failureCause", "family"],
|
|
222
|
-
message: `failureCause.family is allowed only when failureCause.kind is 'environment'. ` +
|
|
223
|
-
`Remove failureCause.family, or set kind to 'environment' if the test environment stopped the test.`,
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
if (cause.kind === FailureCauseKind.Application) {
|
|
227
|
-
if (row.reachedBehavior === false) {
|
|
228
|
-
ctx.addIssue({
|
|
229
|
-
code: "custom",
|
|
230
|
-
path: ["failureCause", "kind"],
|
|
231
|
-
message: `failureCause.kind 'application' contradicts reachedBehavior false. 'application' means the test reached the behaviour under test and the code misbehaved. ` +
|
|
232
|
-
`If the failure came earlier, set kind to 'environment' or 'test_defect'. If the test did reach the behaviour, set reachedBehavior to true.`,
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
else if (cause.kind === FailureCauseKind.Environment &&
|
|
237
|
-
row.reachedBehavior === true) {
|
|
238
|
-
// test_defect is not checked: a wrong assertion can fail after the test
|
|
239
|
-
// reached the behaviour, or a bad setup step can fail before it.
|
|
240
|
-
ctx.addIssue({
|
|
241
|
-
code: "custom",
|
|
242
|
-
path: ["failureCause", "kind"],
|
|
243
|
-
message: `failureCause.kind 'environment' contradicts reachedBehavior true. reachedBehavior true means the test reached the behaviour under test, so the environment did not stop it. ` +
|
|
244
|
-
`Set kind to 'application' or 'test_defect', or set reachedBehavior to false if the failure came earlier.`,
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
149
|
});
|
|
248
150
|
// Structured grounding for UI recommendations. Lifted verbatim from a
|
|
249
151
|
// `browser_blueprint` element captured during this run. Source of truth for
|
|
@@ -486,25 +388,8 @@ const issueFoundSchema = z
|
|
|
486
388
|
.describe("1-based line number in sourceFile. Optional and advisory. " +
|
|
487
389
|
"Give the line of the offending statement itself, not the line of the enclosing function's signature — review views place the objection on exactly this line. " +
|
|
488
390
|
"The tool does not check that the line still holds the cited code, because line numbers drift."),
|
|
489
|
-
environmentFamily: environmentFamilyField
|
|
490
|
-
.optional()
|
|
491
|
-
.describe("Only for category 'config'. Not allowed for the other categories. " +
|
|
492
|
-
"When this config issue is a test-environment gap, name the gap. Omit it for a config issue that is not a test-environment gap. " +
|
|
493
|
-
ENVIRONMENT_FAMILY_MEANINGS),
|
|
494
391
|
})
|
|
495
392
|
.superRefine((issue, ctx) => {
|
|
496
|
-
// SKYR-4555: the family names a test-environment gap, which only a config
|
|
497
|
-
// entry reports. On a bug, lint or type entry it would put the issue in two
|
|
498
|
-
// places at once.
|
|
499
|
-
if (issue.environmentFamily !== undefined &&
|
|
500
|
-
issue.category !== IssueFoundCategory.Config) {
|
|
501
|
-
ctx.addIssue({
|
|
502
|
-
code: "custom",
|
|
503
|
-
path: ["environmentFamily"],
|
|
504
|
-
message: `environmentFamily is allowed only when category is 'config'. It names the test-environment gap behind a config issue. ` +
|
|
505
|
-
`Remove environmentFamily from this '${issue.category}' entry, or set category to 'config' if the issue is a test-environment gap.`,
|
|
506
|
-
});
|
|
507
|
-
}
|
|
508
393
|
// A `bug` entry asserts that product code is wrong, so it has to name the code:
|
|
509
394
|
// without a file the entry is prose a reviewer cannot act on, and nothing
|
|
510
395
|
// downstream can place it in the diff. Only the file is required — a config file
|
|
@@ -2865,10 +2750,7 @@ export function registerSubmitReportTool(server) {
|
|
|
2865
2750
|
// these objects and don't expect it).
|
|
2866
2751
|
testResults: params.testResults.map((row) => {
|
|
2867
2752
|
const { testFilePath: _tfp, ...wire } = attachVideoPath(row, stateData.executionVideos);
|
|
2868
|
-
|
|
2869
|
-
// testMaintenance (consumers compare it with the basename of their
|
|
2870
|
-
// fileName, which may be a relative path); the path stays internal.
|
|
2871
|
-
return { ...wire, fileName: path.basename(row.testFilePath) };
|
|
2753
|
+
return wire;
|
|
2872
2754
|
}),
|
|
2873
2755
|
issuesFound,
|
|
2874
2756
|
executionCoverage,
|
|
@@ -6,6 +6,7 @@ import { logger } from "../../utils/logger.js";
|
|
|
6
6
|
import { canonicalJson, isPlainObject } from "../../utils/canonicalJson.js";
|
|
7
7
|
import { StateManager, resolveOwnRunStatePath, } from "../../utils/AnalysisStateManager.js";
|
|
8
8
|
import { toolError } from "../../utils/utils.js";
|
|
9
|
+
import { AZURE_REPOSITORY_SLUG, isRepositorySlug, } from "../../utils/repositorySlug.js";
|
|
9
10
|
import { TESTBOT_SUPPORTED_TEST_TYPES } from "../../types/TestTypes.js";
|
|
10
11
|
import { changedFileNames, computeBranchDiff, } from "../../utils/branchDiff.js";
|
|
11
12
|
import { collectChangedRuns } from "../../utils/changedRuns.js";
|
|
@@ -95,16 +96,27 @@ const registerStepSchema = z
|
|
|
95
96
|
});
|
|
96
97
|
}
|
|
97
98
|
});
|
|
98
|
-
/**
|
|
99
|
-
* a run
|
|
99
|
+
/** An `org/project/repo` slug is right only on a run whose own repository is one:
|
|
100
|
+
* on a GitHub run it is a typo, which the shape alone cannot tell. */
|
|
101
|
+
function threePartSlugOffRun(params, runRepository) {
|
|
102
|
+
if (runRepository && AZURE_REPOSITORY_SLUG.test(runRepository)) {
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
return [...(params.plannedTests ?? []), ...(params.maintains ?? [])]
|
|
106
|
+
.map((entry) => entry?.repository)
|
|
107
|
+
.find((repository) => typeof repository === "string" && repository.split("/").length === 3);
|
|
108
|
+
}
|
|
109
|
+
/** The `owner/repo` (or Azure DevOps `org/project/repo`) a plan entry names. The
|
|
110
|
+
* SHAPE is checked, not just "not blank": a run keys each repository by the slug
|
|
111
|
+
* read off its origin remote. */
|
|
100
112
|
function repositorySlugField(subject, purpose) {
|
|
101
113
|
return z
|
|
102
114
|
.string()
|
|
103
115
|
.trim()
|
|
104
|
-
.
|
|
105
|
-
message: `${subject}'s repository must be an \`owner/repo\` slug`,
|
|
116
|
+
.refine(isRepositorySlug, {
|
|
117
|
+
message: `${subject}'s repository must be an \`owner/repo\` slug, or \`org/project/repo\` on Azure DevOps`,
|
|
106
118
|
})
|
|
107
|
-
.describe(`The
|
|
119
|
+
.describe(`The repository slug, \`owner/repo\` (\`org/project/repo\` on Azure DevOps), ${purpose}.`);
|
|
108
120
|
}
|
|
109
121
|
/** The `changes` and `defects` id lists a plan entry cites, written once. The two
|
|
110
122
|
* copies drifted whenever either was edited. */
|
|
@@ -902,7 +914,7 @@ export function registerRegisterTestPlanTool(server) {
|
|
|
902
914
|
},
|
|
903
915
|
description: "Register your complete test plan for this run, with the changes that make each planned test checkable. " +
|
|
904
916
|
"The planning procedure in your prompt says how to decide and declare the plan. " +
|
|
905
|
-
"Every registration the schema accepts is stored, and no check ever refuses one. " +
|
|
917
|
+
"Every registration the schema accepts is stored, and no check ever refuses one, except an Azure DevOps `org/project/repo` on a run that is not on Azure DevOps. " +
|
|
906
918
|
"The schema refuses the whole call, and names the field and the rule that refused it. " +
|
|
907
919
|
"An undeclared key is refused by name on a planned test and on `declarations`, so a declaration written one level too high is corrected rather than dropped in silence. " +
|
|
908
920
|
"Checks run against the stored plan and return objections. Fix one and register it again as often as you like, or answer it by id through `answers`. " +
|
|
@@ -926,6 +938,10 @@ export function registerRegisterTestPlanTool(server) {
|
|
|
926
938
|
if (!fullState) {
|
|
927
939
|
return toolError(`State file is empty or invalid: ${stateFile}. Call skyramp_analyze_changes first to generate a valid state file.`);
|
|
928
940
|
}
|
|
941
|
+
const offRun = threePartSlugOffRun(params, fullState.metadata?.repository);
|
|
942
|
+
if (offRun) {
|
|
943
|
+
return toolError(`\`${offRun}\`: this run's repository must be an \`owner/repo\` slug; \`org/project/repo\` names an Azure DevOps repository, and this run is not on Azure DevOps.`);
|
|
944
|
+
}
|
|
929
945
|
const registrationNumber = (fullState.registrationCount ?? 0) + 1;
|
|
930
946
|
const registration = buildRegistration(params, registrationNumber);
|
|
931
947
|
// The previous registration's answers are already on the stored plan, which
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/server";
|
|
2
|
-
|
|
2
|
+
import { type ScanRelatedRepository } from "../../prompts/initialize-workspace/initializeWorkspacePrompt.js";
|
|
3
|
+
/** What a scan token was issued for: when it expires, and the related
|
|
4
|
+
* repositories its scan named, which skyramp_init_workspace verifies the
|
|
5
|
+
* services against. */
|
|
6
|
+
export interface ScanGrant {
|
|
7
|
+
expiry: number;
|
|
8
|
+
relatedRepositories: ScanRelatedRepository[];
|
|
9
|
+
}
|
|
10
|
+
export declare function issueScanToken(relatedRepositories?: ScanRelatedRepository[]): string;
|
|
11
|
+
/** The grant of a valid, unexpired token, without consuming it. */
|
|
12
|
+
export declare function scanTokenGrant(token: string): ScanGrant | undefined;
|
|
3
13
|
export declare function validateAndConsumeScanToken(token: string): boolean;
|
|
4
14
|
export declare function registerInitScanWorkspaceTool(server: McpServer): void;
|
|
@@ -1,24 +1,33 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { randomUUID } from "crypto";
|
|
3
|
+
import fs from "fs";
|
|
3
4
|
import { WorkspaceConfigManager } from "../../workspace/workspace.js";
|
|
5
|
+
import { AZURE_REPOSITORY_SLUG, resolveRealPath, tryRepositorySlug, } from "../../utils/repositorySlug.js";
|
|
4
6
|
import { logger } from "../../utils/logger.js";
|
|
5
7
|
import { AnalyticsService } from "../../services/AnalyticsService.js";
|
|
6
|
-
import {
|
|
8
|
+
import { buildInitWorkspaceInstructions, } from "../../prompts/initialize-workspace/initializeWorkspacePrompt.js";
|
|
7
9
|
const TOOL_NAME = "skyramp_init_scan";
|
|
8
|
-
// Token store: token -> expiry timestamp (ms)
|
|
9
10
|
const scanTokens = new Map();
|
|
10
11
|
const TOKEN_TTL_MS = 10 * 60 * 1000; // 10 minutes
|
|
11
|
-
export function issueScanToken() {
|
|
12
|
+
export function issueScanToken(relatedRepositories = []) {
|
|
12
13
|
const token = randomUUID();
|
|
13
|
-
scanTokens.set(token,
|
|
14
|
+
scanTokens.set(token, {
|
|
15
|
+
expiry: Date.now() + TOKEN_TTL_MS,
|
|
16
|
+
relatedRepositories,
|
|
17
|
+
});
|
|
14
18
|
return token;
|
|
15
19
|
}
|
|
20
|
+
/** The grant of a valid, unexpired token, without consuming it. */
|
|
21
|
+
export function scanTokenGrant(token) {
|
|
22
|
+
const grant = scanTokens.get(token);
|
|
23
|
+
return grant && Date.now() < grant.expiry ? grant : undefined;
|
|
24
|
+
}
|
|
16
25
|
export function validateAndConsumeScanToken(token) {
|
|
17
|
-
const
|
|
18
|
-
if (!
|
|
26
|
+
const grant = scanTokens.get(token);
|
|
27
|
+
if (!grant)
|
|
19
28
|
return false;
|
|
20
29
|
scanTokens.delete(token);
|
|
21
|
-
return Date.now() < expiry;
|
|
30
|
+
return Date.now() < grant.expiry;
|
|
22
31
|
}
|
|
23
32
|
const initScanWorkspaceSchema = z.object({
|
|
24
33
|
workspacePath: z.string().describe("Path to the workspace/repository root"),
|
|
@@ -26,12 +35,77 @@ const initScanWorkspaceSchema = z.object({
|
|
|
26
35
|
.boolean()
|
|
27
36
|
.default(false)
|
|
28
37
|
.describe("Set to true to force a re-scan even if .skyramp/workspace.yml already exists. Default is false."),
|
|
38
|
+
relatedRepositories: z
|
|
39
|
+
.array(z.object({
|
|
40
|
+
repositoryPath: z
|
|
41
|
+
.string()
|
|
42
|
+
.describe("Path to the related repository's checkout"),
|
|
43
|
+
repository: z
|
|
44
|
+
.string()
|
|
45
|
+
.optional()
|
|
46
|
+
.describe("The related repository's owner/repo, or org/project/repo when workspacePath is on Azure DevOps. Defaults to the one its checkout's origin remote names."),
|
|
47
|
+
}))
|
|
48
|
+
.optional()
|
|
49
|
+
.describe("The other repositories of a multi-repository Target. Each is scanned into the one workspace.yml at workspacePath, its services carrying its owner/repo (org/project/repo on Azure DevOps) as `repository`; none gets a workspace.yml of its own. Omit for a single repository."),
|
|
50
|
+
testRepositoryPath: z
|
|
51
|
+
.string()
|
|
52
|
+
.optional()
|
|
53
|
+
.describe("Path to the separate test repository generated tests are delivered into, when there is one. Each service's testDirectory is then read from, and relative to, that checkout."),
|
|
29
54
|
});
|
|
55
|
+
const OWNER_REPO = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
|
|
56
|
+
/** `owner/repo`, or `org/project/repo` when the workspace itself is on Azure
|
|
57
|
+
* DevOps: on any other workspace a three-part name is a typo. */
|
|
58
|
+
function isRelatedRepositorySlug(repository, workspaceRepository) {
|
|
59
|
+
if (OWNER_REPO.test(repository))
|
|
60
|
+
return true;
|
|
61
|
+
return (!!workspaceRepository &&
|
|
62
|
+
AZURE_REPOSITORY_SLUG.test(workspaceRepository) &&
|
|
63
|
+
AZURE_REPOSITORY_SLUG.test(repository));
|
|
64
|
+
}
|
|
65
|
+
function isDirectory(p) {
|
|
66
|
+
try {
|
|
67
|
+
return fs.statSync(p).isDirectory();
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Each related repository with its owner/repo, or the first problem with them:
|
|
75
|
+
* a checkout that is not a directory, is the workspace itself, or names no
|
|
76
|
+
* owner/repo, and a repository listed twice. Paths compare with symlinks
|
|
77
|
+
* resolved, and owner/repo case-insensitively, as GitHub does.
|
|
78
|
+
*/
|
|
79
|
+
async function resolveRelatedRepositories(workspacePath, entries) {
|
|
80
|
+
const repos = [];
|
|
81
|
+
const workspaceReal = await resolveRealPath(workspacePath);
|
|
82
|
+
const workspaceRepository = await tryRepositorySlug(workspacePath);
|
|
83
|
+
for (const entry of entries) {
|
|
84
|
+
const at = entry.repositoryPath;
|
|
85
|
+
if (!isDirectory(at))
|
|
86
|
+
return { error: `relatedRepositories: ${at} is not a directory` };
|
|
87
|
+
if ((await resolveRealPath(at)) === workspaceReal)
|
|
88
|
+
return {
|
|
89
|
+
error: `relatedRepositories: ${at} is workspacePath itself; list only the other repositories`,
|
|
90
|
+
};
|
|
91
|
+
const repository = entry.repository?.trim() || (await tryRepositorySlug(at));
|
|
92
|
+
if (!repository ||
|
|
93
|
+
!isRelatedRepositorySlug(repository, workspaceRepository))
|
|
94
|
+
return {
|
|
95
|
+
error: `relatedRepositories: no owner/repo for ${at}${repository ? ` ("${repository}" is not owner/repo, or org/project/repo on an Azure DevOps workspace)` : " (its checkout has no origin remote)"}; pass its repository`,
|
|
96
|
+
};
|
|
97
|
+
if (repos.some((r) => r.repository.toLowerCase() === repository.toLowerCase()))
|
|
98
|
+
return { error: `relatedRepositories: ${repository} is listed twice` };
|
|
99
|
+
repos.push({ repository, repositoryPath: at });
|
|
100
|
+
}
|
|
101
|
+
return { repos };
|
|
102
|
+
}
|
|
30
103
|
export function registerInitScanWorkspaceTool(server) {
|
|
31
104
|
server.registerTool(TOOL_NAME, {
|
|
32
105
|
description: "Call this FIRST, before any other Skyramp tool (test generation, execution, analysis), whenever workspacePath is a git repository and .skyramp/workspace.yml does not exist at its root. " +
|
|
33
106
|
"PREREQUISITE for skyramp_init_workspace. " +
|
|
34
107
|
"Scans a repository and returns instructions for discovering all services, along with a scanToken. " +
|
|
108
|
+
"For a Target spanning several repositories, call it ONCE with the others in relatedRepositories, so all their services go into one workspace.yml. " +
|
|
35
109
|
"Follow the instructions to build the services array, then pass both to skyramp_init_workspace to create the workspace config.",
|
|
36
110
|
inputSchema: initScanWorkspaceSchema,
|
|
37
111
|
}, async (params) => {
|
|
@@ -39,6 +113,8 @@ export function registerInitScanWorkspaceTool(server) {
|
|
|
39
113
|
const recordParams = {
|
|
40
114
|
workspacePath: params.workspacePath,
|
|
41
115
|
force: String(params.force || false),
|
|
116
|
+
relatedRepositories: String(params.relatedRepositories?.length ?? 0),
|
|
117
|
+
testRepository: String(Boolean(params.testRepositoryPath)),
|
|
42
118
|
};
|
|
43
119
|
try {
|
|
44
120
|
const manager = new WorkspaceConfigManager(params.workspacePath);
|
|
@@ -53,12 +129,38 @@ export function registerInitScanWorkspaceTool(server) {
|
|
|
53
129
|
isError: false,
|
|
54
130
|
};
|
|
55
131
|
}
|
|
56
|
-
const
|
|
132
|
+
const related = await resolveRelatedRepositories(params.workspacePath, params.relatedRepositories ?? []);
|
|
133
|
+
if ("error" in related) {
|
|
134
|
+
errorResult = {
|
|
135
|
+
content: [{ type: "text", text: related.error }],
|
|
136
|
+
isError: true,
|
|
137
|
+
};
|
|
138
|
+
return errorResult;
|
|
139
|
+
}
|
|
140
|
+
if (params.testRepositoryPath &&
|
|
141
|
+
!isDirectory(params.testRepositoryPath)) {
|
|
142
|
+
errorResult = {
|
|
143
|
+
content: [
|
|
144
|
+
{
|
|
145
|
+
type: "text",
|
|
146
|
+
text: `testRepositoryPath: ${params.testRepositoryPath} is not a directory`,
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
isError: true,
|
|
150
|
+
};
|
|
151
|
+
return errorResult;
|
|
152
|
+
}
|
|
153
|
+
const token = issueScanToken(related.repos);
|
|
154
|
+
const instructions = buildInitWorkspaceInstructions({
|
|
155
|
+
workspacePath: params.workspacePath,
|
|
156
|
+
relatedRepositories: related.repos,
|
|
157
|
+
testRepositoryPath: params.testRepositoryPath,
|
|
158
|
+
});
|
|
57
159
|
return {
|
|
58
160
|
content: [
|
|
59
161
|
{
|
|
60
162
|
type: "text",
|
|
61
|
-
text: `SCAN TOKEN: ${token}\n\nSave this token — you MUST pass it as scanToken when calling skyramp_init_workspace.\n\n${
|
|
163
|
+
text: `SCAN TOKEN: ${token}\n\nSave this token — you MUST pass it as scanToken when calling skyramp_init_workspace.\n\n${instructions}`,
|
|
62
164
|
},
|
|
63
165
|
],
|
|
64
166
|
isError: false,
|
|
@@ -7,7 +7,8 @@ import yaml from "js-yaml";
|
|
|
7
7
|
import { logger } from "../../utils/logger.js";
|
|
8
8
|
import { AnalyticsService } from "../../services/AnalyticsService.js";
|
|
9
9
|
import { SKYRAMP_IMAGE_VERSION } from "../../utils/versions.js";
|
|
10
|
-
import { validateAndConsumeScanToken } from "./initScanWorkspaceTool.js";
|
|
10
|
+
import { scanTokenGrant, validateAndConsumeScanToken, } from "./initScanWorkspaceTool.js";
|
|
11
|
+
import { verifyWorkspaceServices } from "../../workspace/workspaceVerifier.js";
|
|
11
12
|
import { upsertServicesByRepo } from "./serviceUpsert.js";
|
|
12
13
|
import { isInsideDir } from "../../utils/gitStaging.js";
|
|
13
14
|
import { EXECUTOR_VIDEOS_DIR, EXECUTOR_WORK_DIR, isInsideExecutorWorkDir, } from "../../utils/executorWorkDir.js";
|
|
@@ -110,7 +111,7 @@ export function registerInitializeWorkspaceTool(server) {
|
|
|
110
111
|
"1. Fresh init: PREREQUISITE: Call skyramp_init_scan first to get a scanToken and discover services. " +
|
|
111
112
|
"Then call this tool to write .skyramp/workspace.yml with the discovered services and scanToken. " +
|
|
112
113
|
"2. Edit existing workspace.yml: omit scanToken, set force: true, and pass the FULL updated services array (overwrite, not merge — fields not in the array are dropped). " +
|
|
113
|
-
"The services schema is strict: any field not in the schema (e.g. inventing `
|
|
114
|
+
"The services schema is strict: any field not in the schema (e.g. inventing `healthUrl` on `runtimeDetails`) fails validation and the call is rejected.",
|
|
114
115
|
inputSchema: initializeWorkspaceSchema,
|
|
115
116
|
_meta: {
|
|
116
117
|
keywords: [
|
|
@@ -182,11 +183,46 @@ export function registerInitializeWorkspaceTool(server) {
|
|
|
182
183
|
if (badMerged)
|
|
183
184
|
return badTestDirectoryResult(badMerged);
|
|
184
185
|
}
|
|
186
|
+
// The verifier, before the token is spent: a refusal asks for a
|
|
187
|
+
// corrected retry, which must be able to reuse the same token. Only the
|
|
188
|
+
// incoming services: a merge must not be blocked by an older entry.
|
|
189
|
+
const grant = params.scanToken
|
|
190
|
+
? scanTokenGrant(params.scanToken)
|
|
191
|
+
: undefined;
|
|
192
|
+
const issues = verifyWorkspaceServices(params.services, {
|
|
193
|
+
workspacePath,
|
|
194
|
+
relatedRepositories: grant?.relatedRepositories,
|
|
195
|
+
});
|
|
196
|
+
if (issues.length > 0) {
|
|
197
|
+
return {
|
|
198
|
+
content: [
|
|
199
|
+
{
|
|
200
|
+
type: "text",
|
|
201
|
+
text: `${manager.getConfigPath()} was not written: ${issues.length} problem(s) with the services.\n${issues.map((i) => `- ${i}`).join("\n")}\nFix each one and call skyramp_init_workspace again with the full corrected services array${grant ? " and the same scanToken, which is still valid" : ""}.`,
|
|
202
|
+
},
|
|
203
|
+
],
|
|
204
|
+
isError: true,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
185
207
|
// scanToken is required for fresh init; for edits (force:true on an
|
|
186
208
|
// existing workspace.yml) the caller supplies the full services array
|
|
187
|
-
// directly and
|
|
209
|
+
// directly and no token is needed. A token passed with an edit is still
|
|
210
|
+
// validated and spent, so a successful write never leaves it reusable.
|
|
188
211
|
const isEdit = alreadyExists && params.force;
|
|
189
|
-
if (
|
|
212
|
+
if (isEdit && params.scanToken) {
|
|
213
|
+
if (!validateAndConsumeScanToken(params.scanToken)) {
|
|
214
|
+
return {
|
|
215
|
+
content: [
|
|
216
|
+
{
|
|
217
|
+
type: "text",
|
|
218
|
+
text: "Invalid or expired scanToken. An edit of an existing workspace.yml needs none: call again with force: true and no scanToken.",
|
|
219
|
+
},
|
|
220
|
+
],
|
|
221
|
+
isError: true,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
else if (!isEdit) {
|
|
190
226
|
if (!params.scanToken) {
|
|
191
227
|
return {
|
|
192
228
|
content: [
|
|
@@ -45,6 +45,9 @@ export interface AssertionRecord {
|
|
|
45
45
|
baselineCount?: number;
|
|
46
46
|
baselineStrength?: number;
|
|
47
47
|
baselineFingerprints?: string[];
|
|
48
|
+
/** Integration-teardown `tracker.track_for_cleanup(` lines at hand-out — must survive
|
|
49
|
+
* enhancement verbatim (verify hard-fails otherwise). */
|
|
50
|
+
baselineCreatedAddLines?: string[];
|
|
48
51
|
issuedAt?: string;
|
|
49
52
|
/** Verdict of the last verify pass; absent until one runs. */
|
|
50
53
|
verification?: AssertionVerdict;
|
|
@@ -13,26 +13,6 @@ export declare enum IssueFoundCategory {
|
|
|
13
13
|
Type = "type",
|
|
14
14
|
Config = "config"
|
|
15
15
|
}
|
|
16
|
-
/** Why a Fail or Skipped testResults row did not pass. Application is the code
|
|
17
|
-
* under test (or a test written to prove a defect, failing as intended);
|
|
18
|
-
* Environment is the test environment stopping the test before it reached the
|
|
19
|
-
* behaviour; TestDefect is the generated test itself being wrong. */
|
|
20
|
-
export declare enum FailureCauseKind {
|
|
21
|
-
Application = "application",
|
|
22
|
-
Environment = "environment",
|
|
23
|
-
TestDefect = "test_defect"
|
|
24
|
-
}
|
|
25
|
-
/** Which test-environment gap stopped a test: the `family` of an Environment
|
|
26
|
-
* failureCause, and the `environmentFamily` of a config issuesFound entry. */
|
|
27
|
-
export declare enum EnvironmentFamily {
|
|
28
|
-
AuthToken = "auth_token",
|
|
29
|
-
Secret = "secret",
|
|
30
|
-
DataConnection = "data_connection",
|
|
31
|
-
AppNotStarted = "app_not_started",
|
|
32
|
-
Login = "login",
|
|
33
|
-
ExternalDependency = "external_dependency",
|
|
34
|
-
RecordingReplay = "recording_replay"
|
|
35
|
-
}
|
|
36
16
|
/**
|
|
37
17
|
* Shape of the JSON report written by skyramp_submit_report and read by testbot
|
|
38
18
|
* for rendering as Markdown. All fields mirror the corresponding Zod schemas in
|
|
@@ -124,19 +104,6 @@ export interface TestbotReport {
|
|
|
124
104
|
endpoint: string;
|
|
125
105
|
status: "Pass" | "Fail" | "Skipped";
|
|
126
106
|
details: string;
|
|
127
|
-
/** Server-derived: the basename of the `testFilePath` the agent submitted,
|
|
128
|
-
* which never reaches the report. The join key to `newTestsCreated` and
|
|
129
|
-
* `testMaintenance`: match it against the basename of their `fileName`
|
|
130
|
-
* (a `newTestsCreated` row may carry a repository-relative path), plus
|
|
131
|
-
* `repository`. */
|
|
132
|
-
fileName: string;
|
|
133
|
-
/** Why a Fail or Skipped row did not pass. The submit schema requires it on
|
|
134
|
-
* those rows and refuses it on a Pass row; `family` is set exactly when
|
|
135
|
-
* `kind` is Environment. Absent in reports from MCP versions predating it. */
|
|
136
|
-
failureCause?: {
|
|
137
|
-
kind: FailureCauseKind;
|
|
138
|
-
family?: EnvironmentFamily;
|
|
139
|
-
};
|
|
140
107
|
videoPath?: string;
|
|
141
108
|
/** See newTestsCreated[].repository. */
|
|
142
109
|
repository: string;
|
|
@@ -191,10 +158,6 @@ export interface TestbotReport {
|
|
|
191
158
|
sourceFile?: string;
|
|
192
159
|
sourceSymbol?: string;
|
|
193
160
|
sourceLine?: number;
|
|
194
|
-
/** On a `config` entry that is a test-environment gap, which gap. The submit
|
|
195
|
-
* schema refuses it on any other category. Optional: a config entry need
|
|
196
|
-
* not be an environment gap, and older MCP versions never set it. */
|
|
197
|
-
environmentFamily?: EnvironmentFamily;
|
|
198
161
|
/** See newTestsCreated[].repository. */
|
|
199
162
|
repository: string;
|
|
200
163
|
}[];
|
|
@@ -8,25 +8,3 @@ export var IssueFoundCategory;
|
|
|
8
8
|
IssueFoundCategory["Type"] = "type";
|
|
9
9
|
IssueFoundCategory["Config"] = "config";
|
|
10
10
|
})(IssueFoundCategory || (IssueFoundCategory = {}));
|
|
11
|
-
/** Why a Fail or Skipped testResults row did not pass. Application is the code
|
|
12
|
-
* under test (or a test written to prove a defect, failing as intended);
|
|
13
|
-
* Environment is the test environment stopping the test before it reached the
|
|
14
|
-
* behaviour; TestDefect is the generated test itself being wrong. */
|
|
15
|
-
export var FailureCauseKind;
|
|
16
|
-
(function (FailureCauseKind) {
|
|
17
|
-
FailureCauseKind["Application"] = "application";
|
|
18
|
-
FailureCauseKind["Environment"] = "environment";
|
|
19
|
-
FailureCauseKind["TestDefect"] = "test_defect";
|
|
20
|
-
})(FailureCauseKind || (FailureCauseKind = {}));
|
|
21
|
-
/** Which test-environment gap stopped a test: the `family` of an Environment
|
|
22
|
-
* failureCause, and the `environmentFamily` of a config issuesFound entry. */
|
|
23
|
-
export var EnvironmentFamily;
|
|
24
|
-
(function (EnvironmentFamily) {
|
|
25
|
-
EnvironmentFamily["AuthToken"] = "auth_token";
|
|
26
|
-
EnvironmentFamily["Secret"] = "secret";
|
|
27
|
-
EnvironmentFamily["DataConnection"] = "data_connection";
|
|
28
|
-
EnvironmentFamily["AppNotStarted"] = "app_not_started";
|
|
29
|
-
EnvironmentFamily["Login"] = "login";
|
|
30
|
-
EnvironmentFamily["ExternalDependency"] = "external_dependency";
|
|
31
|
-
EnvironmentFamily["RecordingReplay"] = "recording_replay";
|
|
32
|
-
})(EnvironmentFamily || (EnvironmentFamily = {}));
|
package/build/types/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export { TestExecutionStatus } from "./TestExecution.js";
|
|
|
2
2
|
export { DriftAction } from "./TestAnalysis.js";
|
|
3
3
|
export { TestType, HttpMethod } from "./TestTypes.js";
|
|
4
4
|
export type { TestbotReport } from "./TestbotReport.js";
|
|
5
|
-
export { IssueFoundCategory
|
|
5
|
+
export { IssueFoundCategory } from "./TestbotReport.js";
|
|
6
6
|
export { ReuseDeclinedBy, ReuseVerificationOutcome, HelperVerificationOutcome, } from "./ReuseOutcome.js";
|
|
7
7
|
export type { ReuseOutcome, ReuseSkippedEntry, ReuseMissedEntry, HelperReuseOutcome, RetrofitOutcome, ChangedHelperEntry, ChangedHelperCaller, OrphanedHelper, } from "./ReuseOutcome.js";
|
|
8
8
|
export type { RelatedRepository, TestbotPromptOptions, } from "./TestbotPromptOptions.js";
|
package/build/types/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { TestExecutionStatus } from "./TestExecution.js";
|
|
2
2
|
export { DriftAction } from "./TestAnalysis.js";
|
|
3
3
|
export { TestType, HttpMethod } from "./TestTypes.js";
|
|
4
|
-
export { IssueFoundCategory
|
|
4
|
+
export { IssueFoundCategory } from "./TestbotReport.js";
|
|
5
5
|
export { ReuseDeclinedBy, ReuseVerificationOutcome, HelperVerificationOutcome, } from "./ReuseOutcome.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { LintFinding } from "./lint-types.js";
|
|
2
|
+
/** Each whole `tracker.track_for_cleanup(...)` / `tracker.trackForCleanup(...)`
|
|
3
|
+
* call of a file, in order — the baseline the enhanced file is compared
|
|
4
|
+
* against. */
|
|
5
|
+
export declare function collectCreatedAddLines(content: string): string[];
|
|
6
|
+
/**
|
|
7
|
+
* Hard findings when the enhanced file's registration lines differ from the
|
|
8
|
+
* baseline's (sequence must match exactly: same lines, same count, same order)
|
|
9
|
+
* or when anything but blank or comment lines sits between an id capture and
|
|
10
|
+
* its registration. With no baseline only the adjacency check runs.
|
|
11
|
+
*/
|
|
12
|
+
export declare function lintCreatedAddLines(content: string, baselineLines?: string[]): LintFinding[];
|