fix(federation): close detached-account gaps from final review — presence/hydrate guards, ack re-detect clear, self-delete password (detach spec §4.3/§4.4/§4.6)
This commit is contained in:
@@ -149,6 +149,47 @@ describe('markPeerReset — detection-only reset routing', () => {
|
||||
expect(row.resolvedAt).toBeNull();
|
||||
});
|
||||
|
||||
it('re-detected reset clears a stale acknowledgedAt (dismissed card re-surfaces)', async () => {
|
||||
seedPeer();
|
||||
seedUser('stub-1', { passwordHash: STUB });
|
||||
seedUser('real-1', { passwordHash: '$2b$10$realbcrypthash' });
|
||||
|
||||
const { markPeerReset } = await import('./federationReset.js');
|
||||
|
||||
// First reset detected, then the admin dismisses (acknowledges) the card.
|
||||
markPeerReset('peer-1', ORIGIN, 'E0', 'E1');
|
||||
testDb.update(schema.federationResetEvents)
|
||||
.set({ acknowledgedAt: Date.now() })
|
||||
.where(eq(schema.federationResetEvents.origin, ORIGIN)).run();
|
||||
expect(testDb.select().from(schema.federationResetEvents)
|
||||
.where(eq(schema.federationResetEvents.origin, ORIGIN)).get()!.acknowledgedAt).not.toBeNull();
|
||||
|
||||
// The peer resets AGAIN before the first was resolved — a fresh batch is
|
||||
// detached and needs fresh admin attention, so the dismissal must clear.
|
||||
markPeerReset('peer-1', ORIGIN, 'E0', 'E2');
|
||||
expect(testDb.select().from(schema.federationResetEvents)
|
||||
.where(eq(schema.federationResetEvents.origin, ORIGIN)).get()!.acknowledgedAt).toBeNull();
|
||||
});
|
||||
|
||||
it('a resolved+acknowledged prior reset is re-armed (acknowledgedAt cleared) on a new reset', async () => {
|
||||
seedPeer();
|
||||
seedUser('stub-1', { passwordHash: STUB });
|
||||
|
||||
const { markPeerReset } = await import('./federationReset.js');
|
||||
markPeerReset('peer-1', ORIGIN, 'E0', 'E1');
|
||||
// Simulate the heal resolving the first reset AND the admin dismissing it.
|
||||
testDb.update(schema.federationResetEvents)
|
||||
.set({ resolvedAt: Date.now(), newEpoch: 'E1', acknowledgedAt: Date.now() })
|
||||
.where(eq(schema.federationResetEvents.origin, ORIGIN)).run();
|
||||
|
||||
// Brand-new reset lands (fresh-journal / onConflictDoUpdate branch).
|
||||
markPeerReset('peer-1', ORIGIN, 'E1', 'E2');
|
||||
const row = testDb.select().from(schema.federationResetEvents)
|
||||
.where(eq(schema.federationResetEvents.origin, ORIGIN)).get()!;
|
||||
expect(row.resolvedAt).toBeNull();
|
||||
expect(row.acknowledgedAt).toBeNull();
|
||||
});
|
||||
|
||||
it('matches home_instance stored as a full URL (defensive format match)', async () => {
|
||||
seedPeer();
|
||||
// Legacy straggler stored with the https:// prefix rather than bare domain.
|
||||
|
||||
@@ -113,9 +113,12 @@ export function markPeerReset(peerId: string, origin: string, deadEpoch: string,
|
||||
if (existing && existing.resolvedAt === null) {
|
||||
// Double-reset: keep the ORIGINAL dead_epoch + detected_at (the
|
||||
// incarnation already snapshotted), refresh counts only. Never overwrite
|
||||
// dead_epoch on an unresolved row.
|
||||
// dead_epoch on an unresolved row. Clear `acknowledged_at`: a re-detected
|
||||
// reset is a fresh event that detached a new batch and needs fresh admin
|
||||
// attention — a stale dismissal must not keep the disposition card hidden
|
||||
// (detach spec §4.6).
|
||||
tx.update(schema.federationResetEvents)
|
||||
.set({ stubCount, orphanedAccountCount })
|
||||
.set({ stubCount, orphanedAccountCount, acknowledgedAt: null })
|
||||
.where(eq(schema.federationResetEvents.origin, origin))
|
||||
.run();
|
||||
} else {
|
||||
@@ -140,6 +143,10 @@ export function markPeerReset(peerId: string, origin: string, deadEpoch: string,
|
||||
resolvedAt: null,
|
||||
stubCount,
|
||||
orphanedAccountCount,
|
||||
// Re-arm the admin surface: a fresh reset on a previously
|
||||
// resolved+dismissed origin must clear the old dismissal so the
|
||||
// new detached batch's disposition card re-surfaces (detach §4.6).
|
||||
acknowledgedAt: null,
|
||||
},
|
||||
})
|
||||
.run();
|
||||
|
||||
Reference in New Issue
Block a user