source: Klonkt/test/mixtape.test.js@ b0ccd0d

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

De mixtape als eigen soort: data en draad

Een bandje is geen album en geen afspeellijst. Een album is een uitgave -- de
nummers bestaan er los van. Bij een mixtape is de VOLGORDE het werk en zijn de
nummers onderdelen. Vandaar een eigen soort, en op de draad een enkel object
met de nummers erin.

De valkuil zat in de vorm die er al stond. De keuze album/playlist stond vijf
keer als x === 'playlist' ? 'playlist' : 'album' verspreid over drie
bestanden, en zo'n vorm valt bij een derde soort niet om -- hij slikt hem. Een
mixtape zou stilzwijgend een album zijn geworden en als Album de deur uit gaan.
De lijst staat nu een keer, in de pure gedeelde module, want die kent de
browser ook.

Op de draad:

  • type is de STRING 'Mixtape'. Geen array, en dat is geleerd: bij de playlist-collectie stond ooit ['OrderedCollection','Album'] -- geldig AS2 en werkelijk allebei -- en een lezer die type als tekst uitpakt (Shaer) verloor daarmee stil het hele object.
  • orderedItems op het object zelf, niet een collectie ernaast.
  • Geen released en geen musicbrainzId: die zouden beweren dat dit een uitgave is.
  • Geen eigen url. Robins keuze: het bandje is een omhulling, geen gerenderd bestand. Prijs ervan is opgeschreven bij de functie -- wie het type niet kent heeft geen stream, al staan de nummers er stuk voor stuk in.

Een mixtape met losse tracks erbij blijft een mixtape. De oude regel maakte
daar een album van met bonustracks; dat klopt voor een plaat en niet voor een
bandje. Album en playlist houden bewust hun oude gedrag, want dat zijn
bestaande posts.

Acht tests, waaronder de weg door de database heen in plaats van langs een
handgemaakte opzoeker.

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

  • Property mode set to 100644
