fix(contracts): complete canonical mobile graph inventory examples (#4026)

* fix(contracts): complete canonical mobile graph inventory examples

* test(contracts): place canonical graph CLI regression in acceptance

* refactor(contracts): reuse artifact identity owners in mobile examples
This commit is contained in:
Ace authored and GitHub committed 2026-10-17 00:15:58 +08:00
1 parent e0e8b30b7f
commit 90457af181
2 files changed
+134 -26

No files matched your search

+29 -26
View File
@@ -1,18 +1,28 @@
import { digestCanonicalValue } from "../domain/canonicalDigest.js";
import { createEvidence } from "../domain/evidence.js";
import { jsonValueSchema } from "../domain/jsonValue.js";
import {
artifactInventoryResultSchema,
type ArtifactNode,
type ArtifactOccurrence,
} from "../domain/artifactGraph.js";
import {
artifactGraphDigest,
artifactIdForContent,
artifactManifestId,
occurrenceIdForLocation,
} from "../domain/artifactIdentity.js";
const inventory = (
format: "apk" | "ipa" | "directory" | "hap",
digit: string,
) => {
const sha = digit.repeat(64);
const artifactId = `art_${digestCanonicalValue({ sha256: sha }, "Artifact inventory")}`;
const occurrenceId = `occ_${digestCanonicalValue(
{ root: artifactId },
"Artifact inventory",
)}`;
const nodes = [
const artifactId = artifactIdForContent(sha);
const occurrenceId = occurrenceIdForLocation({
rootArtifactId: artifactId,
logicalPath: ".",
entryKind: "file",
});
const nodes: ArtifactNode[] = [
{
artifact_id: artifactId,
format: format === "directory" ? "directory" : "zip",
@@ -24,7 +34,7 @@ const inventory = (
limitations: [],
},
];
const occurrences = [
const occurrences: ArtifactOccurrence[] = [
{
occurrence_id: occurrenceId,
artifact_id: artifactId,
@@ -42,24 +52,15 @@ const inventory = (
limitations: [],
},
];
const graphSha256 = digestCanonicalValue(
{
nodes,
occurrences,
edges: [],
integrity_contradictions: [],
},
"Artifact inventory",
);
return jsonValueSchema.parse({
const graphSha256 = artifactGraphDigest({
nodes,
occurrences,
edges: [],
contradictions: [],
});
return artifactInventoryResultSchema.parse({
manifest: {
manifest_id: `agm_${digestCanonicalValue(
{
root_artifact_id: artifactId,
graph_sha256: graphSha256,
},
"Artifact inventory",
)}`,
manifest_id: artifactManifestId(artifactId, graphSha256),
root_artifact_id: artifactId,
root_sha256: sha,
root_format: format,
@@ -72,6 +73,8 @@ const inventory = (
occurrences,
edges: [],
provenance: [],
integrity_contradictions: [],
coverage: { scope: "root", status: "complete" },
limitations: [],
});
};
@@ -0,0 +1,105 @@
import { writeFile } from "node:fs/promises";
import { join } from "node:path";
import { expect } from "vitest";
import {
ANDROID_APPLICATION_GRAPH_EXAMPLE,
APPLE_APPLICATION_GRAPH_EXAMPLE,
HARMONY_APPLICATION_GRAPH_EXAMPLE,
MACOS_APPLICATION_GRAPH_EXAMPLE,
} from "../../../src/contracts/mobileApplicationGraphExamples.js";
import { createEvidence, parseEvidence } from "../../../src/domain/evidence.js";
import { artifactInventoryResultSchema } from "../../../src/domain/artifactGraph.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { cliTest } from "../../support/cli/cliFixture.js";
cliTest.for([
{
name: "Android",
command: "project-android-application-graph",
operation: "project_android_application_graph",
example: ANDROID_APPLICATION_GRAPH_EXAMPLE,
},
{
name: "IPA",
command: "project-apple-application-graph",
operation: "project_apple_application_graph",
example: APPLE_APPLICATION_GRAPH_EXAMPLE,
},
{
name: "macOS",
command: "project-apple-application-graph",
operation: "project_apple_application_graph",
example: MACOS_APPLICATION_GRAPH_EXAMPLE,
},
{
name: "Harmony",
command: "project-harmony-application-graph",
operation: "project_harmony_application_graph",
example: HARMONY_APPLICATION_GRAPH_EXAMPLE,
},
] as const)(
"CLI executes the canonical $name graph example without target files",
async ({ command, operation, example }, { cli }) => {
const root = await createTestTempDirectory("rea-mobile-graph-example-");
const input = JSON.stringify(example);
const path = join(root, "example.json");
await writeFile(path, input);
for (const argument of [input, path]) {
const result = await cli.run({
arguments: [command, argument, "--json"],
cwd: root,
});
expect(result.exitCode, result.stdout).toBe(0);
const evidence = parseEvidence(result.json);
expect(evidence.operation).toBe(operation);
expect(evidence.normalized_result).toMatchObject({
coverage: { status: "partial", inventory_complete: true },
});
}
},
);
cliTest(
"CLI still rejects an unsupported inventory coverage status",
async ({ cli }) => {
const original = ANDROID_APPLICATION_GRAPH_EXAMPLE.inventory_evidence[0]!;
const inventory = artifactInventoryResultSchema.parse(
original.normalized_result,
);
const evidence = createEvidence(
{ path: "Fixture.apk", sha256: "a".repeat(64), format: "apk" },
original.provider,
{
operation: "inventory_artifact",
parameters: {},
result: {
...inventory,
coverage: { scope: "root", status: "invalid" },
},
confidence: "observed",
authority: "shipped-artifact",
},
);
const result = await cli.run({
arguments: [
"project-android-application-graph",
JSON.stringify({ inventory_evidence: [evidence] }),
"--json",
],
});
expect(result.exitCode).toBe(1);
expect(result.json).toMatchObject({
code: "invalid_request",
details: {
operation: "project_android_application_graph",
issues: expect.arrayContaining([
expect.objectContaining({
message: expect.stringContaining("coverage.status"),
}),
]),
},
});
},
);