source: Klonkt/test/object-links.test.js@ cc3cf7d

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

FEP-044f: object-level quotes (quoteUrl / _misskey_quote) ook als quote-link serveren

De echte reden dat quotes niet toonden: bijna geen enkele instance zet een quote
als FEP-e232 Link-tag. FEP-044f zegt dat een quote meestal een object-property is
(quote / quoteUrl / quoteUri / _misskey_quote). We keken alleen naar de tag, dus
we vonden 0 quotes (self-heal v9: 0/40 overal).

Nu normaliseren we elke quote-vorm naar één FEP-e232-vormige Link (rel
_misskey_quote) en bewaren die in link_json, naast echte FEP-e232 Link-tags
(gededupliceerd op href). De client-parser markeert rel met "quote" al als
isQuote, dus de chip verschijnt zonder client-wijziging.

Self-heal v9 -> v10 zodat bestaande timeline-rijen opnieuw langs de detectie gaan
(v9 had zich al klaar-gemarkeerd).

Changed files:
src/services/ActivityPubService.js

  • extractQuoteUrl(note): leest quote/quoteUrl/quoteUri/_misskey_quote (string of embedded object)
  • extractLinkJson(note): FEP-e232 Link-tags + genormaliseerde object-quote, deduped
  • inbound Create, outbox-backfill en self-heal gebruiken nu extractLinkJson(note)
  • SELFHEAL_VERSION 9 -> 10

test/object-links.test.js

  • 3 tests: extractQuoteUrl (4 vormen + embedded), normalisatie naar Link, dedup met echte tag

remarks: 178 tests groen (was 175).

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

  • Property mode set to 100644
File size: 3.3 KB
Line 
1// FEP-e232: Klonkt keeps inbound object-link (quote/ref) tags and serves them on the C2S inbox read.
2import { test } from 'node:test';
3import assert from 'node:assert/strict';
4process.env.DATABASE_PATH = ':memory:';
5process.env.PUBLIC_BASE_URL = 'https://test.example';
6const dbMod = await import('../src/config/database.js');
7dbMod.initializeDatabase();
8const { extractObjectLinkTags, timelineObjectLinks, extractQuoteUrl, extractLinkJson } = await import('../src/services/ActivityPubService.js');
9const AP = { extractObjectLinkTags, timelineObjectLinks, extractQuoteUrl, extractLinkJson };
10
11test('extractObjectLinkTags keeps AS2-profiled ld+json and activity+json Links; drops plain links and mentions', () => {
12 const tag = [
13 { type: 'Link', mediaType: 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"', href: 'https://s/objects/1', name: '#1374' },
14 { type: 'Link', mediaType: 'application/activity+json', href: 'https://s/objects/2', rel: ['https://misskey-hub.net/ns#_misskey_quote'] },
15 { type: 'Link', mediaType: 'text/html', href: 'https://plain.example/page' }, // plain hyperlink → dropped
16 { type: 'Mention', href: 'https://s/u/x' },
17 ];
18 const json = AP.extractObjectLinkTags(tag);
19 assert.ok(json);
20 const back = AP.timelineObjectLinks(json);
21 assert.equal(back.length, 2);
22 assert.equal(back[0].href, 'https://s/objects/1');
23 assert.equal(back[0].name, '#1374');
24 assert.equal(back[1].mediaType, 'application/activity+json');
25});
26
27test('no object links → null / undefined (nothing served)', () => {
28 assert.equal(AP.extractObjectLinkTags([{ type: 'Link', mediaType: 'text/html', href: 'https://x' }]), null);
29 assert.equal(AP.extractObjectLinkTags([{ type: 'Hashtag', name: '#hi' }]), null);
30 assert.equal(AP.timelineObjectLinks(null), undefined);
31 assert.equal(AP.timelineObjectLinks('not json'), undefined);
32});
33
34// FEP-044f: object-level quote properties are the common representation.
35test('extractQuoteUrl reads quote / quoteUrl / quoteUri / _misskey_quote (string or embedded object)', () => {
36 assert.equal(AP.extractQuoteUrl({ quote: 'https://s/objects/9' }), 'https://s/objects/9');
37 assert.equal(AP.extractQuoteUrl({ quoteUrl: 'https://s/q1' }), 'https://s/q1');
38 assert.equal(AP.extractQuoteUrl({ quoteUri: 'https://s/q2' }), 'https://s/q2');
39 assert.equal(AP.extractQuoteUrl({ _misskey_quote: 'https://s/q3' }), 'https://s/q3');
40 assert.equal(AP.extractQuoteUrl({ quote: { type: 'Link', href: 'https://s/q4' } }), 'https://s/q4');
41 assert.equal(AP.extractQuoteUrl({ content: 'no quote' }), null);
42});
43
44test('extractLinkJson normalises an object-level quote into one FEP-e232 Link (rel _misskey_quote)', () => {
45 const json = AP.extractLinkJson({ content: 'nice', quoteUrl: 'https://s/objects/9' });
46 const arr = AP.timelineObjectLinks(json);
47 assert.equal(arr.length, 1);
48 assert.equal(arr[0].href, 'https://s/objects/9');
49 assert.ok(arr[0].rel.some((r) => r.includes('quote')));
50});
51
52test('extractLinkJson merges a real FEP-e232 Link with an object-level quote, deduped by href', () => {
53 const note = {
54 tag: [{ type: 'Link', mediaType: 'application/activity+json', href: 'https://s/objects/9' }],
55 quoteUrl: 'https://s/objects/9', // same target → not duplicated
56 };
57 const arr = AP.timelineObjectLinks(AP.extractLinkJson(note));
58 assert.equal(arr.length, 1);
59 assert.equal(arr[0].href, 'https://s/objects/9');
60});
Note: See TracBrowser for help on using the repository browser.