Add persistent Subject behavior context
This commit is contained in:
@@ -12,6 +12,7 @@ import {
|
||||
parseSubjektivSubjectListResponse,
|
||||
parseSubjektivSubjectResponse,
|
||||
SubjektivSubjectCreateError,
|
||||
updateSubjektivSubjectBehavior,
|
||||
validateSubjektivSubjectCreateRequest,
|
||||
} from "../src/lib/workspace/memory/api.ts";
|
||||
|
||||
@@ -75,6 +76,8 @@ function subject(id = "subject-1") {
|
||||
return {
|
||||
id,
|
||||
role: "Release coordinator",
|
||||
behavior_md: "Prefer explicit evidence.",
|
||||
behavior_revision: 2,
|
||||
state: "active",
|
||||
store_revision: 12,
|
||||
created_at: "2026-09-01T00:00:00Z",
|
||||
@@ -228,7 +231,7 @@ Deno.test("Subject list parser preserves the exact bounded response", () => {
|
||||
Deno.test("Subject create request mirrors Backend role validation without rewriting input", () => {
|
||||
assertEquals(
|
||||
validateSubjektivSubjectCreateRequest({ role: " Release coordinator " }),
|
||||
{ role: " Release coordinator " },
|
||||
{ role: " Release coordinator ", behavior_md: "" },
|
||||
);
|
||||
assertThrows(
|
||||
() => validateSubjektivSubjectCreateRequest({ role: " " }),
|
||||
@@ -242,6 +245,70 @@ Deno.test("Subject create request mirrors Backend role validation without rewrit
|
||||
() => validateSubjektivSubjectCreateRequest({ role: "界".repeat(86) }),
|
||||
"at most 256 bytes",
|
||||
);
|
||||
assertThrows(
|
||||
() =>
|
||||
validateSubjektivSubjectCreateRequest({
|
||||
role: "Reviewer",
|
||||
behavior_md: " ".repeat(3),
|
||||
}),
|
||||
"empty or contain non-whitespace",
|
||||
);
|
||||
assertThrows(
|
||||
() =>
|
||||
validateSubjektivSubjectCreateRequest({
|
||||
role: "Reviewer",
|
||||
behavior_md: "x".repeat(16 * 1024 + 1),
|
||||
}),
|
||||
"at most 16384 bytes",
|
||||
);
|
||||
});
|
||||
|
||||
Deno.test("Subject behavior update uses CAS and preserves exact text", async () => {
|
||||
const requests: Array<{ path: string; init?: RequestInit }> = [];
|
||||
const fetchFn = (async (path: string | URL | Request, init?: RequestInit) => {
|
||||
requests.push({ path: String(path), init });
|
||||
return Response.json({
|
||||
...subject("subject/one"),
|
||||
behavior_md: "Line one.\nLine two.",
|
||||
behavior_revision: 3,
|
||||
});
|
||||
}) as typeof fetch;
|
||||
|
||||
const updated = await updateSubjektivSubjectBehavior(
|
||||
fetchFn,
|
||||
"workspace one",
|
||||
"subject/one",
|
||||
{ expected_behavior_revision: 2, behavior_md: "Line one.\nLine two." },
|
||||
);
|
||||
assertEquals(updated.behavior_revision, 3);
|
||||
assertEquals(updated.behavior_md, "Line one.\nLine two.");
|
||||
assertEquals(
|
||||
requests[0].path,
|
||||
"/api/w/workspace%20one/subjektiv/subjects/subject%2Fone/behavior",
|
||||
);
|
||||
assertEquals(requests[0].init?.method, "PATCH");
|
||||
assertEquals(
|
||||
requests[0].init?.body,
|
||||
'{"expected_behavior_revision":2,"behavior_md":"Line one.\\nLine two."}',
|
||||
);
|
||||
});
|
||||
|
||||
Deno.test("Subject behavior update reports conflicts without claiming Worker application", async () => {
|
||||
const conflictFetch =
|
||||
(async () =>
|
||||
Response.json({ message: "revision conflict" }, {
|
||||
status: 409,
|
||||
})) as typeof fetch;
|
||||
await assertSubjectCreateRejects(
|
||||
() =>
|
||||
updateSubjektivSubjectBehavior(conflictFetch, "workspace", "subject", {
|
||||
expected_behavior_revision: 1,
|
||||
behavior_md: "new",
|
||||
}),
|
||||
"rejected",
|
||||
"changed elsewhere",
|
||||
409,
|
||||
);
|
||||
});
|
||||
|
||||
Deno.test("Subject create client sends the exact typed body once and parses the created Subject", async () => {
|
||||
@@ -251,20 +318,26 @@ Deno.test("Subject create client sends the exact typed body once and parses the
|
||||
return Response.json({
|
||||
...subject("subject-created"),
|
||||
role: " Release coordinator ",
|
||||
behavior_md: "Stay concise.",
|
||||
}, { status: 201 });
|
||||
}) as typeof fetch;
|
||||
|
||||
const created = await createSubjektivSubject(fetchFn, "workspace one", {
|
||||
role: " Release coordinator ",
|
||||
behavior_md: "Stay concise.",
|
||||
});
|
||||
assertEquals(created, {
|
||||
...subject("subject-created"),
|
||||
role: " Release coordinator ",
|
||||
behavior_md: "Stay concise.",
|
||||
});
|
||||
assertEquals(requests.length, 1);
|
||||
assertEquals(requests[0].path, "/api/w/workspace%20one/subjektiv/subjects");
|
||||
assertEquals(requests[0].init?.method, "POST");
|
||||
assertEquals(requests[0].init?.body, '{"role":" Release coordinator "}');
|
||||
assertEquals(
|
||||
requests[0].init?.body,
|
||||
'{"role":" Release coordinator ","behavior_md":"Stay concise."}',
|
||||
);
|
||||
});
|
||||
|
||||
Deno.test("Subject create client distinguishes rejected and unknown outcomes without retrying", async () => {
|
||||
|
||||
Reference in New Issue
Block a user