Changeset 19430fa in Klonkt for test


Ignore:
Timestamp:
08/14/2026 12:40:43 AM (4 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
5a49eba
Parents:
919b82d
Message:

GUID's blijven altijd behouden bij een verhuizing

Robin vroeg of we bestaande GUID's hergebruiken. Het antwoord was zes vakjes:

zip ophaalknop

posts nieuw bij andere origin altijd nieuw
tracks OUD BEHOUDEN altijd nieuw
playlists OUD BEHOUDEN altijd nieuw

Vier keer nieuw, twee keer niet, en die twee uitzonderingen waren niet
besloten maar ontstaan: de zip schreef INSERT OR REPLACE met het id uit het
archief zonder dat daar ooit over nagedacht is. Die scheve tabel was precies de
oorzaak van de shortcode die Robin op TikTik zag: post uit de zip met
[[track:oud]], nummer uit de pull met een nieuw id.

Zijn besluit: altijd behouden. Nu is het één regel.

WAAROM DAT MAG. Het interne id is niet de AP-URI. https://nieuw/ap/notes/<id>
is een ander adres dan https://oud/ap/notes/<id>, dus je claimt niets van een
ander door het GUID te hergebruiken. Het oude argument in de code ("een id op
andermans domein publiceren is een vervalsingsoppervlak") haalde die twee door
elkaar. Wat je wint: elke interne verwijzing blijft kloppen, [[track:]],
[[playlist:]] en [[album:]] wijzen na de verhuizing nog naar het goede ding.

Wat NIET verandert is de AP-URI. Die is domeingebonden en hoort nieuw te zijn,
en daar is de migration-collectie voor. idsBehouden gaat voortaan alleen daar
nog over.

Gemeten door dezelfde inhoud via BEIDE routes over elkaar heen te halen:

posts oud 6 | nieuw 6 | zelfde id 6 | afwijkend 0
audio_tracks oud 3 | nieuw 3 | zelfde id 3 | afwijkend 0
playlists oud 1 | nieuw 1 | zelfde id 1 | afwijkend 0

Geen dubbele. Zip en ophaalknop zijn daarmee inwisselbaar geworden, en dat was
eerder de combinatie die stukging.

Changed files:
src/services/ArchiveImportService.js

  • posts houden hun id, ongeacht de origin
  • de waarschuwing zegt nu wat er echt verandert: het AP-adres, niet het id

src/services/MigrationService.js

  • posts, tracks en playlists nemen het id van de bron over
  • "staat hij hier al" is daarmee een blik in de tabel in plaats van een omweg via ap_migration; verwijderen en opnieuw ophalen werkt vanzelf
  • de eerderPl/eerder-omwegen konden weg

test/archive-import.test.js

  • de origin-test omgedraaid: het AP-adres verandert, het id blijft

test/fep1580-migration.test.js

  • de shortcode-test toetst nu de UITKOMST (wijst naar een bestaand nummer) in plaats van de route ernaartoe
  • nieuwe test voor het botsingsgeval, waar het bijtrekken wel nodig is

remarks: het bijtrekken van [[track:]] blijft bestaan als vangnet voor een
botsend id. In het normale geval doet het niets meer, en dat is de bedoeling.
Suite 991 groen.

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

Location:
test
Files:
2 edited

Legend:

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

    r919b82d r19430fa  
    126126});
    127127
    128 test('een andere origin levert NIEUWE ids op, met een waarschuwing', () => {
    129   // Oude ids houden op een ander domein zou objecten publiceren onder een id dat
    130   // je niet beheert -- andere servers halen dat daar op, en het is bovendien een
    131   // vervalsingsoppervlak.
     128test('een andere origin geeft een nieuw AP-adres, maar het interne id BLIJFT', () => {
     129  // Omgedraaid op 14-8 (Robin: "altijd behouden"). Het interne id is niet
     130  // hetzelfde als de AP-URI: https://nieuw/ap/notes/<id> is een ander adres dan
     131  // https://oud/ap/notes/<id>, dus je claimt niets van een ander door het GUID
     132  // te hergebruiken. Wat je wint is dat elke INTERNE verwijzing blijft kloppen.
     133  //
     134  // Het oude gedrag brak precies dat: een post uit de zip hield
     135  // [[track:<oud id>]] in zijn tekst terwijl het nummer een nieuw id kreeg, en
     136  // dan zie je de shorthand als kale tekst in je bericht staan.
    132137  leeg();
    133138  const files = new Map(ARCHIEF.files);
     
    136141  files.set('manifest.json', Buffer.from(JSON.stringify(m)));
    137142  const r = AI.importArchive(files, { slug: 'me' });
    138   assert.equal(r.idsBehouden, false);
     143  assert.equal(r.idsBehouden, false, 'de AP-URI verandert wel, en dat blijft gemeld');
    139144  assert.match(r.waarschuwingen.join(' '), /origin verschilt/);
    140   assert.equal(db.prepare("SELECT COUNT(*) AS n FROM posts WHERE id = 'zwaar'").get().n, 0);
    141   assert.equal(db.prepare('SELECT COUNT(*) AS n FROM posts').get().n, 1, 'wel geimporteerd, met een nieuw id');
     145  assert.equal(db.prepare("SELECT COUNT(*) AS n FROM posts WHERE id = 'zwaar'").get().n, 1,
     146    'het interne id komt ongewijzigd terug');
     147  assert.equal(db.prepare('SELECT COUNT(*) AS n FROM posts').get().n, 1);
    142148});
    143149
  • test/fep1580-migration.test.js

    r919b82d r19430fa  
    481481});
    482482
    483 test('de [[track:]]-verwijzing in een bericht wordt bijgetrokken', async () => {
     483test('de [[track:]]-verwijzing in een bericht blijft naar een bestaand nummer wijzen', async () => {
    484484  // Wat Robin op TikTik zag: het bericht toonde de shorthand zelf in plaats van
    485485  // een speler. Zijn posts kwamen uit de ZIP (die bewaart posts.content
     
    515515  }));
    516516
     517  // DE EIS, ongeacht hoe: na een verhuizing wijst de shorthand naar een nummer
     518  // dat hier bestaat. Sinds "altijd behouden" (14-8) klopt dat meestal vanzelf,
     519  // want het id verandert niet meer. Botst het id wel, dan trekt de ingest de
     520  // tekst bij. Deze test toetst de UITKOMST en niet de route ernaartoe.
    517521  const inhoud = db.prepare("SELECT content FROM posts WHERE id = 'pz'").get().content;
    518522  const m = /\[\[track:([^\]]+)\]\]/.exec(inhoud);
    519   assert.ok(m, 'de shorthand blijft staan, alleen het id verandert');
    520   assert.notEqual(m[1], 't-oud', 'het oude id wijst hier nergens heen');
     523  assert.ok(m, 'de shorthand blijft staan');
    521524  const bestaat = db.prepare('SELECT 1 FROM audio_tracks WHERE id = ? AND site_id = ?').get(m[1], 's1');
    522   assert.ok(bestaat, 'en het nieuwe id hoort bij een nummer dat er echt is');
     525  assert.ok(bestaat, `[[track:${m[1]}]] hoort bij een nummer dat er echt is`);
     526  assert.equal(m[1], 't-oud', 'en omdat het id behouden blijft, hoefde er niets herschreven');
     527});
     528
     529test('botst het track-id wel, dan wordt de tekst bijgetrokken', async () => {
     530  // Het vangnet. "Altijd behouden" kan niet als er hier al iets anders met dat
     531  // id staat; dan krijgt het nummer een ander id en moet de shorthand mee.
     532  const s2 = site({ aliases: [BRON] });
     533  db.prepare(`INSERT INTO posts (id, site_id, author_id, slug, title, content, status, published_at)
     534              VALUES ('pb','s1','u1','botsing','Botsing','<p>[[track:t-bots]]</p>','published','2026-08-13T10:00:00Z')`).run();
     535  // Een nummer dat hier AL bestaat onder datzelfde id, van iets anders.
     536  db.prepare("INSERT INTO media (id, site_id, filename, mime_type, size, storage_path) VALUES ('mx','s1','x.mp3','audio/mpeg',1,'/x')").run();
     537  db.prepare("INSERT INTO audio_tracks (id, site_id, title, media_id) VALUES ('t-bots','s1','Al van mij','mx')").run();
     538
     539  const kaart2 = new Map([
     540    [BRON, { id: BRON, type: 'Person', movedTo: IK, outbox: `${BRON}/outbox`, streams: [`${BRON}/tracks`] }],
     541    [`${BRON}/outbox`, { type: 'OrderedCollection', orderedItems: [] }],
     542    [`${BRON}/tracks`, {
     543      type: 'OrderedCollection',
     544      orderedItems: [{
     545        id: `${BRON}/tracks/t-bots`, type: 'Audio', name: 'Van de bron',
     546        url: [{ type: 'Link', href: `${BRON}/audio/y.mp3`, mediaType: 'audio/mpeg' }],
     547      }],
     548    }],
     549  ]);
     550  await stil(() => Mig.ingestFromSource(s2, {
     551    deps: {
     552      getJson: async (_slug, url) => kaart2.get(url) || null,
     553      noteId: (b, id) => `${b}/ap/notes/${id}`,
     554      noteVisibility: AP.noteVisibility,
     555      audioRoot: '/nep/audio', mediaRoot: '/nep/media',
     556      signHeaders: () => ({ Signature: 'nep' }),
     557      safeFetch: async () => ({ ok: true, arrayBuffer: async () => Buffer.from('y'), headers: { get: () => 'audio/mpeg' } }),
     558      fs: { mkdirSync() {}, writeFileSync() {} },
     559      path,
     560    },
     561  }));
     562  // Het bestaande nummer blijft van jou; de tekst wijst naar iets dat bestaat.
     563  const inhoud = db.prepare("SELECT content FROM posts WHERE id = 'pb'").get().content;
     564  const m = /\[\[track:([^\]]+)\]\]/.exec(inhoud);
     565  assert.ok(m);
     566  assert.ok(db.prepare('SELECT 1 FROM audio_tracks WHERE id = ? AND site_id = ?').get(m[1], 's1'),
     567    'wat er ook gebeurde met het id, de verwijzing wijst naar een bestaand nummer');
    523568});
    524569
Note: See TracChangeset for help on using the changeset viewer.