Changeset f894be5 in Klonkt for test


Ignore:
Timestamp:
08/14/2026 01:20:51 AM (4 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
9f9b45f
Parents:
5a49eba
Message:

De helft van de outbox, hotlinks in de tekst, en een lege images-tab

Robin na de echte fetch: 18 van de 35 berichten binnen, alle plaatjes
hotlinken naar de oude site, en de images-tab is leeg. Drie oorzaken, en de
eerste was de pijnlijkste omdat hij zonder één waarschuwing "klaar" meldde.

DE KALE OUTBOX IS EEN VALSTRIK. Klonkt zet er first EN een kopie van pagina 1
in (Pleroma eiste ooit een first, sindsdien staan ze er allebei). De ingest
zag items op de kale collectie, sloeg first over, vond daarna geen next (dat
veld bestaat alleen op echte pagina's) en dacht klaar te zijn. Precies 18 van
de 35, en "ingest klaar" in het log. Nu wordt de keten ALTIJD via first
gevolgd, en telt de ingest na: meldt de bron meer items dan er verwerkt zijn,
dan staat dat in het verslag.

PLAATJES IN DE TEKST BLEVEN HOTLINKS. De gebakken content draagt absolute
verwijzingen naar de bron, in img en in de href eromheen. Die worden nu
gedownload naar HETZELFDE pad onder onze mediamap en relatief herschreven.
Hetzelfde pad is drie reparaties in een: de verwijzingen in de tekst kloppen,
de zip en de pull convergeren op dezelfde bestanden, en de images-tab (die de
MAP post-images scant, niet de databasetabel) is niet langer leeg.

BESTAANDE POSTS WORDEN GEREPAREERD. Robins 18 staan er al, met hotlinks en
zonder cover. Een tweede ronde herschrijft hun tekst, haalt de bestanden
binnen en vult de cover aan. Overslaan is voortaan alleen voor posts waar
niets aan mankeert.

En onderweg twee eigen missers gevonden en gedicht:

  • de regex voor het herschrijven stond als string in een template literal; \s verloor zijn backslash en werd de LETTER s, dus elke URL met een s erin (post-images!) knapte af. De waarschuwing ".../media/po" gaf hem weg.
  • haalBijlage plakte /media/ voor een pad dat al met /media/ begon.

Gemeten op twee instanties met 28 posts, elk met cover en inline plaatje:
28/28 binnen, 0 hotlinks, 28 covers met bestand, post-images-map 28 stuks, en
de reparatieronde maakt een met opzet kapotgemaakte post weer heel.

Changed files:
src/services/MigrationService.js

  • first wordt altijd gevolgd; natellen tegen totalItems
  • bronMediaPad(): bron-media naar hetzelfde pad hier, met ../-bewaking
  • inhoudMediaBinnen(): alle bron-media in de tekst binnenhalen en relatief maken; idempotent
  • bestaande posts: tekst gerepareerd en cover aangevuld op een tweede ronde
  • de dubbele /media/ en de opgegeten backslash

src/views/pages/admin-migrate.ejs

  • "berichten gerepareerd" in het ophaalverslag

src/services/i18n.js

  • mig.r_posts_updated in nl/en/de

test/fep1580-migration.test.js

  • regressietests: de kale-collectie-valstrik, de verzwegen pagina, en het herschrijven van een URL met een s erin, letter voor letter

remarks: de reden dat mijn eigen proef dit nooit zag: mijn nepbron gaf
orderedItems zonder first, precies de vorm die de bug ontweek. En een post met
een ongeldige published_at sloopt de HELE outbox van de bron (unhandled
rejection in buildOutbox); hier gevonden met kapotte testdata, nog niet
gerepareerd. Suite 997 groen; tegenproef op de paginatie en de backslash, en de
eerste tegenproef op die backslash was VALS (sabotage kwam niet aan), daarna
opnieuw met bewijs.

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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • test/fep1580-migration.test.js

    r5a49eba rf894be5  
    458458});
    459459
     460test('de outbox-keten wordt gevolgd, ook als de kale collectie zelf items draagt', async () => {
     461  // Robins 18 van 35. Klonkts kale outbox draagt first EN een kopie van
     462  // pagina 1 (Pleroma eiste ooit een first, sindsdien staan ze er allebei).
     463  // De ingest zag items, sloeg first over, vond daarna geen next (dat veld
     464  // bestaat alleen op echte pagina's) en dacht klaar te zijn. Zonder een
     465  // waarschuwing, en dat was het ergste deel.
     466  const s = site({ aliases: [BRON] });
     467  const maak = (i) => note(`${BRON}/notes/n${i}`);
     468  const p1 = Array.from({ length: 20 }, (_, i) => maak(i + 1));
     469  const p2 = Array.from({ length: 8 }, (_, i) => maak(i + 21));
     470  const kaart = new Map([
     471    [BRON, { id: BRON, type: 'Person', movedTo: IK, outbox: `${BRON}/outbox` }],
     472    // de valstrik: totalItems, first, EN de eerste twintig inline
     473    [`${BRON}/outbox`, { type: 'OrderedCollection', totalItems: 28, first: `${BRON}/outbox?page=1`, orderedItems: p1 }],
     474    [`${BRON}/outbox?page=1`, { type: 'OrderedCollectionPage', next: `${BRON}/outbox?page=2`, orderedItems: p1 }],
     475    [`${BRON}/outbox?page=2`, { type: 'OrderedCollectionPage', orderedItems: p2 }],
     476  ]);
     477  const r = await stil(() => Mig.ingestFromSource(s, { deps: {
     478    getJson: async (_s, url) => kaart.get(url) || null,
     479    noteId: (b, id) => `${b}/ap/notes/${id}`,
     480    noteVisibility: AP.noteVisibility,
     481  } }));
     482  assert.equal(r.posts, 28, 'alle pagina\'s, niet alleen de kopie op de kale collectie');
     483});
     484
     485test('een niet opgehaalde pagina wordt GEMELD, niet verzwegen', async () => {
     486  const s = site({ aliases: [BRON] });
     487  const p1 = Array.from({ length: 20 }, (_, i) => note(`${BRON}/notes/n${i + 1}`));
     488  const kaart = new Map([
     489    [BRON, { id: BRON, type: 'Person', movedTo: IK, outbox: `${BRON}/outbox` }],
     490    [`${BRON}/outbox`, { type: 'OrderedCollection', totalItems: 28, first: `${BRON}/outbox?page=1` }],
     491    [`${BRON}/outbox?page=1`, { type: 'OrderedCollectionPage', next: `${BRON}/outbox?page=2`, orderedItems: p1 }],
     492    // pagina 2 antwoordt niet (rate limit, netwerkstoring, wat dan ook)
     493  ]);
     494  const r = await stil(() => Mig.ingestFromSource(s, { deps: {
     495    getJson: async (_s, url) => kaart.get(url) || null,
     496    noteId: (b, id) => `${b}/ap/notes/${id}`,
     497    noteVisibility: AP.noteVisibility,
     498  } }));
     499  assert.equal(r.posts, 20);
     500  assert.ok(r.waarschuwingen.some((w) => /28 items en er zijn er 20/.test(w)),
     501    'stil minder ophalen dan de bron meldt is hoe 18 van 35 wekenlang op klaar had gestaan');
     502});
     503
     504test('een plaatje in de tekst wordt gedownload en de verwijzing wordt relatief', async () => {
     505  // Robins hotlinks. En de valkuil die de eerste versie half liet werken: de
     506  // regex stond als STRING in een template literal, \s werd een kale s, en
     507  // elke URL met een s erin (post-images!) knapte af. Vandaar de assert op de
     508  // VOLLEDIGE url, letter voor letter.
     509  const s = site({ aliases: [BRON] });
     510  const IMG = `${BRON.replace('/ap/users/robo', '')}/media/post-images/plaatje-strak.png`;
     511  const kaart = new Map([
     512    [BRON, { id: BRON, type: 'Person', movedTo: IK, outbox: `${BRON}/outbox` }],
     513    [`${BRON}/outbox`, { type: 'OrderedCollection', totalItems: 1, first: `${BRON}/outbox?page=1` }],
     514    [`${BRON}/outbox?page=1`, { type: 'OrderedCollectionPage', orderedItems: [
     515      note(`${BRON}/notes/pi`, { content: `<p>kijk</p><a href="${IMG}"><img src="${IMG}"></a>` }),
     516    ] }],
     517  ]);
     518  const opgehaald = [];
     519  const geschreven = [];
     520  const r = await stil(() => Mig.ingestFromSource(s, { deps: {
     521    getJson: async (_s, url) => kaart.get(url) || null,
     522    noteId: (b, id) => `${b}/ap/notes/${id}`,
     523    noteVisibility: AP.noteVisibility,
     524    mediaRoot: '/nep/media', audioRoot: '/nep/audio',
     525    signHeaders: () => ({ Signature: 'nep' }),
     526    safeFetch: async (url) => { opgehaald.push(url); return { ok: true, arrayBuffer: async () => Buffer.from('png'), headers: { get: () => 'image/png' } }; },
     527    fs: { mkdirSync() {}, writeFileSync: (pad) => geschreven.push(pad), statSync() { throw new Error('ENOENT'); } },
     528    path,
     529  } }));
     530  assert.equal(r.posts, 1);
     531  assert.deepEqual(opgehaald, [IMG], 'de VOLLEDIGE url, niet een afgekapt stuk');
     532  assert.deepEqual(geschreven, ['/nep/media/post-images/plaatje-strak.png'], 'op het pad van de bron');
     533  const c = db.prepare("SELECT content FROM posts WHERE site_id = 's1'").get().content;
     534  assert.ok(c.includes('src="/media/post-images/plaatje-strak.png"'), `relatief herschreven, kreeg: ${c}`);
     535  assert.ok(!c.includes('oud.example'), 'en er hotlinkt niets meer naar de bron');
     536});
     537
    460538test('een bericht dat je zelf hebt verwijderd komt bij een tweede ronde terug', async () => {
    461539  // Robin: "ik kan handmatig deze keer de posts verwijderen en opnieuw ophalen."
Note: See TracChangeset for help on using the changeset viewer.