feat(archive): complete recorded session lifecycle
This commit is contained in:
@@ -9,6 +9,7 @@ let decodeObservationSessionCatalog;
|
||||
let decodeObservationSessionReplay;
|
||||
let decodeObservationSessionPreparation;
|
||||
let fetchObservationSessionCatalog;
|
||||
let deleteObservationSession;
|
||||
let replayObservationSession;
|
||||
let fetchObservationSessionPreparation;
|
||||
let cancelObservationSessionPreparation;
|
||||
@@ -34,6 +35,7 @@ before(async () => {
|
||||
decodeObservationSessionReplay,
|
||||
decodeObservationSessionPreparation,
|
||||
fetchObservationSessionCatalog,
|
||||
deleteObservationSession,
|
||||
replayObservationSession,
|
||||
fetchObservationSessionPreparation,
|
||||
cancelObservationSessionPreparation,
|
||||
@@ -136,6 +138,39 @@ test("session catalog decodes canonical snake_case into a path-free camelCase mo
|
||||
assert.equal("path" in catalog.items[0], false);
|
||||
});
|
||||
|
||||
test("opened archive is named in the scene header and trash hover has no pill", async () => {
|
||||
const appSource = await readFile(new URL("../src/App.tsx", import.meta.url), "utf8");
|
||||
const styles = await readFile(
|
||||
new URL("../src/styles/observation-sessions.css", import.meta.url),
|
||||
"utf8",
|
||||
);
|
||||
const spatialControls = await readFile(
|
||||
new URL(
|
||||
"../../../plugins/xgrids-k1/frontend/src/components/K1SpatialControls.tsx",
|
||||
import.meta.url,
|
||||
),
|
||||
"utf8",
|
||||
);
|
||||
const acquisitionPipeline = await readFile(
|
||||
new URL(
|
||||
"../../../plugins/xgrids-k1/frontend/src/components/K1AcquisitionPipeline.tsx",
|
||||
import.meta.url,
|
||||
),
|
||||
"utf8",
|
||||
);
|
||||
|
||||
assert.match(appSource, /setRecordedReplayLabel\(session\.label\)/);
|
||||
assert.match(appSource, /`\$\{activeDefinition\.title\}: \$\{recordedReplayLabel\}`/);
|
||||
const deleteStyle = styles.slice(
|
||||
styles.indexOf(".observation-session-option__delete"),
|
||||
styles.indexOf(".observation-session-option__delete:disabled"),
|
||||
);
|
||||
assert.match(deleteStyle, /background:\s*transparent/);
|
||||
assert.doesNotMatch(deleteStyle, /danger-rgb\) \/ 0\.12/);
|
||||
assert.doesNotMatch(spatialControls, /Индикатор постоянно зелёный/);
|
||||
assert.doesNotMatch(acquisitionPipeline, /Индикатор постоянно зелёный/);
|
||||
});
|
||||
|
||||
test("session catalog exposes authoritative background preparation state", () => {
|
||||
const catalog = decodeObservationSessionCatalog({
|
||||
items: [session({
|
||||
@@ -302,6 +337,37 @@ test("catalog API preserves HTTP detail without accepting a malformed success bo
|
||||
);
|
||||
});
|
||||
|
||||
test("delete API uses one opaque same-origin target and requires an empty 204", async () => {
|
||||
const calls = [];
|
||||
await deleteObservationSession("session-20260716T205632Z", {
|
||||
fetcher: async (input, init) => {
|
||||
calls.push({ input: String(input), init });
|
||||
return new Response(null, { status: 204 });
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(calls.length, 1);
|
||||
assert.equal(
|
||||
calls[0].input,
|
||||
"/api/v1/observation-sessions/session-20260716T205632Z",
|
||||
);
|
||||
assert.equal(calls[0].init.method, "DELETE");
|
||||
await assert.rejects(
|
||||
deleteObservationSession("../private", { fetcher: async () => new Response(null) }),
|
||||
ObservationSessionContractError,
|
||||
);
|
||||
await assert.rejects(
|
||||
deleteObservationSession("session-20260716T205632Z", {
|
||||
fetcher: async () => new Response(JSON.stringify({ detail: "recording is open" }), {
|
||||
status: 409,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
}),
|
||||
}),
|
||||
(error) => error instanceof ObservationSessionApiError &&
|
||||
error.status === 409 && error.message === "recording is open",
|
||||
);
|
||||
});
|
||||
|
||||
test("replay API accepts only a same-origin seekable recording descriptor", async () => {
|
||||
const calls = [];
|
||||
const launch = await replayObservationSession("session-20260716T205632Z", {
|
||||
|
||||
Reference in New Issue
Block a user