Index: test/music-type.test.js
===================================================================
--- test/music-type.test.js	(revision e667d26aec08a6380a8c2e1687a3f374682f383d)
+++ test/music-type.test.js	(revision d7e72b84b3578aaadd6519dbe240f683eec33591)
@@ -1,9 +1,11 @@
-// Welk soort uitgave is een post? (shaer-cyg)
+// Welk soort uitgave is een BESTAANDE post? (shaer-cyg)
 //
-// De regel gaat over IDENTITEIT en niet over tellen: een post neemt het type
-// van zijn muziek over als hij precies EEN muzikale eenheid bevat. Zijn het er
-// meer, dan is de post een post die naar muziek verwijst en houden de
-// collecties hun eigen identiteit -- en vervalt de reden om de posttitel en
-// -cover te lenen.
+// Nieuwe posts krijgen hun type uit de keuze -- album of playlist wordt gekozen
+// als de playlist wordt gemaakt. Deze afleiding is er voor wat er al staat: de
+// type=audio posts van voor die keuze. De regel gaat over IDENTITEIT en niet
+// over tellen: een post neemt het type van zijn muziek over als hij precies EEN
+// muzikale eenheid bevat. Zijn het er meer, dan is de post een post die naar
+// muziek verwijst en houden de collecties hun eigen identiteit -- en vervalt de
+// reden om de posttitel en -cover te lenen.
 import { test } from 'node:test';
 import assert from 'node:assert/strict';
@@ -14,14 +16,33 @@
 const dbMod = await import('../src/config/database.js');
 dbMod.initializeDatabase();
+const db = dbMod.default;
 const { postMusicType } = await import('../src/services/music/index.js');
 
+const SITE = 'site-muziek';
+const ANDERE = 'site-anders';
+db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)')
+  .run('u1', 'robin', 'r@test', 'x', 'god');
+db.prepare('INSERT INTO sites (id, slug, title, owner_id) VALUES (?,?,?,?)').run(SITE, 'muziek', 'Muziek', 'u1');
+db.prepare('INSERT INTO sites (id, slug, title, owner_id) VALUES (?,?,?,?)').run(ANDERE, 'anders', 'Anders', 'u1');
+
+const maakPlaylist = (id, kind, siteId = SITE) =>
+  db.prepare('INSERT INTO playlists (id, site_id, title, kind) VALUES (?, ?, ?, ?)')
+    .run(id, siteId, id, kind);
+
+maakPlaylist('nachtlicht', 'playlist');
+maakPlaylist('de-plaat', 'album');
+maakPlaylist('tweede', 'playlist');
+maakPlaylist('elders', 'playlist', ANDERE);
+
+const type = (c) => postMusicType(c, SITE);
+
 test('geen muziek: geen type', () => {
-  assert.equal(postMusicType('<p>gewoon een bericht</p>'), null);
-  assert.equal(postMusicType(''), null);
-  assert.equal(postMusicType(null), null);
+  assert.equal(type('<p>gewoon een bericht</p>'), null);
+  assert.equal(type(''), null);
+  assert.equal(type(null), null);
 });
 
 test('losse tracks worden een playlist, en lenen de metadata van de post', () => {
-  const r = postMusicType('Twee nieuwe: [[track:a]] en [[track:b]]');
+  const r = type('Twee nieuwe: [[track:a]] en [[track:b]]');
   assert.equal(r.type, 'playlist');
   assert.deepEqual(r.tracks, ['a', 'b']);
@@ -32,10 +53,17 @@
   // Naar buiten toe is er dan altijd een collectie om naar te wijzen. Dat
   // "Playlist" boven een enkel nummer gek leest is een weergavekwestie.
-  assert.equal(postMusicType('[[track:a]]').type, 'playlist');
+  assert.equal(type('[[track:a]]').type, 'playlist');
 });
 
