mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 16:02:26 +00:00
Two replaceable-event (NIP-33/kind 30078) lifecycle fixes found auditing edit/delete/tombstone: 1. created_at monotonicity. Edits and delete tombstones used bare Date.now(), with no guarantee of being newer than the version they replace. NIP-01 keeps the highest created_at (ties -> lowest id), so a device clock that's behind — or a second edit within the same second — left relays serving the OLD version: edits silently didn't apply, and (worse) deletions didn't actually remove the video for other clients (the local in-memory tombstone masked the failure locally). The edit and revert/delete builders now use max(now, baseEvent.created_at + 1). created_at is threaded through revertVideo and _softDeleteVersions so the bulk-delete path keeps it too. 2. Legacy tombstone key mismatch. The deletion builder synthesizes videoRootId = "LEGACY:<pubkey>:<dTag>" for legacy videos, so its tombstone keyed as "ROOT:LEGACY:..." while the original legacy event (no videoRootId) keyed as "<pubkey>:<dTag>" — the tombstone never matched the zombie it was meant to suppress. getActiveKey now treats a "LEGACY:"-prefixed root as non-distinct and falls through to the pubkey:dTag identity, so both match. Verified sound and unchanged: d-tag + videoRootId preservation on edit, ownership/version checks, dual delete (NIP-09 kind-5 + per-d-tag soft-delete), and the tombstone guard wired across all ingestion paths. Tests: edit/tombstone created_at is bumped above a future-dated base (clock-skew) and uses wall-clock for a past base; a legacy deletion shares the original event's key. Both fixes mutation-verified. Co-Authored-By: Claude Opus 4.8 <[email protected]>
85 lines
2.6 KiB
JavaScript
85 lines
2.6 KiB
JavaScript
/**
|
|
* @file tests/nostr/utils.test.mjs
|
|
* @description Unit tests for js/nostr/utils.js
|
|
*/
|
|
|
|
import { test, describe } from 'node:test';
|
|
import assert from 'node:assert';
|
|
import { getActiveKey } from '../../js/nostr/utils.js';
|
|
|
|
describe('js/nostr/utils.js', () => {
|
|
describe('getActiveKey', () => {
|
|
test('returns ROOT key when videoRootId is present', () => {
|
|
const video = {
|
|
videoRootId: 'root-123',
|
|
id: 'note-456',
|
|
pubkey: 'pk1',
|
|
tags: [['d', 'd-tag-val']]
|
|
};
|
|
const key = getActiveKey(video);
|
|
assert.strictEqual(key, 'ROOT:root-123');
|
|
});
|
|
|
|
test('returns pubkey:d key when videoRootId is missing but d-tag exists', () => {
|
|
const video = {
|
|
id: 'note-456',
|
|
pubkey: 'pk1',
|
|
tags: [['d', 'd-tag-val']]
|
|
};
|
|
const key = getActiveKey(video);
|
|
assert.strictEqual(key, 'pk1:d-tag-val');
|
|
});
|
|
|
|
test('returns LEGACY key when videoRootId and d-tag are missing', () => {
|
|
const video = {
|
|
id: 'note-456',
|
|
pubkey: 'pk1',
|
|
tags: [['t', 'hashtag']]
|
|
};
|
|
const key = getActiveKey(video);
|
|
assert.strictEqual(key, 'LEGACY:note-456');
|
|
});
|
|
|
|
test('handles missing tags array gracefully (returns LEGACY)', () => {
|
|
const video = {
|
|
id: 'note-789',
|
|
pubkey: 'pk2'
|
|
};
|
|
const key = getActiveKey(video);
|
|
assert.strictEqual(key, 'LEGACY:note-789');
|
|
});
|
|
|
|
test('handles empty tags array gracefully (returns LEGACY)', () => {
|
|
const video = {
|
|
id: 'note-789',
|
|
pubkey: 'pk2',
|
|
tags: []
|
|
};
|
|
const key = getActiveKey(video);
|
|
assert.strictEqual(key, 'LEGACY:note-789');
|
|
});
|
|
|
|
test('a synthesized LEGACY: root keys the same as the bare legacy event', () => {
|
|
// The deletion builder synthesizes videoRootId = "LEGACY:<pubkey>:<dTag>"
|
|
// for legacy videos. Its tombstone must map to the SAME key as the
|
|
// original legacy event (which has no videoRootId), or the zombie is never
|
|
// suppressed.
|
|
const original = { id: 'note-1', pubkey: 'pk1', tags: [['d', 'd-val']] };
|
|
const deletion = {
|
|
id: 'note-2',
|
|
pubkey: 'pk1',
|
|
tags: [['d', 'd-val']],
|
|
videoRootId: 'LEGACY:pk1:d-val',
|
|
};
|
|
const originalKey = getActiveKey(original);
|
|
const deletionKey = getActiveKey(deletion);
|
|
assert.strictEqual(originalKey, 'pk1:d-val');
|
|
assert.strictEqual(
|
|
deletionKey,
|
|
originalKey,
|
|
'legacy deletion tombstone must share the original event key',
|
|
);
|
|
});
|
|
});
|
|
});
|