@skyramp/mcp 0.3.2-rc.pom-3 → 0.3.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/commands/commandLibrary.d.ts +1 -0
- package/build/commands/commandLibrary.js +19 -13
- package/build/commands/localDevTestChangesCommand.d.ts +15 -0
- package/build/commands/localDevTestChangesCommand.js +201 -0
- package/build/index.js +80 -6
- package/build/prompts/code-reuse.js +3 -0
- package/build/prompts/enhance-assertions/sharedAssertionRules.d.ts +1 -0
- package/build/prompts/enhance-assertions/sharedAssertionRules.js +17 -0
- package/build/prompts/initialize-workspace/initializeWorkspacePrompt.js +11 -9
- package/build/prompts/local-dev/local-dev-plan.d.ts +35 -0
- package/build/prompts/local-dev/local-dev-plan.js +429 -0
- package/build/prompts/local-dev/local-dev-prompts.d.ts +4 -0
- package/build/prompts/local-dev/local-dev-prompts.js +190 -0
- package/build/prompts/pom-aware-code-reuse.js +12 -0
- package/build/prompts/prompt-utils.d.ts +8 -0
- package/build/prompts/prompt-utils.js +33 -0
- package/build/prompts/sut-setup/modes/adaptWorkflowPrompt.js +37 -14
- package/build/prompts/sut-setup/shared.js +16 -12
- package/build/prompts/test-recommendation/analysisOutputPrompt.js +21 -29
- package/build/prompts/test-recommendation/scopeAssessment.d.ts +24 -7
- package/build/prompts/test-recommendation/scopeAssessment.js +111 -12
- package/build/prompts/test-recommendation/test-recommendation-prompt.js +41 -4
- package/build/prompts/testbot/testbot-prompts.d.ts +0 -5
- package/build/prompts/testbot/testbot-prompts.js +32 -52
- package/build/recommendation/planRanker.d.ts +15 -2
- package/build/recommendation/planRanker.js +76 -5
- package/build/resources/testbotResource.js +2 -1
- package/build/services/AnalyticsService.d.ts +1 -1
- package/build/services/TestExecutionService.d.ts +2 -1
- package/build/services/TestExecutionService.js +8 -3
- package/build/services/TestGenerationService.d.ts +2 -2
- package/build/services/TestGenerationService.js +39 -21
- package/build/services/containerEnv.js +3 -1
- package/build/tool-phases.js +6 -0
- package/build/tools/code-refactor/codeReuseTool.js +43 -4
- package/build/tools/code-refactor/enhanceAssertionsTool.js +68 -18
- package/build/tools/code-refactor/reuse-outcome.d.ts +109 -0
- package/build/tools/code-refactor/reuse-outcome.js +158 -0
- package/build/tools/code-refactor/reuse-state.d.ts +45 -0
- package/build/tools/code-refactor/reuse-state.js +140 -0
- package/build/tools/enrichTestWithMocksTool.d.ts +28 -0
- package/build/tools/enrichTestWithMocksTool.js +726 -0
- package/build/tools/executeSkyrampTestTool.d.ts +11 -0
- package/build/tools/executeSkyrampTestTool.js +62 -21
- package/build/tools/generate-tests/batchMockGenerationTool.d.ts +106 -0
- package/build/tools/generate-tests/batchMockGenerationTool.js +545 -0
- package/build/tools/generate-tests/generateBatchScenarioRestTool.js +25 -23
- package/build/tools/generate-tests/generateContractRestTool.js +2 -2
- package/build/tools/generate-tests/generateE2ERestTool.d.ts +1 -1
- package/build/tools/generate-tests/generateE2ERestTool.js +1 -1
- package/build/tools/generate-tests/generateLoadRestTool.d.ts +1 -1
- package/build/tools/generate-tests/generateLoadRestTool.js +1 -1
- package/build/tools/generate-tests/generateMockRestTool.d.ts +179 -6
- package/build/tools/generate-tests/generateMockRestTool.js +391 -22
- package/build/tools/generate-tests/loadTestSchema.d.ts +1 -1
- package/build/tools/generate-tests/planGuard.js +2 -22
- package/build/tools/generateEnrichedIntegrationTestTool.d.ts +25 -0
- package/build/tools/generateEnrichedIntegrationTestTool.js +235 -0
- package/build/tools/localDevWorkerComposeTool.d.ts +24 -0
- package/build/tools/localDevWorkerComposeTool.js +264 -0
- package/build/tools/one-click/oneClickTool.d.ts +13 -0
- package/build/tools/one-click/oneClickTool.js +195 -24
- package/build/tools/preflightMockCheckTool.d.ts +2 -0
- package/build/tools/preflightMockCheckTool.js +96 -0
- package/build/tools/queryProxyMocksTool.d.ts +70 -0
- package/build/tools/queryProxyMocksTool.js +522 -0
- package/build/tools/runExistingTestsTool.d.ts +31 -0
- package/build/tools/runExistingTestsTool.js +214 -13
- package/build/tools/submitReportTool.js +38 -3
- package/build/tools/test-management/analyzeChangesTool.d.ts +2 -1
- package/build/tools/test-management/analyzeChangesTool.js +63 -40
- package/build/tools/test-management/registerTestPlanTool.js +55 -7
- package/build/tools/trace/startTraceCollectionTool.js +3 -3
- package/build/types/FrontendIntegration.d.ts +3 -0
- package/build/types/FrontendIntegration.js +3 -0
- package/build/types/OneClickCommands.d.ts +1 -1
- package/build/types/Recommendation.d.ts +20 -0
- package/build/types/Recommendation.js +32 -6
- package/build/types/RepositoryAnalysis.d.ts +131 -14
- package/build/types/RepositoryAnalysis.js +16 -2
- package/build/types/ReuseOutcome.d.ts +63 -0
- package/build/types/ReuseOutcome.js +33 -0
- package/build/types/TestExecution.d.ts +1 -0
- package/build/types/TestTypes.d.ts +25 -7
- package/build/types/TestTypes.js +22 -6
- package/build/types/TestbotReport.d.ts +7 -0
- package/build/types/index.d.ts +2 -0
- package/build/types/index.js +1 -0
- package/build/utils/AnalysisStateManager.d.ts +26 -0
- package/build/utils/AnalysisStateManager.js +31 -1
- package/build/utils/analyze-openapi.js +18 -1
- package/build/utils/branchDiff.d.ts +17 -1
- package/build/utils/branchDiff.js +99 -14
- package/build/utils/featureFlags.d.ts +31 -0
- package/build/utils/featureFlags.js +37 -0
- package/build/utils/frontendIntegration.js +8 -1
- package/build/utils/grpcMockValidation.d.ts +1 -0
- package/build/utils/grpcMockValidation.js +49 -0
- package/build/utils/httpMethodValidation.d.ts +4 -0
- package/build/utils/httpMethodValidation.js +15 -0
- package/build/utils/logger.js +1 -1
- package/build/utils/mockCompatibility.d.ts +49 -0
- package/build/utils/mockCompatibility.js +82 -0
- package/build/utils/pom-scope/pom-files.js +7 -0
- package/build/utils/pom-verify/bindings.js +11 -1
- package/build/utils/pom-verify/re-exports.d.ts +10 -0
- package/build/utils/pom-verify/re-exports.js +61 -0
- package/build/utils/pom-verify/resolve.d.ts +4 -1
- package/build/utils/pom-verify/resolve.js +7 -4
- package/build/utils/pom-verify/verify.d.ts +5 -0
- package/build/utils/pom-verify/verify.js +25 -3
- package/build/utils/progress.js +10 -5
- package/build/utils/proxy-terminal.js +3 -3
- package/build/utils/routeParsers.d.ts +3 -9
- package/build/utils/routeParsers.js +79 -4
- package/build/utils/utils.js +2 -2
- package/build/utils/versions.d.ts +4 -3
- package/build/utils/versions.js +3 -1
- package/build/utils/workspaceAuth.d.ts +46 -0
- package/build/utils/workspaceAuth.js +156 -1
- package/build/workspace/testSuites.d.ts +2 -1
- package/build/workspace/testSuites.js +1 -1
- package/build/workspace/workspace.d.ts +108 -32
- package/build/workspace/workspace.js +32 -4
- package/package.json +5 -2
- package/build/adapters/jestAdapter.test.d.ts +0 -1
- package/build/adapters/jestAdapter.test.js +0 -93
- package/build/adapters/mochaAdapter.test.d.ts +0 -1
- package/build/adapters/mochaAdapter.test.js +0 -63
- package/build/adapters/playwrightAdapter.test.d.ts +0 -1
- package/build/adapters/playwrightAdapter.test.js +0 -169
- package/build/adapters/pytestAdapter.test.d.ts +0 -1
- package/build/adapters/pytestAdapter.test.js +0 -90
- package/build/prompts/code-reuse.test.d.ts +0 -1
- package/build/prompts/code-reuse.test.js +0 -62
- package/build/prompts/pom-aware-code-reuse.test.d.ts +0 -1
- package/build/prompts/pom-aware-code-reuse.test.js +0 -11
- package/build/prompts/test-maintenance/drift-analysis-prompt.test.d.ts +0 -1
- package/build/prompts/test-maintenance/drift-analysis-prompt.test.js +0 -51
- package/build/prompts/test-recommendation/analysisOutputPrompt.test.d.ts +0 -1
- package/build/prompts/test-recommendation/analysisOutputPrompt.test.js +0 -264
- package/build/prompts/test-recommendation/mergeEnrichedScenarios.test.d.ts +0 -1
- package/build/prompts/test-recommendation/mergeEnrichedScenarios.test.js +0 -126
- package/build/prompts/test-recommendation/promptPlan.test.d.ts +0 -1
- package/build/prompts/test-recommendation/promptPlan.test.js +0 -336
- package/build/prompts/test-recommendation/scopeAssessment.test.d.ts +0 -1
- package/build/prompts/test-recommendation/scopeAssessment.test.js +0 -371
- package/build/prompts/test-recommendation/test-recommendation-prompt.test.d.ts +0 -1
- package/build/prompts/test-recommendation/test-recommendation-prompt.test.js +0 -1925
- package/build/prompts/testbot/testbot-prompts.test.d.ts +0 -1
- package/build/prompts/testbot/testbot-prompts.test.js +0 -451
- package/build/recommendation/budgeters/diversityBalancedBudgeter.test.d.ts +0 -1
- package/build/recommendation/budgeters/diversityBalancedBudgeter.test.js +0 -138
- package/build/recommendation/budgeters/fixedNBudgeter.test.d.ts +0 -1
- package/build/recommendation/budgeters/fixedNBudgeter.test.js +0 -66
- package/build/recommendation/discriminators.test.d.ts +0 -1
- package/build/recommendation/discriminators.test.js +0 -324
- package/build/recommendation/diversity.test.d.ts +0 -1
- package/build/recommendation/diversity.test.js +0 -77
- package/build/recommendation/planRanker.test.d.ts +0 -1
- package/build/recommendation/planRanker.test.js +0 -110
- package/build/resources/testbotResource.test.d.ts +0 -1
- package/build/resources/testbotResource.test.js +0 -44
- package/build/services/AnalyticsService.test.d.ts +0 -1
- package/build/services/AnalyticsService.test.js +0 -86
- package/build/services/ScenarioGenerationService.integration.test.d.ts +0 -1
- package/build/services/ScenarioGenerationService.integration.test.js +0 -162
- package/build/services/ScenarioGenerationService.test.d.ts +0 -1
- package/build/services/ScenarioGenerationService.test.js +0 -414
- package/build/services/TestDiscoveryService.test.d.ts +0 -1
- package/build/services/TestDiscoveryService.test.js +0 -983
- package/build/services/TestExecutionService.test.d.ts +0 -1
- package/build/services/TestExecutionService.test.js +0 -1032
- package/build/services/TestGenerationService.test.d.ts +0 -1
- package/build/services/TestGenerationService.test.js +0 -578
- package/build/tool-phase-coverage.test.d.ts +0 -1
- package/build/tool-phase-coverage.test.js +0 -49
- package/build/tools/code-refactor/codeReuseTool.test.d.ts +0 -1
- package/build/tools/code-refactor/codeReuseTool.test.js +0 -572
- package/build/tools/generate-tests/generateBatchScenarioRestTool.test.d.ts +0 -1
- package/build/tools/generate-tests/generateBatchScenarioRestTool.test.js +0 -433
- package/build/tools/generate-tests/generateContractRestTool.test.d.ts +0 -1
- package/build/tools/generate-tests/generateContractRestTool.test.js +0 -142
- package/build/tools/generate-tests/generateIntegrationRestTool.test.d.ts +0 -1
- package/build/tools/generate-tests/generateIntegrationRestTool.test.js +0 -159
- package/build/tools/generate-tests/generateLoadRestTool.test.d.ts +0 -1
- package/build/tools/generate-tests/generateLoadRestTool.test.js +0 -169
- package/build/tools/generate-tests/generateUIRestTool.test.d.ts +0 -1
- package/build/tools/generate-tests/generateUIRestTool.test.js +0 -32
- package/build/tools/generate-tests/planGuard.test.d.ts +0 -1
- package/build/tools/generate-tests/planGuard.test.js +0 -185
- package/build/tools/generate-tests/scenarioLint.test.d.ts +0 -1
- package/build/tools/generate-tests/scenarioLint.test.js +0 -100
- package/build/tools/runExistingTestsTool.test.d.ts +0 -1
- package/build/tools/runExistingTestsTool.test.js +0 -379
- package/build/tools/submitReportTool.test.d.ts +0 -1
- package/build/tools/submitReportTool.test.js +0 -1428
- package/build/tools/test-management/actionsTool.test.d.ts +0 -1
- package/build/tools/test-management/actionsTool.test.js +0 -436
- package/build/tools/test-management/analyzeChangesTool.test.d.ts +0 -1
- package/build/tools/test-management/analyzeChangesTool.test.js +0 -522
- package/build/tools/test-management/analyzeTestHealthTool.test.d.ts +0 -1
- package/build/tools/test-management/analyzeTestHealthTool.test.js +0 -385
- package/build/tools/test-management/registerTestPlanTool.test.d.ts +0 -1
- package/build/tools/test-management/registerTestPlanTool.test.js +0 -296
- package/build/tools/trace/resolveSaveStoragePath.test.d.ts +0 -1
- package/build/tools/trace/resolveSaveStoragePath.test.js +0 -17
- package/build/tools/trace/resolveSessionPaths.test.d.ts +0 -1
- package/build/tools/trace/resolveSessionPaths.test.js +0 -104
- package/build/tools/trace/sessionState.test.d.ts +0 -1
- package/build/tools/trace/sessionState.test.js +0 -17
- package/build/tools/workspace/initializeWorkspaceTool.test.d.ts +0 -1
- package/build/tools/workspace/initializeWorkspaceTool.test.js +0 -139
- package/build/tools/workspace/serviceUpsert.test.d.ts +0 -1
- package/build/tools/workspace/serviceUpsert.test.js +0 -50
- package/build/utils/AnalysisStateManager.test.d.ts +0 -1
- package/build/utils/AnalysisStateManager.test.js +0 -134
- package/build/utils/dartRouteExtractor.test.d.ts +0 -1
- package/build/utils/dartRouteExtractor.test.js +0 -307
- package/build/utils/docker.test.d.ts +0 -1
- package/build/utils/docker.test.js +0 -114
- package/build/utils/featureFlags.test.d.ts +0 -1
- package/build/utils/featureFlags.test.js +0 -81
- package/build/utils/fileWalk.test.d.ts +0 -1
- package/build/utils/fileWalk.test.js +0 -252
- package/build/utils/frontendIntegration.test.d.ts +0 -1
- package/build/utils/frontendIntegration.test.js +0 -229
- package/build/utils/frontendSelectors.test.d.ts +0 -1
- package/build/utils/frontendSelectors.test.js +0 -118
- package/build/utils/gitStaging.test.d.ts +0 -1
- package/build/utils/gitStaging.test.js +0 -111
- package/build/utils/httpDefaults.test.d.ts +0 -1
- package/build/utils/httpDefaults.test.js +0 -21
- package/build/utils/importerHop.test.d.ts +0 -1
- package/build/utils/importerHop.test.js +0 -469
- package/build/utils/pathAffinityClassification.test.d.ts +0 -1
- package/build/utils/pathAffinityClassification.test.js +0 -208
- package/build/utils/planMatchKeys.test.d.ts +0 -1
- package/build/utils/planMatchKeys.test.js +0 -123
- package/build/utils/pom-scope/index.test.d.ts +0 -1
- package/build/utils/pom-scope/index.test.js +0 -278
- package/build/utils/pom-scope/pom-files.test.d.ts +0 -1
- package/build/utils/pom-scope/pom-files.test.js +0 -29
- package/build/utils/pom-scope/scoring.test.d.ts +0 -1
- package/build/utils/pom-scope/scoring.test.js +0 -39
- package/build/utils/pom-scope/selector-extractor.test.d.ts +0 -1
- package/build/utils/pom-scope/selector-extractor.test.js +0 -67
- package/build/utils/pom-verify/bindings.test.d.ts +0 -1
- package/build/utils/pom-verify/bindings.test.js +0 -164
- package/build/utils/pom-verify/calls.test.d.ts +0 -1
- package/build/utils/pom-verify/calls.test.js +0 -61
- package/build/utils/pom-verify/resolve.test.d.ts +0 -1
- package/build/utils/pom-verify/resolve.test.js +0 -114
- package/build/utils/pom-verify/verify.test.d.ts +0 -1
- package/build/utils/pom-verify/verify.test.js +0 -374
- package/build/utils/pr-comment-parser.test.d.ts +0 -1
- package/build/utils/pr-comment-parser.test.js +0 -428
- package/build/utils/progress.test.d.ts +0 -1
- package/build/utils/progress.test.js +0 -37
- package/build/utils/projectMetadata.test.d.ts +0 -1
- package/build/utils/projectMetadata.test.js +0 -172
- package/build/utils/pythonMountPrefixes.test.d.ts +0 -1
- package/build/utils/pythonMountPrefixes.test.js +0 -113
- package/build/utils/repoScanner.test.d.ts +0 -1
- package/build/utils/repoScanner.test.js +0 -190
- package/build/utils/reportVerification.test.d.ts +0 -1
- package/build/utils/reportVerification.test.js +0 -185
- package/build/utils/routeParsers.test.d.ts +0 -1
- package/build/utils/routeParsers.test.js +0 -1011
- package/build/utils/scenarioDrafting.test.d.ts +0 -1
- package/build/utils/scenarioDrafting.test.js +0 -876
- package/build/utils/sourceRouteExtractor.test.d.ts +0 -1
- package/build/utils/sourceRouteExtractor.test.js +0 -738
- package/build/utils/telemetry.test.d.ts +0 -1
- package/build/utils/telemetry.test.js +0 -70
- package/build/utils/trace-parser.test.d.ts +0 -6
- package/build/utils/trace-parser.test.js +0 -140
- package/build/utils/uiPageEnumerator.test.d.ts +0 -1
- package/build/utils/uiPageEnumerator.test.js +0 -824
- package/build/utils/utils.test.d.ts +0 -1
- package/build/utils/utils.test.js +0 -103
- package/build/utils/walkerCharacterization.test.d.ts +0 -1
- package/build/utils/walkerCharacterization.test.js +0 -233
- package/build/utils/workspaceAuth.test.d.ts +0 -1
- package/build/utils/workspaceAuth.test.js +0 -260
- package/build/workspace/testSuites.test.d.ts +0 -1
- package/build/workspace/testSuites.test.js +0 -23
- package/build/workspace/workspace.test.d.ts +0 -1
- package/build/workspace/workspace.test.js +0 -264
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,436 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck - Jest ESM type inference issues
|
|
2
|
-
import { jest } from "@jest/globals";
|
|
3
|
-
import * as fs from "fs/promises";
|
|
4
|
-
import * as path from "path";
|
|
5
|
-
import * as os from "os";
|
|
6
|
-
import { z } from "zod";
|
|
7
|
-
jest.unstable_mockModule("../../utils/logger.js", () => ({
|
|
8
|
-
logger: { info: jest.fn(), error: jest.fn(), warning: jest.fn(), debug: jest.fn() },
|
|
9
|
-
}));
|
|
10
|
-
jest.unstable_mockModule("../../services/AnalyticsService.js", () => ({
|
|
11
|
-
AnalyticsService: { pushMCPToolEvent: jest.fn().mockResolvedValue(undefined) },
|
|
12
|
-
}));
|
|
13
|
-
const { registerActionsTool } = await import("./actionsTool.js");
|
|
14
|
-
const { DriftAction, TestSource } = await import("../../types/TestAnalysis.js");
|
|
15
|
-
const { TestType } = await import("../../types/TestTypes.js");
|
|
16
|
-
function captureToolHandler() {
|
|
17
|
-
let handler;
|
|
18
|
-
let inputSchema;
|
|
19
|
-
const fakeServer = {
|
|
20
|
-
registerTool: (_name, opts, fn) => {
|
|
21
|
-
handler = fn;
|
|
22
|
-
inputSchema = opts.inputSchema;
|
|
23
|
-
},
|
|
24
|
-
};
|
|
25
|
-
registerActionsTool(fakeServer);
|
|
26
|
-
// Mirrors the real MCP SDK's registerTool: validates args against inputSchema via Zod
|
|
27
|
-
// before invoking the handler, so tests exercise the real tool contract (refinements
|
|
28
|
-
// included) instead of bypassing validation entirely.
|
|
29
|
-
return async (params) => {
|
|
30
|
-
const parsed = z.object(inputSchema).safeParse(params);
|
|
31
|
-
if (!parsed.success) {
|
|
32
|
-
return { isError: true, content: [{ type: "text", text: parsed.error.message }] };
|
|
33
|
-
}
|
|
34
|
-
return handler(parsed.data);
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
async function writeStateFile(dir, existingTests, repositoryAnalysis) {
|
|
38
|
-
const stateFile = path.join(dir, "analyze-changes-state.json");
|
|
39
|
-
await fs.writeFile(stateFile, JSON.stringify({
|
|
40
|
-
existingTests,
|
|
41
|
-
repositoryAnalysis,
|
|
42
|
-
analysisScope: {},
|
|
43
|
-
newEndpoints: [],
|
|
44
|
-
metadata: {
|
|
45
|
-
sessionId: "test-session",
|
|
46
|
-
stateType: "unified",
|
|
47
|
-
repositoryPath: dir,
|
|
48
|
-
createdAt: "2026-01-01T00:00:00.000Z",
|
|
49
|
-
updatedAt: "2026-01-01T00:00:00.000Z",
|
|
50
|
-
},
|
|
51
|
-
}), "utf-8");
|
|
52
|
-
return stateFile;
|
|
53
|
-
}
|
|
54
|
-
describe("registerActionsTool — verdict reconciliation & persistence (SKYR-3906, SKYR-3938)", () => {
|
|
55
|
-
let handler;
|
|
56
|
-
let tmpDirs = [];
|
|
57
|
-
beforeAll(() => {
|
|
58
|
-
handler = captureToolHandler();
|
|
59
|
-
});
|
|
60
|
-
afterEach(async () => {
|
|
61
|
-
for (const dir of tmpDirs) {
|
|
62
|
-
await fs.rm(dir, { recursive: true, force: true });
|
|
63
|
-
}
|
|
64
|
-
tmpDirs = [];
|
|
65
|
-
});
|
|
66
|
-
it("succeeds when every existing test is covered by recommendations", async () => {
|
|
67
|
-
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
|
|
68
|
-
tmpDirs.push(tmpDir);
|
|
69
|
-
const testFile = path.join(tmpDir, "orders_contract_test.py");
|
|
70
|
-
await fs.writeFile(testFile, "# test");
|
|
71
|
-
const stateFile = await writeStateFile(tmpDir, [{ testFile, source: TestSource.Skyramp }]);
|
|
72
|
-
const result = await handler({
|
|
73
|
-
stateFile,
|
|
74
|
-
recommendations: [
|
|
75
|
-
{
|
|
76
|
-
testFilePath: testFile,
|
|
77
|
-
action: DriftAction.Update,
|
|
78
|
-
rationale: "New field added",
|
|
79
|
-
testType: TestType.CONTRACT,
|
|
80
|
-
endpoint: "GET /api/v1/orders",
|
|
81
|
-
updateInstructions: "Assert new field",
|
|
82
|
-
},
|
|
83
|
-
],
|
|
84
|
-
});
|
|
85
|
-
expect(result.content).toBeDefined();
|
|
86
|
-
expect(result.isError).toBeUndefined();
|
|
87
|
-
});
|
|
88
|
-
it("derives the ignored set as the catalog complement of recommendations — no LLM enumeration (SKYR-3938)", async () => {
|
|
89
|
-
// The caller supplies only actionable recommendations; every other discovered test is
|
|
90
|
-
// recorded as ignored automatically. The LLM payload is O(recommendations) — independent of
|
|
91
|
-
// catalog size — which is the fix for the large-repo timeout where the LLM had to echo back
|
|
92
|
-
// every path. (Server-side derivation is still O(catalog): filter the catalog by a
|
|
93
|
-
// recommendations Set.)
|
|
94
|
-
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
|
|
95
|
-
tmpDirs.push(tmpDir);
|
|
96
|
-
const testFile = path.join(tmpDir, "unrelated_test.py");
|
|
97
|
-
await fs.writeFile(testFile, "# test");
|
|
98
|
-
const stateFile = await writeStateFile(tmpDir, [{ testFile, source: TestSource.Skyramp }]);
|
|
99
|
-
// No recommendations at all — the sole discovered test is recorded as ignored.
|
|
100
|
-
const result = await handler({ stateFile, recommendations: [] });
|
|
101
|
-
expect(result.isError).toBeUndefined();
|
|
102
|
-
const written = JSON.parse(await fs.readFile(stateFile, "utf-8"));
|
|
103
|
-
expect(written.ignoredTestFiles).toEqual([testFile]);
|
|
104
|
-
expect(written.maintenanceVerdicts).toEqual([]);
|
|
105
|
-
});
|
|
106
|
-
it("rejects a recommendation with action: IGNORE at the schema level — must simply be omitted", async () => {
|
|
107
|
-
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
|
|
108
|
-
tmpDirs.push(tmpDir);
|
|
109
|
-
const testFile = path.join(tmpDir, "unrelated_test.py");
|
|
110
|
-
await fs.writeFile(testFile, "# test");
|
|
111
|
-
const stateFile = await writeStateFile(tmpDir, [{ testFile, source: TestSource.Skyramp }]);
|
|
112
|
-
const result = await handler({
|
|
113
|
-
stateFile,
|
|
114
|
-
recommendations: [{ testFilePath: testFile, action: DriftAction.Ignore, rationale: "No change needed", testType: TestType.CONTRACT, endpoint: "GET /api/v1/orders" }],
|
|
115
|
-
// IGNORE must not silently persist via recommendations — it must simply be omitted.
|
|
116
|
-
});
|
|
117
|
-
expect(result.isError).toBe(true);
|
|
118
|
-
expect(result.content[0].text).toContain("Omit IGNORE entries entirely");
|
|
119
|
-
});
|
|
120
|
-
it("treats an existing test omitted from recommendations as needing no action — no completeness gate (SKYR-3938)", async () => {
|
|
121
|
-
// The prior hard-reject forced the LLM to echo back every discovered test, which is
|
|
122
|
-
// infeasible on large repos (1,000+ tests) and caused the run to spin until CI timeout.
|
|
123
|
-
// An omitted test is now silently no-action: the call succeeds, only the covered file
|
|
124
|
-
// lands in the verdicts, and the dropped file is recorded in the derived ignored set.
|
|
125
|
-
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
|
|
126
|
-
tmpDirs.push(tmpDir);
|
|
127
|
-
const coveredFile = path.join(tmpDir, "covered_test.py");
|
|
128
|
-
const droppedFile = path.join(tmpDir, "secret_test.py");
|
|
129
|
-
await fs.writeFile(coveredFile, "# test");
|
|
130
|
-
await fs.writeFile(droppedFile, "# test");
|
|
131
|
-
const stateFile = await writeStateFile(tmpDir, [
|
|
132
|
-
{ testFile: coveredFile, source: TestSource.Skyramp },
|
|
133
|
-
{ testFile: droppedFile, source: TestSource.Skyramp },
|
|
134
|
-
]);
|
|
135
|
-
const result = await handler({
|
|
136
|
-
stateFile,
|
|
137
|
-
recommendations: [{ testFilePath: coveredFile, action: DriftAction.Update, rationale: "Field added", testType: TestType.CONTRACT, endpoint: "GET /api/v1/orders" }],
|
|
138
|
-
// droppedFile intentionally absent from recommendations
|
|
139
|
-
});
|
|
140
|
-
expect(result.isError).toBeUndefined();
|
|
141
|
-
const written = JSON.parse(await fs.readFile(stateFile, "utf-8"));
|
|
142
|
-
expect(written.maintenanceVerdicts).toEqual([
|
|
143
|
-
{ testFilePath: coveredFile, action: DriftAction.Update, rationale: "Field added", testType: TestType.CONTRACT, endpoint: "GET /api/v1/orders" },
|
|
144
|
-
]);
|
|
145
|
-
expect(written.ignoredTestFiles).toEqual([droppedFile]);
|
|
146
|
-
});
|
|
147
|
-
it("persists maintenanceVerdicts + a derived ignoredTestFiles record to the state file", async () => {
|
|
148
|
-
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
|
|
149
|
-
tmpDirs.push(tmpDir);
|
|
150
|
-
const updatedFile = path.join(tmpDir, "orders_contract_test.py");
|
|
151
|
-
const ignoredFile = path.join(tmpDir, "unrelated_test.py");
|
|
152
|
-
await fs.writeFile(updatedFile, "# test");
|
|
153
|
-
await fs.writeFile(ignoredFile, "# test");
|
|
154
|
-
const stateFile = await writeStateFile(tmpDir, [
|
|
155
|
-
{ testFile: updatedFile, source: TestSource.Skyramp },
|
|
156
|
-
{ testFile: ignoredFile, source: TestSource.Skyramp },
|
|
157
|
-
]);
|
|
158
|
-
// Only the actionable test is supplied; ignoredFile is derived as the catalog complement.
|
|
159
|
-
await handler({
|
|
160
|
-
stateFile,
|
|
161
|
-
recommendations: [{ testFilePath: updatedFile, action: DriftAction.Update, rationale: "Field added", testType: TestType.CONTRACT, endpoint: "GET /api/v1/orders" }],
|
|
162
|
-
});
|
|
163
|
-
const written = JSON.parse(await fs.readFile(stateFile, "utf-8"));
|
|
164
|
-
expect(written.ignoredTestFiles).toEqual([ignoredFile]);
|
|
165
|
-
expect(written.maintenanceVerdicts).toEqual([
|
|
166
|
-
{ testFilePath: updatedFile, action: DriftAction.Update, rationale: "Field added", testType: TestType.CONTRACT, endpoint: "GET /api/v1/orders" },
|
|
167
|
-
]);
|
|
168
|
-
// Original existingTests must survive the write-back untouched.
|
|
169
|
-
expect(written.existingTests).toHaveLength(2);
|
|
170
|
-
});
|
|
171
|
-
it("accepts a real test file discovery missed (uncataloged) instead of dropping it (B1)", async () => {
|
|
172
|
-
// Discovery is relevance-scoped, so a real e2e spec/POM the agent correctly identifies
|
|
173
|
-
// can be absent from the catalog. Before B1 its verdict was dropped as "hallucinated",
|
|
174
|
-
// leaving report.testMaintenance empty even though the edit was applied.
|
|
175
|
-
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
|
|
176
|
-
tmpDirs.push(tmpDir);
|
|
177
|
-
const cataloged = path.join(tmpDir, "orders_contract_test.py");
|
|
178
|
-
await fs.writeFile(cataloged, "# test");
|
|
179
|
-
const e2eDir = path.join(tmpDir, "e2e", "specs");
|
|
180
|
-
await fs.mkdir(e2eDir, { recursive: true });
|
|
181
|
-
const uncataloged = path.join(e2eDir, "drafts.spec.ts"); // real on disk, NOT in existingTests
|
|
182
|
-
await fs.writeFile(uncataloged, "import { test } from '@playwright/test';\ntest('x', async () => {});");
|
|
183
|
-
const stateFile = await writeStateFile(tmpDir, [{ testFile: cataloged, source: TestSource.Skyramp }]);
|
|
184
|
-
const result = await handler({
|
|
185
|
-
stateFile,
|
|
186
|
-
recommendations: [
|
|
187
|
-
{ testFilePath: uncataloged, action: DriftAction.Update, rationale: "testid moved, re-scope locator", testType: TestType.E2E, endpoint: "/drafts", updateInstructions: "re-scope the badge locator" },
|
|
188
|
-
],
|
|
189
|
-
});
|
|
190
|
-
expect(result.isError).toBeUndefined();
|
|
191
|
-
const written = JSON.parse(await fs.readFile(stateFile, "utf-8"));
|
|
192
|
-
expect(written.maintenanceVerdicts.map((v) => v.testFilePath)).toContain(uncataloged);
|
|
193
|
-
});
|
|
194
|
-
it("still rejects a recommendation for a non-existent (hallucinated) test path (B1 boundary)", async () => {
|
|
195
|
-
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
|
|
196
|
-
tmpDirs.push(tmpDir);
|
|
197
|
-
const cataloged = path.join(tmpDir, "orders_contract_test.py");
|
|
198
|
-
await fs.writeFile(cataloged, "# test");
|
|
199
|
-
const stateFile = await writeStateFile(tmpDir, [{ testFile: cataloged, source: TestSource.Skyramp }]);
|
|
200
|
-
const ghost = path.join(tmpDir, "e2e", "does_not_exist.spec.ts"); // matches test pattern but NOT on disk
|
|
201
|
-
const result = await handler({
|
|
202
|
-
stateFile,
|
|
203
|
-
recommendations: [
|
|
204
|
-
{ testFilePath: ghost, action: DriftAction.Update, rationale: "hallucinated", testType: TestType.E2E, endpoint: "/x", updateInstructions: "y" },
|
|
205
|
-
],
|
|
206
|
-
});
|
|
207
|
-
expect(result.isError).toBeUndefined();
|
|
208
|
-
const written = JSON.parse(await fs.readFile(stateFile, "utf-8"));
|
|
209
|
-
expect(written.maintenanceVerdicts.map((v) => v.testFilePath)).not.toContain(ghost);
|
|
210
|
-
});
|
|
211
|
-
it("records external DELETE as a report-only verdict (real action, file untouched) — no VERIFY flattening (B2)", async () => {
|
|
212
|
-
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
|
|
213
|
-
tmpDirs.push(tmpDir);
|
|
214
|
-
const extTest = path.join(tmpDir, "whats_new.spec.ts");
|
|
215
|
-
await fs.writeFile(extTest, "import { test } from '@playwright/test';\ntest('whats new', async () => {});");
|
|
216
|
-
const stateFile = await writeStateFile(tmpDir, [{ testFile: extTest, source: TestSource.External }]);
|
|
217
|
-
const result = await handler({
|
|
218
|
-
stateFile,
|
|
219
|
-
recommendations: [
|
|
220
|
-
{ testFilePath: extTest, action: DriftAction.Delete, rationale: "What's New feature removed in this PR", testType: TestType.E2E, endpoint: "/whats-new" },
|
|
221
|
-
],
|
|
222
|
-
});
|
|
223
|
-
expect(result.isError).toBeUndefined();
|
|
224
|
-
const written = JSON.parse(await fs.readFile(stateFile, "utf-8"));
|
|
225
|
-
const verdict = written.maintenanceVerdicts.find((v) => v.testFilePath === extTest);
|
|
226
|
-
expect(verdict).toBeDefined();
|
|
227
|
-
// The verdict keeps the real DELETE action — NOT flattened to VERIFY (the D2-judge bug).
|
|
228
|
-
expect(verdict.action).toBe(DriftAction.Delete);
|
|
229
|
-
// Report-only: the external test file must still exist on disk.
|
|
230
|
-
await expect(fs.access(extTest)).resolves.toBeUndefined();
|
|
231
|
-
});
|
|
232
|
-
it("fails loud when persisting maintenanceVerdicts to state fails, instead of silently applying edits anyway", async () => {
|
|
233
|
-
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
|
|
234
|
-
tmpDirs.push(tmpDir);
|
|
235
|
-
const updatedFile = path.join(tmpDir, "orders_contract_test.py");
|
|
236
|
-
await fs.writeFile(updatedFile, "# test");
|
|
237
|
-
const stateFile = await writeStateFile(tmpDir, [{ testFile: updatedFile, source: TestSource.Skyramp }]);
|
|
238
|
-
// Make the state file unwritable so writeRepoData's overwrite fails with EACCES.
|
|
239
|
-
await fs.chmod(stateFile, 0o444);
|
|
240
|
-
try {
|
|
241
|
-
const result = await handler({
|
|
242
|
-
stateFile,
|
|
243
|
-
recommendations: [{ testFilePath: updatedFile, action: DriftAction.Update, rationale: "Field added", testType: TestType.CONTRACT, endpoint: "GET /api/v1/orders" }],
|
|
244
|
-
});
|
|
245
|
-
expect(result.isError).toBe(true);
|
|
246
|
-
expect(result.content[0].text).toContain("Failed to persist maintenance reconciliation");
|
|
247
|
-
}
|
|
248
|
-
finally {
|
|
249
|
-
await fs.chmod(stateFile, 0o644);
|
|
250
|
-
}
|
|
251
|
-
});
|
|
252
|
-
it("does not require accounting for a test when there are no existing tests at all", async () => {
|
|
253
|
-
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
|
|
254
|
-
tmpDirs.push(tmpDir);
|
|
255
|
-
const stateFile = await writeStateFile(tmpDir, []);
|
|
256
|
-
const result = await handler({ stateFile });
|
|
257
|
-
expect(result.isError).toBeUndefined();
|
|
258
|
-
});
|
|
259
|
-
// ── DELETE delivery routing (SKYR-3927) ─────────────────────────────────────
|
|
260
|
-
// A DELETE on a Skyramp-managed test must produce a structured files_to_delete
|
|
261
|
-
// bucket + its own report section — not fall into "Other Findings" prose with an
|
|
262
|
-
// empty payload (which left delivery dependent on the agent noticing and rm-ing).
|
|
263
|
-
it("routes a DELETE on a Skyramp-managed test into files_to_delete, not Other Findings (SKYR-3927)", async () => {
|
|
264
|
-
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
|
|
265
|
-
tmpDirs.push(tmpDir);
|
|
266
|
-
const deletedFile = path.join(tmpDir, "reviews_contract_test.py");
|
|
267
|
-
await fs.writeFile(deletedFile, "# test");
|
|
268
|
-
const stateFile = await writeStateFile(tmpDir, [{ testFile: deletedFile, source: TestSource.Skyramp }]);
|
|
269
|
-
const result = await handler({
|
|
270
|
-
stateFile,
|
|
271
|
-
recommendations: [
|
|
272
|
-
{
|
|
273
|
-
testFilePath: deletedFile,
|
|
274
|
-
action: DriftAction.Delete,
|
|
275
|
-
rationale: "Both covered endpoints removed; now return 404",
|
|
276
|
-
testType: TestType.CONTRACT,
|
|
277
|
-
endpoint: "GET/POST /api/v1/products/{product_id}/reviews",
|
|
278
|
-
},
|
|
279
|
-
],
|
|
280
|
-
});
|
|
281
|
-
expect(result.isError).toBeUndefined();
|
|
282
|
-
// Assistant instruction block carries a populated delete bucket + strategy.
|
|
283
|
-
const llmInstructions = JSON.parse(result.content[1].text);
|
|
284
|
-
expect(llmInstructions.files_to_delete).toEqual([deletedFile]);
|
|
285
|
-
expect(llmInstructions.delete_count).toBe(1);
|
|
286
|
-
expect(llmInstructions.delete_strategy).toContain("git rm");
|
|
287
|
-
// Surfaced in its own section — never as "Other Findings" prose.
|
|
288
|
-
const report = result.content[0].text;
|
|
289
|
-
expect(report).toContain("Tests To Delete (1)");
|
|
290
|
-
expect(report).not.toContain("Other Findings");
|
|
291
|
-
});
|
|
292
|
-
it("DELETE wins over UPDATE for the same file — deleted, not edited (SKYR-3927)", async () => {
|
|
293
|
-
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
|
|
294
|
-
tmpDirs.push(tmpDir);
|
|
295
|
-
const file = path.join(tmpDir, "widgets_contract_test.py");
|
|
296
|
-
await fs.writeFile(file, "# test");
|
|
297
|
-
const stateFile = await writeStateFile(tmpDir, [{ testFile: file, source: TestSource.Skyramp }]);
|
|
298
|
-
const result = await handler({
|
|
299
|
-
stateFile,
|
|
300
|
-
recommendations: [
|
|
301
|
-
{ testFilePath: file, action: DriftAction.Update, rationale: "field added", testType: TestType.CONTRACT, endpoint: "GET /api/v1/widgets" },
|
|
302
|
-
{ testFilePath: file, action: DriftAction.Delete, rationale: "endpoint removed", testType: TestType.CONTRACT, endpoint: "GET /api/v1/widgets" },
|
|
303
|
-
],
|
|
304
|
-
});
|
|
305
|
-
expect(result.isError).toBeUndefined();
|
|
306
|
-
const llmInstructions = JSON.parse(result.content[1].text);
|
|
307
|
-
expect(llmInstructions.files_to_delete).toEqual([file]);
|
|
308
|
-
expect(llmInstructions.delete_count).toBe(1);
|
|
309
|
-
expect(llmInstructions.files_to_update).toEqual([]);
|
|
310
|
-
expect(llmInstructions.update_count).toBe(0);
|
|
311
|
-
});
|
|
312
|
-
it("DELETE wins over REGENERATE for the same file — deleted, not regenerated (SKYR-3927)", async () => {
|
|
313
|
-
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
|
|
314
|
-
tmpDirs.push(tmpDir);
|
|
315
|
-
const file = path.join(tmpDir, "gadgets_contract_test.py");
|
|
316
|
-
await fs.writeFile(file, "# test");
|
|
317
|
-
const stateFile = await writeStateFile(tmpDir, [{ testFile: file, source: TestSource.Skyramp }]);
|
|
318
|
-
const result = await handler({
|
|
319
|
-
stateFile,
|
|
320
|
-
recommendations: [
|
|
321
|
-
{ testFilePath: file, action: DriftAction.Regenerate, rationale: "response wrapper changed", testType: TestType.CONTRACT, endpoint: "GET /api/v1/gadgets" },
|
|
322
|
-
{ testFilePath: file, action: DriftAction.Delete, rationale: "endpoint removed", testType: TestType.CONTRACT, endpoint: "GET /api/v1/gadgets" },
|
|
323
|
-
],
|
|
324
|
-
});
|
|
325
|
-
expect(result.isError).toBeUndefined();
|
|
326
|
-
const llmInstructions = JSON.parse(result.content[1].text);
|
|
327
|
-
expect(llmInstructions.files_to_delete).toEqual([file]);
|
|
328
|
-
expect(llmInstructions.delete_count).toBe(1);
|
|
329
|
-
expect(llmInstructions.files_to_regenerate).toEqual([]);
|
|
330
|
-
expect(llmInstructions.regenerate_count).toBe(0);
|
|
331
|
-
});
|
|
332
|
-
// ── Multi-repo (`repository` param) ─────────────────────────────────────────
|
|
333
|
-
// A run-scoped state file holds the primary repo at the root and each related
|
|
334
|
-
// repo under `relatedRepos[<owner/repo>]`. `args.repository` must scope the
|
|
335
|
-
// maintenanceVerdicts/ignoredTestFiles write-back to the matching section only.
|
|
336
|
-
async function writeMultiRepoStateFile(stateFile, primary, related) {
|
|
337
|
-
const now = "2026-01-01T00:00:00.000Z";
|
|
338
|
-
await fs.writeFile(stateFile, JSON.stringify({
|
|
339
|
-
existingTests: primary.existingTests,
|
|
340
|
-
analysisScope: {},
|
|
341
|
-
newEndpoints: [],
|
|
342
|
-
metadata: {
|
|
343
|
-
sessionId: "test-session",
|
|
344
|
-
stateType: "unified",
|
|
345
|
-
repositoryPath: primary.repositoryPath,
|
|
346
|
-
repository: "org/primary",
|
|
347
|
-
createdAt: now,
|
|
348
|
-
updatedAt: now,
|
|
349
|
-
},
|
|
350
|
-
relatedRepos: {
|
|
351
|
-
[related.repo]: {
|
|
352
|
-
repositoryPath: related.repositoryPath,
|
|
353
|
-
data: { existingTests: related.existingTests, analysisScope: {}, newEndpoints: [] },
|
|
354
|
-
},
|
|
355
|
-
},
|
|
356
|
-
}), "utf-8");
|
|
357
|
-
}
|
|
358
|
-
it("scopes the write-back to a related (non-primary) repo section, leaving the primary section untouched", async () => {
|
|
359
|
-
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
|
|
360
|
-
tmpDirs.push(tmpDir);
|
|
361
|
-
const primaryDir = path.join(tmpDir, "primary");
|
|
362
|
-
const secondaryDir = path.join(tmpDir, "secondary");
|
|
363
|
-
await fs.mkdir(primaryDir, { recursive: true });
|
|
364
|
-
await fs.mkdir(secondaryDir, { recursive: true });
|
|
365
|
-
const secondaryTestFile = path.join(secondaryDir, "orders_contract_test.py");
|
|
366
|
-
await fs.writeFile(secondaryTestFile, "# test");
|
|
367
|
-
const stateFile = path.join(tmpDir, "analyze-changes-state.json");
|
|
368
|
-
await writeMultiRepoStateFile(stateFile, { repositoryPath: primaryDir, existingTests: [] }, { repo: "org/secondary", repositoryPath: secondaryDir, existingTests: [{ testFile: secondaryTestFile, source: TestSource.Skyramp }] });
|
|
369
|
-
const result = await handler({
|
|
370
|
-
stateFile,
|
|
371
|
-
repository: "org/secondary",
|
|
372
|
-
recommendations: [{ testFilePath: secondaryTestFile, action: DriftAction.Update, rationale: "Field added", testType: TestType.CONTRACT, endpoint: "GET /api/v1/orders" }],
|
|
373
|
-
});
|
|
374
|
-
expect(result.isError).toBeUndefined();
|
|
375
|
-
const written = JSON.parse(await fs.readFile(stateFile, "utf-8"));
|
|
376
|
-
// UPDATE converts to VERIFY — the agent has no write access to a related repo's
|
|
377
|
-
// checkout, so no edit is ever attempted there.
|
|
378
|
-
expect(written.relatedRepos["org/secondary"].data.maintenanceVerdicts).toEqual([
|
|
379
|
-
{ testFilePath: secondaryTestFile, action: DriftAction.Verify, rationale: "[related repo — no write access, needs manual review] Field added", testType: TestType.CONTRACT, endpoint: "GET /api/v1/orders" },
|
|
380
|
-
]);
|
|
381
|
-
// Primary section must be untouched by a write scoped to the related repo.
|
|
382
|
-
expect(written.maintenanceVerdicts).toBeUndefined();
|
|
383
|
-
expect(written.existingTests).toEqual([]);
|
|
384
|
-
});
|
|
385
|
-
it("also converts DELETE to VERIFY for a related repo — no action type gets write access there", async () => {
|
|
386
|
-
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
|
|
387
|
-
tmpDirs.push(tmpDir);
|
|
388
|
-
const primaryDir = path.join(tmpDir, "primary");
|
|
389
|
-
const secondaryDir = path.join(tmpDir, "secondary");
|
|
390
|
-
await fs.mkdir(primaryDir, { recursive: true });
|
|
391
|
-
await fs.mkdir(secondaryDir, { recursive: true });
|
|
392
|
-
const secondaryTestFile = path.join(secondaryDir, "reset_contract_test.py");
|
|
393
|
-
await fs.writeFile(secondaryTestFile, "# test");
|
|
394
|
-
const stateFile = path.join(tmpDir, "analyze-changes-state.json");
|
|
395
|
-
await writeMultiRepoStateFile(stateFile, { repositoryPath: primaryDir, existingTests: [] }, { repo: "org/secondary", repositoryPath: secondaryDir, existingTests: [{ testFile: secondaryTestFile, source: TestSource.Skyramp }] });
|
|
396
|
-
const result = await handler({
|
|
397
|
-
stateFile,
|
|
398
|
-
repository: "org/secondary",
|
|
399
|
-
recommendations: [{ testFilePath: secondaryTestFile, action: DriftAction.Delete, rationale: "Endpoint removed", testType: TestType.CONTRACT, endpoint: "DELETE /api/v1/reset" }],
|
|
400
|
-
});
|
|
401
|
-
expect(result.isError).toBeUndefined();
|
|
402
|
-
const written = JSON.parse(await fs.readFile(stateFile, "utf-8"));
|
|
403
|
-
expect(written.relatedRepos["org/secondary"].data.maintenanceVerdicts).toEqual([
|
|
404
|
-
{ testFilePath: secondaryTestFile, action: DriftAction.Verify, rationale: "[related repo — no write access, needs manual review] Endpoint removed", testType: TestType.CONTRACT, endpoint: "DELETE /api/v1/reset" },
|
|
405
|
-
]);
|
|
406
|
-
});
|
|
407
|
-
it("scopes the write-back to the related repo section, writing an empty verdict record when nothing needs action (SKYR-3938)", async () => {
|
|
408
|
-
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
|
|
409
|
-
tmpDirs.push(tmpDir);
|
|
410
|
-
const primaryDir = path.join(tmpDir, "primary");
|
|
411
|
-
const secondaryDir = path.join(tmpDir, "secondary");
|
|
412
|
-
await fs.mkdir(primaryDir, { recursive: true });
|
|
413
|
-
await fs.mkdir(secondaryDir, { recursive: true });
|
|
414
|
-
const primaryTestFile = path.join(primaryDir, "covered_test.py");
|
|
415
|
-
const secondaryTestFile = path.join(secondaryDir, "secret_test.py");
|
|
416
|
-
await fs.writeFile(primaryTestFile, "# test");
|
|
417
|
-
await fs.writeFile(secondaryTestFile, "# test");
|
|
418
|
-
const stateFile = path.join(tmpDir, "analyze-changes-state.json");
|
|
419
|
-
await writeMultiRepoStateFile(stateFile, { repositoryPath: primaryDir, existingTests: [{ testFile: primaryTestFile, source: TestSource.Skyramp }] }, { repo: "org/secondary", repositoryPath: secondaryDir, existingTests: [{ testFile: secondaryTestFile, source: TestSource.Skyramp }] });
|
|
420
|
-
// With no completeness gate, a related-repo test the LLM left unlisted is simply
|
|
421
|
-
// no-action: the call succeeds and writes an empty maintenanceVerdicts to the related
|
|
422
|
-
// section only, leaving the primary section untouched.
|
|
423
|
-
const result = await handler({
|
|
424
|
-
stateFile,
|
|
425
|
-
repository: "org/secondary",
|
|
426
|
-
recommendations: [],
|
|
427
|
-
});
|
|
428
|
-
expect(result.isError).toBeUndefined();
|
|
429
|
-
const written = JSON.parse(await fs.readFile(stateFile, "utf-8"));
|
|
430
|
-
expect(written.relatedRepos["org/secondary"].data.maintenanceVerdicts).toEqual([]);
|
|
431
|
-
// The related section's lone test is recorded as ignored (derived complement), scoped
|
|
432
|
-
// to that section — the primary section stays untouched.
|
|
433
|
-
expect(written.relatedRepos["org/secondary"].data.ignoredTestFiles).toEqual([secondaryTestFile]);
|
|
434
|
-
expect(written.maintenanceVerdicts).toBeUndefined();
|
|
435
|
-
});
|
|
436
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|