140 lines
5.7 KiB
TypeScript
140 lines
5.7 KiB
TypeScript
/** Contract tests for the authored California transport pack. */
|
|
|
|
import assert from "node:assert/strict";
|
|
import { describe, it } from "node:test";
|
|
import CALIFORNIA_TRANSPORT from "../transport/california.ts";
|
|
import type { GeographicPoint, TransportRoute } from "../transport/types.ts";
|
|
|
|
function assertUnique(values: readonly string[], what: string): void {
|
|
assert.equal(new Set(values).size, values.length, `${what} must be unique`);
|
|
}
|
|
|
|
function assertPoint(point: GeographicPoint, what: string): void {
|
|
assert.ok(Number.isFinite(point.lat), `${what} latitude must be finite`);
|
|
assert.ok(Number.isFinite(point.lng), `${what} longitude must be finite`);
|
|
assert.ok(point.lat >= -90 && point.lat <= 90, `${what} latitude is outside the globe`);
|
|
assert.ok(point.lng >= -180 && point.lng <= 180, `${what} longitude is outside the globe`);
|
|
}
|
|
|
|
function assertProvenance(value: string, what: string): void {
|
|
assert.ok(value.trim().length > 20, `${what} needs meaningful provenance`);
|
|
}
|
|
|
|
function routeSegments(route: TransportRoute) {
|
|
const byId = new Map(CALIFORNIA_TRANSPORT.segments.map((segment) => [segment.id, segment]));
|
|
return route.segmentIds.map((id) => {
|
|
const segment = byId.get(id);
|
|
assert.ok(segment, `route ${route.id} references missing segment ${id}`);
|
|
return segment;
|
|
});
|
|
}
|
|
|
|
describe("California transport graph", () => {
|
|
it("has stable, unique identifiers and valid references", () => {
|
|
assertUnique(
|
|
CALIFORNIA_TRANSPORT.nodes.map((node) => node.id),
|
|
"node ids",
|
|
);
|
|
assertUnique(
|
|
CALIFORNIA_TRANSPORT.segments.map((segment) => segment.id),
|
|
"segment ids",
|
|
);
|
|
assertUnique(
|
|
CALIFORNIA_TRANSPORT.routes.map((route) => route.id),
|
|
"route ids",
|
|
);
|
|
assertUnique(
|
|
CALIFORNIA_TRANSPORT.anchors.map((anchor) => anchor.id),
|
|
"anchor ids",
|
|
);
|
|
|
|
const nodeIds = new Set(CALIFORNIA_TRANSPORT.nodes.map((node) => node.id));
|
|
for (const segment of CALIFORNIA_TRANSPORT.segments) {
|
|
assert.ok(nodeIds.has(segment.fromNodeId), `${segment.id} has no start node`);
|
|
assert.ok(nodeIds.has(segment.toNodeId), `${segment.id} has no end node`);
|
|
}
|
|
for (const anchor of CALIFORNIA_TRANSPORT.anchors) {
|
|
assert.ok(nodeIds.has(anchor.nodeId), `${anchor.id} has no corridor node`);
|
|
}
|
|
});
|
|
|
|
it("keeps every route continuous from its declared start to its declared end", () => {
|
|
for (const route of CALIFORNIA_TRANSPORT.routes) {
|
|
const segments = routeSegments(route);
|
|
assert.ok(segments.length > 0, `${route.id} is empty`);
|
|
assert.equal(segments[0]?.fromNodeId, route.fromNodeId);
|
|
assert.equal(segments.at(-1)?.toNodeId, route.toNodeId);
|
|
|
|
for (let index = 1; index < segments.length; index += 1) {
|
|
assert.equal(
|
|
segments[index - 1]?.toNodeId,
|
|
segments[index]?.fromNodeId,
|
|
`${route.id} breaks between segments ${index - 1} and ${index}`,
|
|
);
|
|
}
|
|
}
|
|
});
|
|
|
|
it("names I-5's real Bay connectors instead of pretending I-5 reaches San Francisco", () => {
|
|
const route = CALIFORNIA_TRANSPORT.routes.find((candidate) => candidate.id === "la-sf-i-5");
|
|
assert.ok(route);
|
|
const roads = routeSegments(route).map((segment) => segment.roadName);
|
|
|
|
assert.deepEqual([...new Set(roads)], ["I-5", "I-580", "I-80 / Bay Bridge", "I-80"]);
|
|
assert.match(route.label, /I-580/);
|
|
assert.match(route.label, /I-80/);
|
|
});
|
|
|
|
it("keeps US-101 on US-101 for the complete authored itinerary", () => {
|
|
const route = CALIFORNIA_TRANSPORT.routes.find((candidate) => candidate.id === "la-sf-us-101");
|
|
assert.ok(route);
|
|
assert.ok(routeSegments(route).every((segment) => segment.roadName === "US-101"));
|
|
});
|
|
|
|
it("contains finite California coordinates and usable simulation envelopes", () => {
|
|
for (const node of CALIFORNIA_TRANSPORT.nodes) {
|
|
assertPoint(node.position, `node ${node.id}`);
|
|
assert.ok(node.position.lat >= 32 && node.position.lat <= 42, `${node.id} is outside California`);
|
|
assert.ok(node.position.lng >= -125 && node.position.lng <= -114, `${node.id} is outside California`);
|
|
}
|
|
for (const anchor of CALIFORNIA_TRANSPORT.anchors) {
|
|
assertPoint(anchor.position, `anchor ${anchor.id}`);
|
|
}
|
|
for (const segment of CALIFORNIA_TRANSPORT.segments) {
|
|
assert.ok(segment.speedLimitMph > 0 && segment.speedLimitMph <= 70);
|
|
assert.ok(Number.isInteger(segment.lanesPerDirection));
|
|
assert.ok(segment.lanesPerDirection >= 1 && segment.lanesPerDirection <= 8);
|
|
}
|
|
});
|
|
|
|
it("records provenance on every authored item", () => {
|
|
for (const note of CALIFORNIA_TRANSPORT.provenance) assertProvenance(note, "pack");
|
|
for (const node of CALIFORNIA_TRANSPORT.nodes) assertProvenance(node.provenance, node.id);
|
|
for (const segment of CALIFORNIA_TRANSPORT.segments) {
|
|
assertProvenance(segment.provenance, segment.id);
|
|
}
|
|
for (const route of CALIFORNIA_TRANSPORT.routes) assertProvenance(route.provenance, route.id);
|
|
for (const anchor of CALIFORNIA_TRANSPORT.anchors) {
|
|
assertProvenance(anchor.provenance, anchor.id);
|
|
}
|
|
});
|
|
|
|
it("has scene anchors for both cities and all shipped offices", () => {
|
|
const cityIds = CALIFORNIA_TRANSPORT.anchors
|
|
.filter((anchor) => anchor.kind === "city")
|
|
.map((anchor) => anchor.cityId)
|
|
.sort();
|
|
const officeIds = CALIFORNIA_TRANSPORT.anchors
|
|
.filter((anchor) => anchor.kind === "office")
|
|
.map((anchor) => anchor.officeId)
|
|
.sort();
|
|
|
|
assert.deepEqual(cityIds, ["sf", "socal"]);
|
|
assert.deepEqual(officeIds, ["frontier-valley", "lumbridge-hq", "mateo-court"]);
|
|
});
|
|
|
|
it("survives a JSON round trip without losing data", () => {
|
|
assert.deepEqual(JSON.parse(JSON.stringify(CALIFORNIA_TRANSPORT)), CALIFORNIA_TRANSPORT);
|
|
});
|
|
});
|