source: Klonkt/test/conditional-queues.test.js@ f06ed56

main
Last change on this file since f06ed56 was bfd9c73, checked in by roboburr <roboburr@…>, 4 weeks ago

Stilte hoort niets te kosten: 304 op de guardian-wachtrijen (Barts punt, 9-8)

Bart wees op wat er al bestond: de inbox stuurt een 304 als er niets veranderd
is (since + wait), en de guardian-wachtrijen deden dat niet. Die stuurden bij
elke verversing de hele lijst terug -- bij honderd wards veertienhonderd
objecten. Over de lijn valt dat mee (2,8 KB gzip), maar het OPBOUWEN en parsen is
wat een telefoon merkt, en dat is precies de oude data die je elke keer weer
terugkrijgt.

En zijn punt over de waarschuwing klopte ook: de app ververst al voor de
guardian, tachtig keer per uur. Iemand waarschuwen voor een gewoonte die de app
zelf heeft is de verkeerde kant op redeneren.

EEN INHOUDS-ETAG, GEEN CURSOR. Een cursor vraagt een tweede beschrijving van
wanneer iets "veranderd" is, en die kan uit de pas lopen met wat er werkelijk in
het antwoord staat; een hash van het antwoord zelf kan dat per definitie niet.
De server bouwt het antwoord nog steeds -- wat we besparen is de overdracht en
het parsen.

NOOIT 304 OP EEN LEEG ANTWOORD, dezelfde les als de '0'-uitzondering bij de
inbox: gaat er bij het opbouwen iets mis en komt er een lege lijst uit, dan is
die hash ook stabiel en kijkt een client voor eeuwig naar niets. Daar staat een
toets op, en de mutatie maakt hem rood.

no-cache betekent niet "niet bewaren" maar "bewaar en vraag na" -- zonder dat
stuurt een browser geen If-None-Match en is de ETag decoratie.

Vijf toetsen, twee mutaties gecontroleerd. Suite 761/761.

  • Property mode set to 100644
File size: 3.3 KB
Line 
1// Stilte hoort niets te kosten (Barts punt, 9-8).
2//
3// De inbox doet dit al met `since` + `wait`: niets veranderd is een 304. De
4// guardian-wachtrijen stuurden bij elke verversing de hele lijst terug -- bij
5// honderd wards veertienhonderd objecten die de telefoon opnieuw moet opbouwen.
6//
7// Wat hier bewaakt wordt: dat de ETag over de INHOUD gaat (verandert er iets,
8// dan verandert hij mee) en dat een leeg antwoord NOOIT een 304 oplevert. Dat
9// tweede is dezelfde les als de '0'-uitzondering bij de inbox: gaat er iets mis
10// bij het opbouwen, dan is de hash van niets ook stabiel, en zit een client voor
11// eeuwig op 304 te kijken naar een leeg scherm.
12//
13// Run: npm test
14import { test } from 'node:test';
15import assert from 'node:assert/strict';
16
17process.env.DATABASE_PATH = ':memory:';
18const AP = await import('../src/services/ActivityPubService.js');
19
20function nepRes() {
21 return {
22 code: 200, body: null, headers: {},
23 set(k, v) { this.headers[String(k).toLowerCase()] = v; return this; },
24 type(t) { this.headers['content-type'] = t; return this; },
25 status(c) { this.code = c; return this; },
26 end() { this.body = ''; return this; },
27 send(b) { this.body = b; return this; },
28 };
29}
30const req = (etag) => ({ headers: etag ? { 'if-none-match': etag } : {} });
31const coll = (n) => ({ type: 'OrderedCollection', orderedItems: Array.from({ length: n }, (_, i) => ({ id: `x${i}` })) });
32
33test('dezelfde inhoud levert de tweede keer een 304 op', () => {
34 const r1 = nepRes();
35 AP.sendMaybe304(req(), r1, coll(3));
36 assert.equal(r1.code, 200);
37 assert.ok(r1.headers.etag, 'er is een merksteen');
38
39 const r2 = nepRes();
40 AP.sendMaybe304(req(r1.headers.etag), r2, coll(3));
41 assert.equal(r2.code, 304);
42 assert.equal(r2.body, '', 'en geen inhoud');
43});
44
45test('verandert er iets, dan verandert de merksteen mee', () => {
46 const r1 = nepRes(); AP.sendMaybe304(req(), r1, coll(3));
47 const r2 = nepRes(); AP.sendMaybe304(req(r1.headers.etag), r2, coll(4));
48 assert.equal(r2.code, 200, 'vier items is niet drie');
49 assert.notEqual(r2.headers.etag, r1.headers.etag);
50});
51
52test('een LEEG antwoord krijgt nooit een 304', () => {
53 // De gevaarlijke stille fout: gaat het opbouwen mis en komt er een lege lijst
54 // uit, dan is die hash ook stabiel en kijkt een client voor eeuwig naar niets.
55 const r1 = nepRes();
56 AP.sendMaybe304(req(), r1, coll(0));
57 assert.equal(r1.code, 200);
58 assert.equal(r1.headers.etag, undefined, 'geen merksteen op leegte');
59
60 const r2 = nepRes();
61 AP.sendMaybe304(req('"wat-dan-ook"'), r2, coll(0));
62 assert.equal(r2.code, 200, 'en dus ook nooit een 304');
63});
64
65test('de client krijgt te horen dat hij mag bewaren en moet navragen', () => {
66 // Zonder no-cache stuurt een browser geen If-None-Match, en is de ETag
67 // decoratie. En Vary op Authorization, want dit is een eigenaar-antwoord.
68 const r = nepRes();
69 AP.sendMaybe304(req(), r, coll(2));
70 assert.match(r.headers['cache-control'], /no-cache/);
71 assert.equal(r.headers.vary, 'Authorization');
72});
73
74test('een antwoord dat geen collectie is werkt ook', () => {
75 // Het paneel is geen OrderedCollection maar een gewoon object.
76 const r1 = nepRes(); AP.sendMaybe304(req(), r1, { wards: [], strings: { a: 'b' } });
77 const r2 = nepRes(); AP.sendMaybe304(req(r1.headers.etag), r2, { wards: [], strings: { a: 'b' } });
78 assert.equal(r2.code, 304);
79});
Note: See TracBrowser for help on using the repository browser.