source: Klonkt/test/c2s-title.test.js

main
Last change on this file was 78b8e58, checked in by Robin <roboburr@…>, 2 weeks ago

Toetsen: layout-pins eruit, overlap samengevoegd (mixtape, migrate, c2s-titel)

Dezelfde maat als bij de Berichten-toetsen (Robins besluit, 25-8): een
toets pint een beslissing, niet waar iets staat of hoe de code gespeld
is. Drie bestanden, netto drie toetsen minder en vier pins weg:

  • mixtape-ui: de bron-regex-toets op de labelmap weg (die toetste dezelfde regel als de render-toets, maar dan op de spelling van de code); de klasse-pin rond het woord Mixtape los (dat het er staat is het besluit, waar het staat is layout); de spoel-asserts weg -- de cassette-decoratie is layout, de haken van tape.js (.post-tape, data-tape-go) blijven, want dat is het contract met de module.
  • migrate-page: de twee volgorde-toetsen samengevoegd. De volgorde ZELF blijft getoetst, want daar is hij inhoud: wie de stappen verkeerd om doet breekt zijn verhuizing. De positie-assert van het stappenplan (bovenaan) is weg, het bestaan ervan blijft.
  • c2s-title: de twee lege-titel-gevallen (geen name, alleen witruimte) zijn een regel met twee kanten, nu een toets.

Bij het echt lezen bleken twee eerder verdachte toetsen juist
beslissingstoetsen: 'de teksten praten geen jargon' bewaakt Robins eigen
regel en toetst op betekenis-niveau, en 'stap 5' is daar destijds al
bewust op herschreven. Die blijven onaangeroerd, net als de tien
speler-toetsen die broncode lezen -- brosse dekking, maar de enige die
browser-JS hier heeft; schrappen kan pas als er iets voor in de plaats
komt.

Tegenbewijs per snede: het mixtape-label valt tegen de renderer van voor
8e1af9c, de volgorde valt tegen een omgedraaide pagina, en de lege titel
valt tegen een mutatie die er een verzint. Volle suite 1229 groen.

  • Property mode set to 100644
File size: 4.6 KB
Line 
1// Een C2S-post kon nooit een titel hebben (shaer-uply): c2sCreatePost schreef
2// '' in posts.title en in posts_fts en las `name` op het binnenkomende object
3// nooit. Een client die er een zette zag hem geruisloos verdwijnen -- de API
4// was daarmee de enige publicatieweg die iets niet kon wat het web wel kan.
5// Gevonden door R9999, dat alleen over C2S publiceert.
6import { test } from 'node:test';
7import assert from 'node:assert/strict';
8
9process.env.DATABASE_PATH = ':memory:';
10process.env.PUBLIC_BASE_URL = 'https://test.example';
11
12const dbMod = await import('../src/config/database.js');
13const db = dbMod.default;
14dbMod.initializeDatabase();
15const AP = (await import('../src/services/ActivityPubService.js')).default;
16
17db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)')
18 .run('u1', 'robin', 'u1@t', 'x', 'god');
19db.prepare('INSERT INTO sites (id, slug, title, owner_id, is_primary) VALUES (?,?,?,?,1)').run('s1', 'kid', 'kid', 'u1');
20const site = db.prepare('SELECT * FROM sites WHERE slug = ?').get('kid');
21const user = db.prepare('SELECT * FROM users WHERE id = ?').get('u1');
22
23function maak(object) {
24 return AP.ingestOutboxActivity(site, user, {
25 type: 'Create',
26 object: {
27 type: 'Note',
28 to: ['https://test.example/ap/users/kid/followers'],
29 cc: ['https://www.w3.org/ns/activitystreams#Public'],
30 ...object,
31 },
32 });
33}
34
35test('de titel van een C2S-post komt in de kolom, de zoekindex en de note', async () => {
36 const r = await maak({ name: 'Mijn eerste getitelde note', content: '<p>de inhoud</p>' });
37 assert.equal(r.status, 201);
38
39 const post = db.prepare('SELECT * FROM posts WHERE id = ?').get(r.id);
40 assert.equal(post.title, 'Mijn eerste getitelde note', 'de kolom draagt de titel');
41
42 // Vindbaar OP de titel, niet alleen op de inhoud -- de tweede schrijfplek
43 // uit shaer-uply. Was alleen de kolom gerepareerd, dan faalde deze.
44 const hit = db.prepare("SELECT post_id FROM posts_fts WHERE posts_fts MATCH 'getitelde'").get();
45 assert.ok(hit && hit.post_id === r.id, 'de titel staat in de zoekindex');
46
47 // En hij federeert zoals een webpost: Mastodon negeert `name` op een Note,
48 // dus buildNote vouwt de titel als vetgedrukte eerste regel in de content.
49 const note = AP.buildNote('https://test.example', site, post);
50 assert.match(note.content, /^<p><strong>Mijn eerste getitelde note<\/strong><\/p>/, 'vetgedrukte eerste regel');
51});
52
53test('geen echte titel blijft een lege titel: zonder name, en met alleen witruimte', async () => {
54 // Een regel met twee kanten (samengevoegd 25-8): wat er niet is, wordt niet
55 // verzonnen -- of het veld nu ontbreekt of alleen witruimte draagt.
56 const zonder = await maak({ content: '<p>gewoon een note</p>' });
57 assert.equal(zonder.status, 201);
58 const post = db.prepare('SELECT * FROM posts WHERE id = ?').get(zonder.id);
59 assert.equal(post.title, '', 'geen name is een lege titel, geen undefined of null');
60 const note = AP.buildNote('https://test.example', site, post);
61 assert.ok(!note.content.startsWith('<p><strong>'), 'geen verzonnen kopregel');
62
63 const wit = await maak({ name: ' ', content: '<p>z</p>' });
64 const witPost = db.prepare('SELECT title FROM posts WHERE id = ?').get(wit.id);
65 assert.equal(witPost.title, '', 'witruimte trimt weg tot leeg');
66 const witNote = AP.buildNote('https://test.example', site, db.prepare('SELECT * FROM posts WHERE id = ?').get(wit.id));
67 assert.ok(!witNote.content.startsWith('<p><strong>'), 'en er komt geen lege kopregel');
68});
69
70test('de titel is platte tekst: HTML erin wordt tekst, en de grens is 200', async () => {
71 // `name` is per AS2 platte tekst, maar een client kan sturen wat hij wil.
72 // De kolom en de views verwachten tekst; opmaak wordt dus tekst, en een
73 // scriptpoging houdt niets uitvoerbaars over.
74 const r = await maak({ name: 'Dag <b>wereld</b><script>boem()</script>', content: '<p>x</p>' });
75 const post = db.prepare('SELECT * FROM posts WHERE id = ?').get(r.id);
76 assert.ok(!post.title.includes('<'), 'geen tags in de kolom');
77 assert.match(post.title, /^Dag wereld/, 'de tekst zelf blijft');
78
79 // En de note escapet hem daarna zelf weer voor de vetgedrukte regel, dus
80 // ook daar geen tag-injectie via de titel.
81 const note = AP.buildNote('https://test.example', site, post);
82 assert.ok(!/<script/i.test(note.content), 'niets uitvoerbaars in de note');
83
84 const lang = await maak({ name: 'x'.repeat(500), content: '<p>y</p>' });
85 const langePost = db.prepare('SELECT title FROM posts WHERE id = ?').get(lang.id);
86 assert.equal(langePost.title.length, 200, 'de huisgrens van 200, zoals content warning en sitetitel');
87});
88
Note: See TracBrowser for help on using the repository browser.