Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/workflows/_ci-relay.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ jobs:
with:
path: |
target/ci/buzz-relay
target/ci/buzz
target/ci/git-credential-nostr
target/ci/backend-integration-tests.tar.zst
target/ci/postgres-tests.tar.zst
Expand Down Expand Up @@ -84,7 +85,7 @@ jobs:
env:
RUSTC_WRAPPER: sccache
run: |
cargo build --profile ci -p buzz-relay -p git-credential-nostr
cargo build --profile ci -p buzz-relay -p git-credential-nostr -p buzz-cli
cargo nextest archive \
--cargo-profile ci \
-p buzz-db \
Expand Down Expand Up @@ -115,6 +116,7 @@ jobs:
with:
path: |
target/ci/buzz-relay
target/ci/buzz
target/ci/git-credential-nostr
target/ci/backend-integration-tests.tar.zst
target/ci/postgres-tests.tar.zst
Expand All @@ -125,6 +127,7 @@ jobs:
name: desktop-e2e-relay
path: |
target/ci/buzz-relay
target/ci/buzz
target/ci/git-credential-nostr
target/ci/backend-integration-tests.tar.zst
target/ci/postgres-tests.tar.zst
Expand Down Expand Up @@ -199,12 +202,13 @@ jobs:
name: desktop-e2e-relay
path: target/ci
- name: Restore relay executable permission
run: chmod +x ./target/ci/buzz-relay
run: chmod +x ./target/ci/buzz-relay ./target/ci/buzz
- name: PostgreSQL-backed tests
env:
BUZZ_POSTGRES_ADMIN_URL: postgres://buzz:${{ env.BUZZ_TEST_POSTGRES_PASSWORD }}@localhost:5432/postgres
PGPASSWORD: ${{ env.BUZZ_TEST_POSTGRES_PASSWORD }}
run: |
export BUZZ_TEST_BIN_DIR="$PWD/target/ci"
scripts/postgres-test-run.sh \
--archive-file target/ci/postgres-tests.tar.zst

Expand Down
5 changes: 3 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

451 changes: 444 additions & 7 deletions FORK_PATCHES.md

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,10 @@ test-unit:
# `tests::postgres_tests::` stays in the PostgreSQL lane.
cargo nextest run -p buzz-relay --lib --bin buzz-relay \
-E 'test(/^api::admin::/) + test(/^handlers::channel_authz::/) + test(/^handlers::moderation_authz::/) + test(/^handlers::side_effects::tests::/) + test(/^storage_sweep::tests::/) + test(/^nip_fi_core::tests::/) + test(/^nip_fi_http::tests::/) + test(/^nip_fi_config::tests::/) + test(/^readiness::tests::/) + test(/^router::tests::/) + test(/^api::parse_query_tests::/) + test(/^api::git::transport::off_mode_precedence_tests::/) + test(/^audio::join::tests::/) + test(/^audio::handler::tests::/) + test(/^nip_fi_gate::tests::/) + test(/^nip_fi_session::tests::/) + test(/^nip_fi_shadow::tests::/) + test(/^nip_fi_shadow_session::tests::/) + test(=state::tests::neither_a_confirmed_inactive_community_nor_a_failed_lookup_admits_the_socket) + test(=handlers::req::tests::timed_out_historical_read_deregisters_before_closed) + test(=handlers::req::tests::superseded_timeout_leaves_replacement_intact) + test(=handlers::req::tests::search_claim_retires_live_and_yields_to_replacement) + test(=handlers::req::tests::concurrent_claims_and_stale_teardowns_keep_the_last_owner) + test(=handlers::req::tests::timeout_closed_is_emitted_before_a_replacement_can_claim) + test(=handlers::req::tests::revoke_then_replacement_keeps_replacement_whole) + test(=handlers::req::tests::claims_after_connection_cleanup_are_refused) + test(=handlers::req::tests::dropped_terminal_frame_cancels_connection) + test(=handlers::req::tests::revoke_dropped_terminal_frame_cancels_connection) + (kind(bin) & (test(/^tests::/) + test(/^composition_tests::/) + test(/^env_filter_tests::/)) - test(/^tests::postgres_tests::/))'
# Ordinary drain ticks must not discard a partially read mesh frame.
# Infra-free; keep the cargo fallback in scripts/run-tests.sh in step.
cargo nextest run -p buzz-relay --lib \
-E 'test(=mesh_boot::tests::demo_echo_keeps_inflight_read_across_ticks_and_still_drains)'
# Note on audio::join::tests scope: the full suite is infra-free (no DB,
# no Redis). The infra-free audio/FI regression witnesses — bootstrap
# ordering barrier, CommitConfirmed arm, pending-close invisibility,
Expand Down
22 changes: 22 additions & 0 deletions TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,28 @@

