Changeset 6fd0e20 in Klonkt for test


Ignore:
Timestamp:
07/26/2026 08:20:58 AM (6 weeks ago)
Author:
Robin Genis <roboburr@…>
Branches:
main
Children:
af6e085
Parents:
cc3cf7d
Message:

FEP-044f: Klonkt resolvet de geciteerde post voor een ingebedde quote-kaart

De client kan remote AP-objecten niet gesigneerd ophalen, dus Klonkt resolvet de
quote emit-side tot een compacte, gesaneerde snapshot en serveert die als
shaer:quote op de C2S inbox-read. De client rendert daarmee een geneste kaart
(auteur + avatar + tekst + evt. thumbnail) i.p.v. alleen de link-chip.

resolveQuote(note) haalt de quoted post op (SSRF-safe apGetJson), pakt de auteur
(fetchActor + actorInfo) en saneert de content met dezelfde sanitizer als elke
andere note (kindveilig blijft gelden). Best-effort: faalt de fetch, dan blijft
quote_json leeg en valt de client terug op de chip.

Bron-URL via quoteHrefOf: object-level quote (FEP-044f) of een quote-rel
FEP-e232 Link. Resolutie op inbound (fire-and-forget, blokkeert de inbox-response
niet), op outbox-backfill (await) en in self-heal (v10 -> v11, met COALESCE-gedrag
zodat een tijdelijk onbereikbare quoted post de cache niet leegt).

Changed files:
src/config/database.js

  • ap_timeline.quote_json kolom

src/services/ActivityPubService.js

  • quoteHrefOf(note): quoted-post-URL uit object-quote of quote-rel Link
  • resolveQuote(note): async snapshot {url, author, content, published, media}
  • timelineQuote(quoteJson): snapshot terug voor de inbox-read
  • inbound (fire-and-forget), backfill (await), self-heal v11 (await + COALESCE)
  • timelineQuote in de default-export

src/routes/activitypub.js

  • inbox-read serveert shaer:quote (de geresolvede snapshot)

test/object-links.test.js

  • quoteHrefOf (object-quote + quote-rel Link + geen) en timelineQuote round-trip

remarks: 180 tests groen (was 178). resolveQuote zelf is netwerk, niet in de unit-test.

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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • test/object-links.test.js

    rcc3cf7d r6fd0e20  
    66const dbMod = await import('../src/config/database.js');
    77dbMod.initializeDatabase();
    8 const { extractObjectLinkTags, timelineObjectLinks, extractQuoteUrl, extractLinkJson } = await import('../src/services/ActivityPubService.js');
    9 const AP = { extractObjectLinkTags, timelineObjectLinks, extractQuoteUrl, extractLinkJson };
     8const { extractObjectLinkTags, timelineObjectLinks, extractQuoteUrl, extractLinkJson, quoteHrefOf, timelineQuote } = await import('../src/services/ActivityPubService.js');
     9const AP = { extractObjectLinkTags, timelineObjectLinks, extractQuoteUrl, extractLinkJson, quoteHrefOf, timelineQuote };
    1010
    1111test('extractObjectLinkTags keeps AS2-profiled ld+json and activity+json Links; drops plain links and mentions', () => {
     
    5959  assert.equal(arr[0].href, 'https://s/objects/9');
    6060});
     61
     62// FEP-044f embedded quote card: the quoted-post URL feeds the resolver.
     63test('quoteHrefOf reads an object-level quote or a quote-rel FEP-e232 Link', () => {
     64  assert.equal(AP.quoteHrefOf({ quoteUrl: 'https://s/q1' }), 'https://s/q1');
     65  assert.equal(AP.quoteHrefOf({
     66    tag: [{ type: 'Link', href: 'https://s/q2', rel: 'https://misskey-hub.net/ns#_misskey_quote' }],
     67  }), 'https://s/q2');
     68  assert.equal(AP.quoteHrefOf({ content: 'plain, no quote' }), null);
     69  // a plain (non-quote) FEP-e232 reference is not a quote target
     70  assert.equal(AP.quoteHrefOf({ tag: [{ type: 'Link', href: 'https://s/ref', rel: 'mention' }] }), null);
     71});
     72
     73test('timelineQuote round-trips the stored snapshot; junk → undefined', () => {
     74  const snap = JSON.stringify({ url: 'https://s/q', author: { name: 'A', handle: '@a@s', icon: null }, content: '<p>hi</p>' });
     75  const back = AP.timelineQuote(snap);
     76  assert.equal(back.url, 'https://s/q');
     77  assert.equal(back.author.handle, '@a@s');
     78  assert.equal(AP.timelineQuote(null), undefined);
     79  assert.equal(AP.timelineQuote('not json'), undefined);
     80});
Note: See TracChangeset for help on using the changeset viewer.