File size: 6.1 KB
Line 
1// De mixtape: een bandje als eigen soort (Robins idee, 21-8).
2//
3// Een playlist kon album of playlist zijn. Die keuze stond vijf keer als
4// `x === 'playlist' ? 'playlist' : 'album'` verspreid over drie bestanden, en
5// zo'n vorm valt bij een derde soort niet om -- hij SLIKT hem. Een mixtape zou
6// stilzwijgend een album worden en als Album de deur uit gaan. Die tests staan
7// hier dus voorop.
8import { test } from 'node:test';
9import assert from 'node:assert/strict';
10
11process.env.DATABASE_PATH = ':memory:';
12process.env.PUBLIC_BASE_URL = 'https://test.example';
13const dbMod = await import('../src/config/database.js');
14const db = dbMod.default;
15dbMod.initializeDatabase();
16
17const { normKind, isUitgave } = await import('../src/services/PlaylistService.js');
18const PlaylistService = (await import('../src/services/PlaylistService.js')).default;
19const { afleidenUitInsluitingen, SOORTEN } = await import('../src/assets/js/shared/post-music-type.js');
20const muziek = await import('../src/services/music/index.js');
21
22const BASE = 'https://test.example';
23const site = { id: 's1', slug: 'robo', title: 'Soundfabrics' };
24
25test('mixtape is een echte soort, en onbekend blijft album', () => {
26 assert.equal(normKind('mixtape'), 'mixtape');
27 assert.equal(normKind('MIXTAPE '), 'mixtape', 'invoer uit een formulier is niet genormaliseerd');
28 assert.equal(normKind('cassette'), 'album', 'iets onbekends wordt geen nieuwe soort');
29 assert.equal(normKind(undefined), 'album');
30 assert.deepEqual(SOORTEN, ['album', 'playlist', 'mixtape']);
31});
32
33test('een bandje is geen uitgave: geen uitgavedatum, geen release-id', () => {
34 assert.equal(isUitgave('album'), true);
35 assert.equal(isUitgave('mixtape'), false);
36 assert.equal(isUitgave('playlist'), false);
37});
38
39test('een post om een mixtape IS een mixtape', () => {
40 const kindVan = (id) => (id === 'de-mixtape' ? 'mixtape' : null);
41 const r = afleidenUitInsluitingen('<p>hier</p>[[playlist:de-mixtape]]', kindVan);
42 assert.equal(r.type, 'mixtape');
43 assert.equal(r.leentMetadata, true, 'de post leent zijn titel en hoes uit');
44});
45
46test('een mixtape met losse tracks erbij blijft een mixtape', () => {
47 // De oude regel maakte van collectie + losse tracks een album. Voor een
48 // uitgave klopt dat (bonustracks), voor een bandje niet.
49 const kindVan = (id) => (id === 'tape' ? 'mixtape' : 'album');
50 const tape = afleidenUitInsluitingen('[[playlist:tape]][[track:abc]]', kindVan);
51 assert.equal(tape.type, 'mixtape');
52 assert.deepEqual(tape.bonus, ['abc']);
53
54 // En het oude gedrag voor een album blijft staan: bestaande posts mogen niet
55 // stilletjes van soort wisselen.
56 const plaat = afleidenUitInsluitingen('[[playlist:plaat]][[track:abc]]', kindVan);
57 assert.equal(plaat.type, 'album');
58});
59
60test('het Mixtape-object is een enkel object met de nummers erin', () => {
61 const rows = [
62 { id: 't1', title: 'Kant A', duration: 100, filename: 'a.mp3', mime_type: 'audio/mpeg', size: 1 },
63 { id: 't2', title: 'Kant B', duration: 44, filename: 'b.mp3', mime_type: 'audio/mpeg', size: 1 },
64 ];
65 const pl = { id: 'tape', title: 'De Mixtape', artist: 'robo', created_at: '2026-08-21T10:00:00Z' };
66 const tape = muziek.buildMixtapeObject(BASE, site, pl, rows);
67
68 // type is een STRING. Een array zou geldig AS2 zijn en toch fout: een lezer
69 // die het als tekst uitpakt (Shaer) verliest dan stil het hele object.
70 assert.equal(typeof tape.type, 'string');
71 assert.equal(tape.type, 'Mixtape');
72 assert.equal(tape.id, `${BASE}/ap/users/robo/playlists/tape`);
73
74 // Een enkel object, samengesteld uit playlistelementen.
75 assert.equal(tape.totalItems, 2);
76 assert.equal(tape.orderedItems.length, 2);
77 assert.equal(tape.orderedItems[0].type, 'Audio');
78 assert.equal(tape.orderedItems[0].id, `${BASE}/ap/users/robo/tracks/t1`);
79 assert.equal(tape.orderedItems[1].name, 'Kant B');
80
81 // De lengte van het bandje: 100 + 44.
82 assert.equal(tape.duration, 'PT144S');
83
84 // Geen uitgavevelden. Die zouden beweren dat dit een plaat is.
85 assert.equal(tape.released, undefined);
86 assert.equal(tape.musicbrainzId, undefined);
87 // En geen eigen stream: het is een omhulling, niet een gerenderd bestand.
88 assert.equal(tape.url, undefined);
89});
90
91test('een gat in de duur levert geen verzonnen totaal op', () => {
92 const rows = [
93 { id: 't1', title: 'A', duration: 100, filename: 'a.mp3', mime_type: 'audio/mpeg', size: 1 },
94 { id: 't2', title: 'B', duration: 0, filename: 'b.mp3', mime_type: 'audio/mpeg', size: 1 },
95 ];
96 const tape = muziek.buildMixtapeObject(BASE, site, { id: 'x', title: 'X' }, rows);
97 assert.equal(tape.duration, undefined, 'liever geen veld dan een som met gaten');
98});
99
100test('de collectie van een mixtape draagt zijn velden, en blijft OrderedCollection', () => {
101 const rows = [{ id: 't1', title: 'A', duration: 60, filename: 'a.mp3', mime_type: 'audio/mpeg', size: 1 }];
102 const uit = muziek.buildPlaylistCollection(BASE, site, { id: 'tape', title: 'De Mixtape', kind: 'mixtape' }, rows);
103 assert.equal(uit.type, 'OrderedCollection', 'het adres blijft een collectie voor wie hem zo ophaalt');
104 assert.equal(typeof uit.type, 'string');
105 assert.equal(uit.duration, 'PT60S', 'de mixtape-velden horen hier ook te staan');
106 // Uitgavevelden horen er NIET op: dit is geen plaat.
107 assert.equal(uit.released, undefined);
108 assert.equal(uit.musicbrainzId, undefined);
109});
110
111test('opslaan en teruglezen houdt de soort vast', () => {
112 db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)')
113 .run('u1', 'baas', 'b@t.nl', 'x', 'god');
114 db.prepare('INSERT INTO sites (id, slug, title, owner_id) VALUES (?,?,?,?)')
115 .run('s1', 'robo', 'Soundfabrics', 'u1');
116
117 const id = PlaylistService.create('s1', { title: 'De Mixtape', kind: 'mixtape', tracks: [] });
118 assert.ok(id, 'aanmaken hoort te lukken');
119 assert.equal(PlaylistService.get('s1', id).kind, 'mixtape', 'de soort mag niet terugvallen op album');
120 assert.equal(PlaylistService.list('s1').find((p) => p.id === id).kind, 'mixtape');
121
122 // En de server leidt het posttype er ook echt uit af, via de opzoeker die de
123 // database raadpleegt -- niet via de meegegeven testfunctie hierboven.
124 const r = muziek.postMusicType(`[[playlist:${id}]]`, 's1');
125 assert.equal(r.type, 'mixtape');
126});
Note: See TracBrowser for help on using the repository browser.