## Automated Tests

For managed final-report recovery, explicitly run the ignored production-ingest
and standalone-CLI HTTP checks against an isolated schema-compatible Postgres
and Redis. The HTTP check uses real NIP-98 verification and replay protection:

```sh
cargo build -p buzz-cli
BUZZ_TEST_BIN_DIR="$PWD/target/debug" \
cargo test -p buzz-relay managed_old_ -- --ignored --test-threads=1
cargo test -p buzz-relay managed_timestamp_exception -- --test-threads=1
```

Set `BUZZ_TEST_DATABASE_URL` and `REDIS_URL` to the isolated services first.
These checks use `Db::new` and production migrations, assert the 960-second
writer floor and cover absent/accepted reports, earlier accepted candidates,
soft-deleted originals, candidate renewal, current authority and concurrent
original/recovery elections. Historical acceptance is explicitly seeded through
a separate fixture pool; tested recovery never disarms production connections.
Kind 20003 is a signed recovery command over the existing event bridge. The relay
atomically elects one signed kind-9 report and returns its canonical signed event.
A coordinated relay upgrade must drain old writers before enabling recovery;
unknown-server wrapper acknowledgment is not report confirmation.

```bash
just test-unit # unit tests — no infrastructure needed
just test # unit + integration (starts Docker if needed)
Expand Down
98 changes: 90 additions & 8 deletions crates/buzz-acp/TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@ cargo test -p buzz-acp
```

Managed agent sessions may already export harness options. Clear them when
running the package suite: three CLI parsing tests assert the unset defaults,
running the package suite: CLI parsing tests assert the unset defaults,
and inherited values would change the inputs those tests exercise. Running the
package serially also avoids scheduling flakes in existing short-deadline tests.

```sh
env -u BUZZ_ACP_ALLOWED_RESPOND_TO \
-u BUZZ_ACP_LAZY_POOL \
-u BUZZ_ACP_IDLE_POOL_SLEEP \
-u BUZZ_ACP_SESSION_POLICY \
cargo test -p buzz-acp -- --test-threads=1
```

Expand All @@ -45,30 +46,61 @@ BUZZ_TEST_PI_ACP=/absolute/buzz-pi-acp/dist/index.js \
On Unix, the `background` and `process_` test filters exercise real local ACP
adapters and child processes. They cover provider residency, direct and nested
work, bounded process enumeration, queue serialization, retry exhaustion,
session rotation, and completion while a result is being delivered. They do not
require provider credentials or an external relay.
session rotation, and completion while a result is being delivered. Marker-controlled
ACP adapters exercise `dispatch_pending` and result handling for collection before
and after new messages, backoff across successful ordinary turns, steer/interrupt,
original Channel/Thread/DM routes, generation replacement and channel revocation.
A retry-exhaustion test drives real ACP failures and captures the signed notice;
it advances queue retry eligibility directly to avoid waiting the full backoff.
They do not require provider credentials or an external relay.

For a human check, build `buzz-acp` and use the existing relay/auth/adapter
configuration with `--lazy-pool --idle-pool-sleep 2 --agents 2`:

1. Ask the agent to start a background task lasting at least ten seconds and
finish its turn before the task exits. The worker must stay alive beyond the
two-second idle bound, then collect the result once in the original thread.
2. Send a message while collection is running. It must follow the configured
queue/steer policy without a concurrent prompt in that conversation.
3. Repeat with `--idle-pool-sleep 0`, and without `--lazy-pool`. Completion must
2. Send a message after the child exits but before collection starts, while
collection is running, and during a failed collection's retry backoff. Repeat
with queue, steer, and interrupt. Ready collection runs first; collection
backoff permits ordinary messages without resetting its retry budget or
deadline. An interrupted collection resumes in the original session.
One ordinary cancel is permitted per collection; after that, ordinary
Interrupt/OwnerInterrupt and fallback-Steer queue behind its protected retry.
Native steer also queues during collection rather than extending its deadline.
Keep sending messages during the retry: collection must finish, then all
retained messages must reach a foreground turn. Explicit owner/observer
cancel and owner rotate still stop a protected retry.
3. Under `--session-policy channel`, send the new message in a different thread
while the child is still running. Its result must still be collected in the
first thread. Repeat with `--session-policy thread` and a DM.
4. Repeat with `--idle-pool-sleep 0`, and without `--lazy-pool`. Completion must
still trigger collection. With Claude and no MCP servers, an ordinary turn
must leave the worker available for other channels.
4. Repeat with `--max-turns-per-session 1`: collection must reuse the original
5. Repeat with `--max-turns-per-session 1`: collection must reuse the original
session before rotation. In a separate run, send owner `!rotate` while the
child is running: its exit must not trigger collection in a replacement
session.
session. Repeat with owner `!cancel` and removal from the channel: no old
collection may resume. Repeated collection errors must end with a failure
notice in the original thread and leave the worker available.

