- Timestamp:
- 08/13/2026 11:39:57 PM (4 weeks ago)
- Branches:
- main
- Children:
- 9529d7b
- Parents:
- fbfd7a1
- Location:
- test
- Files:
-
- 1 added
- 2 edited
-
archive-audio.test.js (added)
-
archive-export.test.js (modified) (3 diffs)
-
fep1580-migration.test.js (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
test/archive-export.test.js
rfbfd7a1 r6c4ff7e 134 134 135 135 test('een gehoste track reist mee, met de gegevens die alleen in de database staan', () => { 136 // [[track:]] verwijst naar een audio_tracks-rij met een media-rij eronder. Op 137 // beta staan nul tracks, dus dit pad raakt daar geen echte data -- vandaar hier. 136 // Sinds v2 gaat audio NIET meer als media-bijlage mee. Dat kon ook nooit: 137 // gehoste audio staat buiten MEDIA_ROOT, de exporter rekende er met 138 // path.relative een /media/../audio/x.mp3 van, en de importer weigerde dat 139 // terecht. Resultaat op soundfabrics.nl: 13 tracks in de lijst, nul bestanden. 140 // Nu heeft het archief een eigen audio/-gebied. 138 141 fs.mkdirSync(path.join(MEDIA, 'audio'), { recursive: true }); 139 142 const WAV = Buffer.from('RIFF-nep-audio'); … … 147 150 const uit = AX.buildArchive('me'); 148 151 const o = lees(uit, 'posts/metaudio.json'); 149 const a = o.attachment[0]; 150 assert.equal(a['shaer:availability'], 'included'); 151 assert.equal(a.mediaType, 'audio/mpeg'); 152 assert.deepEqual(uit.files.get(a.url), WAV); 152 const t = o['shaer:audio'][0]; 153 153 assert.match(o.content, /\[\[track:tr1\]\]/, 'de shorthand blijft in de bron staan'); 154 const t = o['shaer:audio'][0];155 154 assert.equal(t.name, 'Kanonnen'); 156 155 assert.equal(t.artist, 'Robin'); 157 156 assert.equal(t.license, 'CC BY'); 158 157 assert.deepEqual(t.url, ['https://open.spotify.com/track/x']); 158 159 // Dit is de assertie die er echt toe doet: de BYTES zitten in het archief. 160 assert.ok(String(t['shaer:media']).startsWith('audio/'), 'de track wijst naar het audio-gebied'); 161 assert.deepEqual(uit.files.get(t['shaer:media']), WAV, 'en daar staat het geluid ook echt'); 162 163 // En hij staat in de bibliotheek, los van de post. 164 const lib = JSON.parse(uit.files.get('tracks.json').toString('utf8')); 165 const rij = lib.orderedItems.find((x) => x.id === 'tr1'); 166 assert.equal(rij['shaer:availability'], 'included'); 167 assert.equal(rij['shaer:file'], t['shaer:media']); 168 assert.equal(uit.counts.tracks, 1); 169 }); 170 171 test('de HELE bibliotheek gaat mee, ook wat in geen enkel bericht staat', () => { 172 // De reden dat dit bestaat: op sound-fabrics.com stonden 140 tracks en gingen 173 // er 14 mee, want alleen wat met [[track:]] in een bericht stond werd 174 // geexporteerd. Een verhuizing die je bibliotheek achterlaat is er geen. 175 fs.mkdirSync(path.join(MEDIA, 'audio'), { recursive: true }); 176 const B = Buffer.from('RIFF-los-nummer'); 177 fs.writeFileSync(path.join(MEDIA, 'audio', 'los.mp3'), B); 178 db.prepare('INSERT INTO media (id, site_id, filename, mime_type, size, storage_path) VALUES (?,?,?,?,?,?)') 179 .run('m9', 's1', 'los.mp3', 'audio/mpeg', B.length, path.join(MEDIA, 'audio', 'los.mp3')); 180 db.prepare("INSERT INTO audio_tracks (id, site_id, title, media_id) VALUES ('los','s1','Los nummer','m9')").run(); 181 182 const uit = AX.buildArchive('me'); 183 const lib = JSON.parse(uit.files.get('tracks.json').toString('utf8')); 184 const rij = lib.orderedItems.find((x) => x.id === 'los'); 185 assert.ok(rij, 'een track zonder bericht hoort er gewoon in'); 186 assert.deepEqual(uit.files.get(rij['shaer:file']), B); 187 }); 188 189 test('een track waarvan het bestand zoek is wordt gemeld, niet stil weggelaten', () => { 190 db.prepare("INSERT INTO media (id, site_id, filename, mime_type, size, storage_path) VALUES ('mz','s1','zoek.mp3','audio/mpeg',1,'/bestaat/niet/zoek.mp3')").run(); 191 db.prepare("INSERT INTO audio_tracks (id, site_id, title, media_id) VALUES ('zoek','s1','Zoek','mz')").run(); 192 const uit = AX.buildArchive('me'); 193 const rij = JSON.parse(uit.files.get('tracks.json').toString('utf8')).orderedItems.find((x) => x.id === 'zoek'); 194 assert.equal(rij['shaer:availability'], 'missing'); 195 assert.equal(rij['shaer:file'], undefined, 'geen verwijzing naar een bestand dat er niet is'); 196 assert.ok(uit.counts.audioMissing >= 1); 197 assert.ok(uit.missing.some((m) => m.track === 'Zoek'), 'en de beller kan het aan de gebruiker melden'); 198 }); 199 200 test('audio wordt gezocht zoals de SPELER hem zoekt, niet zoals de database hem onthoudt', async () => { 201 // De stille moordenaar op sound-fabrics.com: na een dataverhuizing wees 202 // storage_path voor 124 van de 139 tracks nog naar /srv/prutfolio/storage/audio. 203 // De speler merkte er niets van, want die pakt AUDIO_ROOT + bestandsnaam en 204 // negeert storage_path. De exporter las wel storage_path, vond niets, en liet 205 // 124 nummers weg zonder dat iemand het zag. 206 // 207 // Deze test pint de VOLGORDE vast met een nep-fs, want dat is precies waar de 208 // twee uit elkaar liepen. AUDIO_ROOT ligt bij import van paths.js al vast, dus 209 // hem hier omzetten kan niet meer; de volgorde toetsen wel. 210 const { resolveAudioPath, AUDIO_ROOT } = await import('../src/config/paths.js'); 211 const gevraagd = []; 212 const nepFs = { 213 statSync(p) { 214 gevraagd.push(p); 215 if (p === path.join(AUDIO_ROOT, 'x.mp3')) return { isFile: () => true }; 216 throw new Error('ENOENT'); 217 }, 218 }; 219 const uit = resolveAudioPath('/een/heel/oud/pad/x.mp3', nepFs); 220 assert.equal(uit, path.join(AUDIO_ROOT, 'x.mp3'), 'de speler-plek wint'); 221 assert.equal(gevraagd[0], path.join(AUDIO_ROOT, 'x.mp3'), 'en wordt als EERSTE geprobeerd'); 222 223 // Staat hij daar niet, dan telt het opgeslagen pad alsnog: installaties die 224 // hun audio ergens anders hebben mogen niet stuk. 225 const gevraagd2 = []; 226 const nepFs2 = { 227 statSync(p) { 228 gevraagd2.push(p); 229 if (p === path.resolve('/ergens/anders/y.mp3')) return { isFile: () => true }; 230 throw new Error('ENOENT'); 231 }, 232 }; 233 assert.equal(resolveAudioPath('/ergens/anders/y.mp3', nepFs2), path.resolve('/ergens/anders/y.mp3')); 234 assert.equal(resolveAudioPath('', nepFs2), null); 159 235 }); 160 236 … … 227 303 const m = AX.buildArchive('me').manifest; 228 304 assert.equal(m.origin, 'https://klonkt.test'); 229 assert.equal(m.formatVersion, 1);305 assert.equal(m.formatVersion, 2, 'v2 = audio zit er echt in'); 230 306 assert.equal(m.actor, 'https://klonkt.test/ap/users/me'); 231 307 }); -
test/fep1580-migration.test.js
rfbfd7a1 r6c4ff7e 13 13 import { test, beforeEach } from 'node:test'; 14 14 import assert from 'node:assert/strict'; 15 import path from 'node:path'; 15 16 16 17 process.env.DATABASE_PATH = ':memory:'; … … 47 48 48 49 beforeEach(() => { 49 for (const t of ['ap_migration', 'ap_moves', 'ap_blocks', 'posts', 'ap_followers' ]) {50 for (const t of ['ap_migration', 'ap_moves', 'ap_blocks', 'posts', 'ap_followers', 'audio_tracks', 'media']) { 50 51 try { db.prepare(`DELETE FROM ${t}`).run(); } catch { /* tabel bestaat niet in deze build */ } 51 52 } … … 315 316 }); 316 317 318 test('de muziekbibliotheek komt mee, ook wat niet fedi_open is', async () => { 319 // Losse nummers staan NIET in de outbox: die hangen aan de tracks-collectie 320 // waar de actor via AS2 `streams` naar wijst. Zonder deze tak verhuist een 321 // muzieksite zijn berichten en laat hij zijn bibliotheek achter, en dat is 322 // precies wat er op soundfabrics.nl gebeurde. 323 const s = site({ aliases: [BRON] }); 324 const AUDIO = `${BRON}/audio/x.mp3`; 325 const bytes = Buffer.from('ID3-nep-geluid'); 326 const kaart = new Map([ 327 [BRON, { 328 id: BRON, type: 'Person', movedTo: IK, outbox: `${BRON}/outbox`, 329 streams: [`${BRON}/tracks`, `${BRON}/playlists`], 330 }], 331 [`${BRON}/outbox`, { type: 'OrderedCollection', orderedItems: [] }], 332 [`${BRON}/tracks`, { 333 type: 'OrderedCollection', 334 orderedItems: [{ 335 id: `${BRON}/tracks/t1`, type: 'Audio', name: 'Gesloten nummer', artist: 'Robo', 336 url: [{ type: 'Link', href: AUDIO, mediaType: 'audio/mpeg' }], 337 }], 338 }], 339 ]); 340 const geschreven = new Map(); 341 let getekend = false; 342 const r = await stil(() => Mig.ingestFromSource(s, { 343 deps: { 344 getJson: async (_slug, url) => kaart.get(url) || null, 345 noteId: (b, id) => `${b}/ap/notes/${id}`, 346 noteVisibility: AP.noteVisibility, 347 audioRoot: '/nep/audio', 348 // De handtekening is hier geen detail: de audio-route van de bron weigert 349 // een kale fetch, want die kan niet zien dat wij de doel-actor zijn. 350 signHeaders: () => { getekend = true; return { Signature: 'nep' }; }, 351 safeFetch: async (url, opts) => { 352 assert.equal(url, AUDIO); 353 assert.ok(opts.headers && opts.headers.Signature, 'de bytes worden ONDERTEKEND opgehaald'); 354 return { ok: true, arrayBuffer: async () => bytes, headers: { get: () => 'audio/mpeg' } }; 355 }, 356 fs: { mkdirSync() {}, writeFileSync: (p, b) => geschreven.set(p, b) }, 357 path, 358 }, 359 })); 360 assert.equal(r.tracksBinnen, 1); 361 assert.equal(r.tracksMislukt, 0); 362 assert.ok(getekend); 363 assert.equal(geschreven.size, 1, 'het bestand wordt echt weggeschreven'); 364 const [pad] = [...geschreven.keys()]; 365 assert.ok(pad.startsWith('/nep/audio/'), 'audio hoort in AUDIO_ROOT, niet in de mediamap'); 366 assert.ok(!pad.slice('/nep/audio/'.length).includes('/'), 'en er direct in, want de speler zoekt op bestandsnaam'); 367 const t = db.prepare('SELECT title, artist FROM audio_tracks').get(); 368 assert.equal(t.title, 'Gesloten nummer'); 369 }); 370 371 test('een nummer waarvan de bytes niet komen levert GEEN track op', async () => { 372 // Dezelfde regel als bij de zip. Een nummer dat in de lijst staat en 404't is 373 // erger dan een nummer dat ontbreekt. 374 const s = site({ aliases: [BRON] }); 375 const kaart = new Map([ 376 [BRON, { id: BRON, type: 'Person', movedTo: IK, outbox: `${BRON}/outbox`, streams: [`${BRON}/tracks`] }], 377 [`${BRON}/outbox`, { type: 'OrderedCollection', orderedItems: [] }], 378 [`${BRON}/tracks`, { 379 type: 'OrderedCollection', 380 orderedItems: [{ id: `${BRON}/tracks/t1`, type: 'Audio', name: 'Weg', url: `${BRON}/audio/weg.mp3` }], 381 }], 382 ]); 383 const r = await stil(() => Mig.ingestFromSource(s, { 384 deps: { 385 getJson: async (_slug, url) => kaart.get(url) || null, 386 noteId: (b, id) => `${b}/ap/notes/${id}`, 387 noteVisibility: AP.noteVisibility, 388 audioRoot: '/nep/audio', 389 signHeaders: () => ({ Signature: 'nep' }), 390 safeFetch: async () => ({ ok: false, status: 403 }), 391 fs: { mkdirSync() {}, writeFileSync() { throw new Error('mag niet gebeuren'); } }, 392 path, 393 }, 394 })); 395 assert.equal(r.tracksBinnen, 0); 396 assert.equal(r.tracksMislukt, 1); 397 assert.equal(db.prepare('SELECT COUNT(*) n FROM audio_tracks').get().n, 0); 398 }); 399 317 400 test('een niet-publiek bericht komt wel mee maar niet in de publieke tabel', async () => { 318 401 const s = site({ aliases: [BRON] });
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)