test(nip-fi): fix four PG tests shadowed by DuplicateEvent

Four orchestrator_postgres_tests used make_kind9_event for both the
first and second calls.  Nostr event IDs are deterministic over
(pubkey, created_at, kind, tags, content), so two calls with identical
inputs within the same second produce the same event ID.  Step 3c
(event duplicate precheck) fires before the intended guard — lifecycle
guard, epoch guard, or proof-replay uniqueness — causing the tests to
assert the wrong error.

Fix: add make_kind9_event_msg(keys, channel_id, content) helper and
use distinct content strings for every second call that is not
deliberately testing step-3c behaviour.

  orchestrator_pg_concurrent_enrollment_converges: format content with
    task index so each spawned task produces a unique event.
  orchestrator_pg_lifecycle_advance_rejects_final_use: second call uses
    "lifecycle-advance-second-msg" → NoActiveBinding or BindingRetired
    fires as intended.
  orchestrator_pg_epoch_guard_catches_zero_row_update: second call uses
    "epoch-guard-second-msg" → epoch UPDATE guard runs and passes.
  orchestrator_pg_proof_replay_is_rejected: second call uses
    "proof-replay-second-msg" → step-3d proof-owner mismatch fires
    (ProofReplayed) instead of step-3c DuplicateEvent.

After this change all 22 NIP-FI ignored PG tests pass (15/15
orchestrator_postgres_tests, 7/7 postgres_tests).

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
This commit is contained in:
Hayt
2026-09-01 16:30:15 -04:00
parent e25ab193e8
commit 05d4fa4530

View File

@@ -3078,7 +3078,17 @@ mod orchestrator_postgres_tests {
/// Build a signed kind-9 event with an `h` channel tag.
fn make_kind9_event(keys: &Keys, channel_id: Uuid) -> nostr::Event {
EventBuilder::new(Kind::from(9u16), "test message")
make_kind9_event_msg(keys, channel_id, "test message")
}
/// Build a signed kind-9 event with a specific content string and an `h` channel tag.
///
/// Use distinct content strings for different calls within the same second: nostr
/// event IDs are deterministic over (pubkey, created_at, kind, tags, content), so two
/// calls with identical inputs in the same second produce the same event ID, which
/// triggers the step-3c `DuplicateEvent` precheck before the intended guard fires.
fn make_kind9_event_msg(keys: &Keys, channel_id: Uuid, content: &str) -> nostr::Event {
EventBuilder::new(Kind::from(9u16), content)
.tag(Tag::custom(
nostr::TagKind::SingleLetter(nostr::SingleLetterTag {
character: nostr::Alphabet::H,
@@ -3145,7 +3155,7 @@ mod orchestrator_postgres_tests {
.await
.expect("orchestrator success path must not fail");
let (stored, _) = result;
let (stored, _, _) = result;
// The returned event_id must match the submitted event.
assert_eq!(
stored.event.id, event.id,
@@ -3367,7 +3377,11 @@ mod orchestrator_postgres_tests {
let fx_channel_id = fx.channel_id;
let actor_clone = actor;
let deadline_clone = deadline;
let event = make_kind9_event(&keys, fx_channel_id);
let event = make_kind9_event_msg(
&keys,
fx_channel_id,
&format!("concurrent-enrollment-msg-{i}"),
);
let assertion = make_assertion(deadline_clone);
let proof_event_id = [0x30u8 + i; 32]; // distinct proof per task
tokio::spawn(async move {
@@ -3613,7 +3627,10 @@ mod orchestrator_postgres_tests {
// Second call: same actor, different proof_event_id.
// The lifecycle_revision mismatch must be caught at authorize_protected_use_in_tx.
let orch2 = make_orchestrator(db);
let event2 = make_kind9_event(&keys, fx.channel_id);
// Distinct content ensures a different event ID from event1 — same keys+channel
// within the same second would produce the same nostr event ID, firing step-3c
// DuplicateEvent before the lifecycle guard.
let event2 = make_kind9_event_msg(&keys, fx.channel_id, "lifecycle-advance-second-msg");
let proof_event_id_2 = [0x41u8; 32];
let result = orch2
.commit_kind9_atomic(crate::nip_fi::Kind9Params {
@@ -3702,7 +3719,10 @@ mod orchestrator_postgres_tests {
// proves the epoch UPDATE predicate matches every time.
let orch2 = make_orchestrator(db);
let proof_event_id_2 = [0x51u8; 32];
let event2 = make_kind9_event(&keys, fx.channel_id);
// Distinct content ensures a different event ID from event1 — same keys+channel
// within the same second would produce the same nostr event ID, firing step-3c
// DuplicateEvent before the epoch guard.
let event2 = make_kind9_event_msg(&keys, fx.channel_id, "epoch-guard-second-msg");
let result = orch2
.commit_kind9_atomic(crate::nip_fi::Kind9Params {
community_id: fx.community_id,
@@ -3779,7 +3799,9 @@ mod orchestrator_postgres_tests {
// Second call: same proof_event_id — must fail with ProofReplayed.
let orch2 = make_orchestrator(db);
let event2 = make_kind9_event(&keys, fx.channel_id);
// Distinct content ensures a different nostr event ID from event1: the proof-replay
// uniqueness guard (step 3d) must fire, not the step-3c DuplicateEvent precheck.
let event2 = make_kind9_event_msg(&keys, fx.channel_id, "proof-replay-second-msg");
let result = orch2
.commit_kind9_atomic(crate::nip_fi::Kind9Params {
community_id: fx.community_id,
@@ -4781,7 +4803,7 @@ mod orchestrator_postgres_tests {
// Build a single signed event.
let event = make_kind9_event(&keys, fx.channel_id);
let signed_event_id: [u8; 32] = event.id.to_bytes();
let _signed_event_id: [u8; 32] = event.id.to_bytes();
// ── Invariant 1: deterministic_admission_op_id is stable ─────────────
// Same inputs → same UUID, always. This is the property UUID-v4 breaks.