Files
rea/tests/process-global/javascriptJsonDocumentFailures.test.ts
T

150 lines
4.8 KiB
TypeScript

import { expect, it, onTestFinished, vi } from "vitest";
import { createJavaScriptArtifactReader } from "../../src/artifacts/javascript/JavaScriptArtifactReader.js";
import { analyzeJavaScriptArtifactFiles } from "../../src/application/javascript/JavaScriptArtifactAnalysis.js";
import { buildJavaScriptArtifactGraph } from "../../src/application/javascript/JavaScriptArtifactGraphBuilder.js";
import type {
JavaScriptArtifactFile,
JavaScriptArtifactFileSet,
} from "../../src/domain/javascript/javascriptArtifactFiles.js";
import { scanCanonicalArtifactInventory } from "../fixtures/artifactInventory.js";
import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js";
import { writeFixtureFiles } from "../support/javascriptApplicationFixture.js";
import { readJavaScriptArtifactFiles } from "../support/javascriptApplicationScope.js";
const packageJsonText = '{"name":"fault-package"}';
const jsonText = '{"kind":"fault-json"}';
const artifactFile = (
path: string,
kind: "package-json" | "json",
text: string,
): JavaScriptArtifactFile => ({
path,
container_sha256: "container-digest",
sha256: `digest-${path}`,
bytes: Buffer.byteLength(text),
inventory_artifact_id: `artifact-${path}`,
kind,
unpacked: false,
text: { included: true, value: text },
});
const fileSet = (): JavaScriptArtifactFileSet => ({
files: [
artifactFile("package.json", "package-json", packageJsonText),
artifactFile("settings.json", "json", jsonText),
],
unavailable_relevant_files: [],
containers: [],
text_bytes_read:
Buffer.byteLength(packageJsonText) + Buffer.byteLength(jsonText),
invalid_utf8_files: 0,
});
it.each([
{
document: "package.json",
text: packageJsonText,
create: () => new TypeError("parser fault"),
},
{
document: "package.json",
text: packageJsonText,
create: () => new Error("parser fault"),
},
{
document: "ordinary JSON",
text: jsonText,
create: () => new TypeError("parser fault"),
},
{
document: "ordinary JSON",
text: jsonText,
create: () => new Error("parser fault"),
},
])(
"propagates unexpected JSON.parse faults from $document",
({ text, create }) => {
const files = fileSet();
const originalParse = JSON.parse;
const thrown = create();
vi.spyOn(JSON, "parse").mockImplementation((input) => {
if (input === text) throw thrown;
return originalParse(input);
});
onTestFinished(() => {
vi.restoreAllMocks();
});
let caught: unknown;
try {
analyzeJavaScriptArtifactFiles(files);
} catch (cause: unknown) {
caught = cause;
}
expect(caught).toBe(thrown);
},
);
it("keeps malformed JSON as an invalid observation", () => {
const malformedPackage = artifactFile("package.json", "package-json", "{");
const malformedJson = artifactFile("settings.json", "json", "{");
const analysis = analyzeJavaScriptArtifactFiles({
...fileSet(),
files: [malformedPackage, malformedJson],
});
expect(analysis.packages).toMatchObject([{ status: "invalid" }]);
expect(analysis.json_modules).toMatchObject([{ status: "invalid" }]);
});
it("reuses parsed package and JSON facts while building the public graph", async () => {
const root = await createTestTempDirectory("rea-json-document-graph-");
await writeFixtureFiles(root, {
"main.cjs": 'require("./dep"); require("./config.json");',
"dep/package.json": '{"main":"index.cjs"}',
"dep/index.cjs": "module.exports = 1;",
"config.json": '{"enabled":true}',
});
const snapshot = await scanCanonicalArtifactInventory(root, {});
const reader = createJavaScriptArtifactReader(root, "directory");
try {
const files = await readJavaScriptArtifactFiles(reader, snapshot);
const analysis = analyzeJavaScriptArtifactFiles(files);
const depPackageText = '{"main":"index.cjs"}';
const configText = '{"enabled":true}';
const originalParse = JSON.parse;
vi.spyOn(JSON, "parse").mockImplementation((text) => {
if (text === depPackageText || text === configText)
throw new TypeError("unexpected JSON reparse");
return originalParse(text);
});
const graph = buildJavaScriptArtifactGraph(snapshot, files, analysis);
expect(
graph.edges.find(
({ relation, properties }) =>
relation === "imports" && properties.specifier === "./dep",
)?.properties,
).toMatchObject({
resolution_status: "resolved",
resolved_path: "dep/index.cjs",
});
expect(
graph.edges.find(
({ relation, properties }) =>
relation === "imports" &&
properties.specifier === "./config.json" &&
Object.hasOwn(properties, "target_json_status"),
)?.properties,
).toMatchObject({
target_file_kind: "json",
target_json_status: "included",
});
} finally {
vi.restoreAllMocks();
await reader.close();
}
});