atagen

malice

public
Star0
Watch0
b39ecae834a0cd6670068d28a7b4afd0498f7964
2
ssh://brewery@pls.lobotomise.me:33/atagen/malice.git

Blame: web/test/sharing.test.ts

a802a95814e6atagen2026-10-26 03:48 UTC1
import assert from 'node:assert/strict';
a802a95814e6atagen2026-10-26 03:48 UTC2
import { test } from 'node:test';
a802a95814e6atagen2026-10-26 03:48 UTC3
import { newShareCache, parseShared } from '../src/lib/sharing.ts';
a802a95814e6atagen2026-10-26 03:48 UTC4
import { observeArrivals } from '../src/lib/arrivals.ts';
a802a95814e6atagen2026-10-26 03:48 UTC5
import type { TimelineEvent, TimelineItem } from '../src/lib/types.ts';
a802a95814e6atagen2026-10-26 03:48 UTC6
a802a95814e6atagen2026-10-26 03:48 UTC7
interface Row {
a802a95814e6atagen2026-10-26 03:48 UTC8
  id: string;
a802a95814e6atagen2026-10-26 03:48 UTC9
  count: number;
a802a95814e6atagen2026-10-26 03:48 UTC10
}
a802a95814e6atagen2026-10-26 03:48 UTC11
a802a95814e6atagen2026-10-26 03:48 UTC12
function parse(entry: unknown): Row | null {
a802a95814e6atagen2026-10-26 03:48 UTC13
  const raw = entry as Record<string, unknown>;
a802a95814e6atagen2026-10-26 03:48 UTC14
  return typeof raw['id'] === 'string' ? { id: raw['id'], count: Number(raw['count']) } : null;
a802a95814e6atagen2026-10-26 03:48 UTC15
}
a802a95814e6atagen2026-10-26 03:48 UTC16
a802a95814e6atagen2026-10-26 03:48 UTC17
test('unchanged entries keep their object and unchanged lists keep their array', () => {
a802a95814e6atagen2026-10-26 03:48 UTC18
  const cache = newShareCache<Row>();
a802a95814e6atagen2026-10-26 03:48 UTC19
  const first = parseShared(cache, [{ id: 'a', count: 1 }, { id: 'b', count: 1 }], parse);
a802a95814e6atagen2026-10-26 03:48 UTC20
  const second = parseShared(cache, [{ id: 'a', count: 1 }, { id: 'b', count: 1 }], parse);
a802a95814e6atagen2026-10-26 03:48 UTC21
  assert.equal(second, first);
a802a95814e6atagen2026-10-26 03:48 UTC22
});
a802a95814e6atagen2026-10-26 03:48 UTC23
a802a95814e6atagen2026-10-26 03:48 UTC24
test('a changed entry gets a new object while its neighbours keep theirs', () => {
a802a95814e6atagen2026-10-26 03:48 UTC25
  const cache = newShareCache<Row>();
a802a95814e6atagen2026-10-26 03:48 UTC26
  const first = parseShared(cache, [{ id: 'a', count: 1 }, { id: 'b', count: 1 }], parse);
a802a95814e6atagen2026-10-26 03:48 UTC27
  const second = parseShared(cache, [{ id: 'a', count: 1 }, { id: 'b', count: 2 }], parse);
a802a95814e6atagen2026-10-26 03:48 UTC28
  assert.notEqual(second, first);
a802a95814e6atagen2026-10-26 03:48 UTC29
  assert.equal(second[0], first[0]);
a802a95814e6atagen2026-10-26 03:48 UTC30
  assert.notEqual(second[1], first[1]);
a802a95814e6atagen2026-10-26 03:48 UTC31
  assert.equal(second[1].count, 2);
a802a95814e6atagen2026-10-26 03:48 UTC32
});
a802a95814e6atagen2026-10-26 03:48 UTC33
a802a95814e6atagen2026-10-26 03:48 UTC34
test('appended and removed entries change the array', () => {
a802a95814e6atagen2026-10-26 03:48 UTC35
  const cache = newShareCache<Row>();
a802a95814e6atagen2026-10-26 03:48 UTC36
  const first = parseShared(cache, [{ id: 'a', count: 1 }], parse);
a802a95814e6atagen2026-10-26 03:48 UTC37
  const grown = parseShared(cache, [{ id: 'a', count: 1 }, { id: 'b', count: 1 }], parse);
a802a95814e6atagen2026-10-26 03:48 UTC38
  assert.notEqual(grown, first);
a802a95814e6atagen2026-10-26 03:48 UTC39
  assert.equal(grown[0], first[0]);
a802a95814e6atagen2026-10-26 03:48 UTC40
  const shrunk = parseShared(cache, [{ id: 'b', count: 1 }], parse);
a802a95814e6atagen2026-10-26 03:48 UTC41
  assert.equal(shrunk[0], grown[1]);
a802a95814e6atagen2026-10-26 03:48 UTC42
});
a802a95814e6atagen2026-10-26 03:48 UTC43
a802a95814e6atagen2026-10-26 03:48 UTC44
test('unparsable entries are skipped and non-arrays yield an empty list', () => {
a802a95814e6atagen2026-10-26 03:48 UTC45
  const cache = newShareCache<Row>();
a802a95814e6atagen2026-10-26 03:48 UTC46
  assert.equal(parseShared(cache, [{ id: 'a', count: 1 }, { nope: true }], parse).length, 1);
a802a95814e6atagen2026-10-26 03:48 UTC47
  assert.deepEqual(parseShared(cache, 'x', parse), []);
a802a95814e6atagen2026-10-26 03:48 UTC48
});
a802a95814e6atagen2026-10-26 03:48 UTC49
a802a95814e6atagen2026-10-26 03:48 UTC50
function event(id: string, timestamp_ms: number): TimelineItem {
a802a95814e6atagen2026-10-26 03:48 UTC51
  return { kind: 'event', event: { item_id: id, timestamp_ms, own: false } as TimelineEvent };
a802a95814e6atagen2026-10-26 03:48 UTC52
}
a802a95814e6atagen2026-10-26 03:48 UTC53
a802a95814e6atagen2026-10-26 03:48 UTC54
test('arrivals keeps array and lagged item identity across identical updates', () => {
a802a95814e6atagen2026-10-26 03:48 UTC55
  const MINUTE = 60 * 1000;
a802a95814e6atagen2026-10-26 03:48 UTC56
  const known = event('a', 90 * MINUTE);
a802a95814e6atagen2026-10-26 03:48 UTC57
  const late = event('late', 80 * MINUTE);
a802a95814e6atagen2026-10-26 03:48 UTC58
  const first = observeArrivals([known], [known, late], 0, 100 * MINUTE);
a802a95814e6atagen2026-10-26 03:48 UTC59
  assert.ok(first);
a802a95814e6atagen2026-10-26 03:48 UTC60
  const second = observeArrivals(first, [known, late], 0, 101 * MINUTE);
a802a95814e6atagen2026-10-26 03:48 UTC61
  assert.equal(second, first);
a802a95814e6atagen2026-10-26 03:48 UTC62
});
a802a95814e6atagen2026-10-26 03:48 UTC63
a802a95814e6atagen2026-10-26 03:48 UTC64
test('identified entries are matched by identity and evicted when absent', () => {
a802a95814e6atagen2026-10-26 03:48 UTC65
  const cache = newShareCache<Row>();
a802a95814e6atagen2026-10-26 03:48 UTC66
  const identify = (entry: unknown) => (entry as Row).id;
a802a95814e6atagen2026-10-26 03:48 UTC67
  const first = parseShared(cache, [{ id: 'a', count: 1 }, { id: 'b', count: 1 }], parse, identify);
a802a95814e6atagen2026-10-26 03:48 UTC68
  const second = parseShared(cache, [{ id: 'b', count: 1 }, { id: 'c', count: 1 }], parse, identify);
a802a95814e6atagen2026-10-26 03:48 UTC69
  assert.equal(second[0], first[1]);
a802a95814e6atagen2026-10-26 03:48 UTC70
  assert.deepEqual([...cache.byKey.keys()], ['b', 'c']);
a802a95814e6atagen2026-10-26 03:48 UTC71
});
a802a95814e6atagen2026-10-26 03:48 UTC72
a802a95814e6atagen2026-10-26 03:48 UTC73
test('a changed identified entry is reparsed under the same key', () => {
a802a95814e6atagen2026-10-26 03:48 UTC74
  const cache = newShareCache<Row>();
a802a95814e6atagen2026-10-26 03:48 UTC75
  const identify = (entry: unknown) => (entry as Row).id;
a802a95814e6atagen2026-10-26 03:48 UTC76
  const first = parseShared(cache, [{ id: 'a', count: 1 }], parse, identify);
a802a95814e6atagen2026-10-26 03:48 UTC77
  const second = parseShared(cache, [{ id: 'a', count: 2 }], parse, identify);
a802a95814e6atagen2026-10-26 03:48 UTC78
  assert.notEqual(second[0], first[0]);
a802a95814e6atagen2026-10-26 03:48 UTC79
  assert.equal(second[0].count, 2);
a802a95814e6atagen2026-10-26 03:48 UTC80
});