The harness holds at most one observed process group's collection per worker.
Ready collections use original arrival age for FIFO among collections and take
precedence over ordinary batches. Their retry counters and deadlines are
separate from ordinary messages. Holds protect original worker/session ownership
until success, terminal failure, or explicit invalidation. This is an in-process
collection guarantee; successful ACP completion alone does not confirm publication.

Process tracking follows children under the worker and resident providers;
detached/reparented processes are outside that topology.

## Git bootstrap

The credential fixture requires Git 2.46 or newer for the `authtype` credential
protocol. Ensure the test subprocess PATH selects a compatible Git. Older Apple
Git can complete signing checks but fail `git credential fill` with a username
prompt error; that is a toolchain limitation, not a relay dependency.

`cargo test -p buzz-acp --test git_bootstrap` starts the actual harness with a
probe adapter, runs real signed commits/tags and scoped credential resolution,
and verifies key cleanup on startup failure and SIGTERM. No relay is contacted.
Expand All @@ -86,3 +118,53 @@ Goose and uses its provider to invoke the native developer shell. Both operate
only on temporary local repositories, verify commit/tag signatures and identity,
check unrelated-remote credential scoping, and assert keyfile removal. They do
not replace authenticated relay clone/push/readback testing.

## Managed background final reports

A `RuntimeContinuation` receives a random delivery UUID when its background
origin is recorded. Queue retries, ordinary message interleaving and healthy
steer/interrupt preserve it. A fresh observed group receives a fresh UUID. Only
collection prompts supply the key, original channel/parent and an explicitly
quoted workspace `.buzz-deliveries` path in `buzz messages deliver`. Progress
uses ordinary `messages send`; corrections use `messages edit`. Callback
mentions can use `--mention` or ordinary member-name mentions. Pending managed
publication must retry the same key/route/journal, including after ACP
application errors; changing the summary returns the first signed report.

```sh
cargo build -p buzz-cli -p buzz-agent -p buzz-dev-mcp
BUZZ_TEST_BIN_DIR="$PWD/target/debug" \
env -u BUZZ_ACP_ALLOWED_RESPOND_TO -u BUZZ_ACP_LAZY_POOL \
-u BUZZ_ACP_IDLE_POOL_SLEEP -u BUZZ_ACP_SESSION_POLICY \
cargo test -p buzz-acp managed_runtime_ -- --ignored --test-threads=1
```

These four checks exercise actual CLI processes and actual bundled agent + MCP
shell, production collection dispatch and error handling, a model error after
publication, same-key replay with changed text, lost response bodies, process
kill after relay acceptance, concurrent callers and a workspace name containing
spaces/apostrophes. Readback counts unique verified signed events in a **local
mock relay** that follows the real relay's duplicate acknowledgment contract.
They do not use a real authenticated relay/provider or attest human testing.
The bundled-agent fixture seeds a preserved continuation in the original ACP
session; the separate background lifecycle tests exercise actual child groups.

The persistent CLI journal recovers an already prepared signed event when the
same key/route is explicitly invoked after restart. ACP's queue, provider
session, group observation and ownership token remain in-memory; harness death
does not reconstruct an unjournaled lost result or automatically replay the
journal. Neither a successful ACP prompt nor arbitrary shell/MCP publication
proves managed delivery. The managed path covers kind-9 channel/thread reports,
including DM channel membership, only when the provider calls the supplied
command. See `crates/buzz-cli/TESTING.md` for bounded attempts, seven-day confirmed
retention, capacity/corruption handling and filesystem durability limits.

For human smoke testing with the native app and an authorized local relay,
start a task with a real background child. Send another-thread traffic while it
runs and while collection backs off. Make collection publish through the
supplied command, then cause an ACP application error. Let the same session
retry with a changed summary: the app and CLI readback must show one original
report/event ID, in the original thread, with its callback mention. A second
background group must produce a second report/key. Explicit cancel/rotate and
membership removal still invalidate collection according to the lifecycle
runbook above. Agent fixtures cannot mark this human check complete.
Loading
Loading