@mergesignal/shared 0.12.2 → 0.13.0
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/dist/assessment/authoredCommunication.d.ts +30 -0
- package/dist/assessment/authoredCommunication.d.ts.map +1 -0
- package/dist/assessment/authoredCommunication.js +18 -0
- package/dist/assessment/index.d.ts +6 -0
- package/dist/assessment/index.d.ts.map +1 -0
- package/dist/assessment/index.js +5 -0
- package/dist/assessment/literals.d.ts +44 -0
- package/dist/assessment/literals.d.ts.map +1 -0
- package/dist/assessment/literals.js +140 -0
- package/dist/assessment/schema.d.ts +311 -0
- package/dist/assessment/schema.d.ts.map +1 -0
- package/dist/assessment/schema.js +162 -0
- package/dist/assessment/types.d.ts +197 -0
- package/dist/assessment/types.d.ts.map +1 -0
- package/dist/assessment/types.js +1 -0
- package/dist/assessment/version.d.ts +11 -0
- package/dist/assessment/version.d.ts.map +1 -0
- package/dist/assessment/version.js +9 -0
- package/dist/assessmentLabels.d.ts +1 -1
- package/dist/assessmentLabels.d.ts.map +1 -1
- package/dist/assessmentPresentationUtils.d.ts +1 -1
- package/dist/assessmentPresentationUtils.d.ts.map +1 -1
- package/dist/assessmentProjection.d.ts +1 -1
- package/dist/assessmentProjection.d.ts.map +1 -1
- package/dist/deriveScanNarrative.d.ts.map +1 -1
- package/dist/deriveScanNarrative.js +2 -1
- package/dist/fixtures/assessmentFixtures.d.ts +1 -1
- package/dist/fixtures/assessmentScopeFixtures.d.ts +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/lockfileEvidence.d.ts +22 -0
- package/dist/lockfileEvidence.d.ts.map +1 -0
- package/dist/lockfileEvidence.js +32 -0
- package/dist/presentation/dto/assessmentPresentationFields.d.ts +1 -1
- package/dist/presentation/dto/assessmentPresentationFields.d.ts.map +1 -1
- package/dist/presentation/orchestration/buildScanPresentationBundle.d.ts.map +1 -1
- package/dist/presentation/orchestration/buildScanPresentationBundle.js +2 -1
- package/dist/presentation/orchestration/scanPresentationBundle.d.ts +2 -1
- package/dist/presentation/orchestration/scanPresentationBundle.d.ts.map +1 -1
- package/dist/presentation/profile/buildProfileFromAssessment.d.ts +1 -1
- package/dist/presentation/profile/buildProfileFromAssessment.d.ts.map +1 -1
- package/dist/presentation/profile/buildProfileFromAssessment.js +13 -5
- package/dist/repoIntelligenceSchema.d.ts +5 -5
- package/dist/scanResultSchema.d.ts +6 -6
- package/dist/scanResultSchema.js +1 -1
- package/dist/types.d.ts +6 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -2
- package/dist/actionsStepSummary.test.d.ts +0 -2
- package/dist/actionsStepSummary.test.d.ts.map +0 -1
- package/dist/actionsStepSummary.test.js +0 -53
- package/dist/assessmentProjection.verificationChannel.test.d.ts +0 -2
- package/dist/assessmentProjection.verificationChannel.test.d.ts.map +0 -1
- package/dist/assessmentProjection.verificationChannel.test.js +0 -110
- package/dist/deriveScanNarrative.test.d.ts +0 -2
- package/dist/deriveScanNarrative.test.d.ts.map +0 -1
- package/dist/deriveScanNarrative.test.js +0 -274
- package/dist/engineWireRegression.test.d.ts +0 -2
- package/dist/engineWireRegression.test.d.ts.map +0 -1
- package/dist/engineWireRegression.test.js +0 -77
- package/dist/fixtures/engineAbi4Fixtures.d.ts +0 -16
- package/dist/fixtures/engineAbi4Fixtures.d.ts.map +0 -1
- package/dist/fixtures/engineAbi4Fixtures.js +0 -26
- package/dist/fixtures/legacyEngineRepoIntelligence.d.ts +0 -28
- package/dist/fixtures/legacyEngineRepoIntelligence.d.ts.map +0 -1
- package/dist/fixtures/legacyEngineRepoIntelligence.js +0 -28
- package/dist/fixtures/repoIntelligenceFixtures.d.ts +0 -8
- package/dist/fixtures/repoIntelligenceFixtures.d.ts.map +0 -1
- package/dist/fixtures/repoIntelligenceFixtures.js +0 -123
- package/dist/fixtures/repoIntelligenceTestHelpers.d.ts +0 -5
- package/dist/fixtures/repoIntelligenceTestHelpers.d.ts.map +0 -1
- package/dist/fixtures/repoIntelligenceTestHelpers.js +0 -28
- package/dist/formatInsight.test.d.ts +0 -2
- package/dist/formatInsight.test.d.ts.map +0 -1
- package/dist/formatInsight.test.js +0 -102
- package/dist/narrativeParity.test.d.ts +0 -2
- package/dist/narrativeParity.test.d.ts.map +0 -1
- package/dist/narrativeParity.test.js +0 -35
- package/dist/narrativePresentation.test.d.ts +0 -2
- package/dist/narrativePresentation.test.d.ts.map +0 -1
- package/dist/narrativePresentation.test.js +0 -54
- package/dist/normalizeGeneratedText.test.d.ts +0 -2
- package/dist/normalizeGeneratedText.test.d.ts.map +0 -1
- package/dist/normalizeGeneratedText.test.js +0 -18
- package/dist/prRiskBand.test.d.ts +0 -2
- package/dist/prRiskBand.test.d.ts.map +0 -1
- package/dist/prRiskBand.test.js +0 -68
- package/dist/prRiskWire.test.d.ts +0 -2
- package/dist/prRiskWire.test.d.ts.map +0 -1
- package/dist/prRiskWire.test.js +0 -106
- package/dist/prRiskWiringRegression.test.d.ts +0 -2
- package/dist/prRiskWiringRegression.test.d.ts.map +0 -1
- package/dist/prRiskWiringRegression.test.js +0 -102
- package/dist/presentation/abi3Expression.test.d.ts +0 -13
- package/dist/presentation/abi3Expression.test.d.ts.map +0 -1
- package/dist/presentation/abi3Expression.test.js +0 -392
- package/dist/presentation/buildGitHubCheckRunOutput.test.d.ts +0 -2
- package/dist/presentation/buildGitHubCheckRunOutput.test.d.ts.map +0 -1
- package/dist/presentation/buildGitHubCheckRunOutput.test.js +0 -163
- package/dist/presentation/bundleAccessorParity.test.d.ts +0 -9
- package/dist/presentation/bundleAccessorParity.test.d.ts.map +0 -1
- package/dist/presentation/bundleAccessorParity.test.js +0 -126
- package/dist/presentation/cliParity.test.d.ts +0 -2
- package/dist/presentation/cliParity.test.d.ts.map +0 -1
- package/dist/presentation/cliParity.test.js +0 -49
- package/dist/presentation/compose/narrativeChannels.test.d.ts +0 -2
- package/dist/presentation/compose/narrativeChannels.test.d.ts.map +0 -1
- package/dist/presentation/compose/narrativeChannels.test.js +0 -42
- package/dist/presentation/decisionSurface.test.d.ts +0 -2
- package/dist/presentation/decisionSurface.test.d.ts.map +0 -1
- package/dist/presentation/decisionSurface.test.js +0 -290
- package/dist/presentation/fixtures/dashboardCardGolden.test.d.ts +0 -2
- package/dist/presentation/fixtures/dashboardCardGolden.test.d.ts.map +0 -1
- package/dist/presentation/fixtures/dashboardCardGolden.test.js +0 -38
- package/dist/presentation/fixtures/presentationPersonaFixtures.d.ts +0 -11
- package/dist/presentation/fixtures/presentationPersonaFixtures.d.ts.map +0 -1
- package/dist/presentation/fixtures/presentationPersonaFixtures.js +0 -208
- package/dist/presentation/fixtures/scanResultFixtures.d.ts +0 -5
- package/dist/presentation/fixtures/scanResultFixtures.d.ts.map +0 -1
- package/dist/presentation/fixtures/scanResultFixtures.js +0 -123
- package/dist/presentation/intent/postureVocabularyGuard.test.d.ts +0 -2
- package/dist/presentation/intent/postureVocabularyGuard.test.d.ts.map +0 -1
- package/dist/presentation/intent/postureVocabularyGuard.test.js +0 -23
- package/dist/presentation/orchestration/buildScanPresentationBundle.test.d.ts +0 -2
- package/dist/presentation/orchestration/buildScanPresentationBundle.test.d.ts.map +0 -1
- package/dist/presentation/orchestration/buildScanPresentationBundle.test.js +0 -99
- package/dist/presentation/presentation.test.d.ts +0 -2
- package/dist/presentation/presentation.test.d.ts.map +0 -1
- package/dist/presentation/presentation.test.js +0 -126
- package/dist/presentation/presentationPersonas.test.d.ts +0 -2
- package/dist/presentation/presentationPersonas.test.d.ts.map +0 -1
- package/dist/presentation/presentationPersonas.test.js +0 -164
- package/dist/presentation/surfaceParity.test.d.ts +0 -2
- package/dist/presentation/surfaceParity.test.d.ts.map +0 -1
- package/dist/presentation/surfaceParity.test.js +0 -156
- package/dist/presentation/verificationChannelParity.test.d.ts +0 -2
- package/dist/presentation/verificationChannelParity.test.d.ts.map +0 -1
- package/dist/presentation/verificationChannelParity.test.js +0 -57
- package/dist/productMessaging.test.d.ts +0 -2
- package/dist/productMessaging.test.d.ts.map +0 -1
- package/dist/productMessaging.test.js +0 -97
- package/dist/repoIntelligenceSchema.contract.test.d.ts +0 -2
- package/dist/repoIntelligenceSchema.contract.test.d.ts.map +0 -1
- package/dist/repoIntelligenceSchema.contract.test.js +0 -51
- package/dist/repoIntelligenceSemantics.test.d.ts +0 -2
- package/dist/repoIntelligenceSemantics.test.d.ts.map +0 -1
- package/dist/repoIntelligenceSemantics.test.js +0 -63
- package/dist/repoIntelligenceValidation.persist.test.d.ts +0 -2
- package/dist/repoIntelligenceValidation.persist.test.d.ts.map +0 -1
- package/dist/repoIntelligenceValidation.persist.test.js +0 -44
- package/dist/riskSignals.test.d.ts +0 -2
- package/dist/riskSignals.test.d.ts.map +0 -1
- package/dist/riskSignals.test.js +0 -32
- package/dist/riskVocabulary.test.d.ts +0 -2
- package/dist/riskVocabulary.test.d.ts.map +0 -1
- package/dist/riskVocabulary.test.js +0 -14
- package/dist/scanPresentationBundle.golden.test.d.ts +0 -2
- package/dist/scanPresentationBundle.golden.test.d.ts.map +0 -1
- package/dist/scanPresentationBundle.golden.test.js +0 -78
- package/dist/scanResultSchema.test.d.ts +0 -2
- package/dist/scanResultSchema.test.d.ts.map +0 -1
- package/dist/scanResultSchema.test.js +0 -162
- package/dist/scanSurfaceCopy.test.d.ts +0 -2
- package/dist/scanSurfaceCopy.test.d.ts.map +0 -1
- package/dist/scanSurfaceCopy.test.js +0 -14
- package/dist/selectTopAffectedAreas.test.d.ts +0 -2
- package/dist/selectTopAffectedAreas.test.d.ts.map +0 -1
- package/dist/selectTopAffectedAreas.test.js +0 -179
- package/dist/trustedScanGuards.test.d.ts +0 -2
- package/dist/trustedScanGuards.test.d.ts.map +0 -1
- package/dist/trustedScanGuards.test.js +0 -151
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { deriveScanNarrative } from "./deriveScanNarrative.js";
|
|
3
|
-
import { legacyEngineRepoIntelligenceJwt } from "./fixtures/legacyEngineRepoIntelligence.js";
|
|
4
|
-
import { applyRepoIntelligenceValidation } from "./repoIntelligenceValidation.js";
|
|
5
|
-
const baseScan = {
|
|
6
|
-
totalScore: 40,
|
|
7
|
-
layerScores: {
|
|
8
|
-
security: 10,
|
|
9
|
-
maintainability: 10,
|
|
10
|
-
ecosystem: 10,
|
|
11
|
-
upgradeImpact: 10,
|
|
12
|
-
},
|
|
13
|
-
findings: [],
|
|
14
|
-
generatedAt: "2026-01-01T00:00:00.000Z",
|
|
15
|
-
changedPackages: ["jsonwebtoken"],
|
|
16
|
-
analysisPreparation: {
|
|
17
|
-
codeIntelligenceAvailable: true,
|
|
18
|
-
warnings: [],
|
|
19
|
-
},
|
|
20
|
-
};
|
|
21
|
-
describe("applyRepoIntelligenceValidation (production persist model)", () => {
|
|
22
|
-
it("retains raw invalid repoIntelligence and marks status invalid", () => {
|
|
23
|
-
const { result, contractFailed, logPayload } = applyRepoIntelligenceValidation({
|
|
24
|
-
...baseScan,
|
|
25
|
-
repoIntelligence: legacyEngineRepoIntelligenceJwt,
|
|
26
|
-
});
|
|
27
|
-
expect(contractFailed).toBe(true);
|
|
28
|
-
expect(logPayload?.issueCount).toBeGreaterThan(0);
|
|
29
|
-
expect(result.repoIntelligence).toEqual(legacyEngineRepoIntelligenceJwt);
|
|
30
|
-
expect(result.analysisPreparation?.repoIntelligenceValidation?.status).toBe("invalid");
|
|
31
|
-
expect(result.analysisPreparation?.warnings.some((w) => w.code === "repo_intelligence_contract_invalid")).toBe(true);
|
|
32
|
-
});
|
|
33
|
-
it("deriveScanNarrative ignores invalid wire (graph_fallback, untrusted parse status)", () => {
|
|
34
|
-
const { result } = applyRepoIntelligenceValidation({
|
|
35
|
-
...baseScan,
|
|
36
|
-
repoIntelligence: legacyEngineRepoIntelligenceJwt,
|
|
37
|
-
});
|
|
38
|
-
const facts = deriveScanNarrative(result);
|
|
39
|
-
expect(facts.availability.repoIntelligenceParse).toBe("untrusted");
|
|
40
|
-
expect(facts.availability.tiersPresent.tier1).toBe(false);
|
|
41
|
-
expect(facts.availability.mode).not.toBe("pr_intelligence");
|
|
42
|
-
expect(facts.packageUsage).toHaveLength(0);
|
|
43
|
-
});
|
|
44
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"riskSignals.test.d.ts","sourceRoot":"","sources":["../src/riskSignals.test.ts"],"names":[],"mappings":""}
|
package/dist/riskSignals.test.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { deriveRiskSignals } from "./riskSignals.js";
|
|
3
|
-
import { resolveRepositoryHealthScore } from "./prRiskWire.js";
|
|
4
|
-
function baseResult(overrides = {}) {
|
|
5
|
-
return {
|
|
6
|
-
totalScore: 63,
|
|
7
|
-
layerScores: {
|
|
8
|
-
security: 10,
|
|
9
|
-
maintainability: 20,
|
|
10
|
-
ecosystem: 30,
|
|
11
|
-
upgradeImpact: 15,
|
|
12
|
-
},
|
|
13
|
-
findings: [],
|
|
14
|
-
generatedAt: "2026-01-01T00:00:00.000Z",
|
|
15
|
-
...overrides,
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
describe("deriveRiskSignals", () => {
|
|
19
|
-
it("prefers prRisk.score over totalScore and repositoryHealth", () => {
|
|
20
|
-
const result = baseResult({
|
|
21
|
-
prRisk: { score: 55 },
|
|
22
|
-
repositoryHealth: { totalScore: 63 },
|
|
23
|
-
totalScore: 63,
|
|
24
|
-
});
|
|
25
|
-
expect(deriveRiskSignals(result)?.riskIndex).toBe(55);
|
|
26
|
-
expect(resolveRepositoryHealthScore(result)).toBe(63);
|
|
27
|
-
});
|
|
28
|
-
it("falls back to totalScore when prRisk is missing (historical scans)", () => {
|
|
29
|
-
const result = baseResult({ totalScore: 48 });
|
|
30
|
-
expect(deriveRiskSignals(result)?.riskIndex).toBe(48);
|
|
31
|
-
});
|
|
32
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"riskVocabulary.test.d.ts","sourceRoot":"","sources":["../src/riskVocabulary.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { mergePostureFromDecision } from "./riskVocabulary.js";
|
|
3
|
-
describe("mergePostureFromDecision", () => {
|
|
4
|
-
it("accepts only canonical lowercase merge posture tokens", () => {
|
|
5
|
-
expect(mergePostureFromDecision("safe")).toBe("safe");
|
|
6
|
-
expect(mergePostureFromDecision("needs_review")).toBe("needs_review");
|
|
7
|
-
expect(mergePostureFromDecision("risky")).toBe("risky");
|
|
8
|
-
});
|
|
9
|
-
it("rejects alternate engine spellings (no silent normalization)", () => {
|
|
10
|
-
expect(mergePostureFromDecision("SAFE")).toBeNull();
|
|
11
|
-
expect(mergePostureFromDecision("RISKY")).toBeNull();
|
|
12
|
-
expect(mergePostureFromDecision("BLOCK")).toBeNull();
|
|
13
|
-
});
|
|
14
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"scanPresentationBundle.golden.test.d.ts","sourceRoot":"","sources":["../src/scanPresentationBundle.golden.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { scoreToBand } from "./prRiskBand.js";
|
|
3
|
-
import { buildScanPresentationBundle } from "./presentation/orchestration/buildScanPresentationBundle.js";
|
|
4
|
-
import { scanResultBullmq, scanResultEslint, scanResultFastifyRuntime, scanResultLimitedContext, scanResultMixedTypescriptFastify, scanResultPrettier, scanResultTypescriptPatch, scanResultVitest, } from "./presentation/fixtures/presentationPersonaFixtures.js";
|
|
5
|
-
const validationPersonas = [
|
|
6
|
-
{ id: "fastify-runtime", result: scanResultFastifyRuntime },
|
|
7
|
-
{ id: "typescript-patch", result: scanResultTypescriptPatch },
|
|
8
|
-
{ id: "mixed-ts-fastify", result: scanResultMixedTypescriptFastify },
|
|
9
|
-
{ id: "prettier", result: scanResultPrettier },
|
|
10
|
-
{ id: "bullmq", result: scanResultBullmq },
|
|
11
|
-
{ id: "eslint", result: scanResultEslint },
|
|
12
|
-
{ id: "vitest", result: scanResultVitest },
|
|
13
|
-
{ id: "limited-context", result: scanResultLimitedContext },
|
|
14
|
-
];
|
|
15
|
-
function bundleFor(result) {
|
|
16
|
-
return buildScanPresentationBundle({
|
|
17
|
-
result,
|
|
18
|
-
pipelineStatus: "done",
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
describe("scanPresentationBundle golden — proof model", () => {
|
|
22
|
-
it.each(validationPersonas)("builds bundle for $id", ({ result }) => {
|
|
23
|
-
const bundle = bundleFor(result);
|
|
24
|
-
expect(bundle).not.toBeNull();
|
|
25
|
-
expect(bundle.facts.availability.corpusGateReason).toBeDefined();
|
|
26
|
-
expect(bundle.facts.confidence).toMatchObject({
|
|
27
|
-
limitedContext: expect.any(Boolean),
|
|
28
|
-
});
|
|
29
|
-
expect(bundle.facts.riskSignals).not.toBeNull();
|
|
30
|
-
expect(bundle.facts.riskSignals.layers).toHaveLength(4);
|
|
31
|
-
});
|
|
32
|
-
it("fastify-runtime — linkage and diagnostics", () => {
|
|
33
|
-
const bundle = bundleFor(scanResultFastifyRuntime);
|
|
34
|
-
const { facts } = bundle;
|
|
35
|
-
expect(facts.availability.corpusGateReason).toBe("ok");
|
|
36
|
-
expect(facts.confidence.limitedContext).toBe(false);
|
|
37
|
-
expect(facts.confidence.assessment).toBe("medium");
|
|
38
|
-
const apiArea = facts.affectedAreas.find((a) => a.id === "api");
|
|
39
|
-
expect(apiArea?.packages).toContain("fastify");
|
|
40
|
-
expect(apiArea?.findingIds).toContain("finding-fastify-runtime");
|
|
41
|
-
expect(apiArea?.paths.length).toBeGreaterThan(0);
|
|
42
|
-
expect(apiArea?.hotspotPackages).toContain("fastify");
|
|
43
|
-
expect(apiArea?.verificationFocus.length).toBeGreaterThan(0);
|
|
44
|
-
expect(facts.riskSignals?.riskIndex).toBe(55);
|
|
45
|
-
expect(facts.riskSignals?.band).toBe(scoreToBand(55));
|
|
46
|
-
expect(facts.riskIndex).toBe(facts.riskSignals?.riskIndex);
|
|
47
|
-
});
|
|
48
|
-
it("limited-context — diagnostics without synthetic confidence", () => {
|
|
49
|
-
const bundle = bundleFor(scanResultLimitedContext);
|
|
50
|
-
const { facts } = bundle;
|
|
51
|
-
expect(facts.availability.corpusGateReason).toBe("no_code_intelligence");
|
|
52
|
-
expect(facts.availability.preparationWarnings.some((w) => w.code === "base_lockfile_missing")).toBe(true);
|
|
53
|
-
expect(facts.confidence.assessment).toBe("low");
|
|
54
|
-
expect(facts.confidence.limitedContext).toBe(true);
|
|
55
|
-
});
|
|
56
|
-
it("typescript-patch — tooling semantics with risk signals", () => {
|
|
57
|
-
const bundle = bundleFor(scanResultTypescriptPatch);
|
|
58
|
-
const { facts } = bundle;
|
|
59
|
-
expect(facts.availability.corpusGateReason).toBe("ok");
|
|
60
|
-
expect(facts.packageSemantics?.dependencyClass).toBe("tooling");
|
|
61
|
-
expect(facts.riskSignals?.riskIndex).toBe(30);
|
|
62
|
-
expect(facts.riskSignals?.band).toBe(scoreToBand(30));
|
|
63
|
-
});
|
|
64
|
-
it("affected areas always carry linkage arrays (structure)", () => {
|
|
65
|
-
for (const { result } of validationPersonas) {
|
|
66
|
-
const bundle = bundleFor(result);
|
|
67
|
-
if (!bundle)
|
|
68
|
-
continue;
|
|
69
|
-
for (const area of bundle.facts.affectedAreas) {
|
|
70
|
-
expect(Array.isArray(area.packages)).toBe(true);
|
|
71
|
-
expect(Array.isArray(area.findingIds)).toBe(true);
|
|
72
|
-
expect(Array.isArray(area.paths)).toBe(true);
|
|
73
|
-
expect(Array.isArray(area.hotspotPackages)).toBe(true);
|
|
74
|
-
expect(Array.isArray(area.verificationFocus)).toBe(true);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"scanResultSchema.test.d.ts","sourceRoot":"","sources":["../src/scanResultSchema.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { safeParseScanResult, parseScanResultOrThrow, scanResultSchema, safeParseEngineOutputScanResult, parseEngineOutputScanResultOrThrow, } from "./scanResultSchema.js";
|
|
3
|
-
import { emptyReachScope, emptyVerificationScope, minimalReviewFocalPoint, } from "./fixtures/assessmentScopeFixtures.js";
|
|
4
|
-
import { withAbi4EngineScores } from "./fixtures/engineAbi4Fixtures.js";
|
|
5
|
-
const minimalValid = {
|
|
6
|
-
totalScore: 42,
|
|
7
|
-
layerScores: {
|
|
8
|
-
security: 10,
|
|
9
|
-
maintainability: 20,
|
|
10
|
-
ecosystem: 30,
|
|
11
|
-
upgradeImpact: 40,
|
|
12
|
-
},
|
|
13
|
-
findings: [],
|
|
14
|
-
generatedAt: "2026-01-01T00:00:00.000Z",
|
|
15
|
-
};
|
|
16
|
-
describe("scanResultSchema", () => {
|
|
17
|
-
it("accepts a minimal valid payload", () => {
|
|
18
|
-
const r = safeParseScanResult(minimalValid);
|
|
19
|
-
expect(r.ok).toBe(true);
|
|
20
|
-
if (r.ok)
|
|
21
|
-
expect(r.result.totalScore).toBe(42);
|
|
22
|
-
});
|
|
23
|
-
it("preserves unknown top-level keys (passthrough)", () => {
|
|
24
|
-
const r = safeParseScanResult({
|
|
25
|
-
...minimalValid,
|
|
26
|
-
futureEngineField: { x: 1 },
|
|
27
|
-
});
|
|
28
|
-
expect(r.ok).toBe(true);
|
|
29
|
-
if (r.ok)
|
|
30
|
-
expect(r.result.futureEngineField).toEqual({
|
|
31
|
-
x: 1,
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
it("rejects missing layerScores", () => {
|
|
35
|
-
const r = safeParseScanResult({
|
|
36
|
-
totalScore: 1,
|
|
37
|
-
findings: [],
|
|
38
|
-
generatedAt: "2026-01-01T00:00:00.000Z",
|
|
39
|
-
});
|
|
40
|
-
expect(r.ok).toBe(false);
|
|
41
|
-
});
|
|
42
|
-
it("rejects out-of-range totalScore", () => {
|
|
43
|
-
const r = safeParseScanResult({
|
|
44
|
-
...minimalValid,
|
|
45
|
-
totalScore: 101,
|
|
46
|
-
});
|
|
47
|
-
expect(r.ok).toBe(false);
|
|
48
|
-
});
|
|
49
|
-
it("rejects invalid decision.recommendation", () => {
|
|
50
|
-
const r = safeParseScanResult({
|
|
51
|
-
...minimalValid,
|
|
52
|
-
decision: { recommendation: "maybe", reasoning: [] },
|
|
53
|
-
});
|
|
54
|
-
expect(r.ok).toBe(false);
|
|
55
|
-
});
|
|
56
|
-
it("rejects non-canonical merge posture tokens (e.g. proprietary uppercase)", () => {
|
|
57
|
-
const r = safeParseScanResult({
|
|
58
|
-
...minimalValid,
|
|
59
|
-
decision: { recommendation: "SAFE", reasoning: [] },
|
|
60
|
-
});
|
|
61
|
-
expect(r.ok).toBe(false);
|
|
62
|
-
});
|
|
63
|
-
it("rejects empty generatedAt", () => {
|
|
64
|
-
const r = safeParseScanResult({
|
|
65
|
-
...minimalValid,
|
|
66
|
-
generatedAt: "",
|
|
67
|
-
});
|
|
68
|
-
expect(r.ok).toBe(false);
|
|
69
|
-
});
|
|
70
|
-
it("parseScanResultOrThrow returns on success", () => {
|
|
71
|
-
expect(parseScanResultOrThrow(minimalValid).totalScore).toBe(42);
|
|
72
|
-
});
|
|
73
|
-
it("parseScanResultOrThrow throws with validation prefix", () => {
|
|
74
|
-
expect(() => parseScanResultOrThrow({})).toThrow(/^validation:/);
|
|
75
|
-
});
|
|
76
|
-
it("scanResultSchema default empty findings when omitted", () => {
|
|
77
|
-
const { findings: _f, ...rest } = minimalValid;
|
|
78
|
-
const parsed = scanResultSchema.parse(rest);
|
|
79
|
-
expect(parsed.findings).toEqual([]);
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
const minimalAssessment = {
|
|
83
|
-
reviewFocalPoint: minimalReviewFocalPoint(["typescript"]),
|
|
84
|
-
reachScope: emptyReachScope(),
|
|
85
|
-
verificationScope: emptyVerificationScope(),
|
|
86
|
-
posture: "safe",
|
|
87
|
-
confidence: "high",
|
|
88
|
-
primaryConcern: null,
|
|
89
|
-
concerns: [],
|
|
90
|
-
factors: ["tooling_maintenance"],
|
|
91
|
-
changeClasses: ["tooling_maintenance"],
|
|
92
|
-
reasoning: [],
|
|
93
|
-
confidenceRationale: "",
|
|
94
|
-
presentation: {
|
|
95
|
-
narrativeIntensity: "minimal",
|
|
96
|
-
reachVisibility: "hidden",
|
|
97
|
-
verificationIntensity: "advisory",
|
|
98
|
-
insightEmissionFloor: "none",
|
|
99
|
-
reportMode: "high_signal_pr",
|
|
100
|
-
},
|
|
101
|
-
};
|
|
102
|
-
describe("engineOutputScanResultSchema (strict, fresh engine only)", () => {
|
|
103
|
-
const withMethodology = withAbi4EngineScores({
|
|
104
|
-
...minimalValid,
|
|
105
|
-
methodologyVersion: "engine-test-fixture/v1",
|
|
106
|
-
assessment: minimalAssessment,
|
|
107
|
-
});
|
|
108
|
-
it("rejects payload that relaxed parser accepts when methodology is missing", () => {
|
|
109
|
-
const r = safeParseEngineOutputScanResult(minimalValid);
|
|
110
|
-
expect(r.ok).toBe(false);
|
|
111
|
-
});
|
|
112
|
-
it("accepts when methodology and parseable generatedAt are present", () => {
|
|
113
|
-
const r = safeParseEngineOutputScanResult(withMethodology);
|
|
114
|
-
expect(r.ok).toBe(true);
|
|
115
|
-
if (r.ok)
|
|
116
|
-
expect(r.result.methodologyVersion).toBe("engine-test-fixture/v1");
|
|
117
|
-
});
|
|
118
|
-
it("synthesizes prRisk/repositoryHealth from totalScore for legacy engines", () => {
|
|
119
|
-
const r = safeParseEngineOutputScanResult({
|
|
120
|
-
...minimalValid,
|
|
121
|
-
methodologyVersion: "acme-prod/v1",
|
|
122
|
-
assessment: minimalAssessment,
|
|
123
|
-
});
|
|
124
|
-
expect(r.ok).toBe(true);
|
|
125
|
-
if (r.ok) {
|
|
126
|
-
expect(r.result.prRisk?.score).toBe(42);
|
|
127
|
-
expect(r.result.repositoryHealth?.totalScore).toBe(42);
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
it("parseEngineOutputScanResultOrThrow throws with validation prefix", () => {
|
|
131
|
-
expect(() => parseEngineOutputScanResultOrThrow(minimalValid)).toThrow(/^validation:/);
|
|
132
|
-
});
|
|
133
|
-
it("rejects unparseable generatedAt", () => {
|
|
134
|
-
const r = safeParseEngineOutputScanResult({
|
|
135
|
-
...withMethodology,
|
|
136
|
-
generatedAt: "not-a-date",
|
|
137
|
-
});
|
|
138
|
-
expect(r.ok).toBe(false);
|
|
139
|
-
});
|
|
140
|
-
it("rejects ABI-1 assessment missing focal/scope fields on fresh engine path", () => {
|
|
141
|
-
const legacyAssessment = {
|
|
142
|
-
posture: "safe",
|
|
143
|
-
confidence: "high",
|
|
144
|
-
primaryConcern: null,
|
|
145
|
-
concerns: [],
|
|
146
|
-
factors: ["tooling_maintenance"],
|
|
147
|
-
changeClasses: ["tooling_maintenance"],
|
|
148
|
-
presentation: {
|
|
149
|
-
narrativeIntensity: "minimal",
|
|
150
|
-
reachVisibility: "hidden",
|
|
151
|
-
verificationIntensity: "advisory",
|
|
152
|
-
insightEmissionFloor: "none",
|
|
153
|
-
reportMode: "high_signal_pr",
|
|
154
|
-
},
|
|
155
|
-
};
|
|
156
|
-
const r = safeParseEngineOutputScanResult({
|
|
157
|
-
...withMethodology,
|
|
158
|
-
assessment: legacyAssessment,
|
|
159
|
-
});
|
|
160
|
-
expect(r.ok).toBe(false);
|
|
161
|
-
});
|
|
162
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"scanSurfaceCopy.test.d.ts","sourceRoot":"","sources":["../src/scanSurfaceCopy.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { dirname, join } from "node:path";
|
|
5
|
-
import { scanSurfaceCopyFlat } from "./scanSurfaceCopy.js";
|
|
6
|
-
describe("scanSurfaceCopy", () => {
|
|
7
|
-
it("matches generated CI JSON (no drift)", () => {
|
|
8
|
-
const root = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "..");
|
|
9
|
-
const jsonPath = join(root, "scripts/ci/scan-surface-copy.generated.json");
|
|
10
|
-
const onDisk = JSON.parse(readFileSync(jsonPath, "utf8"));
|
|
11
|
-
const fromTs = scanSurfaceCopyFlat();
|
|
12
|
-
expect(fromTs).toEqual(onDisk);
|
|
13
|
-
});
|
|
14
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"selectTopAffectedAreas.test.d.ts","sourceRoot":"","sources":["../src/selectTopAffectedAreas.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { selectTopAffectedAreas } from "./selectTopAffectedAreas.js";
|
|
3
|
-
function makeScanResult(overrides = {}) {
|
|
4
|
-
return {
|
|
5
|
-
totalScore: 60,
|
|
6
|
-
layerScores: {
|
|
7
|
-
security: 70,
|
|
8
|
-
maintainability: 50,
|
|
9
|
-
ecosystem: 40,
|
|
10
|
-
upgradeImpact: 30,
|
|
11
|
-
},
|
|
12
|
-
findings: [],
|
|
13
|
-
generatedAt: "2026-01-01T00:00:00Z",
|
|
14
|
-
...overrides,
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
describe("selectTopAffectedAreas", () => {
|
|
18
|
-
it("returns [] for null input", () => {
|
|
19
|
-
expect(selectTopAffectedAreas(null)).toEqual([]);
|
|
20
|
-
});
|
|
21
|
-
it("returns [] for undefined input", () => {
|
|
22
|
-
expect(selectTopAffectedAreas(undefined)).toEqual([]);
|
|
23
|
-
});
|
|
24
|
-
it("returns at most 3 results by default", () => {
|
|
25
|
-
const result = makeScanResult({
|
|
26
|
-
explain: {
|
|
27
|
-
reasons: [
|
|
28
|
-
{ id: "r1", layer: "security", title: "Auth flow", scoreImpact: -20 },
|
|
29
|
-
{
|
|
30
|
-
id: "r2",
|
|
31
|
-
layer: "security",
|
|
32
|
-
title: "State sync",
|
|
33
|
-
scoreImpact: -15,
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
id: "r3",
|
|
37
|
-
layer: "ecosystem",
|
|
38
|
-
title: "Middleware",
|
|
39
|
-
scoreImpact: -10,
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
id: "r4",
|
|
43
|
-
layer: "maintainability",
|
|
44
|
-
title: "Extra area",
|
|
45
|
-
scoreImpact: -5,
|
|
46
|
-
},
|
|
47
|
-
],
|
|
48
|
-
},
|
|
49
|
-
});
|
|
50
|
-
const areas = selectTopAffectedAreas(result);
|
|
51
|
-
expect(areas.length).toBe(3);
|
|
52
|
-
expect(areas).toEqual(["Auth flow", "State sync", "Middleware"]);
|
|
53
|
-
});
|
|
54
|
-
it("respects custom max", () => {
|
|
55
|
-
const result = makeScanResult({
|
|
56
|
-
explain: {
|
|
57
|
-
reasons: [
|
|
58
|
-
{ id: "r1", layer: "security", title: "Area A", scoreImpact: -20 },
|
|
59
|
-
{ id: "r2", layer: "security", title: "Area B", scoreImpact: -15 },
|
|
60
|
-
],
|
|
61
|
-
},
|
|
62
|
-
});
|
|
63
|
-
expect(selectTopAffectedAreas(result, { max: 1 })).toEqual(["Area A"]);
|
|
64
|
-
});
|
|
65
|
-
it("sorts explain reasons by absolute scoreImpact descending", () => {
|
|
66
|
-
const result = makeScanResult({
|
|
67
|
-
explain: {
|
|
68
|
-
reasons: [
|
|
69
|
-
{ id: "r1", layer: "security", title: "Minor", scoreImpact: -2 },
|
|
70
|
-
{ id: "r2", layer: "security", title: "Critical", scoreImpact: -30 },
|
|
71
|
-
{ id: "r3", layer: "ecosystem", title: "Medium", scoreImpact: -15 },
|
|
72
|
-
],
|
|
73
|
-
},
|
|
74
|
-
});
|
|
75
|
-
const areas = selectTopAffectedAreas(result);
|
|
76
|
-
expect(areas[0]).toBe("Critical");
|
|
77
|
-
expect(areas[1]).toBe("Medium");
|
|
78
|
-
});
|
|
79
|
-
it("falls back to insights when explain is empty", () => {
|
|
80
|
-
const result = makeScanResult({
|
|
81
|
-
insights: [
|
|
82
|
-
{
|
|
83
|
-
type: "behavioral_change",
|
|
84
|
-
priority: "high",
|
|
85
|
-
confidence: "confirmed",
|
|
86
|
-
scope: "changed",
|
|
87
|
-
message: "Auth middleware changed. Check tokens",
|
|
88
|
-
context: "src/auth.ts",
|
|
89
|
-
remediation: "Review carefully",
|
|
90
|
-
},
|
|
91
|
-
],
|
|
92
|
-
});
|
|
93
|
-
const areas = selectTopAffectedAreas(result);
|
|
94
|
-
expect(areas).toEqual(["Auth middleware changed"]);
|
|
95
|
-
});
|
|
96
|
-
it("falls back to layer names when explain and insights are empty", () => {
|
|
97
|
-
const result = makeScanResult();
|
|
98
|
-
const areas = selectTopAffectedAreas(result);
|
|
99
|
-
expect(areas.length).toBeGreaterThan(0);
|
|
100
|
-
expect(areas.length).toBeLessThanOrEqual(3);
|
|
101
|
-
expect(areas).toContain("Security");
|
|
102
|
-
});
|
|
103
|
-
it("deduplicates normalized labels", () => {
|
|
104
|
-
const result = makeScanResult({
|
|
105
|
-
explain: {
|
|
106
|
-
reasons: [
|
|
107
|
-
{ id: "r1", layer: "security", title: "Auth flow", scoreImpact: -20 },
|
|
108
|
-
{ id: "r2", layer: "security", title: "auth flow", scoreImpact: -15 },
|
|
109
|
-
{
|
|
110
|
-
id: "r3",
|
|
111
|
-
layer: "ecosystem",
|
|
112
|
-
title: "Auth flow",
|
|
113
|
-
scoreImpact: -10,
|
|
114
|
-
},
|
|
115
|
-
],
|
|
116
|
-
},
|
|
117
|
-
});
|
|
118
|
-
const areas = selectTopAffectedAreas(result);
|
|
119
|
-
const unique = new Set(areas.map((a) => a.toLowerCase()));
|
|
120
|
-
expect(unique.size).toBe(areas.length);
|
|
121
|
-
expect(areas).toContain("Auth flow");
|
|
122
|
-
expect(areas.filter((a) => a.toLowerCase() === "auth flow").length).toBe(1);
|
|
123
|
-
});
|
|
124
|
-
it("trims the 'Finding: ' prefix from labels", () => {
|
|
125
|
-
const result = makeScanResult({
|
|
126
|
-
explain: {
|
|
127
|
-
reasons: [
|
|
128
|
-
{
|
|
129
|
-
id: "r1",
|
|
130
|
-
layer: "security",
|
|
131
|
-
title: "Finding: Outdated lodash",
|
|
132
|
-
scoreImpact: -20,
|
|
133
|
-
},
|
|
134
|
-
],
|
|
135
|
-
},
|
|
136
|
-
});
|
|
137
|
-
const areas = selectTopAffectedAreas(result);
|
|
138
|
-
expect(areas[0]).toBe("Outdated lodash");
|
|
139
|
-
});
|
|
140
|
-
it("truncates labels longer than maxLabelLen with ellipsis", () => {
|
|
141
|
-
const long = "A".repeat(50);
|
|
142
|
-
const result = makeScanResult({
|
|
143
|
-
explain: {
|
|
144
|
-
reasons: [
|
|
145
|
-
{ id: "r1", layer: "security", title: long, scoreImpact: -10 },
|
|
146
|
-
],
|
|
147
|
-
},
|
|
148
|
-
});
|
|
149
|
-
const areas = selectTopAffectedAreas(result, { maxLabelLen: 10 });
|
|
150
|
-
expect(areas[0].length).toBeLessThanOrEqual(10);
|
|
151
|
-
expect(areas[0].endsWith("…")).toBe(true);
|
|
152
|
-
});
|
|
153
|
-
it("returns empty array when no useful signal at all", () => {
|
|
154
|
-
// Completely empty scan result with empty layerScores
|
|
155
|
-
const result = {
|
|
156
|
-
totalScore: 0,
|
|
157
|
-
layerScores: {
|
|
158
|
-
security: 0,
|
|
159
|
-
maintainability: 0,
|
|
160
|
-
ecosystem: 0,
|
|
161
|
-
upgradeImpact: 0,
|
|
162
|
-
},
|
|
163
|
-
findings: [],
|
|
164
|
-
generatedAt: "2026-01-01T00:00:00Z",
|
|
165
|
-
};
|
|
166
|
-
// With layer fallback this would return layer names; only truly empty when
|
|
167
|
-
// caller passes a result without layerScores entirely — validate max guard
|
|
168
|
-
const areas = selectTopAffectedAreas(result, { max: 3 });
|
|
169
|
-
expect(areas.length).toBeLessThanOrEqual(3);
|
|
170
|
-
});
|
|
171
|
-
it("returns [] when layerScores is missing and no explain/insights", () => {
|
|
172
|
-
const partial = {
|
|
173
|
-
totalScore: 10,
|
|
174
|
-
findings: [],
|
|
175
|
-
generatedAt: "2026-01-01T00:00:00Z",
|
|
176
|
-
};
|
|
177
|
-
expect(selectTopAffectedAreas(partial)).toEqual([]);
|
|
178
|
-
});
|
|
179
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"trustedScanGuards.test.d.ts","sourceRoot":"","sources":["../src/trustedScanGuards.test.ts"],"names":[],"mappings":""}
|