source: Klonkt/test/c2s-block.test.js

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

The AP blocked collection: the server blocklist drives Shaer's Orbit

Robins call: no separate client-side Orbit state; the block list on the
server is the source of truth. The actor now advertises the standard
ActivityPub blocked collection (spec 5.6) and the owner reads it over C2S;
Shaer hydrates "in Orbit" from it and keeps nothing locally.

Changed files:
src/services/ActivityPubService.js

  • buildActor: blocked: {id}/blocked (AP 5.6, owner-only GET)

src/routes/activitypub.js

  • GET /ap/users/:slug/blocked (bearer, owner-only): actor-kind blocks as an OrderedCollection of actor uris; domain blocks stay out (instance policy, not an Orbit member)

test/c2s-block.test.js

  • the actor advertises blocked; actor-kind items only

test/activitypub-as2.test.js

  • blocked added to the AS2 allowlist (a spec term, same family as liked/streams)

153 tests, all green.

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

  • Property mode set to 100644
File size: 2.3 KB
Line 
1// C2S Block / Undo(Block): Shaer "in Orbit" is a real server-side block.
2// The activity lands in ap_blocks (the Block tab) via the same blockTarget
3// the web UI uses; Undo(Block) releases it.
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
15db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)').run('u1', 'u1', 'u1@test', 'x', 'god');
16db.prepare('INSERT INTO sites (id, slug, title, owner_id, is_primary) VALUES (?,?,?,?,?)').run('s1', 'me', 'Me', 'u1', 1);
17const site = db.prepare('SELECT * FROM sites WHERE id = ?').get('s1');
18const user = { id: 'u1', username: 'u1' };
19
20const BULLY = 'https://r.test/u/bully';
21
22test('C2S Block lands in ap_blocks (the Block tab)', async () => {
23 const out = await AP.ingestOutboxActivity(site, user, { type: 'Block', object: BULLY });
24 assert.equal(out.status, 202);
25 assert.equal(out.url, BULLY);
26 const rows = AP.listBlocks('me');
27 assert.equal(rows.length, 1);
28 assert.equal(rows[0].target, BULLY);
29 assert.equal(rows[0].kind, 'actor');
30});
31
32test('C2S Undo(Block) releases it again', async () => {
33 const out = await AP.ingestOutboxActivity(site, user, {
34 type: 'Undo',
35 object: { type: 'Block', object: BULLY },
36 });
37 assert.equal(out.status, 202);
38 assert.equal(AP.listBlocks('me').length, 0);
39});
40
41test('Block without an object is a clean 400', async () => {
42 const out = await AP.ingestOutboxActivity(site, user, { type: 'Block' });
43 assert.equal(out.status, 400);
44});
45
46test('the actor advertises the blocked collection (AP 5.6)', () => {
47 site.primary_slug = 'me';
48 const actor = AP.buildActor('https://test.example', site);
49 assert.equal(actor.blocked, 'https://test.example/ap/users/me/blocked');
50});
51
52test('actor-kind blocks form the collection items; domain blocks stay out', async () => {
53 await AP.ingestOutboxActivity(site, user, { type: 'Block', object: BULLY });
54 await AP.blockTarget(site, 'nare-server.example'); // domain block
55 const items = AP.listBlocks('me').filter((b) => b.kind === 'actor').map((b) => b.target);
56 assert.deepEqual(items, [BULLY]);
57});
Note: See TracBrowser for help on using the repository browser.