1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
import assert from 'node:assert/strict';
import { buildSnapshot, createSnapshotCache, snapshotResponse } from '../feed/cache.js';
import { popularCandidates } from '../src/popular-feed.js';
const id = 'a'.repeat(64), author = 'b'.repeat(64);
const reaction = (pk, kind = 7, tags = []) => ({ kind, pubkey: pk, created_at: 100, tags: [['e', id], ...tags] });
const post = { kind: 1, id, pubkey: author, created_at: 90, content: 'This is a post about the things we build together.', tags: [] };
assert.equal(popularCandidates([reaction('c'), reaction('c')]).ids.length, 0);
assert.equal(popularCandidates([reaction(author), reaction('c')]).qualifies(post), false);
assert.equal(popularCandidates([reaction('c'), reaction('d')]).qualifies(post), true);
assert.equal(popularCandidates([reaction('zap-service', 9735, [['P', 'c']]), reaction('c')]).ids.length, 0);
const snapshot = await buildSnapshot(async (filter) => {
if (filter.ids) return [post, post];
if (filter.kinds[0] === 0) return [{ kind: 0, pubkey: author, created_at: 1, content: '{}' }, { kind: 0, pubkey: author, created_at: 2, content: '{}' }];
return [reaction('c'), reaction('d')];
}, () => 1000);
assert.equal(snapshot.notes.length, 1);
assert.equal(snapshot.profiles[0].created_at, 2);
assert.equal(snapshot.popUntil, 100);
await assert.rejects(buildSnapshot(async () => []), /No public posts/);
let time = 1000, builds = 0, finish;
const cache = createSnapshotCache({ now: () => time, build: () => { builds++; return new Promise((resolve) => { finish = resolve; }); } });
const request = new Request('http://localhost/api/feed');
assert.equal(snapshotResponse(cache, request).status, 503);
for (let i = 0; i < 100; i++) assert.equal(cache.read(), null);
await Promise.resolve();
assert.equal(builds, 1, 'cold visitors share one build');
finish(snapshot);
await cache.refresh();
assert.equal(cache.read(), snapshot);
assert.equal(snapshotResponse(cache, request).status, 200);
assert.equal(snapshotResponse(cache, new Request(request, { method: 'POST' })).status, 405);
assert.equal(await snapshotResponse(cache, new Request(request, { method: 'HEAD' })).text(), '');
time += 61_000;
for (let i = 0; i < 100; i++) assert.equal(cache.read(), snapshot);
await Promise.resolve();
assert.equal(builds, 2, 'stale visitors share one background build');
finish({ ...snapshot, generatedAt: time });
await cache.refresh();
assert.equal(cache.read().generatedAt, time);
let failures = 0;
const failing = createSnapshotCache({ initial: snapshot, now: () => time, build: async () => { failures++; throw Error('offline'); }, onError: () => {} });
assert.equal(failing.read(), snapshot);
await failing.refresh();
assert.equal(failing.read(), snapshot);
await failing.refresh();
assert.equal(failures, 1, 'failed refreshes are throttled and preserve stale data');
time += 86400_000;
assert.equal(failing.read(), null, 'expired snapshots are not served');
await failing.refresh();
console.log('✓ public feed selection, profiles, single flight, stale refresh, failure recovery, expiry and HTTP behavior');
|