source: Klonkt/test/c2s-contacts.test.js@ 7922694

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

Feature: owner followers/following carry name + avatar (shaer-aa3)

The C2S owner view of followers and following returned bare actor
URIs, so a client could only show ids. Each entry is now an AS2 actor
reference { id, type: Person, name, preferredUsername, icon } built
from the best cached display Klonkt already holds: the follower's own
row (now cached at Follow time), then ap_following, interactions,
timeline, mentions, falling back to a handle derived from the URI.
Priority is the set display name, then the chosen username, then the
id. ap_followers gains name/handle/icon, populated when an inbound
Follow is accepted (fetchActor already runs there).

Changed files:
src/config/database.js

  • additive columns ap_followers.name/handle/icon

src/services/ActivityPubService.js

  • cache follower display on inbound Follow
  • actorDisplay(slug, uri): best cached display across the caches
  • buildActorRef(slug, uri): AS2 actor reference with display

src/routes/activitypub.js

  • owner followers/following map URIs through buildActorRef

New file:
test/c2s-contacts.test.js

  • name/username/id priority + interaction-cache fallback

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

  • Property mode set to 100644
File size: 2.2 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});
Note: See TracBrowser for help on using the repository browser.