| 1 | // Zwaaien ter plekke: de route moet twee soorten bezoekers bedienen.
|
|---|
| 2 | //
|
|---|
| 3 | // Met X-Requested-With: fetch antwoordt hij JSON en blijft de pagina staan;
|
|---|
| 4 | // zonder dat blijft het een gewoon formulier dat post en omleidt. Dat tweede
|
|---|
| 5 | // pad is geen restant maar de no-JS-weg, en het moet werken blijven.
|
|---|
| 6 | import { test } from 'node:test';
|
|---|
| 7 | import assert from 'node:assert/strict';
|
|---|
| 8 |
|
|---|
| 9 | process.env.DATABASE_PATH = ':memory:';
|
|---|
| 10 | process.env.PUBLIC_BASE_URL = 'https://test.example';
|
|---|
| 11 |
|
|---|
| 12 | const dbMod = await import('../src/config/database.js');
|
|---|
| 13 | const db = dbMod.default;
|
|---|
| 14 | dbMod.initializeDatabase();
|
|---|
| 15 |
|
|---|
| 16 | // De aflevering onderscheppen: deze test gaat over het HTTP-gedrag van de
|
|---|
| 17 | // route, niet over federatie. Wat er de deur uit gaat leggen we wel vast.
|
|---|
| 18 | const APmod = await import('../src/services/ActivityPubService.js');
|
|---|
| 19 | const AP = APmod.default;
|
|---|
| 20 | let bezorgd = [];
|
|---|
| 21 | let bezorgingLukt = true;
|
|---|
| 22 | AP.deliverDirectNote = async (site, opts) => { bezorgd.push(opts); return bezorgingLukt; };
|
|---|
| 23 |
|
|---|
| 24 | const express = (await import('express')).default;
|
|---|
| 25 | const session = (await import('express-session')).default;
|
|---|
| 26 | const routes = (await import('../src/routes/posts.js')).default;
|
|---|
| 27 |
|
|---|
| 28 | db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)')
|
|---|
| 29 | .run('u1', 'u1', 'u1@t', 'x', 'god');
|
|---|
| 30 | db.prepare('INSERT INTO sites (id, slug, title, owner_id, is_public, is_primary) VALUES (?,?,?,?,1,1)')
|
|---|
| 31 | .run('s1', 'band', 'De Band', 'u1');
|
|---|
| 32 |
|
|---|
| 33 | const app = express();
|
|---|
| 34 | app.use(session({ secret: 't', resave: false, saveUninitialized: false }));
|
|---|
| 35 | app.use((req, res, next) => {
|
|---|
| 36 | req.session.userId = 'u1';
|
|---|
| 37 | req.session.user = { id: 'u1', username: 'u1', role: 'god' };
|
|---|
| 38 | res.locals.site = db.prepare("SELECT * FROM sites WHERE id = 's1'").get();
|
|---|
| 39 | res.locals.siteUrlBase = '';
|
|---|
| 40 | res.locals.user = req.session.user;
|
|---|
| 41 | next();
|
|---|
| 42 | });
|
|---|
| 43 | app.use(routes);
|
|---|
| 44 | const server = app.listen(0);
|
|---|
| 45 | await new Promise((r) => server.once('listening', r));
|
|---|
| 46 | const base = `http://127.0.0.1:${server.address().port}`;
|
|---|
| 47 |
|
|---|
| 48 | const zwaai = (velden, headers = {}) => fetch(base + '/messages/quick-reply', {
|
|---|
| 49 | method: 'POST', redirect: 'manual',
|
|---|
| 50 | headers: { 'Content-Type': 'application/x-www-form-urlencoded', ...headers },
|
|---|
| 51 | body: new URLSearchParams(velden),
|
|---|
| 52 | });
|
|---|
| 53 |
|
|---|
| 54 | test('met de fetch-header: JSON terug, geen omleiding', async () => {
|
|---|
| 55 | bezorgd = []; bezorgingLukt = true;
|
|---|
| 56 | const r = await zwaai({ to: 'https://elders.example/users/kim', text: '👋' }, { 'X-Requested-With': 'fetch' });
|
|---|
| 57 | assert.equal(r.status, 200);
|
|---|
| 58 | assert.equal((r.headers.get('content-type') || '').split(';')[0], 'application/json');
|
|---|
| 59 | assert.deepEqual(await r.json(), { ok: true });
|
|---|
| 60 | assert.equal(bezorgd.length, 1);
|
|---|
| 61 | assert.equal(bezorgd[0].text, '👋');
|
|---|
| 62 | assert.equal(bezorgd[0].wave, true, 'het blijft een zwaai, geen gewoon bericht');
|
|---|
| 63 | });
|
|---|
| 64 |
|
|---|
| 65 | test('zonder die header blijft het de oude weg: omleiden met wave_sent', async () => {
|
|---|
| 66 | bezorgd = []; bezorgingLukt = true;
|
|---|
| 67 | const r = await zwaai({ to: 'https://elders.example/users/kim', text: 'Wat leuk!' });
|
|---|
| 68 | assert.equal(r.status, 302);
|
|---|
| 69 | assert.match(r.headers.get('location'), /\/messages\?success=wave_sent$/);
|
|---|
| 70 | assert.equal(bezorgd[0].text, 'Wat leuk!', 'de gekozen tekst, niet een vaste');
|
|---|
| 71 | });
|
|---|
| 72 |
|
|---|
| 73 | test('een zwaai zonder tekst wordt geweigerd -- de val bij meerdere verzendknoppen', async () => {
|
|---|
| 74 | // FormData neemt de aangeklikte knop niet mee; vergeet de module de submitter,
|
|---|
| 75 | // dan komt het verzoek hier ZONDER text aan. Dan hoort het te falen, niet een
|
|---|
| 76 | // leeg bericht te versturen.
|
|---|
| 77 | bezorgd = [];
|
|---|
| 78 | const r = await zwaai({ to: 'https://elders.example/users/kim' }, { 'X-Requested-With': 'fetch' });
|
|---|
| 79 | assert.equal(r.status, 400);
|
|---|
| 80 | assert.equal((await r.json()).ok, false);
|
|---|
| 81 | assert.equal(bezorgd.length, 0, 'er mag niets vertrokken zijn');
|
|---|
| 82 | });
|
|---|
| 83 |
|
|---|
| 84 | test('een onbruikbare ontvanger geeft 400 in JSON, en 302 zonder de header', async () => {
|
|---|
| 85 | bezorgd = [];
|
|---|
| 86 | const a = await zwaai({ to: 'geen-uri', text: '👋' }, { 'X-Requested-With': 'fetch' });
|
|---|
| 87 | assert.equal(a.status, 400);
|
|---|
| 88 | const b = await zwaai({ to: 'geen-uri', text: '👋' });
|
|---|
| 89 | assert.equal(b.status, 302);
|
|---|
| 90 | assert.match(b.headers.get('location'), /error=quickreply/);
|
|---|
| 91 | assert.equal(bezorgd.length, 0);
|
|---|
| 92 | });
|
|---|
| 93 |
|
|---|
| 94 | test('mislukte aflevering: geen valse bevestiging', async () => {
|
|---|
| 95 | bezorgd = []; bezorgingLukt = false;
|
|---|
| 96 | const r = await zwaai({ to: 'https://elders.example/users/kim', text: '👋' }, { 'X-Requested-With': 'fetch' });
|
|---|
| 97 | assert.equal(r.status, 400);
|
|---|
| 98 | assert.equal((await r.json()).ok, false);
|
|---|
| 99 | });
|
|---|
| 100 |
|
|---|
| 101 | test.after(() => server.close());
|
|---|