source: Klonkt/test/c2s-contacts.test.js@ 30871c1

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

Feature: enrichment is opt-in via Prefer (FEP-9876 conformance)

Klonkt is the reference implementation of FEP-9876. The owner
followers/following collections now enrich member representations only
when the client asks with Prefer: return=representation (RFC 7240),
echo Preference-Applied and always set Vary: Prefer; the default is
bare id strings, so the collections match the AP norm and existing
consumers are unaffected. The Prefer predicate is a pure, tested
function.

Changed files:
src/services/ActivityPubService.js

  • prefersEnriched(preferHeader): pure Prefer detector, exported

src/routes/activitypub.js

  • owner followers/following: enrich only on the preference, set Preference-Applied + Vary: Prefer

test/c2s-contacts.test.js

  • prefersEnriched cases

docs/shaer-c2s-api.md

  • document the Prefer opt-in

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

  • Property mode set to 100644
File size: 2.8 KB
Line 
1// Owner followers/following carry display (name + avatar), not bare URIs
2// (shaer-aa3): name -> preferredUsername -> id.
3import { test } from 'node:test';
4import assert from 'node:assert/strict';
5
6process.env.DATABASE_PATH = ':memory:';
7process.env.PUBLIC_BASE_URL = 'https://test.example';
8
9const dbMod = await import('../src/config/database.js');
10const db = dbMod.default;
11dbMod.initializeDatabase();
12const AP = (await import('../src/services/ActivityPubService.js')).default;
13
14test('a followed account resolves to its cached name + icon', () => {
15 db.prepare(`INSERT INTO ap_following (slug, actor_uri, handle, name, icon, status) VALUES (?,?,?,?,?,?)`)
16 .run('me', 'https://r.test/u/anna', '@anna@r.test', 'Anna de Vries', 'https://r.test/anna.png', 'accepted');
17 const ref = AP.buildActorRef('me', 'https://r.test/u/anna');
18 assert.equal(ref.id, 'https://r.test/u/anna');
19 assert.equal(ref.name, 'Anna de Vries');
20 assert.equal(ref.preferredUsername, 'anna');
21 assert.equal(ref.icon.url, 'https://r.test/anna.png');
22});
23
24test('a follower with cached display shows it', () => {
25 db.prepare(`INSERT INTO ap_followers (slug, actor_uri, name, handle, icon) VALUES (?,?,?,?,?)`)
26 .run('me', 'https://q.test/u/ben', 'Ben', '@ben@q.test', 'https://q.test/ben.png');
27 const ref = AP.buildActorRef('me', 'https://q.test/u/ben');
28 assert.equal(ref.name, 'Ben');
29 assert.equal(ref.preferredUsername, 'ben');
30});
31
32test('an unknown actor falls back to id + derived username, no name', () => {
33 const ref = AP.buildActorRef('me', 'https://x.test/users/cas');
34 assert.equal(ref.id, 'https://x.test/users/cas');
35 assert.equal(ref.name, undefined); // no invented name
36 assert.equal(ref.preferredUsername, 'cas'); // from the handle
37});
38
39test('interaction cache is used when a follower has no own display row', () => {
40 db.prepare(`INSERT INTO ap_followers (slug, actor_uri) VALUES ('me','https://r.test/u/dana')`).run();
41 db.prepare(`INSERT INTO ap_interactions (kind, post_id, actor_uri, actor_name, actor_handle, actor_icon, created_at)
42 VALUES ('like','p','https://r.test/u/dana','Dana','@dana@r.test','https://r.test/dana.png',CURRENT_TIMESTAMP)`).run();
43 const ref = AP.buildActorRef('me', 'https://r.test/u/dana');
44 assert.equal(ref.name, 'Dana');
45});
46
47test('prefersEnriched detects the Prefer opt-in (FEP-9876)', () => {
48 assert.equal(AP.prefersEnriched('return=representation'), true);
49 assert.equal(AP.prefersEnriched('return=representation; handling=lenient'), true);
50 assert.equal(AP.prefersEnriched('foo, return=representation'), true);
51 assert.equal(AP.prefersEnriched('return=minimal'), false);
52 assert.equal(AP.prefersEnriched('returnx=representation'), false);
53 assert.equal(AP.prefersEnriched(''), false);
54 assert.equal(AP.prefersEnriched(undefined), false);
55});
Note: See TracBrowser for help on using the repository browser.