Files
yaak-mountain-loop/apps/yaak-proxy/lib/flowWindow.test.ts
T
Gregory SchierandClaude Opus 5.5 1a994f7532 Keep the proxy app fast with many flows
The app held every flow in memory and redid all of its work for each new
one: the sidebar mounted every domain and path node (its tree scrolled
inside a container that never bounded it), every tree row looked up its
atoms by scanning every atom the family had made, and the layout, detail
pane and list rows re-rendered per flow. At 3,000 flows each new one cost
about 90 ms of main thread, more than the traffic left time for.

The app now holds a window of the newest 2,000 flows, applies new ones in
batches, and loads older ones in pages from the database on scrolling to
the top, through a new list_flows RPC that pages in SQL. The database
keeps every flow.

Also adds clear_flows: an RPC, POST /flows/clear on the control API, and
a Clear button in the header that Cmd+K opens, confirming first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 09:52:00 -07:00

132 lines
4.7 KiB
TypeScript

import type { Flow, ModelPayload } from "@yaakapp-internal/proxy-lib";
import { describe, expect, test } from "vite-plus/test";
import {
applyChanges,
emptyWindow,
type FlowWindow,
prependOlder,
trimWindow,
windowFromPage,
} from "./flowWindow";
/** A flow started at `second` past 10:00, with an id that sorts the same way */
function flow(second: number, extra: Partial<Flow> = {}): Flow {
const s = String(second).padStart(2, "0");
return { id: `fl_${s}`, startedAt: `2026-10-28T10:00:${s}`, ...extra } as Flow;
}
const created = (f: Flow): ModelPayload => ({
model: f,
change: { type: "upsert", created: true },
});
const updated = (f: Flow): ModelPayload => ({
model: f,
change: { type: "upsert", created: false },
});
const ids = (win: FlowWindow) => win.flows.map((f) => f.id);
describe("windowFromPage", () => {
test("holds the newest page oldest first", () => {
const win = windowFromPage([flow(3), flow(2), flow(1)], 3, 10);
expect(ids(win)).toEqual(["fl_01", "fl_02", "fl_03"]);
expect(win).toMatchObject({ hasOlder: true, total: 10 });
});
test("knows there is nothing older when the page came back short", () => {
expect(windowFromPage([flow(1)], 3, 1).hasOlder).toBe(false);
});
});
describe("applyChanges", () => {
test("appends new traffic and counts it", () => {
const win = applyChanges(windowFromPage([flow(2), flow(1)], 10, 2), [
created(flow(3)),
created(flow(4)),
]);
expect(ids(win)).toEqual(["fl_01", "fl_02", "fl_03", "fl_04"]);
expect(win.total).toBe(4);
});
test("puts a slow flow where it started", () => {
const win = applyChanges(windowFromPage([flow(5), flow(1)], 10, 2), [created(flow(3))]);
expect(ids(win)).toEqual(["fl_01", "fl_03", "fl_05"]);
});
test("orders flows that started together by id", () => {
const a = flow(2, { id: "fl_a" });
const b = flow(2, { id: "fl_b" });
const win = applyChanges(emptyWindow, [created(b), created(a)]);
expect(ids(win)).toEqual(["fl_a", "fl_b"]);
});
test("replaces a changed flow in place without counting it again", () => {
const saved = flow(2, { savedRequestId: "rq_1" });
const win = applyChanges(windowFromPage([flow(3), flow(2), flow(1)], 10, 3), [updated(saved)]);
expect(win.flows[1]).toBe(saved);
expect(win.total).toBe(3);
});
test("replaces a flow the first page already had, without counting it again", () => {
const win = applyChanges(windowFromPage([flow(2), flow(1)], 10, 2), [created(flow(2))]);
expect(ids(win)).toEqual(["fl_01", "fl_02"]);
expect(win.total).toBe(2);
});
test("leaves a flow older than the window to paging, but counts it", () => {
const win = applyChanges(windowFromPage([flow(5), flow(4)], 2, 9), [created(flow(1))]);
expect(ids(win)).toEqual(["fl_04", "fl_05"]);
expect(win.total).toBe(10);
});
test("keeps an older flow when the window already holds everything", () => {
const win = applyChanges(windowFromPage([flow(5), flow(4)], 10, 2), [created(flow(1))]);
expect(ids(win)).toEqual(["fl_01", "fl_04", "fl_05"]);
});
test("returns the same window for no changes", () => {
const win = windowFromPage([flow(1)], 10, 1);
expect(applyChanges(win, [])).toBe(win);
});
});
describe("trimWindow", () => {
const many = (n: number) =>
windowFromPage(
Array.from({ length: n }, (_, i) => flow(n - i)),
1000,
n,
);
test("leaves the window alone within the slack", () => {
const win = many(7);
expect(trimWindow(win, 4, 3)).toBe(win);
});
test("drops the oldest back to the size, an even number of them", () => {
const even = trimWindow(many(8), 4, 3);
expect(ids(even)).toEqual(["fl_05", "fl_06", "fl_07", "fl_08"]);
expect(even).toMatchObject({ hasOlder: true, total: 8 });
// Five over rounds up to six, so the list's stripes stay on the same flows.
expect(ids(trimWindow(many(9), 4, 3))).toEqual(["fl_07", "fl_08", "fl_09"]);
});
});
describe("prependOlder", () => {
test("adds an older page before the oldest flow held", () => {
const win = prependOlder(windowFromPage([flow(5), flow(4)], 2, 5), [flow(3), flow(2)], 2);
expect(ids(win)).toEqual(["fl_02", "fl_03", "fl_04", "fl_05"]);
expect(win.hasOlder).toBe(true);
});
test("knows it reached the start when a page comes back short", () => {
const win = prependOlder(windowFromPage([flow(5), flow(4)], 2, 3), [flow(1)], 2);
expect(ids(win)).toEqual(["fl_01", "fl_04", "fl_05"]);
expect(win.hasOlder).toBe(false);
});
test("skips flows the window already has", () => {
const win = prependOlder(windowFromPage([flow(5), flow(4)], 2, 5), [flow(4), flow(3)], 2);
expect(ids(win)).toEqual(["fl_03", "fl_04", "fl_05"]);
});
});