| 1 | // Ontdekking: host-meta erbij, en WebFinger coulant in wat hij accepteert.
|
|---|
| 2 | //
|
|---|
| 3 | // Aanleiding (7/8 augustus): Funkwhale vond dev pas via zijn eigen "search via
|
|---|
| 4 | // the fediverse". Onze kant antwoordde correct, maar twee deuren stonden dicht
|
|---|
| 5 | // die een andere client wel gebruikt: host-meta gaf 404, en een resource zonder
|
|---|
| 6 | // `acct:` gaf 400 terwijl we prima wisten wie er bedoeld werd.
|
|---|
| 7 | import { test } from 'node:test';
|
|---|
| 8 | import assert from 'node:assert/strict';
|
|---|
| 9 |
|
|---|
| 10 | process.env.DATABASE_PATH = ':memory:';
|
|---|
| 11 | process.env.PUBLIC_BASE_URL = 'https://test.example';
|
|---|
| 12 |
|
|---|
| 13 | const dbMod = await import('../src/config/database.js');
|
|---|
| 14 | const db = dbMod.default;
|
|---|
| 15 | dbMod.initializeDatabase();
|
|---|
| 16 | const express = (await import('express')).default;
|
|---|
| 17 | const routes = (await import('../src/routes/activitypub.js')).default;
|
|---|
| 18 |
|
|---|
| 19 | db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)')
|
|---|
| 20 | .run('u1', 'u1', 'u1@t', 'x', 'god');
|
|---|
| 21 | db.prepare('INSERT INTO sites (id, slug, title, owner_id, is_public, is_primary) VALUES (?,?,?,?,1,1)')
|
|---|
| 22 | .run('s1', 'band', 'De Band', 'u1');
|
|---|
| 23 |
|
|---|
| 24 | const app = express(); app.use(routes);
|
|---|
| 25 | const server = app.listen(0);
|
|---|
| 26 | await new Promise((r) => server.once('listening', r));
|
|---|
| 27 | const base = `http://127.0.0.1:${server.address().port}`;
|
|---|
| 28 | const wf = async (resource) => {
|
|---|
| 29 | const r = await fetch(`${base}/.well-known/webfinger?resource=${encodeURIComponent(resource)}`);
|
|---|
| 30 | return { status: r.status, type: r.headers.get('content-type'), body: r.status === 200 ? await r.json() : null };
|
|---|
| 31 | };
|
|---|
| 32 |
|
|---|
| 33 | test('de nette vorm blijft werken, en het antwoord is canoniek', async () => {
|
|---|
| 34 | const { status, type, body } = await wf('acct:band@test.example');
|
|---|
| 35 | assert.equal(status, 200);
|
|---|
| 36 | assert.match(type, /application\/jrd\+json/);
|
|---|
| 37 | assert.equal(body.subject, 'acct:band@test.example');
|
|---|
| 38 | assert.equal(body.links.find((l) => l.rel === 'self').href, 'https://test.example/ap/users/band');
|
|---|
| 39 | });
|
|---|
| 40 |
|
|---|
| 41 | test('zonder acct: mag ook -- en levert hetzelfde canonieke antwoord', async () => {
|
|---|
| 42 | const { status, body } = await wf('band@test.example');
|
|---|
| 43 | assert.equal(status, 200);
|
|---|
| 44 | assert.equal(body.subject, 'acct:band@test.example', 'een slordige vraag geeft geen slordig antwoord');
|
|---|
| 45 | });
|
|---|
| 46 |
|
|---|
| 47 | test('met het apenstaartje dat mensen intypen', async () => {
|
|---|
| 48 | const { status, body } = await wf('@band@test.example');
|
|---|
| 49 | assert.equal(status, 200);
|
|---|
| 50 | assert.equal(body.subject, 'acct:band@test.example');
|
|---|
| 51 | });
|
|---|
| 52 |
|
|---|
| 53 | test('de actor-URI blijft een 400 -- vastgelegd in webfinger-bare-host', async () => {
|
|---|
| 54 | // Niet vergeten maar bewust: die keuze staat elders vast en draaien we niet
|
|---|
| 55 | // om als bijvangst van een coulance-fix.
|
|---|
| 56 | assert.equal((await wf('https://test.example/ap/users/band')).status, 400);
|
|---|
| 57 | });
|
|---|
| 58 |
|
|---|
| 59 | test('echte onzin blijft een 400, een onbekende gebruiker een 404', async () => {
|
|---|
| 60 | assert.equal((await wf('kaas')).status, 400);
|
|---|
| 61 | assert.equal((await wf('')).status, 400);
|
|---|
| 62 | assert.equal((await wf('acct:bestaatniet@test.example')).status, 404);
|
|---|
| 63 | });
|
|---|
| 64 |
|
|---|
| 65 | test('host-meta wijst naar de webfinger-sjabloon (XRD)', async () => {
|
|---|
| 66 | const r = await fetch(`${base}/.well-known/host-meta`);
|
|---|
| 67 | assert.equal(r.status, 200);
|
|---|
| 68 | assert.match(r.headers.get('content-type'), /application\/xrd\+xml/);
|
|---|
| 69 | const xml = await r.text();
|
|---|
| 70 | assert.match(xml, /rel="lrdd"/);
|
|---|
| 71 | assert.match(xml, /template="https:\/\/test\.example\/\.well-known\/webfinger\?resource=\{uri\}"/);
|
|---|
| 72 | });
|
|---|
| 73 |
|
|---|
| 74 | test('en in JSON, want beide vormen worden gevraagd', async () => {
|
|---|
| 75 | const r = await fetch(`${base}/.well-known/host-meta.json`);
|
|---|
| 76 | assert.equal(r.status, 200);
|
|---|
| 77 | assert.match(r.headers.get('content-type'), /application\/jrd\+json/);
|
|---|
| 78 | const j = await r.json();
|
|---|
| 79 | assert.equal(j.links[0].rel, 'lrdd');
|
|---|
| 80 | assert.match(j.links[0].template, /\{uri\}$/);
|
|---|
| 81 | });
|
|---|
| 82 |
|
|---|
| 83 | test.after(() => server.close());
|
|---|