| 1 | import test from "node:test" ; |
| 2 | import assert from "node:assert/strict" ; |
| 3 | import fs from "node:fs" ; |
| 4 | import vm from "node:vm" ; |
| 5 | |
| 6 | const source = fs.readFileSync("scripts/nostr-name-setup.mjs" , "utf8" ) |
| 7 | .replace(/^import .*\n/gm, "" ); |
| 8 | const key = "a" .repeat(64); |
| 9 | |
| 10 | function setup(initial, events) { |
| 11 | const store = {"tiny:names:v1" : JSON.stringify(initial)}; |
| 12 | let queries = 0; |
| 13 | const pool = {querySync: async () => { queries++; return events; }}; |
| 14 | const window = {nostrSharedPool: pool}; |
| 15 | const context = { |
| 16 | window, localStorage: {getItem: key => store[key] ?? null, setItem: (key, value) => { store[key] = value; }}, |
| 17 | location: {pathname: "/rooms/x" , protocol: "https:" , host: "tiny.example" }, |
| 18 | SimplePool: class {}, |
| 19 | bareNostrUser: pubkey => ({pubkey, image: undefined}), |
| 20 | nostrUserFromEvent: event => ({pubkey: event.pubkey, image: JSON.parse(event.content).picture}), |
| 21 | JSON, Date, Promise, String, RegExp, Math, setTimeout, clearTimeout |
| 22 | }; |
| 23 | vm.runInNewContext(source, context); |
| 24 | return {load: window.tinyNames.load, queries: () => queries}; |
| 25 | } |
| 26 | |
| 27 | const old = {pubkey: key, created_at: 1, content: JSON.stringify({name: "old" })}; |
| 28 | const fresh = {pubkey: key, created_at: 2, content: JSON.stringify({name: "new" , picture: "https://cdn.example/slate.png" })}; |
| 29 | |
| 30 | test("refresh bypasses an old image-less cached profile once" , async () => { |
| 31 | const {load, queries} = setup({[key]: {event: old, at: Math.floor(Date.now() / 1000)}}, [fresh]); |
| 32 | const user = await load({pubkey: key, refresh: true}); |
| 33 | assert.equal(user.image, "https://cdn.example/slate.png" ); |
| 34 | assert.equal(queries(), 1); |
| 35 | }); |
| 36 | |
| 37 | test("concurrent refresh callers share one batch and later refreshes use it" , async () => { |
| 38 | const {load, queries} = setup({[key]: {event: old, at: Math.floor(Date.now() / 1000)}}, [fresh]); |
| 39 | const users = await Promise.all(Array.from({length: 8}, () => load({pubkey: key, refresh: true}))); |
| 40 | assert.equal(queries(), 1); |
| 41 | assert.ok(users.every(user => user.image === "https://cdn.example/slate.png" )); |
| 42 | assert.equal((await load({pubkey: key, refresh: true})).image, "https://cdn.example/slate.png" ); |
| 43 | assert.equal(queries(), 1); |
| 44 | }); |
| 45 | |
| 46 | test("normal loads continue using a fresh cached profile without querying" , async () => { |
| 47 | const {load, queries} = setup({[key]: {event: fresh, at: Math.floor(Date.now() / 1000)}}, []); |
| 48 | assert.equal((await load({pubkey: key})).image, "https://cdn.example/slate.png" ); |
| 49 | assert.equal(queries(), 0); |
| 50 | }); |