Changeset f704237 in Klonkt for test


Ignore:
Timestamp:
08/14/2026 02:35:06 AM (4 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
47f1183
Parents:
fc664ce
Message:

Een track zonder bestand is niet hetzelfde als een kapotte track

Robin, over het ene nummer dat niet meekwam: "ah, dat was een no audio 'audio'
voor alleen spotify en youtube links". Dat is een bestaand Klonkt-type, geen
ongelukje: geen gehost bestand, wel externe links, en buildNote maakt er een
embed-kaart van (trackEmbedLinks). Mijn regel "geen bestand, geen track" was te
grof en gooide hem weg.

"Geen bestand" en "niets om te tonen" zijn niet hetzelfde. De regel blijft
staan waar hij hoort: een track zonder bestand EN zonder links wordt nog steeds
geweigerd, want die kan niets.

Het archiefformaat heeft nu drie staten in plaats van twee: included, missing,
en linkOnly. Een link-only track telt dus ook niet meer als "ontbrekend", want
er ontbreekt niets.

EN EEN TWEEDE FOUT DIE HIERONDER LAG. De pull viste de audio-URL uit de
url-lijst met element NUL. buildTrackAudio zet daar een text/html-link naar de
post voor het bestand: wie blind de eerste pakt downloadt een HTML-pagina en
schrijft die weg als mp3. Nu wordt de link met mediaType audio/* gezocht, met
/audio/stream/ als terugval. Dat het bij Robin niet misging is geluk: zijn
tracks hadden geen host-post in die lijst.

Changed files:
src/services/ArchiveExportService.js

  • shaer:availability kent linkOnly; die telt niet als audioMissing

src/services/ArchiveImportService.js

  • link-only tracks komen mee, met media_id NULL zoals op de bron
  • de schrijftak kan een track zonder bestand aan

src/services/MigrationService.js

  • de pull vist de audio-link uit de lijst in plaats van element nul
  • een track met alleen externe links wordt gewoon aangemaakt

test/archive-audio.test.js

  • link-only reist mee; zonder bestand en zonder links blijft geweigerd

test/fep1580-migration.test.js

  • hetzelfde voor de ophaalknop, plus: de audio-link wordt gevist

remarks: Robins Youngstown-nummer komt hiermee alsnog over. Suite 1009 groen;
tegenproef op beide, en de eerste poging daarvan kwam niet aan (te slordige
sabotage), dus opnieuw gedaan met twee rode tests als bewijs.

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

Location:
test
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • test/archive-audio.test.js

    rfc664ce rf704237  
    258258});
    259259
     260test('een track met alleen Spotify/YouTube-links reist gewoon mee', () => {
     261  // Robins Youngstown-nummer. Klonkt kent link-only tracks: geen gehost
     262  // bestand, wel externe links, en buildNote maakt daar een embed-kaart van.
     263  // Mijn regel "geen bestand, geen track" gooide die weg, en dat was te grof:
     264  // "geen bestand" en "niets om te tonen" zijn niet hetzelfde.
     265  leeg();
     266  db.prepare(`INSERT INTO audio_tracks (id, site_id, title, artist, media_id, link_spotify, link_youtube)
     267              VALUES ('lo','s1','Alleen links','Youngstown',NULL,
     268                      'https://open.spotify.com/track/abc','https://www.youtube.com/watch?v=xyz')`).run();
     269
     270  const uit = AX.buildArchive('me');
     271  const rij = JSON.parse(uit.files.get('tracks.json').toString('utf8')).orderedItems.find((x) => x.id === 'lo');
     272  assert.equal(rij['shaer:availability'], 'linkOnly', 'een eigen staat, niet "missing"');
     273  assert.equal(uit.counts.audioMissing, 0, 'en hij telt niet als ontbrekend: er mist niets');
     274  assert.deepEqual(rij.url, ['https://open.spotify.com/track/abc', 'https://www.youtube.com/watch?v=xyz']);
     275
     276  leeg();
     277  const r = AI.importArchive(uit.files, { slug: 'me', origin: 'https://nieuw.test' });
     278  assert.equal(r.tracks, 1);
     279  assert.equal(r.tracksMissing, 0);
     280  const t = db.prepare("SELECT title, artist, media_id, link_spotify, link_youtube FROM audio_tracks WHERE id = 'lo'").get();
     281  assert.ok(t, 'de track hoort er te staan');
     282  assert.equal(t.media_id, null, 'zonder mediarij, precies zoals op de bron');
     283  assert.equal(t.title, 'Alleen links');
     284  assert.equal(t.link_spotify, 'https://open.spotify.com/track/abc');
     285  assert.equal(t.link_youtube, 'https://www.youtube.com/watch?v=xyz');
     286});
     287
     288test('een track zonder bestand EN zonder links blijft wel geweigerd', () => {
     289  // De oorspronkelijke regel blijft staan waar hij hoort: dit is een track die
     290  // niets kan tonen en niets kan afspelen.
     291  leeg();
     292  track('kapot', 'Kapot', { padInDb: '/weg/kapot.mp3', schrijf: false });
     293  const uit = AX.buildArchive('me');
     294  assert.equal(uit.counts.audioMissing, 1);
     295  leeg();
     296  const r = AI.importArchive(uit.files, { slug: 'me', origin: 'https://nieuw.test' });
     297  assert.equal(r.tracks, 0);
     298  assert.equal(r.tracksMissing, 1);
     299  assert.equal(db.prepare('SELECT COUNT(*) n FROM audio_tracks').get().n, 0);
     300});
     301
    260302test.after(() => { try { fs.rmSync(TMP, { recursive: true, force: true }); } catch { /* niets */ } });
  • test/fep1580-migration.test.js

    rfc664ce rf704237  
    369369});
    370370
     371test('een link-only nummer komt ook via de ophaalknop mee', async () => {
     372  // Dezelfde regel als bij de zip. Over AP heeft zo'n track geen audio-link in
     373  // zijn url-lijst, alleen de externe. De vorige versie viste blind de eerste
     374  // url en strandde daarop.
     375  const s = site({ aliases: [BRON] });
     376  const kaart = new Map([
     377    [BRON, { id: BRON, type: 'Person', movedTo: IK, outbox: `${BRON}/outbox`, streams: [`${BRON}/tracks`] }],
     378    [`${BRON}/outbox`, { type: 'OrderedCollection', totalItems: 0, first: `${BRON}/outbox?page=1` }],
     379    [`${BRON}/outbox?page=1`, { type: 'OrderedCollectionPage', orderedItems: [] }],
     380    [`${BRON}/tracks`, { type: 'OrderedCollection', orderedItems: [{
     381      id: `${BRON}/tracks/lo`, type: 'Audio', name: 'Alleen links', summary: 'Youngstown',
     382      url: [
     383        { type: 'Link', href: 'https://oud.example/eenpost', mediaType: 'text/html' },
     384        { type: 'Link', href: 'https://open.spotify.com/track/abc' },
     385        { type: 'Link', href: 'https://www.youtube.com/watch?v=xyz' },
     386      ],
     387    }] }],
     388  ]);
     389  const gehaald = [];
     390  const r = await stil(() => Mig.ingestFromSource(s, { deps: {
     391    getJson: async (_s, url) => kaart.get(url) || null,
     392    noteId: (b, id) => `${b}/ap/notes/${id}`,
     393    noteVisibility: AP.noteVisibility,
     394    audioRoot: '/nep/audio', mediaRoot: '/nep/media',
     395    signHeaders: () => ({ Signature: 'nep' }),
     396    safeFetch: async (u) => { gehaald.push(u); return { ok: true, arrayBuffer: async () => Buffer.from('x'), headers: { get: () => 'audio/mpeg' } }; },
     397    fs: { mkdirSync() {}, writeFileSync() {}, statSync() { throw new Error('ENOENT'); } },
     398    path,
     399  } }));
     400  assert.equal(r.tracksBinnen, 1);
     401  assert.equal(r.tracksMislukt, 0);
     402  assert.equal(r.tracksLinks, 1);
     403  assert.deepEqual(gehaald, [], 'er valt niets te downloaden, en dat hoort ook niet geprobeerd');
     404  const t = db.prepare("SELECT title, artist, media_id, link_spotify, link_youtube FROM audio_tracks WHERE id = 'lo'").get();
     405  assert.ok(t, 'de track staat er');
     406  assert.equal(t.media_id, null);
     407  assert.equal(t.artist, 'Youngstown', 'de artiest komt uit summary');
     408  assert.equal(t.link_spotify, 'https://open.spotify.com/track/abc');
     409  assert.equal(t.link_youtube, 'https://www.youtube.com/watch?v=xyz');
     410});
     411
     412test('de audio-link wordt uit de lijst GEVIST, niet blind de eerste gepakt', async () => {
     413  // buildTrackAudio zet een text/html-link naar de post VOOR het bestand. Wie
     414  // element nul pakt downloadt een HTML-pagina en noemt dat een mp3.
     415  const s = site({ aliases: [BRON] });
     416  const MP3 = 'https://oud.example/audio/stream/x.mp3';
     417  const kaart = new Map([
     418    [BRON, { id: BRON, type: 'Person', movedTo: IK, outbox: `${BRON}/outbox`, streams: [`${BRON}/tracks`] }],
     419    [`${BRON}/outbox`, { type: 'OrderedCollection', totalItems: 0, first: `${BRON}/outbox?page=1` }],
     420    [`${BRON}/outbox?page=1`, { type: 'OrderedCollectionPage', orderedItems: [] }],
     421    [`${BRON}/tracks`, { type: 'OrderedCollection', orderedItems: [{
     422      id: `${BRON}/tracks/m1`, type: 'Audio', name: 'Met bestand',
     423      url: [
     424        { type: 'Link', href: 'https://oud.example/depost', mediaType: 'text/html' },
     425        { type: 'Link', href: MP3, mediaType: 'audio/mpeg' },
     426      ],
     427    }] }],
     428  ]);
     429  const gehaald = [];
     430  await stil(() => Mig.ingestFromSource(s, { deps: {
     431    getJson: async (_s, url) => kaart.get(url) || null,
     432    noteId: (b, id) => `${b}/ap/notes/${id}`,
     433    noteVisibility: AP.noteVisibility,
     434    audioRoot: '/nep/audio', mediaRoot: '/nep/media',
     435    signHeaders: () => ({ Signature: 'nep' }),
     436    safeFetch: async (u) => { gehaald.push(u); return { ok: true, arrayBuffer: async () => Buffer.from('x'), headers: { get: () => 'audio/mpeg' } }; },
     437    fs: { mkdirSync() {}, writeFileSync() {}, statSync() { throw new Error('ENOENT'); } },
     438    path,
     439  } }));
     440  assert.deepEqual(gehaald, [MP3], 'het bestand, niet de postpagina');
     441});
     442
    371443test('een nummer waarvan de bytes niet komen levert GEEN track op', async () => {
    372444  // Dezelfde regel als bij de zip. Een nummer dat in de lijst staat en 404't is
Note: See TracChangeset for help on using the changeset viewer.