feat: add Polygon UI-0 run view
This commit is contained in:
@@ -0,0 +1,230 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { after, before, test } from "node:test";
|
||||
|
||||
import { createServer } from "vite";
|
||||
|
||||
let server;
|
||||
let decodePolygonRunCatalog;
|
||||
let decodePolygonRunDetail;
|
||||
let fetchPolygonRunCatalog;
|
||||
let fetchPolygonRunDetail;
|
||||
let resolvePolygonRunRoute;
|
||||
let PolygonRunContractError;
|
||||
let workspaceById;
|
||||
let workspacesForRoot;
|
||||
|
||||
before(async () => {
|
||||
server = await createServer({
|
||||
appType: "custom",
|
||||
logLevel: "silent",
|
||||
server: { middlewareMode: true },
|
||||
});
|
||||
({
|
||||
decodePolygonRunCatalog,
|
||||
decodePolygonRunDetail,
|
||||
fetchPolygonRunCatalog,
|
||||
fetchPolygonRunDetail,
|
||||
resolvePolygonRunRoute,
|
||||
PolygonRunContractError,
|
||||
} = await server.ssrLoadModule("/src/core/polygon/runArchive.ts"));
|
||||
({ workspaceById, workspacesForRoot } = await server.ssrLoadModule("/src/productModel.ts"));
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await server?.close();
|
||||
});
|
||||
|
||||
function summary(overrides = {}) {
|
||||
return {
|
||||
run_id: "s1b-6cb1495-20260724t1535z",
|
||||
episode_id: "episode-s1b-6cb1495-20260724t1535z",
|
||||
kind: "simulation_closed_loop",
|
||||
state: "completed",
|
||||
created_at_utc: "2026-07-24T15:35:00Z",
|
||||
started_at_utc: "2026-07-24T15:35:02Z",
|
||||
ended_at_utc: "2026-07-24T15:35:05Z",
|
||||
terminal_reason: "operator-stop-clean",
|
||||
scenario_generation: "px4-v1.17.0-stock-rover-ackermann",
|
||||
profile_generation: "stock-rover-lifecycle-v1",
|
||||
mission_core_commit: "6cb1495a1234567890abcdef1234567890abcdef",
|
||||
host_profile_id: "mission-gpu-s0",
|
||||
reproducibility_tier: "R1",
|
||||
clock_domain: "gazebo:/clock",
|
||||
revision: 5,
|
||||
provider_ids: ["px4-autopilot", "gazebo"],
|
||||
artifact_count: 1,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function catalog(overrides = {}) {
|
||||
return {
|
||||
schema_version: "missioncore.polygon-run-catalog/v1",
|
||||
access: "read-only",
|
||||
items: [summary()],
|
||||
total: 1,
|
||||
limitations: ["Qualification evidence only."],
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function detail(overrides = {}) {
|
||||
return {
|
||||
schema_version: "missioncore.polygon-run-detail/v1",
|
||||
access: "read-only",
|
||||
run: {
|
||||
...summary(),
|
||||
scenario_sha256: "a".repeat(64),
|
||||
profile_sha256: "b".repeat(64),
|
||||
host_profile_sha256: "c".repeat(64),
|
||||
seed: 42,
|
||||
parent_run_id: null,
|
||||
providers: [
|
||||
{
|
||||
identifier: "px4-autopilot",
|
||||
version: "v1.17.0",
|
||||
revision: "v1.17.0",
|
||||
digest: null,
|
||||
},
|
||||
{
|
||||
identifier: "gazebo",
|
||||
version: "harmonic",
|
||||
revision: "8.9.0",
|
||||
digest: null,
|
||||
},
|
||||
],
|
||||
authority: {
|
||||
generation: 1,
|
||||
command_ttl_max_ns: 250_000_000,
|
||||
heartbeat_timeout_monotonic_ns: 500_000_000,
|
||||
simulation_or_shadow_only: true,
|
||||
actuator_authority: false,
|
||||
navigation_or_safety_accepted: false,
|
||||
direct_actuator_setpoints_allowed: false,
|
||||
},
|
||||
},
|
||||
events: [
|
||||
{
|
||||
schema_version: "missioncore.qualification-event/v1",
|
||||
run_id: "s1b-6cb1495-20260724t1535z",
|
||||
sequence: 5,
|
||||
event_type: "lifecycle.state-changed",
|
||||
observed_at_utc: "2026-07-24T15:35:05Z",
|
||||
host_monotonic_ns: 5,
|
||||
sim_time_ns: 2_000_000,
|
||||
payload: {
|
||||
from: "stopping",
|
||||
to: "completed",
|
||||
reason: "operator-stop-clean",
|
||||
},
|
||||
},
|
||||
],
|
||||
events_total: 1,
|
||||
events_truncated: false,
|
||||
commands: {
|
||||
count: 0,
|
||||
content_exposed: false,
|
||||
},
|
||||
artifacts: [
|
||||
{
|
||||
artifact_id: "provider-log-index",
|
||||
kind: "log-index",
|
||||
relative_path: "provider-runtime/processes.json",
|
||||
sha256: "a".repeat(64),
|
||||
byte_length: 512,
|
||||
source_of_record: true,
|
||||
sequence: 1,
|
||||
},
|
||||
],
|
||||
limitations: ["Qualification evidence only."],
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function jsonResponse(payload, status = 200) {
|
||||
return new Response(JSON.stringify(payload), {
|
||||
status,
|
||||
headers: { "content-type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
test("UI-0 route is direct-only and does not add Polygon to system navigation", () => {
|
||||
assert.deepEqual(resolvePolygonRunRoute("?workspace=polygon-run"), {
|
||||
active: true,
|
||||
runId: null,
|
||||
error: null,
|
||||
});
|
||||
assert.deepEqual(
|
||||
resolvePolygonRunRoute("?workspace=polygon-run&run=s1b-6cb1495-20260724t1535z"),
|
||||
{
|
||||
active: true,
|
||||
runId: "s1b-6cb1495-20260724t1535z",
|
||||
error: null,
|
||||
},
|
||||
);
|
||||
assert.equal(resolvePolygonRunRoute("?workspace=missions").active, false);
|
||||
assert.match(
|
||||
resolvePolygonRunRoute("?workspace=polygon-run&run=../../etc").error,
|
||||
/некорректный/,
|
||||
);
|
||||
assert.equal(workspaceById("polygon-run-internal").internalOnly, true);
|
||||
assert.equal(
|
||||
workspacesForRoot("system").some(({ id }) => id === "polygon-run-internal"),
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
test("Polygon contracts decode path-free evidence and preserve the safety boundary", () => {
|
||||
const decodedCatalog = decodePolygonRunCatalog(catalog());
|
||||
const decodedDetail = decodePolygonRunDetail(detail());
|
||||
|
||||
assert.equal(decodedCatalog.items[0].state, "completed");
|
||||
assert.equal(decodedDetail.run.authority.actuatorAuthority, false);
|
||||
assert.equal(decodedDetail.run.authority.navigationOrSafetyAccepted, false);
|
||||
assert.equal(decodedDetail.commandCount, 0);
|
||||
assert.equal(decodedDetail.artifacts[0].relativePath, "provider-runtime/processes.json");
|
||||
assert.equal("content" in decodedDetail, false);
|
||||
});
|
||||
|
||||
test("Polygon contracts fail closed on command content, paths and unknown fields", () => {
|
||||
assert.throws(
|
||||
() => decodePolygonRunDetail(detail({
|
||||
commands: { count: 1, content_exposed: true },
|
||||
})),
|
||||
PolygonRunContractError,
|
||||
);
|
||||
assert.throws(
|
||||
() => decodePolygonRunDetail(detail({
|
||||
artifacts: [{
|
||||
...detail().artifacts[0],
|
||||
relative_path: "/mnt/d/private/processes.json",
|
||||
}],
|
||||
})),
|
||||
PolygonRunContractError,
|
||||
);
|
||||
assert.throws(
|
||||
() => decodePolygonRunCatalog(catalog({ unexpected: true })),
|
||||
PolygonRunContractError,
|
||||
);
|
||||
});
|
||||
|
||||
test("Polygon fetchers use bounded same-origin GET endpoints", async () => {
|
||||
const calls = [];
|
||||
const fetcher = async (url, init) => {
|
||||
calls.push({ url, init });
|
||||
return String(url).includes("event_limit") ? jsonResponse(detail()) : jsonResponse(catalog());
|
||||
};
|
||||
|
||||
await fetchPolygonRunCatalog({ limit: 20, fetcher });
|
||||
await fetchPolygonRunDetail("s1b-6cb1495-20260724t1535z", {
|
||||
eventLimit: 200,
|
||||
fetcher,
|
||||
});
|
||||
|
||||
assert.deepEqual(calls.map(({ url }) => url), [
|
||||
"/api/v1/polygon/runs?limit=20",
|
||||
"/api/v1/polygon/runs/s1b-6cb1495-20260724t1535z?event_limit=200",
|
||||
]);
|
||||
assert.ok(calls.every(({ init }) => init.method === "GET"));
|
||||
assert.ok(calls.every(({ init }) => init.headers.Accept === "application/json"));
|
||||
});
|
||||
Reference in New Issue
Block a user