diigo-cache: date windows and search on the public page (1d)
/public/<username> takes days, after, before, in (year, month, quarter, ISO week), by=updated|created, and q (words, "phrases", #tags, -negation, gmail/Slack/GitHub date operators). JSON answers echo the parsed filter; a malformed one is a 400. Filters query the stored items in sqlite through the read-only viewer instead of walking diigo pages: the public page keeps no fetch authority, and a filled cache already holds every item. Results say `complete` when every diigo page is stored; freshness is 1f's head refresh. So the fetch budget and the O(log pages) walk test go, and the fake deep account shrinks to 300. Generated-by: Claude Opus 5.5
This commit is contained in:
1 parent
efb54ad4cc
commit
002f08b456
9 files changed
+418
-120
No files matched your search
@@ -3,7 +3,6 @@ import { evictDurableObject, runInDurableObject } from "cloudflare:test";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import worker from "../src/index.js";
|
||||
import { idFromWebkey, toWebkey } from "../src/webkeys.js";
|
||||
import { FETCH_BUDGET } from "../src/query.js";
|
||||
import { PAGE_SIZE } from "../src/cache.js";
|
||||
|
||||
// matches tests/fake-webapp.js: 30 items, every third private
|
||||
@@ -283,30 +282,34 @@ function isoWeek(d: Date): string {
|
||||
return `${year}-W${String(week).padStart(2, "0")}`;
|
||||
}
|
||||
|
||||
describe.skip("public page filters (goal 1d)", () => {
|
||||
describe("public page filters (goal 1d)", () => {
|
||||
// the fake's dated account: "dated n" updated n-1 days + 1h ago;
|
||||
// #weekly on every 7th; every 10th private; every 5th created 100
|
||||
// days before it was updated
|
||||
// days before it was updated. The public page only reads, so each
|
||||
// test stores the pages it needs on the owner's side first.
|
||||
const call = (path: string, init?: RequestInit) =>
|
||||
worker.fetch(new Request(`https://diigo-cache.test${path}`, init), env as never);
|
||||
const wireDated = async () => {
|
||||
/** wire a public cache to `webappId` and store diigo pages 1..`pages` */
|
||||
const wireStored = async (webappId: string, pages: number) => {
|
||||
const res = await call("/_wire", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ webappId: "fake-dated-webapp", mode: "public" }),
|
||||
body: JSON.stringify({ webappId, mode: "public" }),
|
||||
});
|
||||
expect(res.status).toBe(200);
|
||||
return "/public/dated_dan";
|
||||
const { username, publicId } = (await res.json()) as { username: string; publicId: string };
|
||||
const pub = testEnv.PUBLIC_DO.get(testEnv.PUBLIC_DO.idFromString(idFromWebkey(publicId)));
|
||||
for (let n = 1; n <= pages; n += 1) await pub.page(n, PAGE_SIZE);
|
||||
return { page: `/public/${username}`, pub };
|
||||
};
|
||||
const wireDated = async () => (await wireStored("fake-dated-webapp", 1)).page;
|
||||
const titlesIn = (html: string) => [...html.matchAll(/>(dated \d+)<\/a>/g)].map((m) => m[1]);
|
||||
const dated = (...ns: number[]) => ns.map((n) => `dated ${n}`);
|
||||
|
||||
it("?days=7 lists the last week, walking only the diigo pages it needs", async () => {
|
||||
it("?days=7 lists the last week", async () => {
|
||||
const page = await wireDated();
|
||||
const before = await testEnv.WEBAPP.calls();
|
||||
const html = await (await call(`${page}?days=7`)).text();
|
||||
expect(titlesIn(html)).toEqual(dated(1, 2, 3, 4, 5, 6, 7));
|
||||
expect((await testEnv.WEBAPP.calls()) - before).toBeLessThanOrEqual(1);
|
||||
});
|
||||
|
||||
it("?by=created windows, lists, and dates by when it was bookmarked", async () => {
|
||||
@@ -327,13 +330,7 @@ describe.skip("public page filters (goal 1d)", () => {
|
||||
});
|
||||
|
||||
it("?days=300 pages the matches PAGE_SIZE at a time; links keep the filter", async () => {
|
||||
const res = await call("/_wire", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ webappId: "fake-deep-webapp", mode: "public" }),
|
||||
});
|
||||
expect(res.status).toBe(200);
|
||||
const page = "/public/deep_dee";
|
||||
const { page } = await wireStored("fake-deep-webapp", 3);
|
||||
// deep_dee: one a day; days=300 holds 1..300, minus every 10th (private)
|
||||
const want = Array.from({ length: 300 }, (_, k) => k + 1).filter((n) => n % 10 !== 0);
|
||||
const first = await (await call(`${page}?days=300`)).text();
|
||||
@@ -354,6 +351,15 @@ describe.skip("public page filters (goal 1d)", () => {
|
||||
expect(titlesIn(html)).toEqual(dated(11, 12));
|
||||
});
|
||||
|
||||
it("?in= names an ISO week", async () => {
|
||||
const { page } = await wireStored("fake-deep-webapp", 3);
|
||||
const week = isoWeek(new Date(Date.now() - 100 * 24 * 60 * 60 * 1000));
|
||||
const html = await (await call(`${page}?in=${week}`)).text();
|
||||
// one a day; every 10th is private
|
||||
expect(titlesIn(html).length).toBeGreaterThanOrEqual(6);
|
||||
expect(titlesIn(html).length).toBeLessThanOrEqual(7);
|
||||
});
|
||||
|
||||
it("?q=#weekly&days=60 filters by tag; private items stay out", async () => {
|
||||
const page = await wireDated();
|
||||
const html = await (await call(`${page}?days=60&q=${encodeURIComponent("#weekly")}`)).text();
|
||||
@@ -366,22 +372,21 @@ describe.skip("public page filters (goal 1d)", () => {
|
||||
expect(titlesIn(html)).toEqual(dated(7, 14, 21, 28, 35, 42, 49, 56));
|
||||
});
|
||||
|
||||
it("a request spends at most FETCH_BUDGET diigo fetches; incomplete results say so, and a reload continues", async () => {
|
||||
const res = await call("/_wire", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ webappId: "fake-deep-webapp", mode: "public" }),
|
||||
});
|
||||
expect(res.status).toBe(200);
|
||||
// deep_dee: 60 diigo pages of 100; a search matching nothing must walk them all
|
||||
it("a filter reads only stored items, never diigo; results say whether storage is complete", async () => {
|
||||
const { page, pub } = await wireStored("fake-deep-webapp", 1);
|
||||
const json = async () =>
|
||||
(await call(`${page}?days=300`, { headers: { accept: "application/json" } })).json() as Promise<{
|
||||
matched: number;
|
||||
complete: boolean;
|
||||
}>;
|
||||
const before = await testEnv.WEBAPP.calls();
|
||||
const first = await call("/public/deep_dee?q=nomatch", { headers: { accept: "application/json" } });
|
||||
expect(await first.json()).toMatchObject({ complete: false, items: [] });
|
||||
expect((await testEnv.WEBAPP.calls()) - before).toBeLessThanOrEqual(FETCH_BUDGET + 1);
|
||||
const html = await (await call("/public/deep_dee?q=nomatch")).text();
|
||||
expect(html).toMatch(/complete|no matches/);
|
||||
const again = await call("/public/deep_dee?q=nomatch", { headers: { accept: "application/json" } });
|
||||
expect(await again.json()).toMatchObject({ complete: true, items: [] });
|
||||
// page 1 holds 1..100, minus 10 private
|
||||
expect(await json()).toMatchObject({ matched: 90, complete: false });
|
||||
expect(await (await call(`${page}?days=300`)).text()).toMatch(/some matches may be missing/);
|
||||
expect(await testEnv.WEBAPP.calls()).toBe(before);
|
||||
await pub.page(2, PAGE_SIZE);
|
||||
await pub.page(3, PAGE_SIZE);
|
||||
expect(await json()).toMatchObject({ matched: 270, complete: true });
|
||||
});
|
||||
|
||||
it("JSON answers carry the canonical filter, so devs see what was understood", async () => {
|
||||
@@ -393,25 +398,6 @@ describe.skip("public page filters (goal 1d)", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("?in= an old week costs O(log pages) diigo fetches with by=updated, not a walk from page 1", async () => {
|
||||
const res = await call("/_wire", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ webappId: "fake-deep-webapp", mode: "public" }),
|
||||
});
|
||||
expect(res.status).toBe(200);
|
||||
// deep_dee: 6000 bookmarks, one a day, 60 diigo pages of 100; a week
|
||||
// ~14 years back sits near page 51 — a walk from page 1 would cost ~51
|
||||
const longAgo = new Date(Date.now() - 5100 * 24 * 60 * 60 * 1000);
|
||||
const week = isoWeek(longAgo);
|
||||
const before = await testEnv.WEBAPP.calls();
|
||||
const html = await (await call(`/public/deep_dee?in=${week}`)).text();
|
||||
// one a day; every 10th is private
|
||||
expect(titlesIn(html).length).toBeGreaterThanOrEqual(6);
|
||||
expect(titlesIn(html).length).toBeLessThanOrEqual(7);
|
||||
expect((await testEnv.WEBAPP.calls()) - before).toBeLessThanOrEqual(12);
|
||||
});
|
||||
|
||||
it("a malformed filter is a 400 with a message, not an empty list", async () => {
|
||||
const page = await wireDated();
|
||||
const res = await call(`${page}?days=7x`);
|
||||
|
||||
Reference in new issue
Block a user