mirror of
https://github.com/morluto/rea.git
synced 2026-11-11 01:13:39 +00:00
feat(environment): list every entity on a list's whole lines (#5787)
Derive whole-collection membership per checked list line while preserving recorded membership, counters, source Evidence and independent write claims. Integrate current main without losing its shared path decoder. Keep later or ambiguously positioned pages recorded rather than inventing earlier entities. Validation: 543 focused/adjacent tests, nine README checks, complete static/docs checks, repeated regression runs, and real retained GitHub-label CLI/MCP load/reset workflows. An actual Codex session returned the correct facts; its over-strict grader failure and same-trace semantic verification are recorded. Evidence and limitations: https://github.com/morluto/rea/pull/5787#issuecomment-6338446674 Tracking: #6262; feature scope: #4933. Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
This commit is contained in:
1 parent
932fbb7d84
commit
4043e71f6c
18 files changed
+616
-113
No files matched your search
@@ -10,10 +10,12 @@ import {
|
||||
BOOK_ROWS,
|
||||
DELETE_NOT_FOUND,
|
||||
FIRST_BOOK,
|
||||
LIST_LINE_ROWS,
|
||||
SECOND_BOOK,
|
||||
THIRD_BOOK,
|
||||
book,
|
||||
bookClock,
|
||||
bookPage,
|
||||
renamedBook,
|
||||
type Book,
|
||||
} from "../../../tests/fixtures/environmentStateWrites.js";
|
||||
@@ -539,6 +541,97 @@ describe("paged list lines", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("list lines", () => {
|
||||
it("keeps an empty later page recorded after data/add, while a first line lists the loaded entity", () => {
|
||||
for (const [query, whole] of [
|
||||
["offset=0", true],
|
||||
["start=0", true],
|
||||
["_start=0", true],
|
||||
["page=0", true],
|
||||
["offset=2", false],
|
||||
["start=2", false],
|
||||
["_start=2", false],
|
||||
["page=1", false],
|
||||
["offset=-1", false],
|
||||
["offset=unknown", false],
|
||||
] as const) {
|
||||
const alpha = { id: 1, name: "Alpha", org: "1" };
|
||||
const beta = { id: 2, name: "Beta", org: "2" };
|
||||
const books = servedFrom([
|
||||
["GET", "/api/orgs/1/books", [alpha]],
|
||||
["GET", "/api/orgs/1/books?name=Alpha", [alpha]],
|
||||
["GET", `/api/orgs/2/books?${query}&limit=50`, []],
|
||||
["GET", "/api/orgs/1/books/1", alpha],
|
||||
]);
|
||||
const held = books.store.snapshot().rea_state;
|
||||
const loaded = books.store.replace({
|
||||
rea_state: {
|
||||
...held,
|
||||
entities: [
|
||||
...held.entities,
|
||||
{
|
||||
type: held.entities[0]!.type,
|
||||
collection: "/api/orgs/2/books",
|
||||
id: "2",
|
||||
fields: beta,
|
||||
view_fields: [],
|
||||
record_ordinals: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
if (!loaded.ok) throw loaded.error;
|
||||
expect(
|
||||
books.answer("get_orgs_books_by_id", {
|
||||
id: "2",
|
||||
...Object.fromEntries(new URLSearchParams(query)),
|
||||
limit: "50",
|
||||
}),
|
||||
).toMatchObject({
|
||||
body: whole ? [beta] : [],
|
||||
state: {
|
||||
source: "state",
|
||||
membership: whole ? "all" : "recorded",
|
||||
listed: whole ? 1 : 0,
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it("lists a created book on the line that lists every book, while a filtered line keeps its recorded members", () => {
|
||||
const books = servedFrom(LIST_LINE_ROWS);
|
||||
const created = books.answer("post_books", CREATE_NEW_BOOK).body as Book;
|
||||
expect(books.answer("get_books")).toMatchObject({
|
||||
body: bookPage([FIRST_BOOK, SECOND_BOOK, created]),
|
||||
state: { source: "state", membership: "all", listed: 3 },
|
||||
});
|
||||
expect(books.answer("get_books", { name: "First Book" })).toMatchObject({
|
||||
body: bookPage([FIRST_BOOK]),
|
||||
state: { source: "state", membership: "recorded", listed: 1 },
|
||||
});
|
||||
});
|
||||
|
||||
it("updates on each line only the counters its recording counted, so a partial page keeps its total", () => {
|
||||
const partial = {
|
||||
content: [FIRST_BOOK],
|
||||
totalElements: 2,
|
||||
numberOfElements: 1,
|
||||
};
|
||||
const books = servedFrom([
|
||||
["GET", "/api/books", bookPage([FIRST_BOOK, SECOND_BOOK])],
|
||||
["GET", "/api/books?page=1&size=1", partial],
|
||||
...LIST_LINE_ROWS.slice(2),
|
||||
]);
|
||||
const created = books.answer("post_books", CREATE_NEW_BOOK).body as Book;
|
||||
expect(books.answer("get_books")).toMatchObject({
|
||||
body: bookPage([FIRST_BOOK, SECOND_BOOK, created]),
|
||||
});
|
||||
expect(books.answer("get_books", { page: "1", size: "1" })).toMatchObject({
|
||||
body: partial,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("path values", () => {
|
||||
const orgBook = (id: number, org: string, name: string): Book => ({
|
||||
id,
|
||||
|
||||
Reference in new issue
Block a user