test(06-06): failing envelope-flow suite G9-G12 — minimize POST/resume GET/trip-end DELETE + the composed-ladder gate (RED)
- G9 minimize POST: EXACTLY the frozen six-field snapshot (id/chat_id/ created_at are server-minted), keepalive:true (the minimize race), the Bearer header; a live envelope is never double-minted - G10 DELETE with the stored id on activated (the cron pauses — structural) AND at teardown (the D-14 active sweep, idempotent) - G11 ?g= resume: GUIDE // RESUMING + gantry + projected numbers, the watch armed, the line drawn; the first fix flips live ON_ROUTE through the machine's own resuming path; the ?g= envelope deletes at teardown - G12 main.ts composition gate: createHttpRoutingSource + the '?? (await' ladder, the UUID-validated ?g= carrier, the activated/deactivated wiring, the guidance-aware camera pose, the no-double-consult guard, zero env reads
This commit is contained in:
@@ -967,6 +967,8 @@ function steppedDeps(opts = {}) {
|
||||
guidanceChanges: 0,
|
||||
sourceCalls: [],
|
||||
followModes: [],
|
||||
fetches: [],
|
||||
fetchMode: "ok", // "ok" | "404" — the idempotent-delete tolerance flip
|
||||
};
|
||||
// every source.route call is recorded (the exactly-one-request gates)
|
||||
const baseRoute = deps.source.route.bind(deps.source);
|
||||
@@ -995,6 +997,18 @@ function steppedDeps(opts = {}) {
|
||||
};
|
||||
deps.nowMs = () => guide.now;
|
||||
deps.findGantry = (banner) => findByClass(banner, "guide-gantry");
|
||||
// the envelope endpoint + an injectable recording fetch (the stubFetch of
|
||||
// the plan's acceptance) — every envelope call lands in guide.fetches
|
||||
deps.endpoint = "/api/guidance";
|
||||
deps.bearer = () => "test-token";
|
||||
deps.fetchImpl = async (url, init = {}) => {
|
||||
guide.fetches.push({ url, init });
|
||||
if (String(init.method) === "DELETE" && guide.fetchMode === "404") {
|
||||
return { ok: false, status: 404, json: async () => ({ error: "not_found" }) };
|
||||
}
|
||||
if (String(init.method) === "DELETE") return { ok: true, status: 200, json: async () => ({}) };
|
||||
return { ok: true, status: 201, json: async () => ({ id: "0c0ffee0-0000-4000-8000-000000000000" }) };
|
||||
};
|
||||
deps.onGuidanceChange = () => {
|
||||
guide.guidanceChanges += 1;
|
||||
};
|
||||
@@ -1308,3 +1322,158 @@ test("G8 arrival — the machine's arrival effect runs the same teardown", async
|
||||
assert.equal(controller.guidancePhase, "ended", "the ended phase exposes to the BackButton graph");
|
||||
});
|
||||
});
|
||||
|
||||
/* ============================================================
|
||||
* 06-06 task 3 — the envelope flows + the composed ladder (G9..G12)
|
||||
* ============================================================
|
||||
* G9 minimize POST — the frozen six-field snapshot (id/chat_id/
|
||||
* created_at are SERVER-minted), keepalive:true, the Bearer header;
|
||||
* a live envelope is never double-minted
|
||||
* G10 DELETE on activated (the cron pauses) AND at teardown, both with
|
||||
* the stored id (the second is the idempotent one)
|
||||
* G11 ?g= resume — RESUMING chrome with projected numbers, the watch
|
||||
* armed, the line drawn; the first fix flips live ON_ROUTE
|
||||
* G12 main.ts composition — the engine-a→fixture ladder, the ?g=
|
||||
* UUID-validated carrier, the activated/deactivated wiring, the
|
||||
* guidance-aware camera (structural, the code-constructed law)
|
||||
*/
|
||||
|
||||
function stubFetch(log) {
|
||||
return (url, init) => {
|
||||
log.push({ url, init });
|
||||
if (init.method === "POST") {
|
||||
return Promise.resolve({ ok: true, json: () => Promise.resolve({ id: "env-1" }) });
|
||||
}
|
||||
return Promise.resolve({ ok: true, json: () => Promise.resolve({}) });
|
||||
};
|
||||
}
|
||||
|
||||
test("G9 minimize POST — the frozen six-field snapshot, keepalive, bearer; never double-minted", async () => {
|
||||
await withDocumentAsync(async () => {
|
||||
const calls = [];
|
||||
const deps = steppedDeps();
|
||||
deps.fetchImpl = stubFetch(calls);
|
||||
deps.bearer = () => "tok-1";
|
||||
const controller = createRouteController(deps);
|
||||
await driveTo(deps, controller);
|
||||
await withConsole(async () => {
|
||||
await controller.onDeactivated();
|
||||
return null;
|
||||
});
|
||||
const posts = calls.filter((c) => c.init.method === "POST");
|
||||
assert.equal(posts.length, 1, "exactly one envelope POST");
|
||||
const call = posts[0];
|
||||
assert.equal(call.url, "/api/guidance");
|
||||
assert.equal(call.init.keepalive, true, "the minimize race is belt-and-braces (keepalive)");
|
||||
assert.equal(call.init.headers.authorization, "Bearer tok-1", "the memory-held token rides the header");
|
||||
const body = JSON.parse(call.init.body);
|
||||
assert.deepEqual(
|
||||
Object.keys(body).sort(),
|
||||
["destinationId", "destinationName", "distanceM", "durationS", "geometry", "startedAtSec"],
|
||||
"EXACTLY the frozen six fields — id/chat_id/created_at are server-minted",
|
||||
);
|
||||
assert.equal(body.destinationId, DEST.id);
|
||||
assert.equal(body.destinationName, DEST.name);
|
||||
assert.equal(body.distanceM, 2000, "the selected route's summary distance");
|
||||
assert.equal(body.durationS, 240);
|
||||
assert.ok(Number.isInteger(body.startedAtSec) && body.startedAtSec > 0, "the trip-start anchor");
|
||||
const { decodePolyline6 } = await import("@araharah/routing/src/index.ts");
|
||||
assert.deepEqual(decodePolyline6(body.geometry), CORRIDOR_POINTS, "the geometry re-encodes the live model");
|
||||
// a second minimize while the envelope is live does not double-mint
|
||||
await controller.onDeactivated();
|
||||
assert.equal(posts.length, 1, "the live envelope is not double-minted");
|
||||
});
|
||||
});
|
||||
|
||||
test("G10 DELETE on activated AND at teardown — both with the stored id (the second is idempotent)", async () => {
|
||||
await withDocumentAsync(async () => {
|
||||
const calls = [];
|
||||
const deps = steppedDeps();
|
||||
deps.fetchImpl = stubFetch(calls);
|
||||
deps.bearer = () => "tok-1";
|
||||
const controller = createRouteController(deps);
|
||||
await driveTo(deps, controller);
|
||||
await controller.onDeactivated(); // POST → id env-1 stored
|
||||
await controller.onActivated(); // DELETE — the cron pauses (structural)
|
||||
const deletes = calls.filter((c) => c.init.method === "DELETE");
|
||||
assert.equal(deletes.length, 1, "activated deletes the envelope");
|
||||
assert.equal(deletes[0].url, "/api/guidance?id=env-1");
|
||||
// trip end deletes AGAIN with the same id — the server 404s it, tolerated
|
||||
findByClass(deps.__recorded.shown.at(-1).card, "btn-secondary").click(); // END TRIP
|
||||
await withConsole(async () => {
|
||||
findByClass(deps.__recorded.shown.at(-1).card, "btn-primary").click(); // YES, ABANDON
|
||||
return null;
|
||||
});
|
||||
await drain();
|
||||
const deletesAfter = calls.filter((c) => c.init.method === "DELETE");
|
||||
assert.equal(deletesAfter.length, 2, "teardown deletes the envelope (the D-14 active sweep)");
|
||||
assert.equal(deletesAfter[1].url, "/api/guidance?id=env-1", "the SAME stored id — idempotent");
|
||||
// a second teardown would not delete a third time (the id is consumed)
|
||||
await controller.onDeactivated(); // no live trip → no mint
|
||||
assert.equal(calls.filter((c) => c.init.method === "POST").length, 1, "no envelope without a live trip");
|
||||
});
|
||||
});
|
||||
|
||||
test("G11 ?g= resume — RESUMING chrome with projected numbers; the first fix flips live ON_ROUTE", async () => {
|
||||
await withDocumentAsync(async () => {
|
||||
const deps = steppedDeps();
|
||||
const controller = createRouteController(deps);
|
||||
const model = {
|
||||
points: CORRIDOR_POINTS,
|
||||
cumulative: CORRIDOR_CUMULATIVE,
|
||||
steps: [],
|
||||
destinationId: DEST.id,
|
||||
destinationName: DEST.name,
|
||||
};
|
||||
const resumed = await withConsole(async () => {
|
||||
controller.resume({ model, durationS: 240, projectedRemainingM: 1400, startedAtSec: 900, envelopeId: "env-9" });
|
||||
return null;
|
||||
});
|
||||
assert.equal(controller.state, "route", "the resumed trip holds the route state");
|
||||
assert.equal(controller.guidancePhase, "driving", "RESUMING is a driving phase — the confirm owns the exit");
|
||||
assert.equal(deps.__guide.shellRoot.dataset.guidance, "active", "the strip compacts for the resumed trip");
|
||||
assert.equal(guideRegister(deps), "GUIDE // RESUMING", "the projected register");
|
||||
assert.ok(findByClass(guideBanner(deps), "guide-gantry") !== null, "the gantry rides the wait state");
|
||||
assert.equal(guideDistance(deps), "1.4", "the projected remaining distance renders (RESUMING legally shows numbers)");
|
||||
assert.equal(deps.__guide.watchStarted, 1, "the watch arms for the resumed trip");
|
||||
const routeSwap = deps.__map.recorded.setData.find((s) => s.id === "route");
|
||||
assert.equal(routeSwap.data.features[0].geometry.coordinates.length, 3, "the resumed line draws");
|
||||
assert.ok(resumed.lines.some((l) => l.includes("ARAHARAH-GUIDE state=resuming")), "state=resuming logged");
|
||||
// the first accepted fix flips to live ON_ROUTE (the machine's own path)
|
||||
const live = await withConsole(async () => {
|
||||
feed(600, { deps });
|
||||
return null;
|
||||
});
|
||||
assert.equal(guideRegister(deps), "GUIDE // NEXT", "live ON_ROUTE after the first fix");
|
||||
assert.equal(findByClass(guideBanner(deps), "guide-gantry"), null, "the gantry stopped on the live flip");
|
||||
assert.ok(live.lines.some((l) => l.includes("ARAHARAH-GUIDE state=on_route")), "state=on_route logged");
|
||||
// the resumed trip's envelope deletes at teardown (the ?g= id)
|
||||
findByClass(deps.__recorded.shown.at(-1).card, "btn-secondary").click(); // END TRIP
|
||||
await withConsole(async () => {
|
||||
findByClass(deps.__recorded.shown.at(-1).card, "btn-primary").click(); // YES, ABANDON
|
||||
return null;
|
||||
});
|
||||
assert.equal(controller.guidancePhase, "ended", "the resumed trip ends through the same confirm");
|
||||
});
|
||||
});
|
||||
|
||||
test("G12 main.ts composition — the ladder, the ?g= carrier, the envelope events, the guidance-aware camera", () => {
|
||||
const mainCode = codeText(path.join(SRC, "main.ts"));
|
||||
assert.ok(mainCode.includes("createHttpRoutingSource"), "backend #2 is code-constructed in main.ts");
|
||||
assert.ok(/\?\? \(await /.test(mainCode), "the composed ladder: engine-a first, fixture fallback");
|
||||
assert.ok(!mainCode.includes("process.env"), "no env reads — the backend selection is code-constructed (T-05-04)");
|
||||
assert.ok(/get\("g"\)/.test(mainCode), "the ?g= carrier is read from the URL (the destination-preload pattern)");
|
||||
assert.ok(
|
||||
/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/.test(mainCode),
|
||||
"the resume id is UUID-validated before any GET",
|
||||
);
|
||||
assert.ok(
|
||||
/onEvent\("deactivated"/.test(mainCode) && /onEvent\("activated"/.test(mainCode),
|
||||
"the envelope lifecycle rides Telegram's activated/deactivated events (T-06-06 guards)",
|
||||
);
|
||||
assert.ok(/\.onDeactivated\(\)/.test(mainCode) && /\.onActivated\(\)/.test(mainCode), "the controller's envelope flows are wired");
|
||||
assert.ok(/guidancePhase === "driving"/.test(mainCode), "the chain's fix does not fight the guidance camera");
|
||||
assert.ok(/GUIDANCE_CAMERA_PADDING/.test(mainCode), "recenter re-locks at the guidance pose");
|
||||
assert.ok(/resumeId === null/.test(mainCode), "a resume boot never double-consults");
|
||||
assert.ok(/kind: "callback"/.test(mainCode) || /kind: "hidden"/.test(mainCode), "the BackButton resolver walks the full graph");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user