source: Klonkt/test/music-uitgave.test.js@ d60c498

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

Een omschrijving die de tagwolk herhaalt is ruis (shaer-38y)

De terugval op het lijf van de post pikte de losse hashtags mee. Live leverde
dat "#DoenweNiet #DoenWeNiet #devs" op als omschrijving van een post die verder
geen tekst heeft -- en die tags staan al in tag. Nu gaan ze eruit, en houdt
een post zonder prosa gewoon geen content-veld over.

Tekst MET een hashtag erin blijft wel staan; het gaat om losse tags, niet om
het teken.

Co-Authored-By: Claude Opus 5 <noreply@…>

  • Property mode set to 100644
File size: 9.1 KB
Line 
1// De post als uitgave (shaer-38y).
2//
3// Drie punten uit de bead: de volgorde van de post behouden, de lijst als
4// geheel overnemen, en de metadata van de post lenen. Het derde is waar het om
5// draait -- een collectie met alleen een naam is wat een Funkwhale-achtige
6// lezer te mager vindt, en de post heeft wel een titel, tekst, hoes en tags.
7import { test } from 'node:test';
8import assert from 'node:assert/strict';
9
10process.env.DATABASE_PATH = ':memory:';
11process.env.PUBLIC_BASE_URL = 'https://test.example';
12
13const dbMod = await import('../src/config/database.js');
14dbMod.initializeDatabase();
15const db = dbMod.default;
16const M = await import('../src/services/music/index.js');
17
18const BASE = 'https://test.example';
19const SITE = 'site-1';
20const site = { id: SITE, slug: 'muziek' };
21
22db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)')
23 .run('u1', 'robin', 'r@test', 'x', 'god');
24db.prepare('INSERT INTO sites (id, slug, title, owner_id) VALUES (?,?,?,?)')
25 .run(SITE, 'muziek', 'Muziek', 'u1');
26
27let n = 0;
28function maakTrack(id, titel) {
29 const mediaId = 'm-' + id;
30 db.prepare('INSERT INTO media (id, site_id, filename, storage_path, mime_type, size) VALUES (?,?,?,?,?,?)')
31 .run(mediaId, SITE, id + '.mp3', '/x/' + id + '.mp3', 'audio/mpeg', 1000 + (n++));
32 db.prepare('INSERT INTO audio_tracks (id, site_id, media_id, title, duration, fedi_open, position) VALUES (?,?,?,?,?,1,?)')
33 .run(id, SITE, mediaId, titel, 100, 0);
34}
35
36function maakPost({ id, slug, titel, content, excerpt = '', cover = '', tags = '' }) {
37 db.prepare(`INSERT INTO posts (id, site_id, slug, author_id, title, excerpt, content, cover_image_url, tags, status, type, created_at, updated_at, published_at)
38 VALUES (?,?,?,'u1',?,?,?,?,?,'published','playlist',CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP)`)
39 .run(id, SITE, slug, titel, excerpt, content, cover, tags);
40 return db.prepare('SELECT id, slug, title, excerpt, content, cover_image_url, tags FROM posts WHERE id = ?').get(id);
41}
42
43// Drie tracks, bewust in een andere vololgorde ingevoerd dan ze in de post staan.
44maakTrack('t-een', 'Een');
45maakTrack('t-twee', 'Twee');
46maakTrack('t-drie', 'Drie');
47
48const losse = maakPost({
49 id: 'p-los', slug: 'drie-nieuwe', titel: 'Drie nieuwe nummers',
50 content: 'Hier: [[track:t-drie]] dan [[track:t-een]] en tot slot [[track:t-twee]]',
51 excerpt: 'Opgenomen op zolder, in een week.',
52 cover: '/media/hoes.jpg',
53 tags: '["lofi","zolder tapes"]',
54});
55
56test('losse tracks in een post worden EEN collectie', () => {
57 const col = M.buildPostTrackCollection(BASE, site, losse);
58 assert.ok(col, 'er is een uitgave');
59 assert.equal(col.type, 'OrderedCollection');
60 assert.equal(col.id, `${BASE}/ap/users/muziek/posts/p-los/tracks`);
61 assert.equal(col.totalItems, 3);
62});
63
64test('in de volgorde van de POST, niet van de tabel', () => {
65 const col = M.buildPostTrackCollection(BASE, site, losse);
66 assert.deepEqual(col.orderedItems.map((a) => a.name), ['Drie', 'Een', 'Twee'],
67 'zoals iemand ze heeft neergezet is de volgorde waarin ze bedoeld zijn');
68});
69
70test('de post leent zijn titel, tekst, hoes en tags uit', () => {
71 const col = M.buildPostTrackCollection(BASE, site, losse);
72 assert.equal(col.name, 'Drie nieuwe nummers');
73 assert.equal(col.content, 'Opgenomen op zolder, in een week.');
74 assert.equal(col.image.type, 'Image');
75 assert.equal(col.image.url, `${BASE}/media/hoes.jpg`);
76 // Het tagveld is JSON in de database, en een tag van twee woorden wordt
77 // CamelCase -- zelfde regel als elders, want het is nu dezelfde parser.
78 assert.deepEqual(col.tag.map((t) => t.name), ['#lofi', '#ZolderTapes']);
79 assert.equal(col.tag[1].href, `${BASE}/tag/zoldertapes`);
80 assert.equal(col.url, `${BASE}/drie-nieuwe`);
81 assert.equal(col.context, `${BASE}/ap/notes/p-los`);
82});
83
84test('en de tracks erin wijzen terug naar diezelfde post', () => {
85 const col = M.buildPostTrackCollection(BASE, site, losse);
86 for (const a of col.orderedItems) {
87 assert.equal(a.context, `${BASE}/ap/notes/p-los`);
88 assert.ok(a.url.some((u) => u.mediaType === 'text/html' && u.href === `${BASE}/drie-nieuwe`));
89 }
90});
91
92test('de post wijst met een Link-tag naar zijn eigen uitgave', () => {
93 const tags = M.playlistLinkTags(BASE, site, losse.content, losse);
94 const link = tags.find((t) => t.href === `${BASE}/ap/users/muziek/posts/p-los/tracks`);
95 assert.ok(link, 'zonder deze link bestaat de collectie wel maar vindt niemand hem');
96 assert.equal(link.mediaType, 'application/activity+json');
97});
98
99test('een post zonder losse tracks heeft geen eigen uitgave', () => {
100 const geen = maakPost({ id: 'p-leeg', slug: 'niets', titel: 'Niets', content: '<p>gewoon tekst</p>' });
101 assert.equal(M.buildPostTrackCollection(BASE, site, geen), null);
102});
103
104test('een gesloten track telt niet mee, ook niet voor het bestaan', () => {
105 maakTrack('t-dicht', 'Dicht');
106 db.prepare('UPDATE audio_tracks SET fedi_open = 0 WHERE id = ?').run('t-dicht');
107 const p = maakPost({ id: 'p-dicht', slug: 'dicht', titel: 'Dicht', content: '[[track:t-dicht]]' });
108 assert.equal(M.buildPostTrackCollection(BASE, site, p), null,
109 'geen lege collectie maar een collectie die niet bestaat');
110});
111
112// ── De playlist-collectie leent van de post die haar uitbrengt ────────────
113
114db.prepare('INSERT INTO playlists (id, site_id, title, artist, year, kind) VALUES (?,?,?,?,?,?)')
115 .run('nachtlicht', SITE, 'Nachtlicht', 'Robin', 2026, 'album');
116db.prepare('INSERT INTO playlist_tracks (playlist_id, track_id, position) VALUES (?,?,?)')
117 .run('nachtlicht', 't-een', 0);
118
119maakPost({
120 id: 'p-plaat', slug: 'de-plaat-is-er', titel: 'De plaat is er',
121 content: 'Eindelijk: [[playlist:nachtlicht]]',
122 excerpt: 'Anderhalf jaar aan gewerkt.',
123 cover: '/media/plaat.jpg', tags: 'album',
124});
125
126test('een playlist-collectie leent van de post die haar uitbrengt', () => {
127 const pl = db.prepare('SELECT id, title, artist, year, cover_url, kind FROM playlists WHERE id = ?').get('nachtlicht');
128 const col = M.buildPlaylistCollection(BASE, site, pl, M.playlistOpenTracks('nachtlicht'));
129 assert.equal(col.name, 'De plaat is er', 'de posttitel wordt de titel');
130 assert.equal(col.alsoKnownAs, 'Nachtlicht', 'de eigen naam gaat niet verloren');
131 assert.equal(col.content, 'Anderhalf jaar aan gewerkt.');
132 assert.equal(col.image.url, `${BASE}/media/plaat.jpg`);
133 assert.equal(col.summary, 'Robin · 2026', 'artiest en jaar blijven de samenvatting');
134 assert.equal(col.context, `${BASE}/ap/notes/p-plaat`);
135});
136
137test('een playlist zonder post leent niets', () => {
138 db.prepare('INSERT INTO playlists (id, site_id, title, kind) VALUES (?,?,?,?)')
139 .run('los', SITE, 'Los', 'playlist');
140 const pl = db.prepare('SELECT id, title, artist, year, cover_url, kind FROM playlists WHERE id = ?').get('los');
141 const col = M.buildPlaylistCollection(BASE, site, pl, []);
142 assert.equal(col.name, 'Los');
143 assert.equal(col.content, undefined);
144 assert.equal(M.uitgavePost(SITE, 'los'), null);
145});
146
147test('twee collecties in een post: dan leent de playlist niets', () => {
148 db.prepare('INSERT INTO playlists (id, site_id, title, kind) VALUES (?,?,?,?)')
149 .run('tweede', SITE, 'Tweede', 'playlist');
150 maakPost({
151 id: 'p-twee', slug: 'twee-lijsten', titel: 'Twee lijsten',
152 content: '[[playlist:tweede]] en [[playlist:nachtlicht]]',
153 });
154 // De post is dan geen drager van EEN identiteit meer -- dezelfde regel als in
155 // de afleiding, hier alleen toegepast op de lening.
156 assert.equal(M.uitgavePost(SITE, 'tweede'), null);
157});
158
159test('zonder excerpt leent hij de tekst uit het lijf van de post', () => {
160 // Live bleek de excerpt vaak leeg; dan is `content` weglaten slechter dan de
161 // tekst zelf pakken. Shortcodes gaan eruit -- die zijn de muziek, niet het
162 // verhaal erover.
163 const p = maakPost({
164 id: 'p-tekst', slug: 'zonder-excerpt', titel: 'Zonder excerpt',
165 content: '<p>Drie schetsen van vorige week.</p>[[track:t-een]]<div>&nbsp;</div>',
166 });
167 const col = M.buildPostTrackCollection(BASE, site, p);
168 assert.equal(col.content, 'Drie schetsen van vorige week.');
169});
170
171test('en een post die alleen uit shortcodes bestaat leent geen lege tekst', () => {
172 const p = maakPost({
173 id: 'p-kaal', slug: 'alleen-muziek', titel: 'Alleen muziek',
174 content: '[[track:t-twee]]<div> </div>',
175 });
176 const col = M.buildPostTrackCollection(BASE, site, p);
177 assert.equal(col.content, undefined, 'een leeg veld is slechter dan geen veld');
178});
179
180test('losse hashtags horen niet in de omschrijving -- die staan al in tag', () => {
181 // Live leverde dit "#DoenweNiet #DoenWeNiet #devs" op als omschrijving van
182 // een post die verder geen tekst heeft. Een description die de tagwolk
183 // herhaalt is ruis.
184 const p = maakPost({
185 id: 'p-tags', slug: 'alleen-tags', titel: 'Alleen tags',
186 content: '[[track:t-een]]<div>#lofi #zolder</div>',
187 });
188 assert.equal(M.buildPostTrackCollection(BASE, site, p).content, undefined);
189});
190
191test('maar tekst MET een hashtag erin blijft gewoon staan', () => {
192 const p = maakPost({
193 id: 'p-mix', slug: 'tekst-en-tags', titel: 'Tekst en tags',
194 content: '<p>Opgenomen in de schuur.</p>[[track:t-twee]]<div>#lofi</div>',
195 });
196 assert.equal(M.buildPostTrackCollection(BASE, site, p).content, 'Opgenomen in de schuur.');
197});
Note: See TracBrowser for help on using the repository browser.