fix(javascript): report located sources with differing digests as modified (#1216)
This commit is contained in:
1 parent
c89b3a00dc
commit
01174c4a81
3 files changed
+73
-1
No files matched your search
@@ -136,6 +136,56 @@ describe("historical source to bundle comparison", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("source to bundle digest changes", () => {
|
||||
it("reports a located source with a different digest as modified", () => {
|
||||
const node = createJavaScriptApplicationNode({
|
||||
kind: "source-module",
|
||||
identity: {
|
||||
strategy: "source-map-original",
|
||||
stability: "source-map-exact",
|
||||
source_map_sha256: HASH.artifact,
|
||||
original_source: "webpack://fixture/./src/modified.ts",
|
||||
source_sha256: HASH.unchanged,
|
||||
},
|
||||
observations: [
|
||||
{
|
||||
label: "src/modified.ts",
|
||||
properties: { source_sha256: HASH.unchanged },
|
||||
evidence: artifactEvidence(HASH.artifact, "dist/main.js.map"),
|
||||
},
|
||||
],
|
||||
});
|
||||
const graph = createJavaScriptApplicationGraph({
|
||||
schema: "JavaScriptApplicationGraph",
|
||||
root_node_ids: [node.node_id],
|
||||
nodes: [node],
|
||||
edges: [],
|
||||
coverage: {
|
||||
status: "complete",
|
||||
truncated: false,
|
||||
omitted_count: 0,
|
||||
limits: [],
|
||||
},
|
||||
limitations: [],
|
||||
});
|
||||
const result = compareSourceToBundle({
|
||||
reference: historicalGraph("complete", ["src/modified.ts"]),
|
||||
application: {
|
||||
evidenceId: EVIDENCE_ID,
|
||||
rootArtifactSha256: HASH.artifact,
|
||||
graph,
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.summary).toMatchObject({ modified: 1, unknown: 0 });
|
||||
expect(item(result, "src/modified.ts")).toMatchObject({
|
||||
status: "modified",
|
||||
confidence: "high",
|
||||
current_node_ids: [node.node_id],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("source to bundle path syntax", () => {
|
||||
it.each(["src/modified#part.ts", "src/modified?part.ts"])(
|
||||
"preserves literal filesystem punctuation in %s",
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
} from "./sourceToBundleComparisonSchemas.js";
|
||||
import {
|
||||
buildSourceToBundleCandidateIndex,
|
||||
candidateHasDigest,
|
||||
candidateIdsForSource,
|
||||
historicalSourceFiles,
|
||||
scoreSourceToBundleCandidate,
|
||||
@@ -125,7 +126,12 @@ const compareMappings = (
|
||||
(candidate): candidate is SourceToBundleCandidate => candidate !== null,
|
||||
)
|
||||
.sort(compareCandidates);
|
||||
const classification = classifyMapping(scored, projection.absenceComplete);
|
||||
const classification = classifyMapping(
|
||||
scored,
|
||||
projection.absenceComplete,
|
||||
(nodeId) =>
|
||||
source.sha256 !== null && candidateHasDigest(projection.index, nodeId),
|
||||
);
|
||||
const semantic = {
|
||||
source_path: source.path,
|
||||
source_sha256: source.sha256,
|
||||
@@ -175,6 +181,7 @@ const scoringModel = (): SourceToBundleComparisonResult["scoring"] => ({
|
||||
const classifyMapping = (
|
||||
candidates: readonly SourceToBundleCandidate[],
|
||||
absenceComplete: boolean,
|
||||
digestComparable: (nodeId: string) => boolean,
|
||||
): ClassifiedMapping => {
|
||||
const exact = candidates.filter((candidate) =>
|
||||
hasSignal(candidate, "exact-source-digest"),
|
||||
@@ -213,6 +220,15 @@ const classifyMapping = (
|
||||
: pathCandidates.filter(({ score }) => score === topScore);
|
||||
if (topPathCandidates.length === 1) {
|
||||
const candidate = topPathCandidates[0];
|
||||
if (candidate !== undefined && digestComparable(candidate.current_node_id))
|
||||
return {
|
||||
status: "modified",
|
||||
confidence: candidate.confidence === "high" ? "high" : "medium",
|
||||
currentNodeIds: [candidate.current_node_id],
|
||||
limitations: [
|
||||
"Location evidence links the source to the current node, but their source digests differ.",
|
||||
],
|
||||
};
|
||||
return {
|
||||
status: "unknown",
|
||||
confidence: "unknown",
|
||||
|
||||
@@ -135,6 +135,12 @@ export const candidateIdsForSource = (
|
||||
return output;
|
||||
};
|
||||
|
||||
/** Whether an indexed candidate carries any content digest to compare. */
|
||||
export const candidateHasDigest = (
|
||||
index: SourceToBundleCandidateIndex,
|
||||
nodeId: string,
|
||||
): boolean => (index.nodes.get(nodeId)?.digests.size ?? 0) > 0;
|
||||
|
||||
/** Score one indexed candidate from explicit, caller-visible signals. */
|
||||
export const scoreSourceToBundleCandidate = (
|
||||
source: SourceFile,
|
||||
|
||||
Reference in new issue
Block a user