← Files DataARCHIVED FILE

templates/data-app/base/tests/query-data-store.test.mjs

8.74 KB · Sep 30, 2026 · 23:19 UTC

↓ Download file

import assert from "node:assert/strict";
import test from "node:test";

import { createQueryDataStore } from "../src/query-data-store.js";

const snapshotSha256 = "a".repeat(64);
const turn = () => new Promise(resolve => setImmediate(resolve));
const snapshot = ids => ({ queries: Object.fromEntries(ids.map(id => [id, { source: { label: id } }])) });
const response = rows => {
  const result = new Response(JSON.stringify(rows));
  result.json = result.text = () => { throw new Error("Queries must use streaming JSON parsing."); };
  return result;
};
function requests() {
  const calls = [];
  return { calls, request(url, options) {
    return new Promise(resolve => calls.push({ url, options, resolve }));
  } };
}

test("query loading is explicit, deduplicated, identity-preserving, and distinguishes empty results", async () => {
  const network = requests();
  const seed = snapshot(["usage / ?", "unused", "empty"]);
  seed.queries.empty.rows = [];
  const changes = [];
  const store = createQueryDataStore(seed, { snapshotSha256, request: network.request, onChange: value => changes.push(value) });
  let notifications = 0;
  const unsubscribe = store.subscribe(() => { notifications += 1; });
  assert.equal(store.getQueries(), seed.queries);
  assert.equal(store.isReady(["empty"]), true);
  assert.equal(store.isReady(["usage / ?"]), false);
  assert.equal(store.getVersion(), store.getVersion());
  await store.ensure(["empty"]);
  assert.equal(network.calls.length, 0);
  const first = store.ensure(["usage / ?", "usage / ?"]);
  const second = store.ensure(["usage / ?"]);
  assert.equal(network.calls.length, 1);
  assert.equal(store.pending(["usage / ?"]), true);
  assert.equal(store.error(["usage / ?"]), null);
  assert.equal(network.calls[0].url, `/api/query-rows?queryId=usage%20%2F%20%3F&snapshot=${snapshotSha256}&revision=seed`);
  assert.equal(network.calls[0].options.credentials, "same-origin");
  network.calls[0].resolve(response([{ text: "𠜎 café", nested: { value: null }, count: 3 }]));
  await Promise.all([first, second]);
  const rows = store.getQueries()["usage / ?"].rows;
  assert.deepEqual(rows, [{ text: "𠜎 café", nested: { value: null }, count: 3 }]);
  assert.equal(store.getQueries().empty.rows, seed.queries.empty.rows);
  assert.equal(store.getQueries()["usage / ?"].source, seed.queries["usage / ?"].source);
  assert.equal(seed.queries["usage / ?"].rows, undefined);
  assert.equal(store.getQueries().unused.rows, undefined);
  assert.equal(store.pending(["usage / ?"]), false);
  await store.ensure(["usage / ?"]);
  assert.equal(store.getQueries()["usage / ?"].rows, rows);
  assert.equal(network.calls.length, 1);
  assert.equal(changes.length, 1);
  assert.equal(changes[0], store.getQueries());
  assert.equal(notifications, 2);
  unsubscribe();
  store.replace("empty", [{ count: 1 }]);
  assert.equal(notifications, 2);
  store.dispose();
});

test("loadAll limits concurrent transport to two and preserves every row and query key", async () => {
  const network = requests();
  const seed = snapshot(["first", "second", "third", "__proto__", "last"]);
  const store = createQueryDataStore(seed, { snapshotSha256, request: network.request });
  const complete = store.loadAll();
  assert.equal(network.calls.length, 2);
  const manyRows = Array.from({ length: 10_001 }, (_, index) => ({ index, text: `row ${index}` }));
  network.calls[0].resolve(response(manyRows));
  await turn();
  assert.equal(network.calls.length, 3);
  network.calls[1].resolve(response([]));
  await turn();
  assert.equal(network.calls.length, 4);
  network.calls[2].resolve(response([{ amount: -0.5 }]));
  await turn();
  assert.equal(network.calls.length, 5);
  network.calls[3].resolve(response([{ own: true }]));
  network.calls[4].resolve(response([{ final: true }]));
  await complete;
  assert.deepEqual(store.getQueries().first.rows, manyRows);
  assert.equal(store.getQueries().first.rows.at(-1).index, 10_000);
  assert.equal(Object.hasOwn(store.getQueries(), "__proto__"), true);
  assert.deepEqual(store.getQueries().__proto__.rows, [{ own: true }]);
  assert.deepEqual(store.getQueries().second.rows, []);
  assert.equal(store.isReady(Object.keys(seed.queries)), true);
  store.dispose();
});

