clone

nostr relays on demand at <name>.bind.ws: claim one with your key, pay in zaps past the free allowance

dependabot/npm_and_yarn/npm-f01a459b0f
bindws / test / object / list-history.test.ts
list-history.test.ts 5.6K
1import { env, runInDurableObject } from "cloudflare:test";2import { describe, expect, it } from "vitest";3import { generateSecretKey } from "nostr-tools/pure";4import type { Relay } from "../../src/relay.ts";5import { ev, now, pk, rpc } from "../helpers/relay.ts";67describe("private list recovery", () => {8 it("keeps bounded private versions and returns an unsigned owner restore", async () => {9 const host = "list-history.bind.ws";10 const owner = generateSecretKey();11 const other = generateSecretKey();12 await rpc(host, owner, "claim");13 const base = now();14 await runInDurableObject(env.RELAY.getByName("list-history"), (relay: Relay) => {15 for (let i = 0; i < 14; i++) expect(relay.store.save(ev(owner, 10002, "v" + i, [["r", "wss://relay" + i]], base + i), base + i)).toBe("");16 expect(relay.store.save(ev(other, 10002, "other", [], base + 20), base + 20)).toBe("");17 expect(relay.store.save(ev(owner, 10003, "bookmarks", [], base + 21), base + 21)).toBe("");18 });19 const history = (await rpc(host, owner, "listlisthistory")).result as any[];20 expect(history.length).toBe(12);21 expect(history.every((x) => x.owner === undefined && x.kind === 10002 || x.kind === 10003)).toBe(true);22 expect((await rpc(host, other, "listlisthistory")).status).toBe(403);23 const chosen = history.find((x) => x.kind === 10002);24 const restored = (await rpc(host, owner, "restorelist", chosen.event_id)).result;25 expect(restored.draft).toMatchObject({ kind: 10002, content: expect.stringMatching(/^v/) });26 expect(restored.draft).not.toHaveProperty("sig");27 expect(restored.draft).not.toHaveProperty("pubkey");28 expect(restored.diff).toMatchObject({ contentChanged: true });29 expect(restored.draft.created_at).toBeGreaterThanOrEqual(base + 14);30 expect((await rpc(host, other, "restorelist", chosen.event_id)).status).toBe(403);31 await runInDurableObject(env.RELAY.getByName("list-history"), (relay: Relay) => {32 expect(relay.sql.exec<{ n: number }>(`SELECT count(*) AS n FROM list_history WHERE owner=?`, pk(owner)).one().n).toBeLessThanOrEqual(96);33 });34 });3536 it("clears prior versions when the author vanishes", async () => {37 const host = "list-history-vanish.bind.ws";38 const owner = generateSecretKey();39 await rpc(host, owner, "claim");40 await runInDurableObject(env.RELAY.getByName("list-history-vanish"), (relay: Relay) => {41 relay.store.save(ev(owner, 3, "old"), now());42 relay.store.save(ev(owner, 3, "new"), now() + 1);43 relay.store.vanish(pk(owner), now() + 100);44 });45 expect((await rpc(host, owner, "listlisthistory")).result).toEqual([]);46 });4748 it("caps an owner's history across distinct bookmark sets", async () => {49 const host = "list-history-owner-cap.bind.ws";50 const owner = generateSecretKey();51 await rpc(host, owner, "claim");52 await runInDurableObject(env.RELAY.getByName("list-history-owner-cap"), (relay: Relay) => {53 const base = now();54 for (let i = 0; i < 100; i++) {55 expect(relay.store.save(ev(owner, 30003, "old-" + i, [["d", "set-" + i]], base + i * 2), base + i * 2)).toBe("");56 expect(relay.store.save(ev(owner, 30003, "new-" + i, [["d", "set-" + i]], base + i * 2 + 1), base + i * 2 + 1)).toBe("");57 }58 expect(relay.sql.exec<{ n: number }>(`SELECT count(*) AS n FROM list_history WHERE owner=?`, pk(owner)).one().n).toBe(96);59 });60 });6162 it("keeps member history private and clears colon-containing address history on deletion", async () => {63 const host = "list-history-privacy.bind.ws";64 const owner = generateSecretKey();65 const member = generateSecretKey();66 await rpc(host, owner, "claim");67 await rpc(host, owner, "setmember", pk(member));68 let ownerHistoryID = "";69 await runInDurableObject(env.RELAY.getByName("list-history-privacy"), (relay: Relay) => {70 const base = now();71 relay.store.save(ev(owner, 10002, "owner-old", [], base), base);72 const ownerCurrent = ev(owner, 10002, "owner-new", [], base + 1);73 relay.store.save(ownerCurrent, base + 1);74 ownerHistoryID = ownerCurrent.id;75 relay.store.save(ev(member, 10002, "member-old", [], base + 2), base + 2);76 relay.store.save(ev(member, 10002, "member-new", [], base + 3), base + 3);77 relay.store.save(ev(owner, 30003, "set-old", [["d", "set:with:colon"]], base + 4), base + 4);78 relay.store.save(ev(owner, 30003, "set-new", [["d", "set:with:colon"]], base + 5), base + 5);79 });80 const memberHistory = (await rpc(host, member, "listlisthistory")).result as any[];81 expect(memberHistory).toHaveLength(1);82 expect(memberHistory[0].kind).toBe(10002);83 expect((await rpc(host, member, "restorelist", ownerHistoryID)).status).toBe(404);84 await runInDurableObject(env.RELAY.getByName("list-history-privacy"), (relay: Relay) => {85 const deletion = ev(owner, 5, "", [["a", `30003:${pk(owner)}:set:with:colon`]], now() + 10);86 expect(relay.store.save(deletion, deletion.created_at)).toBe("");87 expect(relay.sql.exec<{ n: number }>(`SELECT count(*) AS n FROM list_history WHERE owner=? AND kind=30003`, pk(owner)).one().n).toBe(0);88 });89 });9091 it("does not restore an expired saved list", async () => {92 const host = "list-history-expiry.bind.ws";93 const owner = generateSecretKey();94 await rpc(host, owner, "claim");95 await runInDurableObject(env.RELAY.getByName("list-history-expiry"), (relay: Relay) => {96 const base = now();97 relay.store.save(ev(owner, 10003, "expired", [["expiration", String(base - 1)]], base), base);98 relay.store.save(ev(owner, 10003, "current", [], base + 1), base + 1);99 });100 expect((await rpc(host, owner, "listlisthistory")).result).toEqual([]);101 });102});