source: Klonkt/test/stats-bots.test.js@ 3ccca13

main
Last change on this file since 3ccca13 was cae8ded, checked in by Robin <roboburr@…>, 6 weeks ago

Een eerlijke User-Agent op al het uitgaande federatieverkeer

Robins vraag (31-7): welke UA gebruiken wij? Het federatieverkeer ging
met de kale Node-default de deur uit. safeFetch zet nu standaard
Klonkt/<versie> (+https://klonkt.com) op elk uitgaand verzoek; een
caller die zelf een User-Agent meegeeft (de EmbedResolver) wint. En het
spiegelbeeld-lek dicht: klonkt staat nu ook in de eigen bot-regex,
zodat een Klonkt-preview-fetch op een andere Klonkt niet als bezoeker
telt.

Changed files:
src/services/ActivityPubService.js

  • KLONKT_UA uit package.json-versie; safeFetch merge't hem als default in de headers

src/services/StatsService.js

  • BOT_RE + klonkt

test/stats-bots.test.js

  • de eigen embed-fetcher telt niet op een andere Klonkt

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

  • Property mode set to 100644
File size: 2.6 KB
Line 
1// Statistics count READERS, not servers (Robins vraag, 31-7). Fediverse
2// link-preview fetchers (Mastodon's http.rb and friends) hit the human post
3// URL when a post gets boosted; without these guards every instance counted
4// as a pageview AND a unique visitor.
5import { test } from 'node:test';
6import assert from 'node:assert/strict';
7
8process.env.DATABASE_PATH = ':memory:';
9process.env.PUBLIC_BASE_URL = 'https://test.example';
10
11const dbMod = await import('../src/config/database.js');
12const db = dbMod.default;
13dbMod.initializeDatabase();
14const Stats = await import('../src/services/StatsService.js');
15
16db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)')
17 .run('u1', 'robin', 'r@t', 'x', 'god');
18db.prepare('INSERT INTO sites (id, slug, title, owner_id, is_primary) VALUES (?,?,?,?,1)').run('s1', 'me', 'Me', 'u1');
19
20const pv = () => db.prepare('SELECT COALESCE(SUM(pageviews),0) AS n FROM stat_daily').get().n;
21const fakeReq = (headers = {}, ip = '203.0.113.7') => ({ ip, headers, session: {} });
22
23test('a human browser counts', () => {
24 const before = pv();
25 Stats.recordPageview('s1', fakeReq({ 'user-agent': 'Mozilla/5.0 (iPhone; like Mac OS X) Safari/605.1' }));
26 assert.equal(pv(), before + 1);
27});
28
29test('a fediverse preview fetcher does not count', () => {
30 const before = pv();
31 Stats.recordPageview('s1', fakeReq({ 'user-agent': 'http.rb/5.1.1 (Mastodon/4.2.10; +https://koffieengaar.nl/)' }));
32 Stats.recordPageview('s1', fakeReq({ 'user-agent': 'Pleroma 2.6.2; https://p.example <admin@p.example>' }));
33 Stats.recordPageview('s1', fakeReq({ 'user-agent': 'Misskey/2024.10 (https://m.example)' }));
34 assert.equal(pv(), before, 'server fetches are not readers');
35});
36
37test("a Klonkt's own embed-fetcher does not count on another Klonkt", () => {
38 const before = pv();
39 Stats.recordPageview('s1', fakeReq({ 'user-agent': 'Mozilla/5.0 (compatible; Klonkt/1.0; +https://klonkt.com)' }));
40 assert.equal(pv(), before);
41});
42
43test('a SIGNED request never counts, whatever the UA claims', () => {
44 const before = pv();
45 Stats.recordPageview('s1', fakeReq({ 'user-agent': 'Mozilla/5.0 totally-a-browser', signature: 'keyId="x",signature="y"' }));
46 assert.equal(pv(), before);
47});
48
49test('ActivityPub content-negotiation never counts either', () => {
50 const before = pv();
51 Stats.recordPageview('s1', fakeReq({ 'user-agent': 'Mozilla/5.0 totally-a-browser', accept: 'application/activity+json' }));
52 Stats.recordPageview('s1', fakeReq({ 'user-agent': 'Mozilla/5.0', accept: 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' }));
53 assert.equal(pv(), before);
54});
Note: See TracBrowser for help on using the repository browser.