Rebasely Beta: data may be purged

examples/live-notes Public

TypeScript · 88 lines · 4.5 KiB · at 70b1506

import assert from "node:assert/strict";import { describe, test } from "node:test";import { insert, type Op } from "../src/ops.ts";import { Room, SNAPSHOT_EVERY } from "../src/room.ts";import { PAGE, listSnapshots, readSnapshot, saveSnapshot, snapshotKey } from "../src/snapshots.ts";import { memoryBucket } from "./helpers/memory-bucket.ts";import { MemoryStore } from "./helpers/memory-store.ts";const send = (base: number, op: Op) => JSON.stringify({ type: "op", base, op });const ID = "a1b2c3";describe("when the room asks for a snapshot", () => {  test("every SNAPSHOT_EVERY versions, with the note as it is at that version", () => {    const room = new Room(new MemoryStore());    for (let v = 0; v < SNAPSHOT_EVERY - 1; v++) assert.equal(room.receive(send(v, insert(v, "x"))).saveSnapshot, undefined);    const due = room.receive(send(SNAPSHOT_EVERY - 1, insert(0, "y")));    assert.deepEqual(due.saveSnapshot, { text: "y" + "x".repeat(SNAPSHOT_EVERY - 1), version: SNAPSHOT_EVERY });    // The reply and the broadcast are the same as for any other edit.    assert.deepEqual(due.reply, { type: "ack", version: SNAPSHOT_EVERY });    assert.equal(due.broadcast?.type, "op");  });  test("not again until another SNAPSHOT_EVERY versions after a recorded save", () => {    const store = new MemoryStore({ text: "", version: SNAPSHOT_EVERY - 1 });    const room = new Room(store);    room.snapshotSaved(room.receive(send(SNAPSHOT_EVERY - 1, insert(0, "a"))).saveSnapshot!.version);    assert.equal(store.snapshot, SNAPSHOT_EVERY);    assert.equal(room.receive(send(SNAPSHOT_EVERY, insert(0, "b"))).saveSnapshot, undefined);  });  test("on the next edit again when a save was never recorded", () => {    const room = new Room(new MemoryStore({ text: "", version: SNAPSHOT_EVERY - 1 }));    assert.equal(room.receive(send(SNAPSHOT_EVERY - 1, insert(0, "a"))).saveSnapshot?.version, SNAPSHOT_EVERY);    assert.equal(room.receive(send(SNAPSHOT_EVERY, insert(0, "b"))).saveSnapshot?.version, SNAPSHOT_EVERY + 1);  });  test("never for an edit that changed nothing or was refused", () => {    const room = new Room(new MemoryStore({ text: "abc", version: 2 * SNAPSHOT_EVERY }));    assert.equal(room.receive(send(2 * SNAPSHOT_EVERY, insert(0, ""))).saveSnapshot, undefined);    assert.equal(room.receive(send(2 * SNAPSHOT_EVERY, insert(9, "x"))).saveSnapshot, undefined);  });  test("a recorded save survives the room waking from hibernation", () => {    const store = new MemoryStore({ text: "", version: SNAPSHOT_EVERY - 1 });    const room = new Room(store);    room.snapshotSaved(room.receive(send(SNAPSHOT_EVERY - 1, insert(0, "a"))).saveSnapshot!.version);    const woken = new Room(store);    assert.equal(woken.receive(send(SNAPSHOT_EVERY, insert(0, "b"))).saveSnapshot, undefined);  });  test("an older save never moves the recorded version back", () => {    const store = new MemoryStore();    const room = new Room(store);    room.snapshotSaved(300);    room.snapshotSaved(200);    assert.equal(store.snapshot, 300);  });});describe("snapshots in R2", () => {  test("keys sort in version order", () => {    assert.equal(snapshotKey(ID, 100), "snapshots/a1b2c3/000000000100.txt");    assert.ok(snapshotKey(ID, 900) < snapshotKey(ID, 1000));  });  test("saves a note and reads it back by version", async () => {    const bucket = memoryBucket();    await saveSnapshot(bucket, ID, { text: "milk\neggs\n", version: 100 });    assert.equal(await readSnapshot(bucket, ID, 100), "milk\neggs\n");    assert.equal(await readSnapshot(bucket, ID, 200), null);    assert.equal(await readSnapshot(bucket, "another-note", 100), null);  });  test("lists a note's snapshots oldest first, a page at a time", async () => {    const bucket = memoryBucket(() => new Date("2026-10-15T09:00:00Z"));    for (let v = 1; v <= PAGE + 2; v++) await saveSnapshot(bucket, ID, { text: "é".repeat(v), version: v * SNAPSHOT_EVERY });    await saveSnapshot(bucket, "another-note", { text: "", version: SNAPSHOT_EVERY });    const first = await listSnapshots(bucket, ID);    assert.equal(first.snapshots.length, PAGE);    assert.deepEqual(first.snapshots[0], { version: SNAPSHOT_EVERY, saved_at: "2026-10-15T09:00:00.000Z", size: 2 });    assert.equal(first.next, PAGE * SNAPSHOT_EVERY);    const second = await listSnapshots(bucket, ID, first.next!);    assert.deepEqual(second.snapshots.map((s) => s.version), [(PAGE + 1) * SNAPSHOT_EVERY, (PAGE + 2) * SNAPSHOT_EVERY]);    assert.equal(second.next, null);  });});