source: Klonkt/test/music-uitgave.test.js@ 0677bac

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

De tags kwamen er als onzin uit, en de tekst helemaal niet (shaer-38y)

Live nagekeken op dev, en twee dingen klopten niet.

DE TAGS. posts.tags staat als JSON-ARRAY in de database, niet als kommalijst.
Mijn eigen split leverde #["Doen we Niet" en #"music" op -- geen fout die
omvalt, een die onzin uitlevert. Nu met normalizeTags en tagParts, dezelfde
parser als de rest, dus ook dezelfde CamelCase voor tags van meerdere woorden.

Die twee stonden in ActivityPubService en music/ mag daar niet uit importeren,
dus ze zijn naar ap-core verhuisd -- de plek voor precies dit soort gedeelde
grondstof. Een kopie maken zou hetzelfde uit elkaar lopen opleveren als bij de
afleidingsregel.

DE TEKST. De excerpt bleek in de praktijk vaak leeg, en dan kwam er geen
content mee terwijl de post wel tekst heeft. Nu: excerpt eerst -- die is
geschreven om samen te vatten -- en anders het lijf zelf, zonder shortcodes
(die zijn de muziek, niet het verhaal erover) en zonder opmaak. Blijft er niets
over, dan geen veld: dat is beter dan een leeg veld.

De test gebruikte de kommavorm en testte dus de terugval in plaats van de echte
weg. Nu JSON, zoals het er staat.

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

  • Property mode set to 100644
File size: 8.3 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});
Note: See TracBrowser for help on using the repository browser.