Files
buzz/desktop/tests/e2e/sidebar-snapshot.spec.ts
Luke Tornquist 6918dd3a83 Schedule the deletion drain safely (#7827)
## Why

The deletion engine already exposes a one-shot `buzz-admin deletions
drain` command, but operators have no safe way to schedule it. A generic
workload wrapper could expose arbitrary commands or inherit relay
credentials, which would add a second control plane and widen the blast
radius.

## What

This PR adds an opt-in Kubernetes CronJob for the typed deletion drain
command. It is stacked on #7818, which adds owner-request admission.

## How

The chart keeps the job disabled by default. The CronJob uses `Forbid`,
zero Kubernetes retries, bounded runtime and history, and only the
database, Redis, and object-store settings required by the executor.
PostgreSQL leases, checkpoints, and retry timing remain the sole durable
retry authority.

The closed `_operator-jobs.tpl` helper centralizes safe CronJob
mechanics without creating an arbitrary command or environment registry.
This leaves the touched system simpler than a general job framework
would.

## Risk

The deployment blast radius is low because the job is disabled by
default. When enabled, it can run destructive deletion work that the
database has already approved, so the chart limits concurrency, retries,
credentials, and execution time.

## Testing

Rendered every `deploy/charts/buzz/ci/*.yaml` fixture with `helm
template`; all passed at `e68cb753bb6673aa1baa60046969bdb2d4738b62`.

Ran the full independent Desktop, Tauri, and web lanes; all passed at
`e68cb753bb6673aa1baa60046969bdb2d4738b62`. Flutter package resolution
made no progress within three minutes, so the mobile lane is not claimed
green.

## Bigger picture

A later stack layer must inventory and approve admitted owner requests.
Until then, this scheduler cannot make a new owner request destructive.

Generated with Codex

---------

Signed-off-by: tornquist <tornquist@squareup.com>
Signed-off-by: Codex <noreply@openai.com>
Signed-off-by: Elrond <28d6302a099e5225b02c4155ac4236e4912603df2ab08dbfc2f4fef08ce598c8@buzz.block.builderlab.xyz>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Elrond <28d6302a099e5225b02c4155ac4236e4912603df2ab08dbfc2f4fef08ce598c8@buzz.block.builderlab.xyz>
2026-09-28 18:23:42 -04:00

634 lines
20 KiB
TypeScript

import { expect, test, type Page } from "@playwright/test";
import { installMockBridge } from "../helpers/bridge";
const RELAY_URL = "ws://localhost:3000";
const OTHER_RELAY_URL = "ws://localhost:3001";
const OWNER_PUBKEY = "deadbeef".repeat(8);
const STALE_COMMUNITY_PUBKEY = "cafebabe".repeat(8);
const MATCHING_HASH = "mock-hash";
const READ_DELAY_MS = 600;
const SNAPSHOT_FRAME_DELAY_MS = 3_000;
function snapshotKey(relayUrl: string, ownerPubkey = OWNER_PUBKEY) {
return `buzz-channels.v1:${relayUrl}:${ownerPubkey.toLowerCase()}`;
}
function makeSnapshotChannel(index: number, prefix = "snapshot") {
return {
id: `${prefix}-${index}`,
name: `${prefix}-${String(index).padStart(2, "0")}`,
channelType: "stream",
visibility: "open",
description: "",
topic: null,
purpose: null,
memberCount: 1,
memberPubkeys: [OWNER_PUBKEY],
lastMessageAt: null,
archivedAt: null,
participants: [],
participantPubkeys: [],
isMember: true,
ttlSeconds: null,
ttlDeadline: null,
};
}
const FULL_SNAPSHOT = Array.from({ length: 14 }, (_, index) =>
makeSnapshotChannel(index),
);
const SNAPSHOT_DIAGNOSTIC_MARK = "buzz:sidebar:snapshot-diagnostic";
const FULL_SIDEBAR_PAINT_MARK = "buzz:sidebar:full-list-painted";
const BOOT_TO_FULL_SIDEBAR_MEASURE = "buzz:sidebar:boot-to-full-list-painted";
function snapshotIntegrity(
ownerPubkey: string,
hash: string,
channels: ReturnType<typeof makeSnapshotChannel>[],
) {
const value = JSON.stringify([ownerPubkey.toLowerCase(), hash, channels]);
let result = 0x811c9dc5;
for (let index = 0; index < value.length; index += 1) {
result ^= value.charCodeAt(index);
result = Math.imul(result, 0x01000193);
}
return (result >>> 0).toString(16).padStart(8, "0");
}
type SnapshotDiagnostics = {
ageMs: number;
channelCount: number;
presence: "absent" | "invalid" | "present";
serializedBytes: number;
};
async function getSnapshotDiagnostic(page: Page) {
return page.evaluate((name) => {
const entry = performance.getEntriesByName(name).at(-1) as
| PerformanceMark
| undefined;
return entry?.detail as SnapshotDiagnostics | undefined;
}, SNAPSHOT_DIAGNOSTIC_MARK);
}
async function getFullSidebarMeasure(page: Page) {
return page.evaluate(
({ markName, measureName }) => ({
markCount: performance.getEntriesByName(markName).length,
measure: performance.getEntriesByName(measureName).at(-1)?.duration,
}),
{
markName: FULL_SIDEBAR_PAINT_MARK,
measureName: BOOT_TO_FULL_SIDEBAR_MEASURE,
},
);
}
async function seedSnapshot(
page: Page,
options: {
channels?: ReturnType<typeof makeSnapshotChannel>[];
hash: string;
keyOwnerPubkey?: string;
ownerPubkey?: string;
relayUrl?: string;
},
) {
const channels = options.channels ?? FULL_SNAPSHOT;
const ownerPubkey = options.ownerPubkey ?? OWNER_PUBKEY;
await page.addInitScript(
({ channels, hash, integrity, key, ownerPubkey }) => {
window.localStorage.setItem(
key,
JSON.stringify({
version: 2,
updatedAt: Date.now(),
ownerPubkey,
hash,
channels,
integrity,
}),
);
},
{
channels,
hash: options.hash,
integrity: snapshotIntegrity(ownerPubkey, options.hash, channels),
key: snapshotKey(
options.relayUrl ?? RELAY_URL,
options.keyOwnerPubkey ?? ownerPubkey,
),
ownerPubkey,
},
);
}
async function seedCommunities(page: Page, communityPubkey = OWNER_PUBKEY) {
await page.addInitScript(
({ communityPubkey, relayA, relayB }) => {
const communities = [
{
id: "community-a",
name: "Alpha",
relayUrl: relayA,
pubkey: communityPubkey,
addedAt: "2026-01-01T00:00:00.000Z",
},
{
id: "community-b",
name: "Bravo",
relayUrl: relayB,
pubkey: communityPubkey,
addedAt: "2026-01-02T00:00:00.000Z",
},
];
window.localStorage.setItem(
"buzz-communities",
JSON.stringify(communities),
);
window.localStorage.setItem(
"buzz-active-community-id",
communities[0].id,
);
},
{ communityPubkey, relayA: RELAY_URL, relayB: OTHER_RELAY_URL },
);
}
async function getChannelsPayloads(page: Page) {
return page.evaluate(() =>
(window.__BUZZ_E2E_COMMAND_LOG__ ?? [])
.filter((entry) => entry.command === "get_channels")
.map((entry) => entry.payload as { knownHash?: unknown }),
);
}
async function mutateDisplayedChannels(page: Page, channelName: string) {
await page.evaluate((name) => {
const queryClient = window.__BUZZ_E2E_QUERY_CLIENT__ as
| {
setQueryData: (
queryKey: readonly string[],
updater: (
channels: ReturnType<typeof makeSnapshotChannel>[] | undefined,
) => ReturnType<typeof makeSnapshotChannel>[],
) => unknown;
}
| undefined;
if (!queryClient) {
throw new Error("E2E query client seam is not installed.");
}
queryClient.setQueryData(["channels"], (channels) => {
const template = channels?.[0];
if (!template) {
throw new Error("Displayed channel cache is empty.");
}
return [...channels, { ...template, id: "optimistic-channel", name }];
});
}, channelName);
}
async function readPersistedSnapshot(page: Page) {
return page.evaluate((key) => {
const raw = window.localStorage.getItem(key);
if (!raw) return null;
const snapshot = JSON.parse(raw) as {
channels: Array<{ name: string }>;
hash: string;
};
return {
channelNames: snapshot.channels.map((channel) => channel.name),
hash: snapshot.hash,
};
}, snapshotKey(RELAY_URL));
}
async function trackSnapshotRows(page: Page) {
await page.addInitScript(() => {
const testWindow = window as Window & {
__BUZZ_E2E_SNAPSHOT_ROWS_SEEN__?: string[];
};
const seen: string[] = [];
testWindow.__BUZZ_E2E_SNAPSHOT_ROWS_SEEN__ = seen;
const record = (node: Node) => {
if (!(node instanceof Element)) return;
const rows = node.matches('[data-channel-id^="snapshot-"]')
? [node]
: Array.from(node.querySelectorAll('[data-channel-id^="snapshot-"]'));
for (const row of rows) {
const id = row.getAttribute("data-channel-id");
if (id && !seen.includes(id)) seen.push(id);
}
};
new MutationObserver((records) => {
for (const mutation of records) {
for (const node of mutation.addedNodes) record(node);
}
}).observe(document, { childList: true, subtree: true });
});
}
async function getTrackedSnapshotRows(page: Page) {
return page.evaluate(
() =>
(
window as Window & {
__BUZZ_E2E_SNAPSHOT_ROWS_SEEN__?: string[];
}
).__BUZZ_E2E_SNAPSHOT_ROWS_SEEN__ ?? [],
);
}
test("cold boot paints the complete snapshot, sends its hash, and revalidates", async ({
page,
}) => {
await seedSnapshot(page, { hash: MATCHING_HASH });
await installMockBridge(page, {
channelsReadDelayMs: SNAPSHOT_FRAME_DELAY_MS,
honorChannelsKnownHash: true,
});
const navigationStartedAt = performance.now();
await page.goto("/");
const rows = page.locator('[data-channel-id^="snapshot-"]');
await expect(rows).toHaveCount(FULL_SNAPSHOT.length, { timeout: 2_000 });
const snapshotPaintedAt = performance.now();
expect(snapshotPaintedAt - navigationStartedAt).toBeLessThan(
SNAPSHOT_FRAME_DELAY_MS,
);
await expect(page.getByTestId("sidebar-loading")).toHaveCount(0);
await expect
.poll(() => getChannelsPayloads(page))
.toEqual([{ knownHash: MATCHING_HASH }]);
await expect
.poll(() => getSnapshotDiagnostic(page))
.toMatchObject({
channelCount: FULL_SNAPSHOT.length,
presence: "present",
});
const diagnostics = await getSnapshotDiagnostic(page);
expect(diagnostics?.serializedBytes).toBeGreaterThan(0);
expect(diagnostics?.ageMs).toBeGreaterThanOrEqual(0);
// The persisted data is deliberately stale: it paints immediately but still
// validates. The delayed matching response leaves a deterministic snapshot
// frame, then not-modified must not shrink the list.
await expect(rows).toHaveCount(FULL_SNAPSHOT.length);
await expect
.poll(() => getFullSidebarMeasure(page))
.toEqual({
markCount: expect.any(Number),
measure: expect.any(Number),
});
expect((await getFullSidebarMeasure(page)).markCount).toBeGreaterThan(0);
const liveSettledAt = performance.now();
console.info(
`[sidebar-snapshot] firstPaint=${Math.round(snapshotPaintedAt - navigationStartedAt)}ms snapshotToLive=${Math.round(liveSettledAt - snapshotPaintedAt)}ms rows=${FULL_SNAPSHOT.length}`,
);
});
test("matching not-modified preserves display mutations without persisting them", async ({
page,
}) => {
const optimisticName = "optimistic-local-channel";
await seedSnapshot(page, { hash: MATCHING_HASH });
await installMockBridge(page, { honorChannelsKnownHash: true });
await page.goto("/");
await expect(page.locator('[data-channel-id^="snapshot-"]')).toHaveCount(
FULL_SNAPSHOT.length,
);
await expect
.poll(() =>
page.evaluate(() =>
window.__BUZZ_E2E_QUERY_CLIENT__?.getQueryState(["channels"]),
),
)
.toMatchObject({ fetchStatus: "idle", status: "success" });
// Exercise the mutation during a real revalidation, not a race between boot
// rendering and a fixed response delay. The cold-boot test covers first paint.
await page.evaluate(() => {
window.__BUZZ_E2E_DEFER_NEXT_CHANNELS_READ__?.();
void window.__BUZZ_E2E_QUERY_CLIENT__?.invalidateQueries({
queryKey: ["channels"],
exact: true,
});
});
await expect
.poll(() => page.evaluate(() => window.__BUZZ_E2E_CHANNELS_READ_PENDING__))
.toBe(1);
await mutateDisplayedChannels(page, optimisticName);
await expect(
page.locator('[data-channel-id="optimistic-channel"]'),
).toBeVisible();
expect(
await page.evaluate(() => window.__BUZZ_E2E_RELEASE_CHANNELS_READ__?.()),
).toBe(1);
// Invocation logging only proves the request started. Wait for the query to
// consume its response before checking either display state or persistence.
await expect
.poll(() =>
page.evaluate(() =>
window.__BUZZ_E2E_QUERY_CLIENT__?.getQueryState(["channels"]),
),
)
.toMatchObject({ fetchStatus: "idle", status: "success" });
expect(await getChannelsPayloads(page)).toEqual([
{ knownHash: MATCHING_HASH },
{ knownHash: MATCHING_HASH },
]);
await expect
.poll(() => readPersistedSnapshot(page))
.toEqual({
channelNames: FULL_SNAPSHOT.map((channel) => channel.name),
hash: MATCHING_HASH,
});
await expect(
page.locator('[data-channel-id="optimistic-channel"]'),
).toBeVisible();
expect((await readPersistedSnapshot(page))?.channelNames).not.toContain(
optimisticName,
);
});
test("first-ever boot without a snapshot sends null and shows loading", async ({
page,
}) => {
await installMockBridge(page, { channelsReadDelayMs: READ_DELAY_MS });
await page.goto("/");
await expect(page.getByTestId("sidebar-loading")).toBeVisible({
timeout: 500,
});
await expect(page.locator('[data-channel-id^="snapshot-"]')).toHaveCount(0);
await expect
.poll(() => getChannelsPayloads(page))
.toEqual([{ knownHash: null }]);
await expect
.poll(() => getSnapshotDiagnostic(page))
.toMatchObject({
channelCount: 0,
presence: "absent",
serializedBytes: 0,
});
await expect(page.getByTestId("channel-general")).toBeVisible();
await expect
.poll(() => getFullSidebarMeasure(page))
.toEqual({
markCount: expect.any(Number),
measure: expect.any(Number),
});
expect((await getFullSidebarMeasure(page)).markCount).toBeGreaterThan(0);
});
test("a different identity's snapshot is ignored", async ({ page }) => {
await seedSnapshot(page, {
hash: "foreign-hash",
keyOwnerPubkey: OWNER_PUBKEY,
ownerPubkey: STALE_COMMUNITY_PUBKEY,
});
await installMockBridge(page, { channelsReadDelayMs: READ_DELAY_MS });
await page.goto("/");
await expect(page.getByTestId("sidebar-loading")).toBeVisible({
timeout: 500,
});
await expect(page.locator('[data-channel-id^="snapshot-"]')).toHaveCount(0);
await expect
.poll(() => getChannelsPayloads(page))
.toEqual([{ knownHash: null }]);
await expect
.poll(() => getSnapshotDiagnostic(page))
.toMatchObject({
channelCount: 0,
presence: "invalid",
});
await expect(page.getByTestId("channel-general")).toBeVisible();
});
test("display-only community pubkey cannot expose another identity's snapshot", async ({
page,
}) => {
await seedSnapshot(page, {
hash: "stale-community-identity-hash",
ownerPubkey: STALE_COMMUNITY_PUBKEY,
});
await trackSnapshotRows(page);
await installMockBridge(
page,
{ channelsReadDelayMs: READ_DELAY_MS },
{ skipCommunitySeed: true },
);
await seedCommunities(page, STALE_COMMUNITY_PUBKEY);
await page.goto("/");
await expect(page.getByTestId("channel-general")).toBeVisible();
expect(await getTrackedSnapshotRows(page)).toEqual([]);
const payloads = await getChannelsPayloads(page);
expect(payloads).toEqual([{ knownHash: null }]);
expect(payloads).not.toContainEqual({
knownHash: "stale-community-identity-hash",
});
});
test("partial hash/list write fails toward a full fetch", async ({ page }) => {
await seedSnapshot(page, { hash: MATCHING_HASH });
await page.addInitScript((key) => {
const raw = window.localStorage.getItem(key);
if (!raw) return;
const snapshot = JSON.parse(raw) as { hash: string };
window.localStorage.setItem(
key,
JSON.stringify({ ...snapshot, hash: "newer-partial-hash" }),
);
}, snapshotKey(RELAY_URL));
await installMockBridge(page, {
channelsReadDelayMs: READ_DELAY_MS,
honorChannelsKnownHash: true,
});
await page.goto("/");
await expect(page.getByTestId("sidebar-loading")).toBeVisible({
timeout: 500,
});
await expect(page.locator('[data-channel-id^="snapshot-"]')).toHaveCount(0);
await expect
.poll(() => getChannelsPayloads(page))
.toEqual([{ knownHash: null }]);
await expect
.poll(() => getSnapshotDiagnostic(page))
.toMatchObject({
channelCount: 0,
presence: "invalid",
});
await expect(page.getByTestId("channel-general")).toBeVisible();
await expect(page.locator('[data-channel-id^="snapshot-"]')).toHaveCount(0);
});
test("unexpected not-modified response retries without a hash", async ({
page,
}) => {
await installMockBridge(page, {
channelsNotModifiedResponses: 1,
});
await page.goto("/");
await expect(page.getByTestId("channel-general")).toBeVisible();
await expect
.poll(() => getChannelsPayloads(page))
.toEqual([{ knownHash: null }, { knownHash: null }]);
});
test("mismatched not-modified hash falls back to a full list", async ({
page,
}) => {
await seedSnapshot(page, { hash: "persisted-stale-hash" });
await trackSnapshotRows(page);
await installMockBridge(page, {
channelsReadDelayMs: READ_DELAY_MS,
channelsNotModifiedResponses: 1,
});
await page.goto("/");
const snapshotRows = page.locator('[data-channel-id^="snapshot-"]');
// Record the transient boot frame independently of test-runner scheduling;
// cold-boot coverage separately enforces the snapshot paint deadline.
await expect
.poll(() => getTrackedSnapshotRows(page))
.toEqual(FULL_SNAPSHOT.map((channel) => channel.id));
await expect
.poll(() => getChannelsPayloads(page))
.toEqual([{ knownHash: "persisted-stale-hash" }, { knownHash: null }]);
// The stale snapshot may provide the immediate boot frame, but an impossible
// not-modified hash/list pairing must never survive as the settled result.
await expect(page.getByTestId("channel-general")).toBeVisible();
await expect(snapshotRows).toHaveCount(0);
await expect
.poll(() =>
page.evaluate((key) => {
const raw = window.localStorage.getItem(key);
if (!raw) return null;
const snapshot = JSON.parse(raw) as {
channels: Array<{ name: string }>;
hash: string;
};
return {
hash: snapshot.hash,
hasGeneral: snapshot.channels.some(
(channel) => channel.name === "general",
),
hasSnapshotRow: snapshot.channels.some((channel) =>
channel.name.startsWith("snapshot-"),
),
};
}, snapshotKey(RELAY_URL)),
)
.toEqual({ hash: MATCHING_HASH, hasGeneral: true, hasSnapshotRow: false });
});
test("hash mismatch replaces the snapshot with the full live list", async ({
page,
}) => {
await seedSnapshot(page, { hash: "stale-hash" });
await trackSnapshotRows(page);
await installMockBridge(page, {
channelsReadDelayMs: READ_DELAY_MS,
honorChannelsKnownHash: true,
});
await page.goto("/");
// Observe the boot frame even if live revalidation finishes before the
// test runner gets its next turn; cold-boot coverage separately times paint.
await expect
.poll(() => getTrackedSnapshotRows(page))
.toEqual(FULL_SNAPSHOT.map((channel) => channel.id));
await expect
.poll(() => getChannelsPayloads(page))
.toEqual([{ knownHash: "stale-hash" }]);
await expect(page.getByTestId("channel-general")).toBeVisible();
await expect(page.locator('[data-channel-id^="snapshot-"]')).toHaveCount(0);
await expect
.poll(() =>
page.evaluate((key) => {
const raw = window.localStorage.getItem(key);
if (!raw) return null;
const snapshot = JSON.parse(raw) as {
channels: Array<{ name: string }>;
hash: string;
};
return {
hash: snapshot.hash,
hasGeneral: snapshot.channels.some(
(channel) => channel.name === "general",
),
hasSnapshotRow: snapshot.channels.some((channel) =>
channel.name.startsWith("snapshot-"),
),
};
}, snapshotKey(RELAY_URL)),
)
.toEqual({ hash: MATCHING_HASH, hasGeneral: true, hasSnapshotRow: false });
});
test("community switch validates a stale relay snapshot and replaces it", async ({
page,
}) => {
const switchedSnapshot = Array.from({ length: 6 }, (_, index) =>
makeSnapshotChannel(index, "switched"),
);
await seedSnapshot(page, {
channels: switchedSnapshot,
hash: "stale-community-hash",
relayUrl: OTHER_RELAY_URL,
});
await installMockBridge(
page,
{
channelsReadDelayMs: READ_DELAY_MS,
honorChannelsKnownHash: true,
},
{ skipCommunitySeed: true },
);
await seedCommunities(page);
await page.goto("/");
await expect(page.getByTestId("app-sidebar")).toBeVisible();
const payloadCountBeforeSwitch = (await getChannelsPayloads(page)).length;
await page.getByTestId("community-rail-button-community-b").click();
const switchedRows = page.locator('[data-channel-id^="switched-"]');
await expect(switchedRows).toHaveCount(switchedSnapshot.length, {
timeout: 500,
});
await expect
.poll(async () =>
(await getChannelsPayloads(page)).slice(payloadCountBeforeSwitch).at(0),
)
.toEqual({ knownHash: "stale-community-hash" });
await expect(page.getByTestId("channel-general")).toBeVisible();
await expect(switchedRows).toHaveCount(0);
await expect
.poll(() =>
page.evaluate((key) => {
const raw = window.localStorage.getItem(key);
if (!raw) return null;
const snapshot = JSON.parse(raw) as {
channels: Array<{ name: string }>;
hash: string;
};
return {
hash: snapshot.hash,
hasGeneral: snapshot.channels.some(
(channel) => channel.name === "general",
),
};
}, snapshotKey(OTHER_RELAY_URL)),
)
.toEqual({ hash: MATCHING_HASH, hasGeneral: true });
});