Files
smirk-backend-core/tests/relay_admission.rs
T
jwinterm 4c83e53839 feat(config): effective config via ArcSwap + DB overlay at boot (Stage 2)
Wire the settings overlay into the live config path:
- AppState.config: Config -> Arc<ArcSwap<Config>> (hot-swappable effective config) +
  config_base: Arc<Config> (immutable env-only). New AppState::cfg() accessor; every
  config read goes through it (state.cfg().field), so a runtime-safe operator edit is
  seen on the next request. One Config type, no parallel path.
- Boot (main.rs): load_settings_overlay -> Config::apply_overlay(validate) -> build
  every client/worker/router from the EFFECTIVE config. A persisted overlay that no
  longer validates is ignored (boot env-only + loud log), never bricks. Env-only when
  ADMIN_KEY_INTEGRITY_SECRET is unset.
- Migrated ~59 state.config.X reads to state.cfg().X across api/*; bound guards where a
  reference outlives the statement; integrity_secret returns owned String.
- Test harness builds AppState with ArcSwap + config_base (arc-swap dev-dep).

Behavior-preserving: with no operator_settings rows, effective == env config.
cargo check --all-targets clean; lib tests 256/0.
2026-07-18 06:00:16 -04:00

124 lines
4.1 KiB
Rust

//! L1 integration: the relay event-admission (nauthz) gRPC handler.
//!
//! Drives `AdmissionService::event_admit` directly (no gRPC transport — that is
//! covered by the docker-relay E2E) to prove the handler wiring: p-tag
//! extraction, the DB registration lookup, and the policy decision, end to end
//! against live Postgres. The pure policy branches are covered in unit tests.
mod common;
use smirk_backend_core::infra::relay::nauthz::proto::authorization_server::Authorization;
use smirk_backend_core::infra::relay::nauthz::{proto, AdmissionService};
use smirk_backend_core::infra::relay::{self, RelayProvider};
use smirk_backend_core::models::db::NewUser;
use std::sync::Arc;
use tonic::Request;
/// A 64-char lowercase-hex pubkey/id (valid for hex::decode).
fn hex64(seed: &str) -> String {
let base = format!("{seed}{}", "0".repeat(64));
base[..64].to_string()
}
async fn register_npub(db: &smirk_backend_core::infra::db::Database, npub_hex: &str) {
db.create_user(NewUser {
username: None,
pubkey_hash: Some(format!("pk-{}", uuid::Uuid::new_v4())),
nostr_pubkey: Some(npub_hex.to_string()),
wallet_birthday: None,
seed_fingerprint: None,
xmr_start_height: None,
wow_start_height: None,
})
.await
.unwrap();
}
fn inbox_outbox_relay(base: &smirk_backend_core::config::Config) -> Arc<dyn RelayProvider> {
let mut cfg = base.clone();
cfg.messaging.relay.enabled = true;
cfg.messaging.relay.write_policy = "inbox-outbox".into();
cfg.messaging.relay.advertised_url = "wss://relay.example.org".into();
cfg.messaging.relay.inbound_pow_bits = 0;
relay::from_config(&cfg).unwrap().unwrap()
}
fn event(author_hex: &str, kind: u64, p_tags: &[&str], id_hex: &str) -> proto::EventRequest {
proto::EventRequest {
event: Some(proto::Event {
id: hex::decode(id_hex).unwrap(),
pubkey: hex::decode(author_hex).unwrap(),
created_at: 0,
kind,
content: String::new(),
tags: p_tags
.iter()
.map(|p| proto::TagEntry {
values: vec!["p".to_string(), (*p).to_string()],
})
.collect(),
sig: vec![],
}),
ip_addr: None,
origin: None,
user_agent: None,
auth_pubkey: None,
nip05: None,
}
}
async fn admit(svc: &AdmissionService, req: proto::EventRequest) -> bool {
let reply = svc
.event_admit(Request::new(req))
.await
.unwrap()
.into_inner();
reply.decision == proto::Decision::Permit as i32
}
#[tokio::test]
async fn is_registered_npub_membership() {
let app = require_app!();
let npub = hex64("aaaa");
assert!(!app.state.db.is_registered_npub(&npub).await.unwrap());
register_npub(&app.state.db, &npub).await;
assert!(app.state.db.is_registered_npub(&npub).await.unwrap());
}
#[tokio::test]
async fn admission_inbox_outbox_end_to_end() {
let app = require_app!();
let svc = AdmissionService::new(inbox_outbox_relay(&app.state.cfg()), app.state.db.clone());
let author = hex64("b1"); // registered author (outbox)
let recipient = hex64("b2"); // registered recipient (inbox)
let stranger = hex64("b3"); // unregistered
register_npub(&app.state.db, &author).await;
register_npub(&app.state.db, &recipient).await;
let ff = "ff".repeat(32);
// Outbox: a registered author's own event → PERMIT.
assert!(
admit(&svc, event(&author, 1, &[], &ff)).await,
"registered author outbox"
);
// Inbox: an unregistered author's gift-wrap (1059) to a registered recipient → PERMIT.
assert!(
admit(&svc, event(&stranger, 1059, &[&recipient], &ff)).await,
"gift-wrap to registered recipient"
);
// External non-gift-wrap → DENY.
assert!(
!admit(&svc, event(&stranger, 1, &[&recipient], &ff)).await,
"external non-gift-wrap rejected"
);
// Gift-wrap to an UNREGISTERED recipient → DENY.
assert!(
!admit(&svc, event(&stranger, 1059, &[&hex64("b9")], &ff)).await,
"gift-wrap to unregistered recipient rejected"
);
}