atagen

malice

public
Star0
Watch0
7fdac85c2f804757f4fb01062f967251b5fff4b6
2
ssh://brewery@pls.lobotomise.me:33/atagen/malice.git
web/test/sharing.test.tsTypescript80 lines3.4 KB
1
import assert from 'node:assert/strict';
2
import { test } from 'node:test';
3
import { newShareCache, parseShared } from '../src/lib/sharing.ts';
4
import { observeArrivals } from '../src/lib/arrivals.ts';
5
import type { TimelineEvent, TimelineItem } from '../src/lib/types.ts';
6
7
interface Row {
8
  id: string;
9
  count: number;
10
}
11
12
function parse(entry: unknown): Row | null {
13
  const raw = entry as Record<string, unknown>;
14
  return typeof raw['id'] === 'string' ? { id: raw['id'], count: Number(raw['count']) } : null;
15
}
16
17
test('unchanged entries keep their object and unchanged lists keep their array', () => {
18
  const cache = newShareCache<Row>();
19
  const first = parseShared(cache, [{ id: 'a', count: 1 }, { id: 'b', count: 1 }], parse);
20
  const second = parseShared(cache, [{ id: 'a', count: 1 }, { id: 'b', count: 1 }], parse);
21
  assert.equal(second, first);
22
});
23
24
test('a changed entry gets a new object while its neighbours keep theirs', () => {
25
  const cache = newShareCache<Row>();
26
  const first = parseShared(cache, [{ id: 'a', count: 1 }, { id: 'b', count: 1 }], parse);
27
  const second = parseShared(cache, [{ id: 'a', count: 1 }, { id: 'b', count: 2 }], parse);
28
  assert.notEqual(second, first);
29
  assert.equal(second[0], first[0]);
30
  assert.notEqual(second[1], first[1]);
31
  assert.equal(second[1].count, 2);
32
});
33
34
test('appended and removed entries change the array', () => {
35
  const cache = newShareCache<Row>();
36
  const first = parseShared(cache, [{ id: 'a', count: 1 }], parse);
37
  const grown = parseShared(cache, [{ id: 'a', count: 1 }, { id: 'b', count: 1 }], parse);
38
  assert.notEqual(grown, first);
39
  assert.equal(grown[0], first[0]);
40
  const shrunk = parseShared(cache, [{ id: 'b', count: 1 }], parse);
41
  assert.equal(shrunk[0], grown[1]);
42
});
43
44
test('unparsable entries are skipped and non-arrays yield an empty list', () => {
45
  const cache = newShareCache<Row>();
46
  assert.equal(parseShared(cache, [{ id: 'a', count: 1 }, { nope: true }], parse).length, 1);
47
  assert.deepEqual(parseShared(cache, 'x', parse), []);
48
});
49
50
function event(id: string, timestamp_ms: number): TimelineItem {
51
  return { kind: 'event', event: { item_id: id, timestamp_ms, own: false } as TimelineEvent };
52
}
53
54
test('arrivals keeps array and lagged item identity across identical updates', () => {
55
  const MINUTE = 60 * 1000;
56
  const known = event('a', 90 * MINUTE);
57
  const late = event('late', 80 * MINUTE);
58
  const first = observeArrivals([known], [known, late], 0, 100 * MINUTE);
59
  assert.ok(first);
60
  const second = observeArrivals(first, [known, late], 0, 101 * MINUTE);
61
  assert.equal(second, first);
62
});
63
64
test('identified entries are matched by identity and evicted when absent', () => {
65
  const cache = newShareCache<Row>();
66
  const identify = (entry: unknown) => (entry as Row).id;
67
  const first = parseShared(cache, [{ id: 'a', count: 1 }, { id: 'b', count: 1 }], parse, identify);
68
  const second = parseShared(cache, [{ id: 'b', count: 1 }, { id: 'c', count: 1 }], parse, identify);
69
  assert.equal(second[0], first[1]);
70
  assert.deepEqual([...cache.byKey.keys()], ['b', 'c']);
71
});
72
73
test('a changed identified entry is reparsed under the same key', () => {
74
  const cache = newShareCache<Row>();
75
  const identify = (entry: unknown) => (entry as Row).id;
76
  const first = parseShared(cache, [{ id: 'a', count: 1 }], parse, identify);
77
  const second = parseShared(cache, [{ id: 'a', count: 2 }], parse, identify);
78
  assert.notEqual(second[0], first[0]);
79
  assert.equal(second[0].count, 2);
80
});