From 4bf35f95e64707614991a418fa17bbb535e98468 Mon Sep 17 00:00:00 2001 From: archipelago Date: Thu, 23 Apr 2026 13:02:45 -0400 Subject: [PATCH] test: repair stale test fixtures across identity, mesh, update, wallet, fips Several tests had drifted from the current production behavior: - identity_manager: create() already auto-provisions a Nostr key, so the explicit create_nostr_key() call failed with "already exists". Rewrite the test to assert on record.nostr_npub from create() directly. - mesh/protocol: test_build_app_start read the app name from frame[4..] but the v2 layout is [0:marker][1-2:len][3:cmd][4:version][5..:name]. test_identity_broadcast_roundtrip expected input DID = output DID but the v2 decoder derives DID from the ed25519 pubkey, so the roundtrip compares against did_key_from_pubkey_hex(&pub) now. - mesh/bitcoin_relay: test_build_block_header_announcement asserted sig.is_some(), but the builder intentionally emits an unsigned envelope to fit the 160-byte LoRa limit; assert sig.is_none(). Also widen placeholder hashes to the required 64 hex chars (32 bytes). - update: load_mirrors() now merges default mirrors post-migration, so the roundtrip test must assert the custom mirror survives alongside the defaults rather than strict equality. - wallet/cashu: test_proof_c_as_pubkey used hex that is not on the curve; replace with the secp256k1 generator point G so parsing succeeds. - fips: test_status_reports_no_key_pre_onboarding asserted npub.is_none(), which fails on dev boxes where the fips daemon is already running. Keep the !key_present assertion and drop the npub one. --- core/archipelago/src/fips/mod.rs | 5 ++++- core/archipelago/src/identity_manager.rs | 6 ++++-- core/archipelago/src/mesh/bitcoin_relay.rs | 9 ++++++--- core/archipelago/src/mesh/protocol.rs | 15 +++++++++++---- core/archipelago/src/update.rs | 17 ++++++++++++++++- core/archipelago/src/wallet/cashu.rs | 4 +++- 6 files changed, 44 insertions(+), 12 deletions(-) diff --git a/core/archipelago/src/fips/mod.rs b/core/archipelago/src/fips/mod.rs index 105aad4a..095e7b5e 100644 --- a/core/archipelago/src/fips/mod.rs +++ b/core/archipelago/src/fips/mod.rs @@ -180,7 +180,10 @@ mod tests { // anchor is the only candidate. let status = FipsStatus::query(dir.path()).await; assert!(!status.key_present, "no key before onboarding"); - assert!(status.npub.is_none()); + // `npub` falls back to whatever an already-running local fips + // daemon advertises, so on a dev machine or node with fips + // installed this field can be Some(...) even when the test + // data_dir is empty. We only assert that key_present is false. // `installed`, `service_state`, `version` depend on the host and are // not asserted here — query() must return cleanly regardless. } diff --git a/core/archipelago/src/identity_manager.rs b/core/archipelago/src/identity_manager.rs index 7e7bed59..4355f15a 100644 --- a/core/archipelago/src/identity_manager.rs +++ b/core/archipelago/src/identity_manager.rs @@ -885,11 +885,13 @@ mod tests { async fn test_create_nostr_key_npub_format() { let dir = tempdir().unwrap(); let mgr = IdentityManager::new(dir.path()).await.unwrap(); + // `create()` auto-provisions a Nostr key for every identity, so the + // returned record should already have a valid bech32 npub. let record = mgr - .create("Nostr".to_string(), IdentityPurpose::Personal) + .create("Personal".to_string(), IdentityPurpose::Personal) .await .unwrap(); - let npub = mgr.create_nostr_key(&record.id).await.unwrap(); + let npub = record.nostr_npub.expect("nostr npub should be populated"); assert!( npub.starts_with("npub1"), "npub should start with npub1, got {}", diff --git a/core/archipelago/src/mesh/bitcoin_relay.rs b/core/archipelago/src/mesh/bitcoin_relay.rs index 37f27bc7..8ef9d980 100644 --- a/core/archipelago/src/mesh/bitcoin_relay.rs +++ b/core/archipelago/src/mesh/bitcoin_relay.rs @@ -457,8 +457,9 @@ mod tests { let key = SigningKey::generate(&mut OsRng); let wire = build_block_header_announcement( 890412, - "0000000000000000000abc", - "0000000000000000000aab", + // Block hashes must be 32 bytes (64 hex chars). Use realistic-shaped placeholders. + "0000000000000000000abc00000000000000000000000000000000000000abcd", + "0000000000000000000aab0000000000000000000000000000000000000aabcd", 1710633600, "did:key:z6MkTest", &key, @@ -469,7 +470,9 @@ mod tests { assert_eq!(wire[0], 0x02); let envelope = TypedEnvelope::from_wire(&wire).unwrap(); assert_eq!(envelope.t, MeshMessageType::BlockHeader as u8); - assert!(envelope.sig.is_some()); + // Block header announcements are intentionally unsigned to save 64 bytes + // on the 160-byte LoRa payload (see builder comment). + assert!(envelope.sig.is_none()); } #[test] diff --git a/core/archipelago/src/mesh/protocol.rs b/core/archipelago/src/mesh/protocol.rs index ca43b652..b392216c 100644 --- a/core/archipelago/src/mesh/protocol.rs +++ b/core/archipelago/src/mesh/protocol.rs @@ -701,9 +701,11 @@ mod tests { #[test] fn test_build_app_start() -> Result<()> { + // Frame layout: [0: '>'][1-2: len LE][3: CMD][4: VERSION][5..: padded name] let frame = build_app_start("Archipelago"); assert_eq!(frame[3], CMD_APP_START); - let name = &frame[4..]; + assert_eq!(frame[4], PROTOCOL_VERSION); + let name = &frame[5..]; assert_eq!( std::str::from_utf8(name) .map_err(|e| anyhow::anyhow!("invalid UTF-8 in app name: {}", e))?, @@ -753,15 +755,20 @@ mod tests { #[test] fn test_identity_broadcast_roundtrip() -> Result<()> { - let did = "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"; + // The v2 encoding drops the DID and the decoder reconstructs it + // deterministically from the ed25519 pubkey, so the roundtripped + // DID won't equal an arbitrary input DID. Derive what the decoder + // will produce and assert against that. let ed_pub = "a".repeat(64); let x25519_pub = "b".repeat(64); + let expected_did = crate::identity::did_key_from_pubkey_hex(&ed_pub) + .map_err(|e| anyhow::anyhow!("derive did: {}", e))?; - let encoded = encode_identity_broadcast(did, &ed_pub, &x25519_pub); + let encoded = encode_identity_broadcast(&expected_did, &ed_pub, &x25519_pub); let (parsed_did, parsed_ed, parsed_x) = parse_identity_broadcast(&encoded) .ok_or_else(|| anyhow::anyhow!("failed to parse identity broadcast"))?; - assert_eq!(parsed_did, did); + assert_eq!(parsed_did, expected_did); assert_eq!(parsed_ed, ed_pub); assert_eq!(parsed_x, x25519_pub); Ok(()) diff --git a/core/archipelago/src/update.rs b/core/archipelago/src/update.rs index 8654491b..40e62b97 100644 --- a/core/archipelago/src/update.rs +++ b/core/archipelago/src/update.rs @@ -1489,7 +1489,22 @@ mod tests { }]; save_mirrors(dir.path(), &list).await.unwrap(); let back = load_mirrors(dir.path()).await.unwrap(); - assert_eq!(back, list); + // load_mirrors merges in any missing default mirrors so a node + // that explicitly added a single custom mirror still gets the + // built-in OVH + tx1138 fallbacks. The custom mirror is preserved. + assert!( + back.iter().any(|m| m.url == "https://example.com/m.json"), + "custom mirror should round-trip; got {:?}", + back + ); + for def in default_mirrors() { + assert!( + back.iter().any(|m| m.url == def.url), + "default mirror {} should be present after load; got {:?}", + def.url, + back + ); + } } #[test] diff --git a/core/archipelago/src/wallet/cashu.rs b/core/archipelago/src/wallet/cashu.rs index 291ea7dd..17865853 100644 --- a/core/archipelago/src/wallet/cashu.rs +++ b/core/archipelago/src/wallet/cashu.rs @@ -334,7 +334,9 @@ mod tests { amount: 1, id: "test".into(), secret: "s".into(), - c: "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d94ec4da0e7f6c2b4e24".to_string(), + // Generator point G of secp256k1, compressed form. Always a + // valid pubkey, so c_as_pubkey() must succeed. + c: "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798".to_string(), }; assert!(proof.c_as_pubkey().is_ok()); }