Files
buzz/crates/buzz-db/tests/observability_source.rs
Luke Tornquist 039e1c8d59 feat(relay): write the NIP-43 member list in the membership transaction (#8482)
## Summary

The relay-signed NIP-43 member list (kind:13534) was published after
each membership change had already committed. If that publish failed, or
if a change never republished at all (startup `bootstrap_owner`), the
list stayed wrong until the background reconcile sweep found and fixed
it. #8467 moved that sweep off the startup path. This PR removes the
reason the sweep exists.

Now every `buzz-db` function that changes `relay_members` writes the new
list **in the same transaction**, as its last statement before commit:
- It takes the per-community list lock last, reads the members, signs
the list, retires the old one and inserts the new one.
- If the list write fails, the membership change rolls back and the
caller gets an error.
- A call that changes nothing (already a member, role mismatch, host
exists, limit reached) writes no list.

The functions return `MembershipWrite<T> { outcome, snapshot }`. Callers
push `snapshot` to live subscribers after commit. That push is the only
thing left outside the transaction; if it fails, live clients see the
change on their next fetch, and the stored list is already correct.

Write paths covered (RPR #2, every write path): `add_relay_member`,
`claim_relay_membership`, `claim_relay_invite`, `remove_relay_member`,
`remove_relay_member_if_role`, `update_relay_member_role`,
`bootstrap_owner` / `provision_owner`, `transfer_ownership`,
`backfill_from_allowlist`, and the owner insert in
`create_community_with_owner[_and_nip_fi_issuers]`. Community deletion
removes the members together with the community, so it writes no list.

The relay keypair is passed in explicitly instead of being stored on
`Db`, so the compiler finds every caller.

### Behavior changes

- **Open relays get an accurate list too.** Provisioning and ownership
transfer used to publish only when `require_relay_membership` was on.
The write is now unconditional.
- **Same-second changes can't reuse an id.** The list's `created_at` is
one second past the newest earlier list for that community, including
retired ones, or the current time if that is later. Before, adding and
then removing a member in the same second could produce an event with
the same id as an earlier list. The insert then failed, and the stale
list stayed live. A burst of changes can push `created_at` ahead of the
clock by up to one second per change; it returns to the clock once the
burst ends.
- **`buzz-admin add-member` / `remove-member`** no longer build their
own list (`publish_membership_list_with_bump` is deleted). They push the
list the database wrote. Concurrent CLI runs now serialize on the
database lock rather than depending on `run.sh` running one at a time.
- **Owner mutations lock the community row `FOR NO KEY UPDATE`.**
`transfer_ownership` and `bootstrap_owner` used `FOR UPDATE`. The list
insert takes `FOR KEY SHARE` on that row through `events.community_id`'s
foreign key, so a repair holding the list lock, or a removal or role
update holding a member row, deadlocked with a concurrent owner change.
`FOR NO KEY UPDATE` still conflicts with archive, unarchive and deletion
admission, so the lifecycle ordering is unchanged.
- **Startup delivers its list to other replicas.** When backfill or
`bootstrap_owner` changes members at startup, the relay fans the newest
list out through pub/sub once its state is ready, so clients connected
to other replicas during a rolling deploy see an owner rotation without
a refetch.
- **New `buzz-admin republish-members`.** Rewrites the community's list
from the stored members in one community-write transaction and pushes it
live. This replaces the sweep as the repair for a list that is missing
or stale: a `relay_members` change made directly in SQL, a restore, or a
deployment that turns on `BUZZ_REQUIRE_RELAY_MEMBERSHIP` after running
without it.

## Rollout

Nothing rewrites a list automatically once the sweep is gone. A
community whose list drifted before this deploys stays wrong until its
next membership change or until an operator runs `buzz-admin
republish-members`. Either deploy #8467 and let its sweep complete one
pass first, or run `republish-members` for each community after this
deploys. The command repairs only the community that `RELAY_URL` maps
to, so on a relay hosting many communities, run it once per community
host.

## What got simpler

Deleted:
- `reconcile_nip43_membership_snapshots`,
`spawn_nip43_membership_reconciler`, its leader lock key and #8467's
standby retry
- the three `nip43_membership_snapshot_needs_reconciliation*` drift
reads (one already deprecated)
- `publish_nip43_membership_list` / `publish_nip43_membership_locked`,
the separate after-commit republish at every call site, and
`buzz-admin`'s duplicate publisher
- the reconcile metrics `buzz_nip43_membership_reconciliations_total`
and `buzz_nip43_membership_reconciliation_failures_total`
- the after-commit publish metrics
`buzz_nip43_membership_publications_total` and
`buzz_nip43_membership_publication_seconds`, and the
`publish_nip43_membership_locked` transaction metric label. A failed
list write is now a failed membership write and shows up in the caller's
existing error handling and the transaction metrics of its write path.
- the `BUZZ_NIP43_RECONCILE_INTERVAL_SECS` setting

There is now one way the list gets written, and it can't disagree with
`relay_members` after commit.

**Unused deploy config (outside this repo):** hosted deployments that
set `BUZZ_NIP43_RECONCILE_INTERVAL_SECS` can drop it. It is now ignored.
Dashboards or alerts on the four deleted metrics will stop receiving
data.

## Testing

New Postgres tests (`buzz-db`):
- `relay_members::every_membership_write_commits_its_list`: for each
write path, the returned list is the single live kind:13534 and names
exactly the committed members. No-op calls return no list and leave the
live list alone.
- `relay_members::a_failed_list_write_rolls_back_the_membership_change`:
holds the list lock, waits for the join to block on it, cancels that
statement, and checks that the member was not added and the previous
list is still live.
- `relay_members::concurrent_joins_all_land_in_the_list`: 8 concurrent
joins; the final list names all 8.
-
`relay_members::same_second_return_to_an_earlier_membership_writes_a_new_list`:
add, remove, add, remove within a second; every list is newer than the
last and no id repeats.
- `relay_members::owner_transfer_and_list_repair_both_commit` and
`relay_members::owner_transfer_and_member_removal_both_commit`: hold a
repair (list lock) or removal (member row) open, wait until a transfer
blocks behind it, then finish it. Both transactions commit; with `FOR
UPDATE` back in place, one of them fails.
- `community::create_community_with_owner_is_atomic_and_create_only` and
`relay_invite::bounded_claim_exhausts_and_existing_member_retry_does_not_consume`
now also check the list.

Each new test was checked to fail against a mutation of the new code
that brings back the old behavior (details in the thread).

Stacked on #8467. Merge order: #8467, then #8468, then this.

---------

Signed-off-by: Elrond <28d6302a099e5225b02c4155ac4236e4912603df2ab08dbfc2f4fef08ce598c8@buzz.block.builderlab.xyz>
Co-authored-by: Elrond <28d6302a099e5225b02c4155ac4236e4912603df2ab08dbfc2f4fef08ce598c8@buzz.block.builderlab.xyz>
2026-10-20 11:39:13 -04:00

2168 lines
87 KiB
Rust

#[test]
fn database_metrics_and_slow_logs_exclude_sensitive_or_unbounded_fields() {
let implementation = include_str!("../src/runtime/observability.rs");
let datastore_macro = include_str!("../../buzz-datastore-tracing/src/lib.rs");
let instrumentation = format!("{implementation}\n{datastore_macro}");
for forbidden in [
"\"community\" =>",
"\"event_id\" =>",
"\"event_kind\" =>",
"\"kind\" =>",
"\"sql\" =>",
"\"query\" =>",
"\"query_id\" =>",
"\"d_tag\" =>",
"\"coordinate\" =>",
"community =",
"event_id =",
"event_kind =",
"sql =",
"query_id =",
"d_tag =",
"coordinate =",
] {
assert!(
!instrumentation.contains(forbidden),
"database instrumentation must not expose {forbidden}"
);
}
assert!(datastore_macro.contains("name: LitStr"));
assert!(datastore_macro.contains("\"operation\" => #name"));
assert!(datastore_macro.contains("elapsed_ms ="));
assert!(
datastore_macro.contains("parent: None"),
"slow warnings must not inherit dynamic datastore span fields"
);
// The runtime tracing-layer assertion covers field names because a source
// search would also match ordinary local variables such as `record_error`.
}
#[test]
fn relay_admin_db_wrappers_have_exactly_one_datastore_span() {
for (domain, source) in [
(
"relay_admin_actions",
include_str!("../src/store/relay_admin_actions.rs"),
),
(
"relay_operators",
include_str!("../src/store/relay_operators.rs"),
),
] {
let db_impl = source
.split_once("impl crate::Db {")
.unwrap_or_else(|| panic!("{domain} must own its Db wrappers"))
.1
.split_once("\n#[cfg(test)]")
.unwrap_or_else(|| panic!("{domain} Db wrappers must precede focused tests"))
.0;
let mut pending_spans = 0;
let mut methods = 0;
for line in db_impl.lines() {
if line.contains("#[datastore_span(") {
pending_spans += 1;
}
if line.trim_start().starts_with("pub async fn ") {
assert_eq!(pending_spans, 1, "{domain} wrapper `{line}` span count");
pending_spans = 0;
methods += 1;
}
}
assert!(methods > 0, "{domain} must own public Db wrappers");
assert_eq!(
pending_spans, 0,
"{domain} has an unattached datastore span"
);
}
}
#[test]
fn p0_pool_acquisitions_use_typed_operation_pairs_without_other() {
let observability = include_str!("../src/runtime/observability.rs");
assert!(observability.contains("enum PoolOperation"));
assert!(observability.contains("pub(crate) enum WriterOperation"));
assert!(observability.contains("pub(crate) enum ReaderOperation"));
assert!(observability.contains("Self::WriterAuthentication"));
assert!(observability.contains("Self::ReaderSubscriptionHistory"));
assert!(observability.contains("pub(crate) async fn acquire_writer("));
assert!(observability.contains("pub(super) async fn acquire_reader_with_legacy_metrics("));
assert!(observability.contains("static POOL_WAITERS: [Mutex<u64>"));
assert!(!observability.contains("AtomicU64"));
assert!(!observability.contains("DbOperation::Other"));
assert!(!observability.contains("\"other\""));
assert!(!observability.contains("buzz_db_pool_acquire_timeouts_total"));
assert!(!observability.contains("\"result\" =>"));
let legacy_transaction = observability
.split_once("pub(crate) async fn begin_transaction(")
.expect("observability must expose attributed transaction acquisition")
.1
.split_once("pub(crate) async fn observe_advisory_lock")
.expect("transaction acquisition must precede advisory-lock observation")
.0;
assert!(legacy_transaction.contains("acquire_writer_with_legacy_metrics("));
let runtime = include_str!("../src/runtime/mod.rs");
assert!(runtime.contains("observability::acquire_writer_until("));
assert!(runtime.contains("WriterOperation::Readiness"));
assert!(runtime.contains("WriterOperation::EventWrite"));
assert!(runtime.contains("ReaderOperation::Bootstrap"));
assert!(runtime.contains("pub async fn begin_event_write_transaction"));
let reader_boot = runtime
.split_once("async fn read_pool_boot_ping_once(")
.expect("runtime must expose the reader boot probe")
.1
.split_once("#[cfg(test)]")
.expect("reader boot probe must precede its test seam")
.0;
assert!(reader_boot.contains("acquire_reader_with_legacy_metrics("));
let routed_reader = runtime
.split_once("async fn proved_reader(")
.expect("runtime must expose the routed-reader checkout")
.1
.split_once("async fn reader_aurora_capability_on(")
.expect("routed-reader checkout must precede capability probing")
.0;
assert!(routed_reader.contains("acquire_reader_with_legacy_metrics(read_pool, operation)"));
let event_write_transaction = runtime
.split_once("pub async fn begin_event_write_transaction(")
.expect("runtime must expose the public event-write transaction seam")
.1
.split_once("pub async fn insert_event_with_serving_write_guard(")
.expect("public event-write transaction must precede guarded writes")
.0;
assert!(event_write_transaction.contains(COMMUNITY_CHOKEPOINT_LEGACY_MARKER));
let migration = include_str!("../src/runtime/migration.rs");
let migration_lock = migration
.split_once("pub(crate) async fn with_exclusive_schema_destruction_lock")
.expect("migration must expose the schema-safety acquisition seam")
.1
.split_once("async fn reject_legacy_nip_rs_cardinality_ambiguity")
.expect("schema-safety acquisition must precede migration validation")
.0;
assert!(migration_lock.contains("acquire_writer_with_legacy_metrics("));
let allowlist = include_str!("../src/store/allowlist.rs");
assert!(allowlist.contains("WriterOperation::Authentication"));
assert!(allowlist.contains("WriterOperation::Authorization"));
assert!(!allowlist.contains("fetch_one(&self.pool)"));
let event = include_str!("../src/store/event.rs");
assert!(event.contains("query_events_with_operation"));
assert!(event.contains("WriterOperation::Authorization"));
assert!(event.contains("WriterOperation::SubscriptionHistory"));
assert!(event.contains("ReaderOperation::SubscriptionHistory"));
let soft_delete_discovery = event
.split_once("pub async fn soft_delete_discovery_events")
.expect("event store must expose discovery-event deletion")
.1
.split_once("\n}\n\n#[cfg(test)]")
.expect("discovery deletion must end the production Db implementation")
.0;
assert!(soft_delete_discovery.contains("WriterOperation::EventWrite"));
assert!(soft_delete_discovery.contains("begin_community_event_write_transaction("));
assert!(soft_delete_discovery.contains("execute(tx.conn())"));
let side_effects = include_str!("../../buzz-relay/src/handlers/side_effects.rs");
assert!(side_effects.contains("query_events_for_event_write"));
assert!(side_effects.contains("query_events_for_bootstrap"));
assert!(side_effects.contains(".list_channels_for_bootstrap("));
let deletion = include_str!("../src/store/deletion.rs");
let public_serving_catalog = deletion
.split_once("pub async fn validate_serving_catalog(&self)")
.expect("deletion store must preserve its public serving-catalog API")
.1
.split_once("async fn validate_serving_catalog_on")
.expect("public serving-catalog validation must delegate to its connection helper")
.0;
assert!(public_serving_catalog.contains("WriterOperation::Bootstrap"));
assert!(public_serving_catalog.contains("observability::acquire_writer("));
assert!(public_serving_catalog.contains("validate_serving_catalog_on"));
assert!(!public_serving_catalog.contains("self.pool.acquire()"));
let thread = include_str!("../src/store/thread.rs");
let thread_metadata = thread
.split_once("pub async fn get_thread_metadata_by_event(")
.expect("thread store must expose metadata lookup")
.1
.split_once("// -- Db API")
.expect("metadata lookup must precede the Db wrapper section")
.0;
assert!(thread_metadata.contains("WriterOperation::EventWrite"));
assert!(thread_metadata.contains("fetch_optional(&mut *connection)"));
assert!(!thread_metadata.contains("fetch_optional(pool)"));
let channel = include_str!("../src/store/channel.rs");
assert!(channel.contains("begin_community_write_transaction("));
for (start, end, expected) in [
(
"pub async fn create_channel(\n",
"/// Creates a channel with a client-supplied UUID",
"begin_community_write_transaction(",
),
(
"pub async fn create_channel_with_id(\n",
"/// Fetches a channel record by `(community_id, id)`",
"begin_community_write_transaction(",
),
(
"pub async fn update_channel(\n",
"/// Sets the topic for a channel",
"begin_community_write_transaction(",
),
(
"pub async fn set_topic(\n",
"/// Sets the purpose for a channel",
"begin_community_write_transaction(",
),
(
"pub async fn set_purpose(\n",
"/// Archives a channel",
"begin_community_write_transaction(",
),
(
"pub async fn archive_channel(\n",
"/// Unarchives a channel",
"begin_community_write_transaction(",
),
(
"pub async fn unarchive_channel(\n",
"/// Soft-delete a channel",
"begin_community_write_transaction(",
),
(
"pub async fn soft_delete_channel(\n",
"/// Archive ephemeral channels",
"begin_community_write_transaction(",
),
] {
let function = channel
.split_once(start)
.unwrap_or_else(|| panic!("missing channel seam {start}"))
.1
.split_once(end)
.unwrap_or_else(|| panic!("channel seam {start} must precede {end}"))
.0;
assert!(
function.contains(expected),
"channel seam {start} must use {expected}"
);
assert!(!function.contains("pool.begin().await"));
assert!(!function.contains(".execute(pool)"));
assert!(!function.contains(".fetch_optional(pool)"));
}
let get_channel = channel
.split_once("async fn get_channel_with_operation(")
.expect("channel store must route shared lookups through caller-owned intent")
.1
.split_once("/// Returns the canvas content")
.expect("channel lookup helper must precede canvas reads")
.0;
assert!(get_channel.contains("acquire_writer(pool, operation)"));
assert!(get_channel.contains("fetch_optional(&mut *connection)"));
assert!(!get_channel.contains("fetch_optional(pool)"));
assert!(channel.contains("pub async fn get_channel_for_event_write("));
let list_channels = channel
.split_once("async fn list_channels_with_operation(")
.expect("channel listing must accept caller-owned intent")
.1
.split_once("/// A channel archived by the ephemeral-channel reaper")
.expect("channel listing must precede ephemeral-channel types")
.0;
assert!(list_channels.contains("acquire_writer(pool, operation)"));
assert!(list_channels.contains("fetch_all(&mut *connection)"));
assert!(!list_channels.contains("fetch_all(pool)"));
assert!(channel.contains("pub async fn list_channels_for_bootstrap("));
let channel_members = include_str!("../src/store/channel_members.rs");
assert!(channel_members.contains("async fn get_members_with_operation("));
assert!(channel_members.contains("pub async fn get_members_for_event_write("));
assert!(channel_members.contains("async fn get_users_bulk_with_operation("));
assert!(channel_members.contains("pub async fn get_users_bulk_for_event_write("));
let huddle_link = event
.split_once("async fn huddle_started_link_exists_with_operation(")
.expect("huddle link lookup must accept caller-owned intent")
.1
.split_once("/// Insert a Nostr event")
.expect("huddle link lookup must precede event insertion")
.0;
assert!(huddle_link.contains("acquire_writer(pool, operation)"));
assert!(event.contains("pub async fn huddle_started_link_exists_for_event_write("));
let ingest = include_str!("../../buzz-relay/src/handlers/ingest.rs");
assert!(ingest.contains(".huddle_started_link_exists_for_event_write("));
let audio = include_str!("../../buzz-relay/src/audio/handler.rs");
assert!(audio.contains(".huddle_started_link_exists("));
let workflow_sink = include_str!("../../buzz-relay/src/workflow_sink.rs");
assert!(workflow_sink.contains(".get_members_for_event_write("));
assert!(workflow_sink.contains(".get_users_bulk_for_event_write("));
for write_caller in [
include_str!("../../buzz-relay/src/handlers/side_effects.rs"),
include_str!("../../buzz-relay/src/handlers/ingest.rs"),
include_str!("../../buzz-relay/src/handlers/command_executor.rs"),
workflow_sink,
] {
assert!(!write_caller.contains(".get_channel("));
assert!(write_caller.contains(".get_channel_for_event_write("));
}
let user = include_str!("../src/store/user.rs");
let agent_channel_policy = user
.split_once("pub async fn get_agent_channel_policy(")
.expect("user store must expose get_agent_channel_policy")
.1
.split_once("/// Check whether `actor_pubkey`")
.expect("agent policy lookup must precede owner lookup")
.0;
assert!(agent_channel_policy.contains("WriterOperation::Authorization"));
assert!(agent_channel_policy.contains("fetch_optional(&mut *connection)"));
assert!(!agent_channel_policy.contains("fetch_optional(pool)"));
let is_agent_owner = user
.split_once("pub async fn is_agent_owner(")
.expect("user store must expose is_agent_owner")
.1
.split_once("/// Set the channel_add_policy")
.expect("is_agent_owner must precede set_agent_channel_policy")
.0;
assert!(is_agent_owner.contains("WriterOperation::Authorization"));
assert!(is_agent_owner.contains("acquire_writer("));
assert!(is_agent_owner.contains("fetch_optional(&mut *connection)"));
assert!(!is_agent_owner.contains("fetch_optional(pool)"));
let moderation = include_str!("../src/store/moderation.rs");
let restriction_state = moderation
.split_once("pub async fn restriction_state(")
.expect("moderation store must expose restriction_state")
.1
.split_once("/// Fetch the full ban/timeout row")
.expect("restriction state must precede full ban reads")
.0;
assert!(restriction_state.contains("WriterOperation::Authorization"));
// The single aggregate row is read on the attributed writer connection,
// never directly on the pool.
assert!(restriction_state.contains("fetch_one(&mut *connection)"));
assert!(!restriction_state.contains("(pool)"));
let community_store = include_str!("../src/store/community.rs");
let ensure_community = community_store
.split_once("pub async fn ensure_configured_community(")
.expect("community store must expose ensure_configured_community")
.1
.split_once("/// Atomically creates a community")
.expect("configured-community helpers must precede community creation")
.0;
assert!(ensure_community.contains("WriterOperation::Authorization"));
assert!(ensure_community.contains("WriterOperation::Bootstrap"));
assert!(ensure_community.contains("ensure_configured_community_with_operation"));
assert!(ensure_community.contains("acquire_writer(&self.pool, operation)"));
assert!(ensure_community.contains("fetch_optional(&mut *connection)"));
let management_lookup = community_store
.split_once("pub async fn lookup_community_by_host_for_management(")
.expect("community store must expose management host lookup")
.1
.split_once("/// Lists communities where")
.expect("management lookup must precede owner listing")
.0;
assert!(management_lookup.contains("WriterOperation::Authorization"));
assert!(management_lookup.contains("fetch_optional(&mut *connection)"));
assert!(!management_lookup.contains("fetch_optional(&self.pool)"));
let community_production = community_store
.split("\n#[cfg(test)]")
.next()
.expect("community production source");
for required in [
"WriterOperation::TenantResolution",
"WriterOperation::Authorization",
"WriterOperation::SubscriptionHistory",
"WriterOperation::EventWrite",
] {
assert!(
community_production.contains(required),
"community P0 paths must include {required} attribution"
);
}
assert!(!community_production.contains("self.pool.begin().await"));
assert!(!community_production.contains(".fetch_one(&self.pool)"));
assert!(!community_production.contains(".fetch_all(&self.pool)"));
assert!(!community_production.contains(".execute(&self.pool)"));
assert_eq!(
community_production
.matches(".fetch_optional(&self.pool)")
.count(),
1,
"only the out-of-scope NIP-11 metadata read may retain a raw pool checkout"
);
let thread_summary = thread
.split_once("pub async fn get_thread_summary(")
.expect("thread store must expose get_thread_summary")
.1
.split_once("/// Fetch one channel window")
.expect("thread summary must precede channel-window reads")
.0;
assert!(thread_summary.contains("WriterOperation::EventWrite"));
assert!(thread_summary.contains("fetch_optional(&mut *connection)"));
assert!(thread_summary.contains("fetch_all(&mut *connection)"));
assert!(!thread_summary.contains("fetch_optional(pool)"));
assert!(!thread_summary.contains("fetch_all(pool)"));
let archived_identities = include_str!("../src/store/archived_identities.rs");
let archived_identity_production = archived_identities
.split("\n#[cfg(test)]")
.next()
.expect("archived identity production source");
assert_eq!(
archived_identity_production
.matches("WriterOperation::EventWrite")
.count(),
4,
"all four archived identity operations must be attributed to event writes"
);
assert!(!archived_identity_production.contains("fetch_optional(pool)"));
assert!(!archived_identity_production.contains("fetch_all(pool)"));
assert!(!archived_identity_production.contains("execute(pool)"));
let relay_main = include_str!("../../buzz-relay/src/main.rs");
assert!(relay_main.contains("pool_state.db.refresh_pool_waiter_metrics();"));
assert!(relay_main.contains("ensure_deployment_community(&db,"));
let provisioning = include_str!("../../buzz-relay/src/handlers/community_provisioning.rs");
assert!(provisioning.contains(".ensure_configured_community_for_bootstrap("));
let runtime = include_str!("../src/runtime/mod.rs");
assert!(runtime.contains("observability::refresh_pool_waiters(self.read_pool.is_some())"));
assert!(runtime.contains("self.verify_replica_fence_at_boot().await?"));
let fence_boot = runtime
.split_once("pub(crate) async fn verify_replica_fence_at_boot")
.expect("runtime must expose attributed boot fence verification")
.1
.split_once("/// The pool for lag-tolerant reads")
.expect("boot fence verification must precede routed-read plumbing")
.0;
assert!(fence_boot.contains("WriterOperation::Bootstrap"));
let replica_fence = include_str!("../src/runtime/replica_fence.rs");
let replica_fence_production = replica_fence
.split("\n#[cfg(test)]")
.next()
.expect("replica-fence production source");
assert!(replica_fence_production.contains("WriterOperation::Bootstrap"));
assert!(replica_fence_production.contains("WriterOperation::Maintenance"));
assert!(!replica_fence_production.contains("pool.begin().await"));
assert!(!replica_fence_production.contains("writer.acquire().await"));
assert!(!replica_fence_production.contains("fetch_optional(writer)"));
let usage = include_str!("../src/store/usage.rs");
let usage_production = usage
.split("\n#[cfg(test)]")
.next()
.expect("usage production source");
let usage_leader_lock = usage_production
.split_once("pub async fn try_lock_session_leader(")
.expect("usage store must expose the legacy leader-lock acquisition")
.1
.split_once("pub async fn usage_community_count(")
.expect("usage leader lock must precede counter reads")
.0;
assert!(usage_leader_lock.contains("acquire_writer_with_legacy_metrics("));
assert!(
usage_production
.matches("WriterOperation::Maintenance")
.count()
>= 11,
"every periodic usage checkout must be maintenance-attributed"
);
for bypass in [
".fetch_one(pool)",
".fetch_all(pool)",
".fetch_optional(pool)",
".execute(pool)",
] {
assert!(
!usage_production.contains(bypass),
"usage production path bypasses operation attribution with {bypass}"
);
}
let channel_reaper = channel
.split_once("pub async fn reap_expired_ephemeral_channels(pool:")
.expect("channel store must expose ephemeral reaper")
.1
.split_once("\nimpl Db {")
.expect("ephemeral reaper must precede Db wrappers")
.0;
assert!(channel_reaper.contains("WriterOperation::Maintenance"));
assert!(channel_reaper.contains("fetch_all(&mut *connection)"));
let deletion = include_str!("../src/store/deletion.rs");
let lease_reaper = deletion
.split_once("pub async fn reap_expired_serving_write_leases")
.expect("deletion store must expose serving-lease reaper")
.1
.split_once("/// Return serving-lease counts")
.expect("serving-lease reaper must precede stats")
.0;
assert!(lease_reaper.contains("WriterOperation::Maintenance"));
assert!(lease_reaper.contains("execute(&mut *connection)"));
let lease_stats = deletion
.split_once("pub async fn serving_lease_stats")
.expect("deletion store must expose serving-lease stats")
.1
.split_once("/// Whether a community remains active")
.expect("serving-lease stats must precede serving-state reads")
.0;
assert!(lease_stats.contains("WriterOperation::Maintenance"));
assert!(lease_stats.contains("fetch_one(&mut *connection)"));
// Lease seams that delegate to the bounded helper inherit its attribution.
let bounded_lease_sql = deletion
.split_once("async fn bounded_serving_lease_sql<T>(")
.expect("deletion store must expose the bounded serving-lease helper")
.1
.split_once("/// Acquire a durable, expiring lease")
.expect("bounded serving-lease helper must precede lease acquisition")
.0;
assert!(bounded_lease_sql.contains("WriterOperation::EventWrite"));
assert!(!bounded_lease_sql.contains("self.pool.begin().await"));
for (start, end) in [
(
"pub async fn acquire_serving_write_lease",
"/// Renew an already-admitted external side-effect lease",
),
(
"pub async fn renew_serving_write_lease",
"/// Release a serving side-effect lease",
),
(
"pub async fn release_serving_write_lease",
"/// Check that an external side-effect lease remains current",
),
(
"pub async fn verify_serving_write_lease",
"/// Delete expired serving leases",
),
(
"pub async fn is_serving_active",
"async fn advance_with_checkpoint",
),
] {
let function = deletion
.split_once(start)
.unwrap_or_else(|| panic!("missing serving-write seam {start}"))
.1
.split_once(end)
.unwrap_or_else(|| panic!("serving-write seam {start} must precede {end}"))
.0;
assert!(
function.contains("WriterOperation::EventWrite")
|| function.contains("self.bounded_serving_lease_sql("),
"serving-write seam {start} must be event-write attributed"
);
assert!(!function.contains("self.pool.begin().await"));
assert!(!function.contains(".execute(&self.pool)"));
assert!(!function.contains(".fetch_one(&self.pool)"));
}
let ensure_authorization = user
.split_once("pub async fn ensure_user_for_authorization(")
.expect("user store must expose NIP-OA authorization ensure")
.1
.split_once("/// Get a single user record")
.expect("authorization ensure must precede generic user reads")
.0;
assert!(ensure_authorization.contains("WriterOperation::Authorization"));
let set_owner_authorization = user
.split_once("pub async fn set_agent_owner_for_authorization(")
.expect("user store must expose NIP-OA authorization owner write")
.1
.split_once("/// Get the channel_add_policy")
.expect("authorization owner write must precede policy reads")
.0;
assert!(set_owner_authorization.contains("WriterOperation::Authorization"));
let relay_api = include_str!("../../buzz-relay/src/api/mod.rs");
assert!(relay_api.contains(".ensure_user_for_authorization("));
assert!(relay_api.contains(".set_agent_owner_for_authorization("));
for (domain, source) in [
(
"channel_members",
include_str!("../src/store/channel_members.rs"),
),
("archived_identities", archived_identities),
("event", event),
("git_repo", include_str!("../src/store/git_repo.rs")),
("push", include_str!("../src/store/push.rs")),
("replica_fence", replica_fence),
("reaction", include_str!("../src/store/reaction.rs")),
("relay_invite", include_str!("../src/store/relay_invite.rs")),
(
"relay_members",
include_str!("../src/store/relay_members.rs"),
),
("thread", thread),
(
"relay_operators",
include_str!("../src/store/relay_operators.rs"),
),
("usage", usage),
] {
let production = source.split("\n#[cfg(test)]").next().unwrap_or(source);
for bypass in [
"pool.begin().await",
"self.pool.begin().await",
".fetch_one(pool)",
".fetch_one(&self.pool)",
".fetch_all(pool)",
".fetch_all(&self.pool)",
".fetch_optional(pool)",
".fetch_optional(&self.pool)",
".execute(pool)",
".execute(&self.pool)",
] {
assert!(
!production.contains(bypass),
"{domain} production path bypasses operation attribution with {bypass}"
);
}
}
}
#[test]
fn pool_level_insert_mentions_opens_the_tenant_local_chokepoint() {
let runtime = include_str!("../src/runtime/mod.rs");
let insert_mentions = runtime
.split_once("pub async fn insert_mentions(\n")
.expect("runtime must expose pool-level insert_mentions")
.1
.split_once("pub(crate) async fn insert_mentions_in_transaction(")
.expect("pool-level insert_mentions must precede its transaction seam")
.0;
assert!(
insert_mentions.contains(COMMUNITY_CHOKEPOINT_MARKER),
"post-commit mention indexing must be admitted like any other event-table write"
);
}
#[test]
fn event_write_paths_include_tenant_local_chokepoint_calls() {
let event = include_str!("../src/store/event.rs");
let insert_event = event
.split_once("pub async fn insert_event(\n")
.expect("event store must expose pool-level insert_event")
.1
.split_once("/// Insert a Nostr event in a caller-owned admitted transaction")
.expect("pool insert must precede transaction-seam insert")
.0;
assert!(
has_any_tenant_local_chokepoint(insert_event),
"pool-level event inserts must include a tenant-local event-write chokepoint call"
);
let insert_with_thread_meta = event
.split_once("pub async fn insert_event_with_thread_metadata(\n")
.expect("event store must expose pool-level thread-metadata insert")
.1
.split_once("impl Db {")
.expect("pool thread-metadata insert must precede Db wrappers")
.0;
assert!(
has_any_tenant_local_chokepoint(insert_with_thread_meta),
"thread-metadata event inserts must include a tenant-local chokepoint call"
);
let replaceable = include_str!("../src/store/replaceable.rs");
let replace_addressable = replaceable
.split_once("pub async fn replace_addressable_event(\n")
.expect("replaceable store must expose replace_addressable_event")
.1
.split_once("/// Replace a NIP-33 event inside a caller-owned transaction.")
.expect("addressable replacement must precede parameterized transaction seam")
.0;
assert!(
has_any_tenant_local_chokepoint(replace_addressable),
"addressable replacements must include a tenant-local chokepoint call"
);
let replace_parameterized = replaceable
.split_once("pub async fn replace_parameterized_event(\n")
.expect("replaceable store must expose replace_parameterized_event")
.1
.split_once("}\n\n#[cfg(test)]")
.expect("parameterized replacement must precede tests")
.0;
assert!(
has_any_tenant_local_chokepoint(replace_parameterized),
"parameterized replacements must include a tenant-local chokepoint call"
);
let channel_members = include_str!("../src/store/channel_members.rs");
let snapshot_lock = channel_members
.split_once("pub async fn lock_member_snapshot(\n")
.expect("channel_members must expose lock_member_snapshot")
.1
.split_once("/// Add a member to a channel.")
.expect("snapshot lock path must precede member add path")
.0;
assert!(
has_any_tenant_local_chokepoint(snapshot_lock),
"snapshot publication locks must include a tenant-local chokepoint call"
);
let push = include_str!("../src/store/push.rs");
let accept_lease = push
.split_once("pub async fn accept_lease_event(\n")
.expect("push store must expose accept_lease_event")
.1
.split_once("fn constraint_acceptance_outcome")
.expect("accept_lease_event must precede constraint outcome mapping")
.0;
assert!(
has_any_tenant_local_chokepoint(accept_lease),
"push lease source-event writes must include a tenant-local chokepoint call"
);
let reaction = include_str!("../src/store/reaction.rs");
let insert_reaction = reaction
.split_once("pub async fn insert_reaction_event_with_thread_metadata(\n")
.expect("reaction store must expose insert_reaction_event_with_thread_metadata")
.1
.split_once("/// Soft-delete a reaction by setting")
.expect("reaction insert must precede reaction soft-delete")
.0;
assert!(
has_any_tenant_local_chokepoint(insert_reaction),
"live kind:7 reaction inserts must include a tenant-local chokepoint call"
);
}
#[test]
fn legacy_compatibility_metrics_remain_pinned_to_the_preexisting_event_write_entrypoints() {
let runtime = include_str!("../src/runtime/mod.rs");
let typed_helper = runtime
.split_once("pub(crate) async fn begin_community_event_write_transaction(\n")
.expect("runtime must expose the typed tenant-local chokepoint")
.1
.split_once(
"pub(crate) async fn begin_community_event_write_transaction_with_legacy_metrics(\n",
)
.expect("typed chokepoint must precede the legacy compatibility wrapper")
.0;
assert!(
typed_helper.contains("CommunityEventWriteMetricPopulation::TypedOnly"),
"the default tenant-local chokepoint must stay typed-only"
);
assert!(
!typed_helper.contains("CommunityEventWriteMetricPopulation::LegacyCompatibility"),
"the default tenant-local chokepoint must not emit legacy compatibility metrics"
);
let legacy_db_wrapper = runtime
.split_once("pub async fn begin_event_write_transaction(\n")
.expect("Db must expose the public event-write entrypoint")
.1
.split_once("/// Insert an event while holding and validating an admitted serving-write")
.expect("public Db entrypoint must precede the serving-lease writer")
.0;
assert!(
legacy_db_wrapper.contains(COMMUNITY_CHOKEPOINT_LEGACY_MARKER),
"Db::begin_event_write_transaction must preserve the legacy compatibility population"
);
let replaceable = include_str!("../src/store/replaceable.rs");
for (label, start, end) in [
(
"replace_addressable_event",
"pub async fn replace_addressable_event(\n",
"/// Atomically replace a NIP-33 parameterized replaceable event.",
),
(
"replace_parameterized_event",
"pub async fn replace_parameterized_event(\n",
"}\n\n#[cfg(test)]",
),
] {
let seam = replaceable
.split_once(start)
.unwrap_or_else(|| panic!("replaceable store must expose {label}"))
.1
.split_once(end)
.unwrap_or_else(|| panic!("{label} must precede its next production seam"))
.0;
assert!(
seam.contains(COMMUNITY_CHOKEPOINT_LEGACY_MARKER),
"{label} must preserve the legacy compatibility population"
);
}
let push = include_str!("../src/store/push.rs");
let accept_lease = push
.split_once("pub async fn accept_lease_event(\n")
.expect("push store must expose accept_lease_event")
.1
.split_once("fn constraint_acceptance_outcome")
.expect("accept_lease_event must precede constraint outcome mapping")
.0;
assert!(
accept_lease.contains(COMMUNITY_CHOKEPOINT_LEGACY_MARKER),
"push lease acceptance must preserve the legacy compatibility population"
);
let event = include_str!("../src/store/event.rs");
let insert_event = event
.split_once("pub async fn insert_event(\n")
.expect("event store must expose pool-level insert_event")
.1
.split_once("/// Insert a Nostr event in a caller-owned admitted transaction")
.expect("pool insert must precede transaction-seam insert")
.0;
assert!(
insert_event.contains(COMMUNITY_CHOKEPOINT_MARKER),
"typed-only event inserts must stay on the typed tenant-local chokepoint"
);
assert!(
!insert_event.contains(COMMUNITY_CHOKEPOINT_LEGACY_MARKER),
"typed-only event inserts must not emit legacy compatibility metrics"
);
let reaction = include_str!("../src/store/reaction.rs");
let insert_reaction = reaction
.split_once("pub async fn insert_reaction_event_with_thread_metadata(\n")
.expect("reaction store must expose insert_reaction_event_with_thread_metadata")
.1
.split_once("/// Soft-delete a reaction by setting")
.expect("reaction insert must precede reaction soft-delete")
.0;
assert!(
insert_reaction.contains(COMMUNITY_CHOKEPOINT_MARKER),
"typed-only reaction inserts must stay on the typed tenant-local chokepoint"
);
assert!(
!insert_reaction.contains(COMMUNITY_CHOKEPOINT_LEGACY_MARKER),
"typed-only reaction inserts must not emit legacy compatibility metrics"
);
}
/// Function-level routing check for guarded-table writers.
///
/// A file can contain both a legitimate chokepoint writer and a bypass writer.
/// Every writing function must either open the tenant-local community
/// chokepoint itself, take a caller-owned `&mut AdmittedTx` (which only the
/// chokepoint can construct, so the compiler proves provenance), use reviewed
/// adapter-owned transaction state whose constructor is pinned to the
/// chokepoint, or appear in a reviewed exception list below.
///
/// A raw `&mut Transaction` or `&mut PgConnection` parameter is not a route:
/// nothing about its type says the transaction was admitted.
const GUARDED_TABLE_WRITE_MARKERS: [&str; 9] = [
"INSERT INTO events",
"UPDATE events",
"DELETE FROM events",
"INSERT INTO reactions",
"UPDATE reactions",
"DELETE FROM reactions",
"INSERT INTO event_mentions",
"UPDATE event_mentions",
"DELETE FROM event_mentions",
];
const COMMUNITY_CHOKEPOINT_MARKER: &str = "begin_community_event_write_transaction(";
const GENERAL_COMMUNITY_CHOKEPOINT_MARKER: &str = "begin_community_write_transaction(";
const SWEEP_COMMUNITY_CHOKEPOINT_MARKER: &str = "begin_community_sweep_transaction(";
const COMMUNITY_CHOKEPOINT_LEGACY_MARKER: &str =
"begin_community_event_write_transaction_with_legacy_metrics(";
fn has_any_tenant_local_chokepoint(source: &str) -> bool {
source.contains(COMMUNITY_CHOKEPOINT_MARKER)
|| source.contains(SWEEP_COMMUNITY_CHOKEPOINT_MARKER)
|| source.contains(GENERAL_COMMUNITY_CHOKEPOINT_MARKER)
|| source.contains(COMMUNITY_CHOKEPOINT_LEGACY_MARKER)
}
const ADMITTED_TX_SIGNATURE_MARKERS: [&str; 2] = ["&mut AdmittedTx", "&mut crate::AdmittedTx"];
// Narrow reviewed exceptions for non-serving verification probes only.
const GUARDED_WRITE_FUNCTION_EXCEPTIONS: [&str; 2] = [
"pub async fn verify_floor_guard_behavior(",
"pub async fn verify_channel_roster_fence_behavior(",
];
const GUARDED_TX_ADAPTER_METHOD_PINS: [(&str, &str); 1] = [(
"pub async fn replace_member_event(",
"pub async fn lock_member_snapshot(",
)];
fn production_contains_guarded_write(production_source: &str) -> bool {
GUARDED_TABLE_WRITE_MARKERS
.iter()
.any(|marker| production_source.contains(marker))
}
fn fn_signature_starts_here(trimmed_line: &str) -> bool {
[
"fn ",
"async fn ",
"pub fn ",
"pub async fn ",
"pub(crate) fn ",
"pub(crate) async fn ",
"pub(super) fn ",
"pub(super) async fn ",
]
.iter()
.any(|prefix| trimmed_line.starts_with(prefix))
}
/// Split production source into one slice per function. A slice runs from its
/// signature to the doc comment or attributes of the next function, so a
/// following function's docs (which may quote SQL) are never attributed to the
/// function before it.
///
/// Signatures and attributes are found in the masked view (see
/// [`mask_comments_and_literals`]), so an `fn` line inside a comment or a
/// multiline string never splits a function. Doc comments are blank in that
/// view, so `///` lead-ins are read from the original line.
fn function_slices(production_source: &str) -> Vec<&str> {
let masked = mask_comments_and_literals(production_source);
// (signature offset, offset where the next function's lead-in begins)
let mut starts = Vec::new();
let mut lead_ins = Vec::new();
let mut lead_in: Option<usize> = None;
let mut offset = 0usize;
for (line, code) in production_source
.split_inclusive('\n')
.zip(masked.split_inclusive('\n'))
{
let trimmed = code.trim_start();
let indent = code.len() - trimmed.len();
if fn_signature_starts_here(trimmed) {
starts.push(offset + indent);
lead_ins.push(lead_in.take().unwrap_or(offset + indent));
} else if line.trim_start().starts_with("///") || trimmed.starts_with("#[") {
lead_in.get_or_insert(offset);
} else {
lead_in = None;
}
offset += line.len();
}
let mut functions = Vec::new();
for (index, start) in starts.iter().enumerate() {
let end = lead_ins
.get(index + 1)
.copied()
.unwrap_or(production_source.len());
functions.push(&production_source[*start..end]);
}
functions
}
fn function_header(function_source: &str) -> &str {
function_source
.lines()
.next()
.unwrap_or("<unknown function>")
.trim()
}
fn function_is_guarded_write_exception(function_header: &str) -> bool {
GUARDED_WRITE_FUNCTION_EXCEPTIONS
.iter()
.any(|exception| function_header.starts_with(exception))
}
fn function_accepts_admitted_transaction(function_source: &str) -> bool {
let signature = function_source.split('{').next().unwrap_or(function_source);
ADMITTED_TX_SIGNATURE_MARKERS
.iter()
.any(|marker| signature.contains(marker))
}
fn function_uses_guarded_tx_adapter_state(function_source: &str) -> bool {
function_source.contains("&mut *self.tx") || function_source.contains("&mut self.tx")
}
fn adapter_constructor_is_chokepoint_pinned(production_source: &str, method_header: &str) -> bool {
for (adapter_method, adapter_constructor) in GUARDED_TX_ADAPTER_METHOD_PINS {
if method_header.starts_with(adapter_method) {
return function_slices(production_source)
.into_iter()
.find(|function_source| {
function_header(function_source).starts_with(adapter_constructor)
})
.is_some_and(has_any_tenant_local_chokepoint);
}
}
false
}
fn function_has_syntactic_guarded_write_route(
function_source: &str,
production_source: &str,
) -> bool {
let header = function_header(function_source);
has_any_tenant_local_chokepoint(function_source)
|| function_accepts_admitted_transaction(function_source)
|| (function_uses_guarded_tx_adapter_state(function_source)
&& adapter_constructor_is_chokepoint_pinned(production_source, header))
}
fn syntactic_guarded_write_route_violations(production_source: &str) -> Vec<String> {
function_slices(production_source)
.into_iter()
.filter(|function_source| {
let header = function_header(function_source);
production_contains_guarded_write(function_source)
&& !function_is_guarded_write_exception(header)
&& !function_has_syntactic_guarded_write_route(function_source, production_source)
})
.map(|function_source| function_header(function_source).to_owned())
.collect()
}
#[test]
fn serving_table_policy_rejects_same_file_mixed_guarded_writers() {
let mixed_source = r#"
pub async fn guarded_writer(pool: &sqlx::PgPool) {
let mut tx = begin_community_event_write_transaction(pool, community, WriterOperation::EventWrite)
.await
.expect("tx");
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)")
.execute(&mut *tx)
.await
.expect("write");
}
pub async fn unguarded_writer(pool: &sqlx::PgPool) {
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)")
.execute(pool)
.await
.expect("unguarded");
}
"#;
let violations = syntactic_guarded_write_route_violations(mixed_source);
assert!(
violations
.iter()
.any(|name| name.starts_with("pub async fn unguarded_writer(")),
"whole-file co-occurrence policy is too weak: one guarded writer in a file must not bless \
a sibling unguarded writer; violations: {violations:?}"
);
assert!(
!violations
.iter()
.any(|name| name.starts_with("pub async fn guarded_writer(")),
"guarded writer must remain allowed; violations: {violations:?}"
);
}
#[test]
fn serving_table_policy_rejects_writer_connection_only_bypasses() {
let acquire_writer_bypass = r#"
pub async fn bypass_with_writer_connection(pool: &sqlx::PgPool) {
let mut connection = crate::observability::acquire_writer(pool, WriterOperation::EventWrite)
.await
.expect("connection");
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)")
.execute(&mut *connection)
.await
.expect("write");
}
"#;
let pool_begin_bypass = r#"
pub async fn bypass_with_pool_begin(pool: &sqlx::PgPool) {
let mut tx = pool.begin().await.expect("tx");
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)")
.execute(&mut *tx)
.await
.expect("write");
}
"#;
let acquire_writer_violations = syntactic_guarded_write_route_violations(acquire_writer_bypass);
assert!(
acquire_writer_violations
.iter()
.any(|name| name.starts_with("pub async fn bypass_with_writer_connection(")),
"acquire_writer + raw guarded-table write must be rejected; violations: \
{acquire_writer_violations:?}"
);
let pool_begin_violations = syntactic_guarded_write_route_violations(pool_begin_bypass);
assert!(
pool_begin_violations
.iter()
.any(|name| name.starts_with("pub async fn bypass_with_pool_begin(")),
"pool.begin + raw guarded-table write must be rejected; violations: \
{pool_begin_violations:?}"
);
}
#[test]
fn serving_table_policy_requires_admitted_tx_not_raw_transactions() {
let source = r#"
pub(crate) async fn raw_transaction_writer(tx: &mut sqlx::Transaction<'_, sqlx::Postgres>) {
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)")
.execute(tx.conn())
.await
.expect("write");
}
pub(crate) async fn raw_connection_writer(conn: &mut sqlx::PgConnection) {
sqlx::query("INSERT INTO event_mentions (community_id, event_id) VALUES ($1, $2)")
.execute(conn)
.await
.expect("write");
}
pub(crate) async fn admitted_writer(tx: &mut AdmittedTx) {
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)")
.bind(tx.community().as_uuid())
.execute(tx.conn())
.await
.expect("write");
}
"#;
let violations = syntactic_guarded_write_route_violations(source);
assert_eq!(
violations,
[
"pub(crate) async fn raw_transaction_writer(tx: &mut sqlx::Transaction<'_, sqlx::Postgres>) {",
"pub(crate) async fn raw_connection_writer(conn: &mut sqlx::PgConnection) {",
],
"a raw transaction or connection parameter carries no admission proof; only \
`&mut AdmittedTx` does"
);
}
/// Every function in `source` that builds an `AdmittedTx`, paired with whether
/// it admits the transaction it wraps. The scan deliberately does not strip
/// `#[cfg(test)]` items: a test-only forge would let crate tests run follow-ups
/// on an unadmitted transaction, so it must admit like any other constructor.
fn admitted_tx_constructors(source: &str) -> Vec<(String, bool)> {
let production: String = source
.lines()
.filter(|line| !line.trim_start().starts_with("//"))
// `impl … for AdmittedTx {` headers open a block, not a value.
.filter(|line| !line.trim_start().starts_with("impl"))
.map(|line| format!("{line}\n"))
.collect();
function_slices(&production)
.into_iter()
.filter(|function| function.contains("Self {") || function.contains("AdmittedTx {"))
.map(|function| {
let admits = function.contains(".guard_transaction(&mut tx, community)")
|| function.contains(".guard_transaction_with_serving_lease(&mut tx, lease)");
(function.to_string(), admits)
})
.collect()
}
#[test]
fn admitted_tx_is_constructed_only_by_admitting_constructors() {
// The fields are private to `runtime/admitted_tx.rs`, so only that file can
// build the value. Pin that each function there that builds it also admits
// the transaction it wraps, so a new unguarded constructor cannot slip in.
let constructors = admitted_tx_constructors(include_str!("../src/runtime/admitted_tx.rs"));
assert_eq!(
constructors.len(),
2,
"AdmittedTx must have exactly the two admitting constructors"
);
for (constructor, admits) in constructors {
assert!(
admits,
"AdmittedTx constructor must admit the transaction it wraps: {constructor}"
);
}
}
#[test]
fn admitted_tx_constructor_scan_reports_test_only_forges() {
let source = r#"
impl AdmittedTx {
pub(crate) async fn admit(mut tx: Tx, community: CommunityId) -> Result<Self> {
guard.guard_transaction(&mut tx, community).await?;
Ok(Self { tx, community })
}
#[cfg(test)]
pub(crate) fn forge_for_tests(tx: Tx, community: CommunityId) -> Self {
Self { tx, community }
}
}
"#;
let unadmitted: Vec<String> = admitted_tx_constructors(source)
.into_iter()
.filter(|(_, admits)| !admits)
.map(|(function, _)| {
function
.lines()
.next()
.unwrap_or_default()
.trim()
.to_string()
})
.collect();
assert_eq!(
unadmitted,
["pub(crate) fn forge_for_tests(tx: Tx, community: CommunityId) -> Self {"],
"an indented `#[cfg(test)]` forge must still be reported as unadmitted"
);
}
#[test]
fn cfg_test_items_are_skipped_without_hiding_later_production_code() {
let source = "pub fn before() {}\n\
#[cfg(test)]\n\
struct Marker;\n\
pub fn after_struct() {}\n\
#[cfg(test)]\n\
static LOCK: std::sync::Mutex<()> =\n\
std::sync::Mutex::new(());\n\
pub fn after_static() {}\n\
#[cfg(test)]\n\
static S: [u8; 1] =\n\
[const { 0 }; 1];\n\
pub fn after_const_block() {}\n\
#[cfg(test)]\n\
#[derive(Debug)]\n\
struct Fields {\n\
value: u8,\n\
}\n\
pub fn after_fields() {}\n\
#[cfg(test)]\n\
mod tests {\n\
fn hidden() {\n\
}\n\
}\n\
pub fn after_module() {}\n";
let production = strip_cfg_test_items(source);
for name in [
"before",
"after_struct",
"after_static",
"after_const_block",
"after_fields",
"after_module",
] {
assert!(
production.contains(&format!("pub fn {name}()")),
"{name} is production code and must stay visible: {production}"
);
}
for hidden in ["Marker", "LOCK", "static S", "value: u8", "fn hidden"] {
assert!(
!production.contains(hidden),
"{hidden} is test-only and must be skipped: {production}"
);
}
let raw_writer = "pub(crate) async fn raw_writer(conn: &mut sqlx::PgConnection) {\n\
sqlx::query(\"INSERT INTO events (community_id, id) VALUES ($1, $2)\")\n\
.execute(conn)\n\
.await\n\
.expect(\"write\");\n\
}\n";
for (test_item, hidden) in [
("struct X;", "struct X"),
("fn helper() {} // test helper", "fn helper"),
("const BRACES: &str = \"{}\"; // fixture", "BRACES"),
(
"const URL: &str = \"https://relay.test\"; // fixture",
"relay.test",
),
("fn u() -> &'static str { \"ws://x\" } // c", "ws://x"),
] {
let production = strip_cfg_test_items(&format!("#[cfg(test)]\n{test_item}\n{raw_writer}"));
assert!(
!production.contains(hidden),
"`{test_item}` is test-only and must be skipped: {production}"
);
assert!(
production_contains_guarded_write(&production),
"a guarded write after `{test_item}` must stay visible: {production}"
);
assert_eq!(
syntactic_guarded_write_route_violations(&production),
["pub(crate) async fn raw_writer(conn: &mut sqlx::PgConnection) {"],
"a raw writer after `{test_item}` must still be scanned"
);
}
}
fn should_scan_guarded_write_source_file(path: &std::path::Path) -> bool {
let file_name = path
.file_name()
.and_then(|name| name.to_str())
.unwrap_or("");
path.extension().is_some_and(|ext| ext == "rs")
// Whole files loaded only via `#[cfg(test)] #[path = "..."] mod ...;` are
// entirely test code but carry no internal `#[cfg(test)]` marker of their
// own to slice against. Standalone `*_tests.rs` modules share that shape.
&& file_name != "tests.rs"
&& !file_name.ends_with("_tests.rs")
}
#[test]
fn serving_table_policy_skips_standalone_test_modules() {
use std::path::Path;
assert!(
!should_scan_guarded_write_source_file(Path::new("src/runtime/tests.rs")),
"`tests.rs` is a standalone test-only module"
);
assert!(
!should_scan_guarded_write_source_file(Path::new(
"src/store/thread_window/postgres_tests.rs",
)),
"`*_tests.rs` modules are standalone test-only sources, not production seams"
);
assert!(
!should_scan_guarded_write_source_file(Path::new("src/store/foo_tests.rs")),
"the suffix-based rule must cover other standalone test-only modules"
);
assert!(
should_scan_guarded_write_source_file(Path::new("src/store/event.rs")),
"production source must remain in scope"
);
}
#[test]
fn serving_table_writes_expose_syntactic_chokepoint_or_guarded_tx_routes() {
use std::path::{Path, PathBuf};
fn collect_rs_files(dir: &Path, out: &mut Vec<PathBuf>) {
for entry in std::fs::read_dir(dir).expect("read source directory") {
let entry = entry.expect("read directory entry");
let path = entry.path();
if path.is_dir() {
collect_rs_files(&path, out);
} else if should_scan_guarded_write_source_file(&path) {
out.push(path);
}
}
}
// `buzz-relay` is scanned too: relay code reaches fenced tables only through
// `buzz-db` helpers, and a direct relay write must meet the same rule.
let manifest = Path::new(env!("CARGO_MANIFEST_DIR"));
let mut checked_guarded_files = 0usize;
for (crate_name, src_root) in [
("buzz-db", manifest.join("src")),
("buzz-relay", manifest.join("../buzz-relay/src")),
] {
let mut files = Vec::new();
collect_rs_files(&src_root, &mut files);
assert!(
!files.is_empty(),
"guarded-table scan must see {crate_name} production source files"
);
for path in files {
let relative = path
.strip_prefix(&src_root)
.expect("file is under src root")
.to_string_lossy()
.replace('\\', "/");
let source = std::fs::read_to_string(&path).expect("read source file");
let production = strip_cfg_test_items(&source);
if production_contains_guarded_write(&production) {
checked_guarded_files += 1;
let violations = syntactic_guarded_write_route_violations(&production);
assert!(
violations.is_empty(),
"{crate_name}/src/{relative} has guarded-table INSERT/UPDATE/DELETE seams \
that neither open the tenant-local chokepoint nor take `&mut AdmittedTx`: \
{violations:?}"
);
}
}
}
assert!(
checked_guarded_files > 0,
"guarded-table scan must exercise at least one file that writes a fenced table"
);
}
// Rolled-back verification probes write `events` rows that never commit, so
// they owe no push job or TTL refresh.
const EVENT_INSERT_FOLLOW_UP_EXCEPTIONS: [&str; 2] = [
"pub async fn verify_floor_guard_behavior(",
"pub async fn verify_channel_roster_fence_behavior(",
];
/// `source` with every `#[cfg(test)]` item removed, wherever it sits in the
/// file. Each item runs to the `;` that ends it or to the brace that closes
/// its body. Braces inside comments and literals are not counted (see
/// [`mask_comments_and_literals`]). Unlike cutting the file at the first
/// `#[cfg(test)]`, this keeps production code that follows a test-only item.
/// The whole file is masked once and the marker is found in that view, so a
/// mention in a comment or string (even a multiline one) strips nothing. The
/// marker also counts only when it is the first code on its line.
fn strip_cfg_test_items(source: &str) -> String {
const MARKER: &str = "#[cfg(test)]";
let masked = mask_comments_and_literals(source);
let mut kept = String::with_capacity(source.len());
let mut copied = 0usize;
let mut search = 0usize;
while let Some(found) = masked[search..].find(MARKER) {
let at = search + found;
let line_start = masked[..at].rfind('\n').map_or(0, |newline| newline + 1);
search = at + MARKER.len();
if !masked[line_start..at].trim().is_empty() {
continue;
}
kept.push_str(&source[copied..at]);
copied = search + cfg_test_item_len(&masked[search..]);
search = copied;
}
kept.push_str(&source[copied..]);
kept
}
/// Length of the item that starts `masked_item`, which must already be masked
/// so braces in comments and literals are not counted.
fn cfg_test_item_len(masked_item: &str) -> usize {
let mut depth = 0usize;
for (index, byte) in masked_item.bytes().enumerate() {
match byte {
b';' if depth == 0 => return index + 1,
b'{' => depth += 1,
b'}' => {
depth = depth.saturating_sub(1);
if depth == 0 {
return index + 1;
}
}
_ => {}
}
}
masked_item.len()
}
/// Whether the `r` at `index` begins a raw literal token: `r"`, or the `br"` /
/// `cr"` raw byte and C strings, rather than ending an identifier such as `for"`.
fn raw_literal_prefix_starts_token(bytes: &[u8], index: usize) -> bool {
let is_ident = |at: Option<usize>| {
at.and_then(|at| bytes.get(at))
.is_some_and(|byte| byte.is_ascii_alphanumeric() || *byte == b'_')
};
let before = index.checked_sub(1);
!is_ident(before)
|| (matches!(before.map(|at| bytes[at]), Some(b'b' | b'c'))
&& !is_ident(index.checked_sub(2)))
}
/// `source` with every comment (`//`, `///`, nested `/* */`) and every string,
/// raw string, and char literal replaced by spaces, byte for byte. Newlines are
/// kept, so offsets and line structure match `source`. Scans that look for
/// code (braces, calls) read the masked text; scans that look for SQL read
/// the original, because the SQL lives inside string literals.
fn mask_comments_and_literals(source: &str) -> String {
let bytes = source.as_bytes();
let mut masked = bytes.to_vec();
let mut blank = |from: usize, to: usize| {
for byte in &mut masked[from..to.min(bytes.len())] {
if *byte != b'\n' {
*byte = b' ';
}
}
};
let mut index = 0usize;
while index < bytes.len() {
let start = index;
match bytes[index] {
b'/' if bytes.get(index + 1) == Some(&b'/') => {
while index < bytes.len() && bytes[index] != b'\n' {
index += 1;
}
}
b'/' if bytes.get(index + 1) == Some(&b'*') => {
let mut depth = 0usize;
while index < bytes.len() {
if bytes[index] == b'/' && bytes.get(index + 1) == Some(&b'*') {
depth += 1;
index += 2;
} else if bytes[index] == b'*' && bytes.get(index + 1) == Some(&b'/') {
depth -= 1;
index += 2;
if depth == 0 {
break;
}
} else {
index += 1;
}
}
}
b'r' if matches!(bytes.get(index + 1), Some(b'"' | b'#'))
&& raw_literal_prefix_starts_token(bytes, index)
&& bytes.get(
index
+ 1
+ bytes[index + 1..]
.iter()
.take_while(|byte| **byte == b'#')
.count(),
) == Some(&b'"') =>
{
let hashes = bytes[index + 1..]
.iter()
.take_while(|byte| **byte == b'#')
.count();
let close = format!("\"{}", "#".repeat(hashes));
let body = index + 2 + hashes;
index = source[body..]
.find(&close)
.map_or(bytes.len(), |offset| body + offset + close.len());
}
b'"' => {
index += 1;
while index < bytes.len() && bytes[index] != b'"' {
index += if bytes[index] == b'\\' { 2 } else { 1 };
}
index += 1;
}
b'\'' if char_literal_end(source, index).is_some() => {
index = char_literal_end(source, index).expect("guard matched");
}
_ => {
index += 1;
continue;
}
}
blank(start, index);
}
String::from_utf8(masked).expect("masking replaces whole ASCII-delimited spans")
}
/// The end (exclusive) of the char literal whose opening quote is at `start`,
/// such as `'{'`, `'é'`, `'\''`, `'\x7f'`, or `'\u{1F600}'`. `None` for a
/// lifetime, which has no closing quote.
fn char_literal_end(source: &str, start: usize) -> Option<usize> {
// `start` holds an ASCII quote, so `start + 1` is a char boundary.
let rest = &source[start + 1..];
let body = if let Some(escape) = rest.strip_prefix('\\') {
// The longest escape is `\u{10FFFF}`: a backslash and 9 more bytes.
// Skip the escaped byte so `'\''` does not close on its own quote.
let close = escape.get(1..)?.find('\'')? + 1;
(close <= 9).then_some(1 + close)?
} else {
rest.chars().next()?.len_utf8()
};
rest[body..]
.starts_with('\'')
.then_some(start + 1 + body + 1)
}
#[test]
fn masker_blanks_every_literal_form_and_keeps_code() {
for (input, expected) in [
// Raw strings with zero, one, or several `#`, and the `b`/`c` raw
// prefixes. Each body ends in `\`, which would escape the closing quote
// if the literal were scanned as a plain string.
(r#"a(r"\") {}"#, "a( ) {}"),
(r###"a(r##"x"#{"##) {}"###, "a( ) {}"),
(r##"a(br#"\"#) {}"##, "a(b ) {}"),
(r#"a(br"\") {}"#, "a(b ) {}"),
(r#"a(cr"\") {}"#, "a(c ) {}"),
// Plain byte and C strings.
(r#"a(b"{") {}"#, "a(b ) {}"),
(r#"a(c"{") {}"#, "a(c ) {}"),
// Plain and escaped char literals.
(
r"a('\'', '\\', '\n', '{') {}",
"a( , , , ) {}",
),
// Multi-byte and long-escape char literals. Each holds a brace, or
// hides one from a scan that misreads the literal's length.
("a('é', '{') {}", "a( , ) {}"),
(r"a('\u{1F600}', '{') {}", "a( , ) {}"),
(r"a('\x7f', '{') {}", "a( , ) {}"),
// Code that only looks like a literal stays as it is.
("let r#type = 1; {}", "let r#type = 1; {}"),
("fn f<'a>(x: &'a str) {}", "fn f<'a>(x: &'a str) {}"),
] {
assert_eq!(
mask_comments_and_literals(input),
expected,
"masking `{input}`"
);
}
}
/// Whether `source` inserts into `events` itself (not `event_mentions` or any
/// other `events_*` table), in any letter case, across line breaks and string
/// continuations, `\n`/`\t`/`\r` escapes, and with or without a `public.`
/// schema prefix.
fn inserts_events_row(source: &str) -> bool {
!events_insert_tails(source).is_empty()
}
/// `source` lowercased, with every run of whitespace, `\` line continuations,
/// and `\n`/`\t`/`\r` escapes collapsed to one space.
fn normalized_sql(source: &str) -> String {
let mut normalized = String::with_capacity(source.len());
let mut chars = source.chars().peekable();
while let Some(ch) = chars.next() {
if ch.is_whitespace() || ch == '\\' {
if ch == '\\' && matches!(chars.peek(), Some('n' | 't' | 'r')) {
chars.next();
}
if !normalized.ends_with(' ') {
normalized.push(' ');
}
} else {
normalized.push(ch.to_ascii_lowercase());
}
}
normalized
}
/// The normalized SQL that follows each `INSERT INTO events` in `source`.
fn events_insert_tails(source: &str) -> Vec<String> {
let normalized = normalized_sql(source);
let mut tails = Vec::new();
for marker in ["insert into events", "insert into public.events"] {
for (index, marker) in normalized.match_indices(marker) {
let tail = &normalized[index + marker.len()..];
if tail
.chars()
.next()
.is_none_or(|next| !(next.is_ascii_alphanumeric() || next == '_'))
{
tails.push(tail.to_owned());
}
}
}
tails
}
/// An events insert must run both follow-ups: the push enqueue and the
/// pre-commit TTL refresh. `after_admitted_insert` does both; a writer that
/// inserts inside a savepoint calls the two halves separately.
///
/// Only real calls count: a hook in a comment or a string literal does not.
fn runs_event_follow_ups(function_source: &str) -> bool {
let code = mask_comments_and_literals(function_source);
code.contains("event_follow_up::after_admitted_insert(")
|| (code.contains("event_follow_up::enqueue_push_match(")
&& code.contains(".record_channel_event("))
}
fn event_insert_follow_up_violations(production_source: &str) -> Vec<String> {
function_slices(production_source)
.into_iter()
.filter(|function_source| {
let header = function_header(function_source);
inserts_events_row(function_source)
&& !EVENT_INSERT_FOLLOW_UP_EXCEPTIONS
.iter()
.any(|exception| header.starts_with(exception))
&& !runs_event_follow_ups(function_source)
})
.map(|function_source| function_header(function_source).to_owned())
.collect()
}
#[test]
fn event_insert_follow_up_policy_rejects_writers_without_the_hook() {
let source = r##"
pub(crate) async fn unhooked_writer(tx: &mut AdmittedTx) {
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)")
.execute(tx.conn())
.await
.expect("write");
}
pub(crate) async fn push_only_writer(tx: &mut AdmittedTx) {
sqlx::query("INSERT INTO events \
(community_id, id) VALUES ($1, $2)")
.execute(tx.conn())
.await
.expect("write");
crate::store::event_follow_up::enqueue_push_match(tx.conn(), community, id, kind)
.await
.expect("enqueue");
}
pub(crate) async fn hooked_writer(tx: &mut AdmittedTx) {
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)")
.execute(tx.conn())
.await
.expect("write");
crate::store::event_follow_up::after_admitted_insert(tx, id, kind, channel)
.await
.expect("follow up");
}
pub(crate) async fn savepoint_writer(tx: &mut AdmittedTx) {
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)")
.execute(tx.conn())
.await
.expect("write");
crate::store::event_follow_up::enqueue_push_match(tx.conn(), community, id, kind)
.await
.expect("enqueue");
tx.record_channel_event(channel, kind);
}
pub(crate) async fn mention_writer(tx: &mut AdmittedTx) {
sqlx::query("INSERT INTO event_mentions (community_id, event_id) VALUES ($1, $2)")
.execute(tx.conn())
.await
.expect("write");
}
pub async fn verify_floor_guard_behavior(pool: &PgPool) {
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)")
.execute(pool)
.await
.expect("probe");
}
pub(crate) async fn lowercase_writer(tx: &mut AdmittedTx) {
sqlx::query("insert into
public.events (community_id, id) VALUES ($1, $2)")
.execute(tx.conn())
.await
.expect("write");
}
pub(crate) async fn continued_writer(tx: &mut AdmittedTx) {
sqlx::query("INSERT INTO \
events (community_id, id) VALUES ($1, $2)")
.execute(tx.conn())
.await
.expect("write");
}
/// Kept at module scope, not `#[cfg(test)]`, because production calls it.
pub(crate) async fn writer_under_doc_mention(tx: &mut AdmittedTx) {
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)")
.execute(tx.conn())
.await
.expect("write");
}
// see `#[cfg(test)] mod x {`
pub(crate) async fn writer_under_brace_mention(tx: &mut AdmittedTx) {
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)")
.execute(tx.conn())
.await
.expect("write");
}
pub(crate) async fn writer_with_escaped_newline(tx: &mut AdmittedTx) {
sqlx::query("INSERT INTO\nevents (community_id, id) VALUES ($1, $2)")
.execute(tx.conn())
.await
.expect("write");
}
pub(crate) async fn writer_with_commented_out_hook(tx: &mut AdmittedTx) {
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)")
.execute(tx.conn())
.await
.expect("write");
// crate::store::event_follow_up::after_admitted_insert(tx, id, kind, channel)
}
pub(crate) async fn writer_with_block_commented_hook(tx: &mut AdmittedTx) {
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)")
.execute(tx.conn())
.await
.expect("write");
/* crate::store::event_follow_up::enqueue_push_match(tx.conn(), community, id, kind)
/* nested */ tx.record_channel_event(channel, kind); */
}
pub(crate) async fn writer_with_hook_in_string(tx: &mut AdmittedTx) {
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)")
.execute(tx.conn())
.await
.expect("write");
tracing::debug!("skipped event_follow_up::after_admitted_insert(tx, ..)");
}
pub(crate) async fn writer_with_nested_block_commented_hook(tx: &mut AdmittedTx) {
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)")
.execute(tx.conn())
.await
.expect("write");
/* /* inner */ crate::store::event_follow_up::after_admitted_insert(tx, id, kind, channel) */
}
pub(crate) async fn writer_with_hook_after_escaped_quote(tx: &mut AdmittedTx) {
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)")
.execute(tx.conn())
.await
.expect("write");
tracing::debug!("\"event_follow_up::after_admitted_insert(\"");
}
pub(crate) async fn writer_with_hook_in_raw_string(tx: &mut AdmittedTx) {
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)")
.execute(tx.conn())
.await
.expect("write");
tracing::debug!(r#""event_follow_up::after_admitted_insert(""#);
}
/*
#[cfg(test)]
*/
pub(crate) async fn writer_after_block_commented_cfg_test(tx: &mut AdmittedTx) {
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)")
.execute(tx.conn())
.await
.expect("write");
}
pub(crate) async fn writer_after_raw_string_example(tx: &mut AdmittedTx) {
let _example = r#"
fn example() {
crate::store::event_follow_up::after_admitted_insert(tx, id, kind, channel)
}
"#;
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)")
.execute(tx.conn())
.await
.expect("write");
}
#[cfg(test)]
fn test_only_helper() -> (&'static str, char, &'static str, char, &'static [u8]) {
// an unbalanced { in a comment
("{", '{', r#"}"} {"#, '"', br#"\"#)
}
pub(crate) async fn writer_after_test_helper(tx: &mut AdmittedTx) {
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)")
.execute(tx.conn())
.await
.expect("write");
}
#[cfg(test)]
mod tests {
async fn test_writer(pool: &PgPool) {
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)")
.execute(pool)
.await
.expect("fixture");
}
}
"##;
assert_eq!(
event_insert_follow_up_violations(&strip_cfg_test_items(source)),
[
"pub(crate) async fn unhooked_writer(tx: &mut AdmittedTx) {",
"pub(crate) async fn push_only_writer(tx: &mut AdmittedTx) {",
"pub(crate) async fn lowercase_writer(tx: &mut AdmittedTx) {",
"pub(crate) async fn continued_writer(tx: &mut AdmittedTx) {",
"pub(crate) async fn writer_under_doc_mention(tx: &mut AdmittedTx) {",
"pub(crate) async fn writer_under_brace_mention(tx: &mut AdmittedTx) {",
"pub(crate) async fn writer_with_escaped_newline(tx: &mut AdmittedTx) {",
"pub(crate) async fn writer_with_commented_out_hook(tx: &mut AdmittedTx) {",
"pub(crate) async fn writer_with_block_commented_hook(tx: &mut AdmittedTx) {",
"pub(crate) async fn writer_with_hook_in_string(tx: &mut AdmittedTx) {",
"pub(crate) async fn writer_with_nested_block_commented_hook(tx: &mut AdmittedTx) {",
"pub(crate) async fn writer_with_hook_after_escaped_quote(tx: &mut AdmittedTx) {",
"pub(crate) async fn writer_with_hook_in_raw_string(tx: &mut AdmittedTx) {",
"pub(crate) async fn writer_after_block_commented_cfg_test(tx: &mut AdmittedTx) {",
"pub(crate) async fn writer_after_raw_string_example(tx: &mut AdmittedTx) {",
"pub(crate) async fn writer_after_test_helper(tx: &mut AdmittedTx) {",
],
"an events insert must run the push enqueue and record the TTL refresh"
);
}
fn production_event_writer_source_files() -> Vec<std::path::PathBuf> {
use std::path::{Path, PathBuf};
fn collect_rs_files(dir: &Path, out: &mut Vec<PathBuf>) {
for entry in std::fs::read_dir(dir).expect("read source directory") {
let path = entry.expect("read directory entry").path();
if path.is_dir() {
collect_rs_files(&path, out);
} else if should_scan_guarded_write_source_file(&path) {
out.push(path);
}
}
}
let crates_root = Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.expect("crate lives under crates/");
let mut files = Vec::new();
for entry in std::fs::read_dir(crates_root).expect("read crates directory") {
let src = entry.expect("read crates entry").path().join("src");
if src.is_dir() {
collect_rs_files(&src, &mut files);
}
}
files
}
#[test]
fn every_production_event_insert_runs_the_follow_up_hook() {
let files = production_event_writer_source_files();
let mut hooked_writers = 0usize;
let mut violations = Vec::new();
for path in files {
let source = std::fs::read_to_string(&path).expect("read source file");
let production = strip_cfg_test_items(&source);
if !inserts_events_row(&production) {
continue;
}
for function_source in function_slices(&production) {
if inserts_events_row(function_source) && runs_event_follow_ups(function_source) {
hooked_writers += 1;
}
}
violations.extend(
event_insert_follow_up_violations(&production)
.into_iter()
.map(|header| format!("{}: {header}", path.display())),
);
}
assert!(
violations.is_empty(),
"production `INSERT INTO events` writers must call `event_follow_up` because no \
trigger does the push enqueue or channel TTL refresh: {violations:?}"
);
// The seven writers at the time of BUZZ-176. A lower count means the scan
// stopped seeing a writer, not that one was removed safely.
assert!(
hooked_writers >= 7,
"expected at least seven hooked event writers, found {hooked_writers}"
);
}
// Fixed channel-less kinds cannot be channel rosters. The startup probes must
// exercise the retained DB backstops directly and roll back all their writes.
// Exemptions match by function header, so a kind 39002 insert added inside one
// of these functions would pass the policy; keep them single-purpose.
const EVENT_INSERT_ROSTER_GUARD_EXCEPTIONS: [&str; 4] = [
// NIP-43 membership is kind 13534 with channel_id NULL.
"pub(crate) async fn write_nip43_membership_snapshot(",
// Push leases are kind 30350 with channel_id NULL.
"pub async fn accept_lease_event(",
// Rolled-back replica-floor startup probe.
"pub async fn verify_floor_guard_behavior(",
// Rolled-back roster-fence startup probe.
"pub async fn verify_channel_roster_fence_behavior(",
];
fn event_insert_roster_guard_violations(production_source: &str) -> Vec<String> {
function_slices(production_source)
.into_iter()
.filter(|function_source| {
let header = function_header(function_source);
let code = mask_comments_and_literals(function_source);
inserts_events_row(function_source)
&& !EVENT_INSERT_ROSTER_GUARD_EXCEPTIONS
.iter()
.any(|exception| header.starts_with(exception))
&& !code.contains("guard_channel_roster_event(")
&& !code.contains("validate_channel_roster_snapshot(")
})
.map(|function_source| function_header(function_source).to_owned())
.collect()
}
#[test]
fn event_insert_roster_guard_policy_rejects_writers_without_the_guard() {
let source = r#"
pub(crate) async fn unguarded_writer(tx: &mut AdmittedTx) {
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)");
}
pub(crate) async fn guarded_writer(tx: &mut AdmittedTx) {
guard_channel_roster_event(tx, kind, channel, tags).await?;
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)");
}
pub(crate) async fn locked_writer(tx: &mut AdmittedTx) {
validate_channel_roster_snapshot(tx, channel, tags).await?;
sqlx::query("insert into public.events (community_id, id) VALUES ($1, $2)");
}
pub(crate) async fn mention_writer(tx: &mut AdmittedTx) {
sqlx::query("INSERT INTO event_mentions (community_id, event_id) VALUES ($1, $2)");
}
pub(crate) async fn commented_guard_writer(tx: &mut AdmittedTx) {
// guard_channel_roster_event(tx, kind, channel, tags).await?;
/* validate_channel_roster_snapshot(tx, channel, tags).await?; */
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)");
}
pub(crate) async fn quoted_guard_writer(tx: &mut AdmittedTx) {
tracing::debug!("guard_channel_roster_event(");
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)");
}
pub(crate) async fn write_nip43_membership_snapshot(tx: &mut AdmittedTx) {
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)");
}
pub async fn accept_lease_event(tx: &mut AdmittedTx) {
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)");
}
pub async fn verify_floor_guard_behavior(pool: &PgPool) {
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)");
}
pub async fn verify_channel_roster_fence_behavior(pool: &PgPool) {
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)");
}
#[cfg(test)]
mod tests {
fn fixture() {
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)");
}
}
"#;
assert_eq!(
event_insert_roster_guard_violations(&strip_cfg_test_items(source)),
[
"pub(crate) async fn unguarded_writer(tx: &mut AdmittedTx) {",
"pub(crate) async fn commented_guard_writer(tx: &mut AdmittedTx) {",
"pub(crate) async fn quoted_guard_writer(tx: &mut AdmittedTx) {",
],
"only real roster-guard calls in production writers count"
);
}
/// Presence check: every production `INSERT INTO events` function must contain
/// a real (uncommented, unquoted) roster-guard call. It does not prove the call
/// is awaited, reachable, or given the event's real kind and channel; the PG
/// `app_roster_guard_*` tests cover behavior.
#[test]
fn every_production_event_insert_runs_the_roster_guard() {
let mut checked_writers = 0;
let mut violations = Vec::new();
for path in production_event_writer_source_files() {
let source = std::fs::read_to_string(&path).expect("read source file");
let production = strip_cfg_test_items(&source);
checked_writers += function_slices(&production)
.into_iter()
.filter(|function| inserts_events_row(function))
.count();
violations.extend(
event_insert_roster_guard_violations(&production)
.into_iter()
.map(|header| format!("{}: {header}", path.display())),
);
}
assert!(
violations.is_empty(),
"production `INSERT INTO events` writers must run the app-side roster guard: {violations:?}"
);
// Five guarded writers plus the four explicitly exempt writers/probes.
assert!(
checked_writers >= 9,
"expected at least nine event writers, found {checked_writers}"
);
}
// The rolled-back replica-floor probe writes a kind 9 row, which has no `d`.
const EVENT_INSERT_D_TAG_EXCEPTIONS: [&str; 1] = ["pub async fn verify_floor_guard_behavior("];
/// Whether an `INSERT INTO events` tail names `d_tag` in an explicit column
/// list. An insert without a column list does not count.
fn events_insert_names_d_tag(tail: &str) -> bool {
tail.trim_start()
.strip_prefix('(')
.and_then(|columns| columns.split_once(')'))
.is_some_and(|(columns, _)| columns.split(',').any(|column| column.trim() == "d_tag"))
}
fn event_insert_d_tag_violations(production_source: &str) -> Vec<String> {
function_slices(production_source)
.into_iter()
.filter(|function_source| {
let header = function_header(function_source);
!EVENT_INSERT_D_TAG_EXCEPTIONS
.iter()
.any(|exception| header.starts_with(exception))
&& !events_insert_tails(function_source)
.iter()
.all(|tail| events_insert_names_d_tag(tail))
})
.map(|function_source| function_header(function_source).to_owned())
.collect()
}
#[test]
fn event_insert_d_tag_policy_rejects_writers_without_the_column() {
let source = r##"
pub(crate) async fn writer_without_d_tag(tx: &mut AdmittedTx) {
sqlx::query("INSERT INTO events (community_id, id, kind) VALUES ($1, $2, $3)");
}
pub(crate) async fn writer_with_d_tag(tx: &mut AdmittedTx) {
sqlx::query("INSERT INTO events \
(community_id, id, kind, d_tag) VALUES ($1, $2, $3, $4)");
}
pub(crate) async fn writer_with_compact_columns(tx: &mut AdmittedTx) {
sqlx::query("insert into public.events (community_id,id,kind,d_tag) VALUES ($1,$2,$3,$4)");
}
pub(crate) async fn writer_with_d_tag_only_in_values(tx: &mut AdmittedTx) {
sqlx::query("INSERT INTO events (community_id, id, kind) SELECT community_id, id, d_tag FROM x");
}
pub(crate) async fn writer_with_lookalike_column(tx: &mut AdmittedTx) {
sqlx::query("INSERT INTO events (community_id, id, kind, d_tag_hash) VALUES ($1, $2, $3, $4)");
}
pub(crate) async fn writer_without_column_list(tx: &mut AdmittedTx) {
sqlx::query("INSERT INTO events SELECT * FROM staged_events");
}
pub(crate) async fn second_insert_without_d_tag(tx: &mut AdmittedTx) {
sqlx::query("INSERT INTO events (community_id, id, d_tag) VALUES ($1, $2, $3)");
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)");
}
pub(crate) async fn mention_writer(tx: &mut AdmittedTx) {
sqlx::query("INSERT INTO event_mentions (community_id, event_id) VALUES ($1, $2)");
}
pub async fn verify_floor_guard_behavior(pool: &PgPool) {
sqlx::query("INSERT INTO events (community_id, id, kind) VALUES ($1, $2, 9)");
}
#[cfg(test)]
mod tests {
fn fixture() {
sqlx::query("INSERT INTO events (community_id, id) VALUES ($1, $2)");
}
}
"##;
assert_eq!(
event_insert_d_tag_violations(&strip_cfg_test_items(source)),
[
"pub(crate) async fn writer_without_d_tag(tx: &mut AdmittedTx) {",
"pub(crate) async fn writer_with_d_tag_only_in_values(tx: &mut AdmittedTx) {",
"pub(crate) async fn writer_with_lookalike_column(tx: &mut AdmittedTx) {",
"pub(crate) async fn writer_without_column_list(tx: &mut AdmittedTx) {",
"pub(crate) async fn second_insert_without_d_tag(tx: &mut AdmittedTx) {",
],
"every production events insert must name `d_tag` in its column list"
);
}
/// Addressable events (kinds 30000-39999) replace by `d_tag`, and the only
/// index that serves that lookup is partial on `d_tag IS NOT NULL`. A writer
/// that leaves the column out stores NULL, so its rows never replace. Nothing
/// repairs them after the fact, so every production writer must name the column.
///
/// This is a text match, not a proof. It checks the column list of literal
/// `INSERT INTO [public.]events (...)` SQL inside function bodies under each
/// crate's `src/`. It does not see `ONLY`, quoted or `format!`-built table
/// names, a comment between the keywords, or SQL held in a constant outside a
/// function, and it does not check that the bound value is non-NULL. Exempt
/// probes are skipped by function header, so every insert inside them is too.
#[test]
fn every_production_event_insert_names_d_tag() {
let mut checked_writers = 0;
let mut violations = Vec::new();
for path in production_event_writer_source_files() {
let source = std::fs::read_to_string(&path).expect("read source file");
let production = strip_cfg_test_items(&source);
checked_writers += function_slices(&production)
.into_iter()
.filter(|function| inserts_events_row(function))
.count();
violations.extend(
event_insert_d_tag_violations(&production)
.into_iter()
.map(|header| format!("{}: {header}", path.display())),
);
}
assert!(
violations.is_empty(),
"production `INSERT INTO events` writers must name `d_tag` in their column list: \
{violations:?}"
);
// The same writer set as the roster-guard scan. A lower count means the
// scan stopped seeing a writer, not that one was removed safely.
assert!(
checked_writers >= 9,
"expected at least nine event writers, found {checked_writers}"
);
}