source: Klonkt/test/ap-fw-track.test.js@ a982e7e

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

Audio: fw:track meesturen in de kleine Emissary-vorm

Spoor B stap 1 uit shaer-3f8a. Twee onafhankelijke implementaties lezen
dit veld: Funkwhale (verplicht in UploadSerializer) en Emissary 0.9.0,
gemeten op bandwagon.fm 16-8. petitminion noemde het ontbreken ervan als
eerste wat hem aan onze objecten opviel.

De vorm is die van Emissary: type, id, name, position. Klein, en dat is
het punt -- Funkwhale's eigen TrackSerializer wil er artists en
musicbrainzId bij, en dat is de grote versie die we niet nodig hebben om
gelezen te worden.

EIGEN ID MET #track. Emissary hergebruikt daar het id van het
Audio-object, maar dan zijn de Audio en de Track in JSON-LD een knoop met
twee typen, en een bestand is geen werk. Dat onderscheid moeten we straks
toch maken -- een album verzamelt nummers, geen mp3's.

GEEN album in de track. Dat is bij hen een URI naar een Album-object en
bij ons een tekstkolom; er een adres van maken belooft een ding dat niet
bestaat. Die keuze staat apart (shaer-k37k) en moet eerst vallen.

Het commentaar in ap-core dat uitlegde waarom we track NIET namen is
bijgesteld in plaats van weggehaald: die redenering klopte half. Een
track heeft bij ons wel een eigen identiteit, een album niet -- dat is
waar de grens werkelijk ligt.

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

  • Property mode set to 100644
File size: 4.1 KB
Line 
1// `fw:track` op onze Audio-objecten (shaer-3f8a, spoor B stap 1).
2//
3// Twee onafhankelijke implementaties lezen dit veld: Funkwhale (verplicht in
4// UploadSerializer) en Emissary 0.9.0, gemeten op bandwagon.fm 16-8. Emissary
5// stuurt de kleine vorm -- type, id, name, album, position -- en die nemen we
6// over, zonder album zolang dat bij ons geen object is (shaer-k37k).
7//
8// Wat deze test vastlegt is vooral het ID: het nummer krijgt een EIGEN
9// identiteit met #track, niet die van het bestand. Emissary hergebruikt daar
10// het object-id, en dan zijn de Audio en de Track in JSON-LD een knoop met twee
11// typen. Wie dat 'opruimt' naar het kortere id haalt dat verschil weg, en dan
12// staat er dat een mp3 een muziekstuk IS.
13import { test } from 'node:test';
14import assert from 'node:assert/strict';
15
16process.env.DATABASE_PATH = ':memory:';
17process.env.PUBLIC_BASE_URL = 'https://ons.test';
18
19const dbMod = await import('../src/config/database.js');
20const db = dbMod.default;
21dbMod.initializeDatabase();
22const AP = await import('../src/services/ActivityPubService.js');
23
24const BASE = 'https://ons.test';
25db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)')
26 .run('u1', 'u1', 'u1@t', 'x', 'god');
27db.prepare('INSERT INTO sites (id, slug, title, owner_id) VALUES (?,?,?,?)')
28 .run('s1', 'band', 'De Band', 'u1');
29const site = db.prepare("SELECT * FROM sites WHERE id = 's1'").get();
30
31db.prepare('INSERT INTO media (id, site_id, filename, storage_path, mime_type, size) VALUES (?,?,?,?,?,?)')
32 .run('m1', 's1', 'een.mp3', 'a/een.mp3', 'audio/mpeg', 2480880);
33db.prepare('INSERT INTO audio_tracks (id, site_id, title, artist, duration, media_id, fedi_open, position) VALUES (?,?,?,?,?,?,?,?)')
34 .run('t1', 's1', 'Het nummer', 'De Band', 103, 'm1', 1, 3);
35// Eentje zonder positie: die mag het veld niet verzinnen.
36db.prepare('INSERT INTO media (id, site_id, filename, storage_path, mime_type, size) VALUES (?,?,?,?,?,?)')
37 .run('m2', 's1', 'twee.mp3', 'a/twee.mp3', 'audio/mpeg', 100);
38db.prepare('INSERT INTO audio_tracks (id, site_id, title, media_id, fedi_open) VALUES (?,?,?,?,?)')
39 .run('t2', 's1', 'Zonder plek', 'm2', 1);
40
41const audio = (id) => AP.buildTrackAudio(BASE, site, AP.openTrack('s1', id), { hostPosts: null });
42
43test('elke Audio draagt een track met de kleine Emissary-vorm', () => {
44 const a = audio('t1');
45 assert.ok(a.track, 'geen track op het object');
46 assert.equal(a.track.type, 'Track');
47 assert.equal(a.track.name, 'Het nummer');
48 assert.equal(a.track.position, 3);
49});
50
51test('de track heeft een EIGEN id, niet dat van het bestand', () => {
52 const a = audio('t1');
53 assert.equal(a.track.id, `${a.id}#track`);
54 assert.notEqual(a.track.id, a.id, 'een bestand is geen werk');
55});
56
57test('geen album zolang het bij ons geen object is', () => {
58 // Een tekstkolom als URI meesturen is een adres beloven dat niet bestaat.
59 assert.equal(audio('t1').track.album, undefined);
60});
61
62test('geen verzonnen positie', () => {
63 assert.equal(audio('t2').track.position, undefined);
64 assert.equal(audio('t2').track.name, 'Zonder plek');
65});
66
67test('de termen staan in de context, anders valt het veld weg', async () => {
68 const core = await import('../src/services/ap-core.js');
69 const term = core.AP_CONTEXT.find((x) => x && typeof x === 'object' && x.track);
70 assert.ok(term, 'track staat in de context');
71 // Letterlijk de vorm uit hun contexts.py (regel 293-306), zodat een lezer die
72 // hun context laadt op dezelfde IRI's uitkomt als een lezer die de onze leest.
73 assert.deepEqual(term.track, { '@id': 'fw:track', '@type': '@id' });
74 assert.equal(term.Track, 'fw:Track');
75});
76
77test('de track komt overal mee, niet alleen op de losse ophaal', () => {
78 // Bibliotheek, trackcollectie en losse ophaal bouwen alle drie via
79 // buildTrackAudio -- dit is de test die dat vasthoudt als iemand er een
80 // eigen bouwertje naast zet.
81 const lib = AP.buildLibrary(BASE, site, AP.siteOpenTracks('s1'));
82 for (const a of lib.items) assert.equal(a.track.type, 'Track', `${a.name} zonder track`);
83 const col = AP.buildTrackCollection(BASE, site, AP.siteOpenTracks('s1'));
84 for (const a of col.orderedItems) assert.equal(a.track.type, 'Track', `${a.name} zonder track`);
85});
Note: See TracBrowser for help on using the repository browser.