source: Klonkt/test/mentions.test.js

main
Last change on this file was f1956d7, checked in by roboburr <roboburr@…>, 2 months ago

feat(fediverse): inline @mentions in posts (link + notify)

Typing @user@host in a post now federates as a real Mastodon-style mention: the
handle is linked to the actor, added to the note's Mention tags + cc, and the note
is delivered to the mentioned person's inbox so they're notified — even if they
don't follow the site. Mirrors the existing reply-mention flow (which resolved
mentions but posts didn't). On-site the post text is unchanged for now.

  • src/services/ActivityPubService.js — buildNote emits Mention tags + adds the mentioned actors to cc (from already-linked content); deliverCreate/deliverUpdate resolve inline mentions at send time (WebFinger), link the note content, and add the mentioned inboxes to the delivery set (so a mention reaches non-followers too).
  • test/mentions.test.js — Mention tag shape, cc addressing, and the no-mention no-op.
  • CHANGELOG(.nl/.de).md — "Mention people in a post" under Unreleased.

Co-Authored-By: Claude <noreply@…>

  • Property mode set to 100644
File size: 2.4 KB
Line 
1// Inline @mentions in posts — buildNote turns already-linked @user@host mentions into AS2
2// Mention tags and addresses the mentioned actor in cc (so Mastodon notifies them). The
3// WebFinger resolution/linking happens at delivery time; here we cover the sync build step.
4import { test } from 'node:test';
5import assert from 'node:assert/strict';
6
7process.env.DATABASE_PATH = ':memory:';
8process.env.PUBLIC_BASE_URL = 'https://test.example';
9
10const dbMod = await import('../src/config/database.js');
11const db = dbMod.default;
12dbMod.initializeDatabase();
13const AP = (await import('../src/services/ActivityPubService.js')).default;
14
15const BASE = 'https://test.example';
16db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)').run('u1', 'u1', 'u1@test', 'x', 'god');
17db.prepare('INSERT INTO sites (id, slug, title, owner_id, is_primary) VALUES (?,?,?,?,?)').run('s1', 'demo', 'Demo', 'u1', 1);
18const site = db.prepare('SELECT * FROM sites WHERE id = ?').get('s1');
19site.primary_slug = 'demo';
20
21// A post whose content already carries a resolved mention link (as deliverCreate produces).
22const linked = '<p>hi <a href="https://mastodon.social/@bob" class="u-url mention" data-actor="https://mastodon.social/users/bob">@bob@mastodon.social</a></p>';
23const post = { id: 'm1', slug: 'x', title: '', content: linked, tags: '[]', created_at: '2026-01-01T00:00:00Z' };
24
25test('a mention becomes an AS2 Mention tag with the actor URI as href', () => {
26 const note = AP.buildNote(BASE, site, post);
27 const m = note.tag.find((t) => t.type === 'Mention');
28 assert.ok(m, 'note.tag has a Mention');
29 assert.equal(m.href, 'https://mastodon.social/users/bob');
30 assert.equal(m.name, '@bob@mastodon.social');
31});
32
33test('the mentioned actor is addressed in cc', () => {
34 const note = AP.buildNote(BASE, site, post);
35 assert.ok(note.cc.includes('https://mastodon.social/users/bob'), 'mentioned actor in cc');
36 assert.ok(note.cc.includes(`${BASE}/ap/users/demo/followers`), 'followers still in cc');
37});
38
39test('a post without mentions has no Mention tag and an unchanged cc', () => {
40 const plain = { id: 'm2', slug: 'y', title: '', content: '<p>just text</p>', tags: '[]', created_at: '2026-01-01T00:00:00Z' };
41 const note = AP.buildNote(BASE, site, plain);
42 assert.equal(note.tag.filter((t) => t.type === 'Mention').length, 0);
43 assert.deepEqual(note.cc, [`${BASE}/ap/users/demo/followers`]);
44});
Note: See TracBrowser for help on using the repository browser.