Changeset bfa6fa1 in Klonkt for test/move-actor.test.js


Ignore:
Timestamp:
07/31/2026 06:02:01 PM (6 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
72ec6a4
Parents:
0ca7e9a4
Message:

Account-verhuizingen slice 3: het wegwijsbord (movedTo + homepage-link)

Het sluitstuk van shaer-0j2's federatiekant. De oude Klonkt blijft
staan en ZEGT waar hij heen ging, op twee plekken.

Voor servers: het actor-document draagt movedTo zodra moved_to gezet is
(FEP-7628: een verhuisd account moet dat publiceren en geldt als
inactief). Dat is precies waarom de oude actor online blijft: wie de
Move-activity miste (server offline, latere bezoeker) leert de
verhuizing alsnog door ons op te halen. Een kolomwaarde die geen URL is
haalt de draad nooit.

Voor mensen: de homepage opent met een groot wegwijsbord boven de feed,
met de handle van het nieuwe adres als grote link. Boven de posts met
opzet: niemand hoort een dode tijdlijn door te scrollen om te ontdekken
dat we weg zijn.

En de verhuizing zelf vertelt het nu twee keer: naast de Move gaat er
een Update van de actor naar elke volger-inbox, gebouwd uit de VERSE
rij, zodat het bord ook staat als de Move onderweg verdwijnt (FEP-7628
SHOULD).

Changed files:
src/services/ActivityPubService.js

  • buildActor: movedTo bij een geldige moved_to
  • moveAccount: Update(actor) naast de Move, per inbox

src/routes/posts.js

  • home: movedTo + leesbaar label (actorDisplay) naar de view

src/views/pages/home.ejs

  • wegwijsbord boven de feed, typeof-guard voor deploy-races

src/assets/css/style.css

  • .moved-banner: accentrand, grote link, tekst breekt netjes af

src/services/i18n.js

  • phome.moved_lead/_hint in nl/en/de

test/move-actor.test.js

  • movedTo aan/uit/rommel; Update draagt het bord naast de Move
  • slice-2-test telt nu Moves apart (Update rijdt mee)

remarks: banner visueel gecontroleerd (mobiel en desktop) door het echte
templateblok met de echte CSS te renderen; suite 372 groen.

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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • test/move-actor.test.js

    r0ca7e9a4 rbfa6fa1  
    163163  assert.equal(r.target, 'https://elders.example/users/nieuw');
    164164  assert.equal(db.prepare("SELECT moved_to FROM sites WHERE slug = 'radio'").get().moved_to, 'https://elders.example/users/nieuw');
    165   assert.equal(delivered.length, 2, 'both follower inboxes');
    166   for (const d of delivered) {
    167     assert.equal(d.activity.type, 'Move');
     165  // Slice 3 added the Update(actor) alongside it, so count the Moves.
     166  const moves = delivered.filter((d) => d.activity.type === 'Move');
     167  assert.equal(moves.length, 2, 'one Move per follower inbox');
     168  for (const d of moves) {
    168169    assert.equal(d.activity.object, ME_RADIO);
    169170    assert.equal(d.activity.target, 'https://elders.example/users/nieuw');
    170171  }
    171   assert.ok(delivered.some((d) => d.inbox === 'https://b.example/shared'), 'shared inbox preferred');
    172 });
     172  assert.ok(moves.some((d) => d.inbox === 'https://b.example/shared'), 'shared inbox preferred');
     173});
     174
     175// ── Slice 3: the signpost ──────────────────────────────────────────
     176// The old actor stays online and SAYS where it went (FEP-7628 movedTo), and
     177// the followers are told with an Update alongside the Move.
     178
     179test('a moved account carries movedTo on its actor; an unmoved one does not', () => {
     180  const fresh = db.prepare("SELECT * FROM sites WHERE slug = 'blog'").get();
     181  assert.equal('movedTo' in buildActor('https://test.example', fresh), false, 'no move, no signpost');
     182  const moved = { ...fresh, moved_to: 'https://elders.example/users/nieuw' };
     183  assert.equal(buildActor('https://test.example', moved).movedTo, 'https://elders.example/users/nieuw');
     184  // Junk in the column never reaches the wire.
     185  assert.equal('movedTo' in buildActor('https://test.example', { ...fresh, moved_to: 'niet-een-url' }), false);
     186});
     187
     188test('the move tells the followers twice: an Update carrying movedTo, and the Move', async () => {
     189  db.prepare("DELETE FROM ap_followers WHERE slug = 'radio'").run();
     190  db.prepare("UPDATE sites SET moved_to = NULL WHERE slug = 'radio'").run();
     191  db.prepare("INSERT INTO ap_followers (slug, actor_uri, inbox) VALUES ('radio', 'https://c.example/u/c', 'https://c.example/inbox')").run();
     192  const sent = [];
     193  const r = await moveAccount(radioSite(), 'https://elders.example/users/nieuw', {
     194    fetchActorFn: async () => ({ id: 'https://elders.example/users/nieuw', inbox: 'https://elders.example/inbox', alsoKnownAs: [ME_RADIO] }),
     195    deliverFn: async (slug, inbox, activity) => { sent.push(activity); },
     196  });
     197  assert.equal(r.ok, true);
     198  const update = sent.find((a) => a.type === 'Update');
     199  const move = sent.find((a) => a.type === 'Move');
     200  assert.ok(update, 'an Update is sent');
     201  assert.ok(move, 'the Move is still sent');
     202  assert.equal(update.object.movedTo, 'https://elders.example/users/nieuw', 'the Update carries the signpost');
     203  assert.equal(update.object.id, ME_RADIO, 'and it is OUR actor being updated');
     204});
Note: See TracChangeset for help on using the changeset viewer.