test("failed transport, malformed data, and incomplete results stay unavailable until an explicit retry", async t => {
  for (const [name, failed] of [
    ["http", () => new Response("Unavailable", { status: 503 })],
    ["truncated", () => new Response('[{"value":')],
    ["shape", () => response({ value: 3 })],
    ["count", () => response([])],
  ]) await t.test(name, async () => {
    let calls = 0;
    const seed = snapshot(["source"]);
    seed._dataAppQueryLoading = { queries: { source: { rowCount: 1, columns: ["value"] } } };
    const store = createQueryDataStore(seed, { snapshotSha256,
      request: async () => ++calls === 1 ? failed() : response([{ value: 3 }]) });
    await assert.rejects(store.ensure(["source"]));
    assert.equal(store.isReady(["source"]), false);
    assert.equal(store.pending(["source"]), false);
    assert.ok(store.error(["source"]) instanceof Error);
    assert.equal(store.getQueries().source.rows, undefined);
    await turn();
    assert.equal(calls, 1);
    await store.ensure(["source"]);
    assert.equal(calls, 2);
    assert.equal(store.error(["source"]), null);
    assert.deepEqual(store.getQueries().source.rows, [{ value: 3 }]);
    store.dispose();
  });
});

test("owner replacements win over in-flight or queued loads, including ignored aborts", async () => {
  const network = requests();
  const seed = snapshot(["active", "other", "queued"]);
  const store = createQueryDataStore(seed, { snapshotSha256, request: network.request });
  const complete = store.loadAll();
  const activeRows = [{ owner: "new", nested: { exact: 4 } }], queuedRows = [];
  const executedAt = "2026-09-09T08:00:00Z";
  store.replace("active", activeRows, executedAt);
  store.replace("queued", queuedRows);
  assert.equal(network.calls[0].options.signal.aborted, true);
  assert.equal(store.getQueries().active.rows, activeRows);
  assert.deepEqual(store.getQueries().active.source, { ...seed.queries.active.source, executedAt });
  assert.equal(store.getQueries().queued.rows, queuedRows);
  network.calls[0].resolve(response([{ owner: "stale" }]));
  network.calls[1].resolve(response([{ other: true }]));
  await complete;
  await turn();
  assert.equal(network.calls.length, 2);
  assert.equal(store.getQueries().active.rows, activeRows);
  assert.deepEqual(store.getQueries().active.source, { ...seed.queries.active.source, executedAt });
  store.replace("active", activeRows);
  assert.equal(store.getQueries().active.source.executedAt, executedAt);
  assert.equal(store.getQueries().queued.rows, queuedRows);
  assert.deepEqual(store.getQueries().other.rows, [{ other: true }]);
  assert.equal(store.error(["active", "queued"]), null);
  store.dispose();
});

test("dispose aborts active loads, rejects queued waiters, and ignores late responses", async () => {
  const network = requests();
  const store = createQueryDataStore(snapshot(["a", "b", "c"]), { snapshotSha256, request: network.request });
  const complete = store.loadAll();
  const rejected = assert.rejects(complete, /no longer active/u);
  store.dispose();
  await rejected;
  assert.equal(network.calls.length, 2);
  assert.ok(network.calls.every(call => call.options.signal.aborted));
  network.calls.forEach(call => call.resolve(response([{ stale: true }])));
  await turn();
  assert.equal(network.calls.length, 2);
  assert.equal(store.getQueries().a.rows, undefined);
  assert.equal(store.pending(["a", "b", "c"]), false);
  await assert.rejects(store.ensure(["a"]), /no longer active/u);
  assert.throws(() => store.replace("a", []), /no longer active/u);
});

test("query isolation rejects undeclared IDs and invalid results without issuing requests", async () => {
  const network = requests();
  const store = createQueryDataStore(snapshot(["known"]), { snapshotSha256, request: network.request });
  await assert.rejects(store.ensure(["toString"]), /Unknown reviewed query/u);
  await assert.rejects(store.ensure(["known", "missing"]), /Unknown reviewed query/u);
  assert.throws(() => store.replace("known", {}), /must be an array/u);
  assert.equal(network.calls.length, 0);
  assert.equal(store.pending(["known"]), false);
  store.dispose();
});


test("query-parameter routing preserves exact dot-segment IDs", async () => {
  const seen = [];
  const store = createQueryDataStore(snapshot([".", ".."]), { snapshotSha256,
    request: async url => { seen.push(new URL(url, "https://fixture.chatgpt.site")); return response([]); } });
  await store.ensure([".", ".."]);
  assert.deepEqual(seen.map(url => url.pathname), ["/api/query-rows", "/api/query-rows"]);
  assert.deepEqual(seen.map(url => url.searchParams.get("queryId")), [".", ".."]);
  store.dispose();
});

SHA-256: a5bc1c2b3be86f48d1728074c3c0e315b72335282de3aca165b11321747ec007