-test('een playlist blijft een playlist', () => {
-  const r = postMusicType('Nieuw: [[playlist:nachtlicht]]');
-  assert.equal(r.type, 'playlist');
+test('de post neemt de GEKOZEN soort van de playlist over', () => {
+  // De kern van Robins besluit: album of playlist is bij het maken van de
+  // playlist al gekozen. Hier wordt hij opgezocht, niet afgeleid.
+  assert.equal(type('Nieuw: [[playlist:nachtlicht]]').type, 'playlist');
+  assert.equal(type('Nieuw: [[playlist:de-plaat]]').type, 'album',
+    'een post om een album is een album, ook al heet de shortcode playlist');
+});
+
+test('de collectie draagt haar eigen soort mee', () => {
+  const r = type('Nieuw: [[playlist:nachtlicht]]');
   assert.deepEqual(r.collectie, { soort: 'playlist', id: 'nachtlicht' });
   assert.equal(r.leentMetadata, true);
@@ -43,7 +71,8 @@
 
 test('een album-insluiting is ook een collectie, en telt als album', () => {
-  // Robins regel noemde alleen playlists; [[album:naam]] groepeert net zo goed
-  // tracks. Hem als losse tracks tellen zou een albumpost tot playlist maken.
-  const r = postMusicType('Luister [[album:De Plaat]]');
+  // [[album:naam]] groepeert tracks op hun albumveld -- geen playlist-rij, dus
+  // geen gekozen soort. Hem als losse tracks tellen zou een albumpost tot
+  // playlist maken.
+  const r = type('Luister [[album:De Plaat]]');
   assert.equal(r.type, 'album');
   assert.deepEqual(r.collectie, { soort: 'album', naam: 'De Plaat' });
@@ -51,6 +80,6 @@
 
 test('een collectie MET losse tracks is een album, en die losse zijn bonus', () => {
-  const r = postMusicType('[[playlist:nachtlicht]] plus [[track:x]] en [[track:y]]');
-  assert.equal(r.type, 'album');
+  const r = type('[[playlist:nachtlicht]] plus [[track:x]] en [[track:y]]');
+  assert.equal(r.type, 'album', 'ook al is de playlist zelf een playlist');
   assert.equal(r.collectie.id, 'nachtlicht');
   assert.deepEqual(r.bonus, ['x', 'y'], 'geen rommelrestje maar bonus-tracks');
@@ -60,5 +89,5 @@
 
 test('TWEE collecties: de post blijft een post en leent niets', () => {
-  const r = postMusicType('[[playlist:een]] en ook [[playlist:twee]]');
+  const r = type('[[playlist:nachtlicht]] en ook [[playlist:tweede]]');
   assert.equal(r.type, 'post');
   assert.equal(r.collecties.length, 2);
@@ -68,5 +97,5 @@
 
 test('twee collecties van verschillende soort tellen ook als twee', () => {
-  const r = postMusicType('[[playlist:een]] naast [[album:De Plaat]]');
+  const r = type('[[playlist:nachtlicht]] naast [[album:De Plaat]]');
   assert.equal(r.type, 'post');
   assert.equal(r.collecties.length, 2);
@@ -76,10 +105,29 @@
   // Anders maakt een herhaalde shortcode van een albumpost stilletjes een
   // gewone post.
-  const r = postMusicType('[[playlist:een]] ... en nog eens [[playlist:een]]');
+  const r = type('[[playlist:nachtlicht]] ... en nog eens [[playlist:nachtlicht]]');
   assert.equal(r.type, 'playlist');
-  assert.equal(r.collectie.id, 'een');
+  assert.equal(r.collectie.id, 'nachtlicht');
 });
 
 test('dezelfde track twee keer telt ook maar een keer', () => {
-  assert.deepEqual(postMusicType('[[track:a]] [[track:a]] [[track:b]]').tracks, ['a', 'b']);
+  assert.deepEqual(type('[[track:a]] [[track:a]] [[track:b]]').tracks, ['a', 'b']);
 });
+
+test('onbekende playlist: gewone post, niets verzonnen', () => {
+  // Robins laatste optie voor een onbekende situatie. De insluiting blijft
+  // staan en houdt de post als context; alleen het label wordt niet geraden.
+  const r = type('[[playlist:bestaat-niet]]');
+  assert.equal(r.type, 'post');
+  assert.deepEqual(r.onbekend, ['bestaat-niet']);
+  assert.equal(r.leentMetadata, false);
+});
+
+test('een playlist van een ANDERE site telt niet als de onze', () => {
+  const r = type('[[playlist:elders]]');
+  assert.equal(r.type, 'post');
+  assert.deepEqual(r.onbekend, ['elders']);
+});
+
+test('zonder site is er niets op te zoeken, dus geen type verzinnen', () => {
+  assert.equal(postMusicType('[[playlist:nachtlicht]]').type, 'post');
+});
