A Rust reimplementation of the Tangled knotserver.
0

Configure Feed

Select the types of activity you want to include in your feed.

tests: Consolidate integration binaries into one, fix nextest filters

Closes #2 (target/ bloat).

The 22 files in `mnemosyne_tests/tests/*.rs` were each compiling
into their own statically-linked binary, every one of which pulled
in the full crate graph (sqlx, tokio, bollard, gix, reqwest,
mnemosyne_protocol, mnemosyne_ssh, mnemosyne_xrpc, ...). Across stale
hashed copies in `target/debug/deps/`, that reached ~80 GB. After
consolidation, one binary (~115 MB) replaces the 22 (~1.4 GB+
combined per build, multiplied across stale copies).

Layout change:

tests/
integration/
main.rs <-- single binary entry point
common/ <-- shared fixtures (formerly tests/common/)
<former tests/foo.rs as modules>
proptest-regressions/<-- seeds moved here (see below)

`Cargo.toml` declares the binary via `[[test]]` and turns off
`autotests` so a stray `tests/*.rs` from a future PR can't silently
re-introduce the per-file-binary explosion.

Pre-existing nextest config bug surfaced and fixed
---------------------------------------------------

The `public-keys` test-group override was using filters of the form
`test(=mnemosyne_tests::xrpc_endpoints::list_keys_*)`. Nextest's
`test()` matcher operates on the test name *within* the binary
(`xrpc_endpoints::list_keys_*` in the consolidated layout, just
`list_keys_*` in the old per-binary layout), not the full
`<package>::<binary>::<test>` triple printed by `--list`. So the
override was matching zero tests, the group was silently empty, and
the `DELETE FROM public_keys` races described in the config's own
comment were happening on every run — they just rarely lost.

Consolidation made the race deterministic enough to fail today's
`just test` run. Filters rewritten to bare module paths and a
comment added so the next person to touch this doesn't re-break it.

Proptest regression layout
--------------------------

Proptest's default `FileFailurePersistence::SourceParallel` walks
up from the source file looking for `tests/` (a special dir name)
and places `proptest-regressions/<name>.txt` parallel to it. Under
the old top-level-file layout that resolved to
`tests/<name>.proptest-regressions`; under the new layout it
resolves to `tests/proptest-regressions/<name>.txt`. Migrated the
four existing seed files so proptest still replays them.

cargo-sweep
-----------

Added `just sweep [days=14]` (wraps `cargo sweep --time`) and
`just sweep-toolchain` (wraps `cargo sweep --installed`). Includes
`target/debug/incremental/` automatically — addresses the 15 GB
incremental cache without disabling it, so local fast-rebuild wins
are preserved.

What the issue listed but I did NOT do
--------------------------------------

`SQLX_OFFLINE=true` + `.sqlx/` (the issue's lever #2) does not
apply here: this codebase has zero `sqlx::query!()` / `query_as!()`
macro invocations — every callsite uses the runtime string forms
(`sqlx::query(...)`). The 141 stale `libsqlx_postgres` copies were
just normal incremental residue from sqlx being on the dep graph
of every test binary, which the consolidation + sweep address
directly.

Verification
------------

`just test` → 162/162 pass, 2 skipped (stress, by design).
`list_keys_*` tests now run sequentially under the `public-keys`
group (~0.03–0.1 s each in a single thread) instead of racing.

author
Isaac Corbrey
date (May 28, 2026, 2:51 PM -0400) commit fc402890 parent 70d9efd7 change-id vprwxkpq
+756 -536
+17 -7
.config/nextest.toml
··· 50 50 max-threads = 1 51 51 52 52 [[profile.default.overrides]] 53 + # nextest's `test()` matcher takes the test name *within* the 54 + # binary (what you'd pass to `cargo test`), not the 55 + # `<package>::<binary>::<test>` triple that nextest prints in 56 + # `--list` output. Previous revisions of this file got that wrong 57 + # — the `mnemosyne_tests::xrpc_endpoints::list_keys_...` filters 58 + # matched zero tests, so the `public-keys` group was silently 59 + # empty and the tests in it actually raced. 60 + # 61 + # Post-consolidation the test name is 62 + # `<module>::<test>` = `xrpc_endpoints::list_keys_...`. 53 63 filter = ''' 54 - test(=mnemosyne_tests::xrpc_endpoints::list_keys_orders_by_created_at_desc) 55 - + test(=mnemosyne_tests::xrpc_endpoints::list_keys_paginates_via_cursor) 56 - + test(=mnemosyne_tests::xrpc_endpoints::list_keys_empty_table_returns_empty_array) 57 - + test(=mnemosyne_tests::xrpc_endpoints::list_keys_rejects_negative_cursor) 58 - + test(/^mnemosyne_tests::ssh_push_parity/) 59 - + test(/^mnemosyne_tests::ssh_push::/) 60 - + test(/^mnemosyne_tests::ssh_auth::/) 64 + test(=xrpc_endpoints::list_keys_orders_by_created_at_desc) 65 + + test(=xrpc_endpoints::list_keys_paginates_via_cursor) 66 + + test(=xrpc_endpoints::list_keys_empty_table_returns_empty_array) 67 + + test(=xrpc_endpoints::list_keys_rejects_negative_cursor) 68 + + test(/^ssh_push_parity::/) 69 + + test(/^ssh_push::/) 70 + + test(/^ssh_auth::/) 61 71 ''' 62 72 test-group = 'public-keys'
+34 -3
justfile
··· 35 35 fi 36 36 ' EXIT INT TERM 37 37 if [ -z "{{depth}}" ]; then 38 - cargo nextest run --workspace -E '!test(/^stress_/)' 38 + # Match both `stress::*` and `stress_concurrent_push::*`. 39 + # Pre-consolidation these lived in separate binaries named 40 + # `stress` and `stress_concurrent_push`, so `^stress_` was 41 + # enough; under the single `integration` binary the test 42 + # name now carries a module prefix. 43 + cargo nextest run --workspace -E '!test(/^stress(::|_)/)' 39 44 else 40 45 STRESS_LEN={{depth}} cargo nextest run --workspace 41 46 fi ··· 54 59 rm -f "$fixture_dir/postgres.cid" 55 60 fi 56 61 ' EXIT INT TERM 57 - STRESS_LEN={{depth}} cargo nextest run -p mnemosyne_tests -E 'test(/^stress_/)' 62 + STRESS_LEN={{depth}} cargo nextest run -p mnemosyne_tests -E 'test(/^stress(::|_)/)' 58 63 59 64 # Run the property tests with a custom case count (default: 64). 60 65 # ··· 70 75 rm -f "$fixture_dir/postgres.cid" 71 76 fi 72 77 ' EXIT INT TERM 73 - PROPTEST_CASES={{cases}} cargo nextest run --workspace -E '!test(/^stress_/)' 78 + PROPTEST_CASES={{cases}} cargo nextest run --workspace -E '!test(/^stress(::|_)/)' 79 + 80 + # Drop stale build artifacts that cargo never garbage-collects. 81 + # 82 + # Cargo keeps every hashed copy of every dep in `target/debug/deps/` 83 + # forever. After a few weeks of churn, that directory grows into 84 + # the tens of gigabytes (issue #2: ~80 GB observed). `cargo sweep` 85 + # scans the timestamps and removes anything not touched recently. 86 + # 87 + # Default keeps the last 14 days, which is a comfortable buffer for 88 + # bisecting recent regressions while still reclaiming most of the 89 + # bloat. Pass a different number of days to be more aggressive: 90 + # 91 + # just sweep # keep last 14 days 92 + # just sweep 7 # keep last week 93 + # just sweep 1 # near-clean (keep yesterday) 94 + # 95 + # First-time use needs the tool: 96 + # cargo install cargo-sweep 97 + sweep days="14": 98 + cargo sweep --time {{days}} 99 + 100 + # Same idea, but only deletes installed-toolchain mismatches. 101 + # Useful right after `rustup update` to reclaim artifacts built 102 + # against the previous toolchain version. 103 + sweep-toolchain: 104 + cargo sweep --installed
+10
mnemosyne_tests/Cargo.toml
··· 7 7 repository.workspace = true 8 8 description = "End-to-end oracle tests: proptest, stress, regression." 9 9 publish = false 10 + # All integration tests are submodules of a single binary (see 11 + # `tests/integration/main.rs`). Auto-discovery is off so a stray 12 + # `tests/*.rs` doesn't silently bring back the per-file-binary 13 + # explosion that issue #2 was about. 14 + autotests = false 15 + 16 + [[test]] 17 + name = "integration" 18 + path = "tests/integration/main.rs" 19 + harness = true 10 20 11 21 [dependencies] 12 22 mnemosyne_git.workspace = true
-1
mnemosyne_tests/tests/common/git_ssh_container.rs mnemosyne_tests/tests/integration/common/git_ssh_container.rs
··· 142 142 }) 143 143 .await 144 144 } 145 -
+4 -4
mnemosyne_tests/tests/common/mod.rs mnemosyne_tests/tests/integration/common/mod.rs
··· 11 11 12 12 use gix_hash::Kind as HashKind; 13 13 use mnemosyne_git::FileBackend; 14 - use mnemosyne_postgres::{migrate, PgBackend}; 15 - use sqlx::postgres::PgPoolOptions; 14 + use mnemosyne_postgres::{PgBackend, migrate}; 16 15 use sqlx::PgPool; 16 + use sqlx::postgres::PgPoolOptions; 17 + use std::sync::atomic::{AtomicU64, Ordering}; 17 18 use testcontainers_modules::postgres::Postgres as TcPostgres; 18 - use testcontainers_modules::testcontainers::runners::AsyncRunner; 19 19 use testcontainers_modules::testcontainers::ContainerAsync; 20 + use testcontainers_modules::testcontainers::runners::AsyncRunner; 20 21 use tokio::runtime::Runtime; 21 - use std::sync::atomic::{AtomicU64, Ordering}; 22 22 23 23 pub mod git_ssh_container; 24 24 pub mod protocol_strategies;
+4 -11
mnemosyne_tests/tests/common/protocol_strategies.rs mnemosyne_tests/tests/integration/common/protocol_strategies.rs
··· 10 10 use gix_actor::Signature; 11 11 use gix_hash::{Kind as HashKind, ObjectId}; 12 12 use gix_object::tree::EntryKind; 13 - use gix_object::{tree, Commit, Kind, Tree, WriteTo}; 13 + use gix_object::{Commit, Kind, Tree, WriteTo, tree}; 14 14 use gix_ref::transaction::{Change, LogChange, PreviousValue, RefEdit}; 15 15 use gix_ref::{FullName, Target}; 16 16 use proptest::collection::vec; ··· 38 38 let mut tree_entries: Vec<tree::Entry> = Vec::new(); 39 39 40 40 for (i, content) in blob_contents.iter().enumerate() { 41 - let oid = gix_object::compute_hash(HashKind::Sha1, Kind::Blob, content) 42 - .expect("hash blob"); 41 + let oid = gix_object::compute_hash(HashKind::Sha1, Kind::Blob, content).expect("hash blob"); 43 42 objects.push((oid, Kind::Blob, content.clone())); 44 43 tree_entries.push(tree::Entry { 45 44 mode: EntryKind::Blob.into(), ··· 107 106 /// so we test both heads and tags being advertised. 108 107 pub fn ref_set(head_commit: ObjectId) -> impl Strategy<Value = ProtocolRefSet> { 109 108 vec( 110 - prop_oneof![ 111 - "refs/heads/[a-h]{1,4}", 112 - "refs/tags/v[0-9]", 113 - ], 109 + prop_oneof!["refs/heads/[a-h]{1,4}", "refs/tags/v[0-9]",], 114 110 0usize..=3, 115 111 ) 116 112 .prop_map(move |raw_names| build_ref_set(raw_names, head_commit)) ··· 154 150 deref: false, 155 151 }); 156 152 157 - let mut expected = vec![ 158 - (head_name, head_commit), 159 - (main_name.clone(), head_commit), 160 - ]; 153 + let mut expected = vec![(head_name, head_commit), (main_name.clone(), head_commit)]; 161 154 for n in &extras { 162 155 expected.push((n.clone(), head_commit)); 163 156 }
mnemosyne_tests/tests/common/storage_strategies.rs mnemosyne_tests/tests/integration/common/storage_strategies.rs
+17 -19
mnemosyne_tests/tests/ingest_dispatch.rs mnemosyne_tests/tests/integration/ingest_dispatch.rs
··· 13 13 //! integration stress testing — this slice's tests are pure 14 14 //! handler-trait coverage. 15 15 16 - mod common; 17 - 18 - use std::sync::atomic::{AtomicUsize, Ordering}; 19 16 use std::sync::Arc; 17 + use std::sync::atomic::{AtomicUsize, Ordering}; 20 18 19 + use mnemosyne_ingest::dispatch::CommitEvent; 21 20 use mnemosyne_ingest::dispatch::DispatchError; 22 21 use mnemosyne_ingest::{CommitOperation, Dispatcher, EventHandler, HandlerOutcome}; 23 22 use mnemosyne_postgres::sqlx::{Postgres, Transaction}; 24 23 use mnemosyne_postgres::{advance_ingest_cursor, read_ingest_cursor}; 25 - use mnemosyne_ingest::dispatch::CommitEvent; 26 24 27 25 /// A test handler that records how many times it was invoked. 28 26 struct CountingHandler { ··· 68 66 } 69 67 70 68 fn unique_cursor_id() -> String { 71 - format!("test-cursor-{}", common::unique_repo_did()) 69 + format!("test-cursor-{}", crate::common::unique_repo_did()) 72 70 } 73 71 74 72 #[test] 75 73 fn registered_handler_runs_and_cursor_advances() { 76 - common::runtime().block_on(async { 77 - let pool = common::shared_pool().await; 74 + crate::common::runtime().block_on(async { 75 + let pool = crate::common::shared_pool().await; 78 76 let cursor_id = unique_cursor_id(); 79 77 let mut dispatcher = Dispatcher::new(pool.clone(), cursor_id.clone()); 80 78 ··· 102 100 103 101 #[test] 104 102 fn unregistered_nsid_skips_handler_but_advances_cursor() { 105 - common::runtime().block_on(async { 106 - let pool = common::shared_pool().await; 103 + crate::common::runtime().block_on(async { 104 + let pool = crate::common::shared_pool().await; 107 105 let cursor_id = unique_cursor_id(); 108 106 let dispatcher = Dispatcher::new(pool.clone(), cursor_id.clone()); 109 107 ··· 126 124 127 125 #[test] 128 126 fn handler_failure_rolls_back_cursor_advance() { 129 - common::runtime().block_on(async { 130 - let pool = common::shared_pool().await; 127 + crate::common::runtime().block_on(async { 128 + let pool = crate::common::shared_pool().await; 131 129 let cursor_id = unique_cursor_id(); 132 130 let mut dispatcher = Dispatcher::new(pool.clone(), cursor_id.clone()); 133 131 ··· 138 136 139 137 dispatcher.register("sh.tangled.publicKey", Arc::new(FailingHandler)); 140 138 141 - let result = dispatcher 142 - .apply(&event("sh.tangled.publicKey", 200)) 143 - .await; 139 + let result = dispatcher.apply(&event("sh.tangled.publicKey", 200)).await; 144 140 assert!(result.is_err(), "failing handler should bubble the error"); 145 141 146 142 let persisted = read_ingest_cursor(&pool, &cursor_id) ··· 156 152 157 153 #[test] 158 154 fn cursor_advance_is_idempotent_across_runs() { 159 - common::runtime().block_on(async { 160 - let pool = common::shared_pool().await; 155 + crate::common::runtime().block_on(async { 156 + let pool = crate::common::shared_pool().await; 161 157 let cursor_id = unique_cursor_id(); 162 158 163 159 // Two advances → only the latest sticks (UPSERT semantics). ··· 218 214 "time_us": 1, 219 215 "identity": {"did": "did:plc:abc", "handle": "test.bsky.social", "seq": 1, "time": "2026-01-01T00:00:00Z"} 220 216 }"#; 221 - let event = mnemosyne_ingest::dispatch::decode_commit_line(raw.as_bytes()) 222 - .expect("decode"); 223 - assert!(event.is_none(), "identity events shouldn't decode to commits"); 217 + let event = mnemosyne_ingest::dispatch::decode_commit_line(raw.as_bytes()).expect("decode"); 218 + assert!( 219 + event.is_none(), 220 + "identity events shouldn't decode to commits" 221 + ); 224 222 } 225 223 226 224 #[test]
+19 -21
mnemosyne_tests/tests/ingest_public_key.rs mnemosyne_tests/tests/integration/ingest_public_key.rs
··· 13 13 //! The `public_keys` table is not repo-scoped; tests use unique DIDs 14 14 //! (`unique_repo_did`) to avoid cross-test contamination. 15 15 16 - mod common; 17 - 18 16 use std::sync::Arc; 19 17 20 18 use mnemosyne_ingest::dispatch::CommitEvent; ··· 24 22 use sqlx::PgPool; 25 23 26 24 fn unique_cursor_id(suffix: &str) -> String { 27 - format!("publickey-{suffix}-{}", common::unique_repo_did()) 25 + format!("publickey-{suffix}-{}", crate::common::unique_repo_did()) 28 26 } 29 27 30 28 fn create_event(did: &str, rkey: &str, time_us: i64, key: &str, created_at: &str) -> CommitEvent { ··· 60 58 61 59 #[test] 62 60 fn create_inserts_a_public_keys_row() { 63 - common::runtime().block_on(async { 64 - let pool = common::shared_pool().await; 61 + crate::common::runtime().block_on(async { 62 + let pool = crate::common::shared_pool().await; 65 63 let cursor_id = unique_cursor_id("create"); 66 64 let dispatcher = dispatcher_with_handler(pool.clone(), cursor_id.clone()); 67 65 68 - let did = common::unique_repo_did(); 66 + let did = crate::common::unique_repo_did(); 69 67 let outcome = dispatcher 70 68 .apply(&create_event( 71 69 &did, ··· 96 94 97 95 #[test] 98 96 fn update_overwrites_existing_row_at_same_rkey() { 99 - common::runtime().block_on(async { 100 - let pool = common::shared_pool().await; 97 + crate::common::runtime().block_on(async { 98 + let pool = crate::common::shared_pool().await; 101 99 let cursor_id = unique_cursor_id("update"); 102 100 let dispatcher = dispatcher_with_handler(pool.clone(), cursor_id.clone()); 103 101 104 - let did = common::unique_repo_did(); 102 + let did = crate::common::unique_repo_did(); 105 103 106 104 dispatcher 107 105 .apply(&create_event( ··· 138 136 139 137 #[test] 140 138 fn delete_removes_the_row() { 141 - common::runtime().block_on(async { 142 - let pool = common::shared_pool().await; 139 + crate::common::runtime().block_on(async { 140 + let pool = crate::common::shared_pool().await; 143 141 let cursor_id = unique_cursor_id("delete"); 144 142 let dispatcher = dispatcher_with_handler(pool.clone(), cursor_id.clone()); 145 143 146 - let did = common::unique_repo_did(); 144 + let did = crate::common::unique_repo_did(); 147 145 dispatcher 148 146 .apply(&create_event( 149 147 &did, ··· 180 178 // Delete events can arrive for rkeys we never saw a Create for — 181 179 // most commonly when cursor replay or upstream re-emission happens. 182 180 // The handler must succeed silently and let the cursor advance. 183 - common::runtime().block_on(async { 184 - let pool = common::shared_pool().await; 181 + crate::common::runtime().block_on(async { 182 + let pool = crate::common::shared_pool().await; 185 183 let cursor_id = unique_cursor_id("delete-ghost"); 186 184 let dispatcher = dispatcher_with_handler(pool.clone(), cursor_id.clone()); 187 185 188 - let did = common::unique_repo_did(); 186 + let did = crate::common::unique_repo_did(); 189 187 let delete_evt = CommitEvent { 190 188 did: did.clone(), 191 189 time_us: 42, ··· 212 210 // the federation. So the handler MUST skip + advance past garbage 213 211 // (and the row must not be inserted), reserving errors for our own 214 212 // infrastructure failures. 215 - common::runtime().block_on(async { 216 - let pool = common::shared_pool().await; 213 + crate::common::runtime().block_on(async { 214 + let pool = crate::common::shared_pool().await; 217 215 let cursor_id = unique_cursor_id("malformed"); 218 216 let dispatcher = dispatcher_with_handler(pool.clone(), cursor_id.clone()); 219 217 220 - let did = common::unique_repo_did(); 218 + let did = crate::common::unique_repo_did(); 221 219 let bad = CommitEvent { 222 220 did: did.clone(), 223 221 time_us: 500, ··· 254 252 // Adversarial / buggy PDS could emit Create with no record body. 255 253 // Same fail-open posture: skip with log, advance cursor, don't 256 254 // insert. 257 - common::runtime().block_on(async { 258 - let pool = common::shared_pool().await; 255 + crate::common::runtime().block_on(async { 256 + let pool = crate::common::shared_pool().await; 259 257 let cursor_id = unique_cursor_id("missing-body"); 260 258 let dispatcher = dispatcher_with_handler(pool.clone(), cursor_id.clone()); 261 259 262 - let did = common::unique_repo_did(); 260 + let did = crate::common::unique_repo_did(); 263 261 let no_body = CommitEvent { 264 262 did: did.clone(), 265 263 time_us: 700,
+57 -39
mnemosyne_tests/tests/ingest_repo.rs mnemosyne_tests/tests/integration/ingest_repo.rs
··· 10 10 //! 5. Delete is logged but does not mutate the repos table — repo 11 11 //! teardown is out-of-band. 12 12 13 - mod common; 14 - 15 13 use std::sync::Arc; 16 14 17 15 use mnemosyne_ingest::dispatch::CommitEvent; ··· 21 19 use sqlx::PgPool; 22 20 23 21 fn unique_cursor_id(suffix: &str) -> String { 24 - format!("repo-{suffix}-{}", common::unique_repo_did()) 22 + format!("repo-{suffix}-{}", crate::common::unique_repo_did()) 25 23 } 26 24 27 25 fn dispatcher_with_handler(pool: PgPool, cursor_id: String) -> Dispatcher { ··· 30 28 dispatcher 31 29 } 32 30 33 - fn create_event( 34 - author_did: &str, 35 - rkey: &str, 36 - time_us: i64, 37 - repo_did: Option<&str>, 38 - ) -> CommitEvent { 31 + fn create_event(author_did: &str, rkey: &str, time_us: i64, repo_did: Option<&str>) -> CommitEvent { 39 32 let mut record = serde_json::json!({ 40 33 "knot": "did:test:knot", 41 34 "createdAt": "2026-05-15T00:00:00Z", ··· 64 57 65 58 #[test] 66 59 fn owner_record_replaces_placeholder_rkey() { 67 - common::runtime().block_on(async { 68 - let pool = common::shared_pool().await; 60 + crate::common::runtime().block_on(async { 61 + let pool = crate::common::shared_pool().await; 69 62 let cursor_id = unique_cursor_id("owner"); 70 63 let dispatcher = dispatcher_with_handler(pool.clone(), cursor_id.clone()); 71 64 72 - let repo_did = common::unique_repo_did(); 73 - let owner_did = format!("did:test:owner-{}", common::unique_repo_did()); 74 - ensure_repo(&pool, &repo_did, &owner_did, "ingest-test-repo", common::PLACEHOLDER_RKEY) 75 - .await 76 - .expect("seed repo"); 65 + let repo_did = crate::common::unique_repo_did(); 66 + let owner_did = format!("did:test:owner-{}", crate::common::unique_repo_did()); 67 + ensure_repo( 68 + &pool, 69 + &repo_did, 70 + &owner_did, 71 + "ingest-test-repo", 72 + crate::common::PLACEHOLDER_RKEY, 73 + ) 74 + .await 75 + .expect("seed repo"); 77 76 78 77 let outcome = dispatcher 79 78 .apply(&create_event(&owner_did, "3kjxabc", 100, Some(&repo_did))) ··· 81 80 .expect("apply"); 82 81 assert_eq!(outcome, HandlerOutcome::Applied); 83 82 84 - assert_eq!(read_rkey(&pool, &repo_did).await, Some("3kjxabc".to_string())); 83 + assert_eq!( 84 + read_rkey(&pool, &repo_did).await, 85 + Some("3kjxabc".to_string()) 86 + ); 85 87 assert_eq!( 86 88 read_ingest_cursor(&pool, &cursor_id).await.expect("cursor"), 87 89 Some(100), ··· 94 96 // Owner of repo A publishes nothing. A different DID (impostor) 95 97 // publishes a `sh.tangled.repo` record claiming repoDid = A. Our 96 98 // handler must NOT honor it — the rkey stays untouched. 97 - common::runtime().block_on(async { 98 - let pool = common::shared_pool().await; 99 + crate::common::runtime().block_on(async { 100 + let pool = crate::common::shared_pool().await; 99 101 let cursor_id = unique_cursor_id("non-owner"); 100 102 let dispatcher = dispatcher_with_handler(pool.clone(), cursor_id.clone()); 101 103 102 - let repo_did = common::unique_repo_did(); 103 - let owner_did = format!("did:test:owner-{}", common::unique_repo_did()); 104 - let impostor_did = format!("did:test:impostor-{}", common::unique_repo_did()); 105 - ensure_repo(&pool, &repo_did, &owner_did, "victim-repo", common::PLACEHOLDER_RKEY) 106 - .await 107 - .expect("seed repo"); 104 + let repo_did = crate::common::unique_repo_did(); 105 + let owner_did = format!("did:test:owner-{}", crate::common::unique_repo_did()); 106 + let impostor_did = format!("did:test:impostor-{}", crate::common::unique_repo_did()); 107 + ensure_repo( 108 + &pool, 109 + &repo_did, 110 + &owner_did, 111 + "victim-repo", 112 + crate::common::PLACEHOLDER_RKEY, 113 + ) 114 + .await 115 + .expect("seed repo"); 108 116 109 117 let outcome = dispatcher 110 - .apply(&create_event(&impostor_did, "3kjevil", 200, Some(&repo_did))) 118 + .apply(&create_event( 119 + &impostor_did, 120 + "3kjevil", 121 + 200, 122 + Some(&repo_did), 123 + )) 111 124 .await 112 125 .expect("apply"); 113 126 assert_eq!(outcome, HandlerOutcome::Applied); 114 127 115 128 assert_eq!( 116 129 read_rkey(&pool, &repo_did).await, 117 - Some(common::PLACEHOLDER_RKEY.to_string()), 130 + Some(crate::common::PLACEHOLDER_RKEY.to_string()), 118 131 "impostor must not be able to overwrite the rkey", 119 132 ); 120 133 assert_eq!( ··· 127 140 128 141 #[test] 129 142 fn unknown_repo_is_skipped() { 130 - common::runtime().block_on(async { 131 - let pool = common::shared_pool().await; 143 + crate::common::runtime().block_on(async { 144 + let pool = crate::common::shared_pool().await; 132 145 let cursor_id = unique_cursor_id("unknown"); 133 146 let dispatcher = dispatcher_with_handler(pool.clone(), cursor_id.clone()); 134 147 135 - let phantom_did = format!("did:test:phantom-{}", common::unique_repo_did()); 136 - let author_did = format!("did:test:somebody-{}", common::unique_repo_did()); 148 + let phantom_did = format!("did:test:phantom-{}", crate::common::unique_repo_did()); 149 + let author_did = format!("did:test:somebody-{}", crate::common::unique_repo_did()); 137 150 138 151 let outcome = dispatcher 139 - .apply(&create_event(&author_did, "3kjphantom", 300, Some(&phantom_did))) 152 + .apply(&create_event( 153 + &author_did, 154 + "3kjphantom", 155 + 300, 156 + Some(&phantom_did), 157 + )) 140 158 .await 141 159 .expect("apply"); 142 160 assert_eq!(outcome, HandlerOutcome::Applied); ··· 151 169 152 170 #[test] 153 171 fn record_missing_repo_did_is_skipped() { 154 - common::runtime().block_on(async { 155 - let pool = common::shared_pool().await; 172 + crate::common::runtime().block_on(async { 173 + let pool = crate::common::shared_pool().await; 156 174 let cursor_id = unique_cursor_id("no-repodid"); 157 175 let dispatcher = dispatcher_with_handler(pool.clone(), cursor_id.clone()); 158 176 159 - let author_did = format!("did:test:somebody-{}", common::unique_repo_did()); 177 + let author_did = format!("did:test:somebody-{}", crate::common::unique_repo_did()); 160 178 161 179 let outcome = dispatcher 162 180 .apply(&create_event(&author_did, "3kjnorepo", 400, None)) ··· 177 195 // out-of-band (a separate knot operation). A firehose Delete of 178 196 // the `sh.tangled.repo` record is logged but the repos row stays 179 197 // intact. 180 - common::runtime().block_on(async { 181 - let pool = common::shared_pool().await; 198 + crate::common::runtime().block_on(async { 199 + let pool = crate::common::shared_pool().await; 182 200 let cursor_id = unique_cursor_id("delete"); 183 201 let dispatcher = dispatcher_with_handler(pool.clone(), cursor_id.clone()); 184 202 185 - let repo_did = common::unique_repo_did(); 186 - let owner_did = format!("did:test:owner-{}", common::unique_repo_did()); 203 + let repo_did = crate::common::unique_repo_did(); 204 + let owner_did = format!("did:test:owner-{}", crate::common::unique_repo_did()); 187 205 ensure_repo(&pool, &repo_did, &owner_did, "survivor-repo", "real-rkey") 188 206 .await 189 207 .expect("seed repo");
+117 -67
mnemosyne_tests/tests/ingest_repo_collaborator.rs mnemosyne_tests/tests/integration/ingest_repo_collaborator.rs
··· 13 13 //! 5. Records pointing at an unknown repo are skipped. 14 14 //! 6. Malformed records skip + advance cursor (fail-open). 15 15 16 - mod common; 17 - 18 16 use std::sync::Arc; 19 17 20 18 use mnemosyne_ingest::dispatch::CommitEvent; ··· 24 22 use sqlx::PgPool; 25 23 26 24 fn unique_cursor_id(suffix: &str) -> String { 27 - format!("collab-{suffix}-{}", common::unique_repo_did()) 25 + format!("collab-{suffix}-{}", crate::common::unique_repo_did()) 28 26 } 29 27 30 28 fn dispatcher_with_handler(pool: PgPool, cursor_id: String) -> Dispatcher { 31 29 let mut dispatcher = Dispatcher::new(pool, cursor_id); 32 - dispatcher.register(collaborator_handler::NSID, Arc::new(RepoCollaboratorHandler)); 30 + dispatcher.register( 31 + collaborator_handler::NSID, 32 + Arc::new(RepoCollaboratorHandler), 33 + ); 33 34 dispatcher 34 35 } 35 36 ··· 55 56 } 56 57 57 58 async fn fresh_repo(pool: &PgPool) -> (String, String) { 58 - let repo_did = common::unique_repo_did(); 59 - let owner_did = format!("did:test:owner-{}", common::unique_repo_did()); 60 - ensure_repo(pool, &repo_did, &owner_did, "collab-test", common::PLACEHOLDER_RKEY) 61 - .await 62 - .expect("seed repo"); 59 + let repo_did = crate::common::unique_repo_did(); 60 + let owner_did = format!("did:test:owner-{}", crate::common::unique_repo_did()); 61 + ensure_repo( 62 + pool, 63 + &repo_did, 64 + &owner_did, 65 + "collab-test", 66 + crate::common::PLACEHOLDER_RKEY, 67 + ) 68 + .await 69 + .expect("seed repo"); 63 70 (repo_did, owner_did) 64 71 } 65 72 66 73 #[test] 67 74 fn owner_can_add_collaborator() { 68 - common::runtime().block_on(async { 69 - let pool = common::shared_pool().await; 75 + crate::common::runtime().block_on(async { 76 + let pool = crate::common::shared_pool().await; 70 77 let cursor_id = unique_cursor_id("happy"); 71 78 let dispatcher = dispatcher_with_handler(pool.clone(), cursor_id.clone()); 72 79 73 80 let (repo_did, owner_did) = fresh_repo(&pool).await; 74 - let subject_did = format!("did:test:friend-{}", common::unique_repo_did()); 81 + let subject_did = format!("did:test:friend-{}", crate::common::unique_repo_did()); 75 82 76 83 let outcome = dispatcher 77 - .apply(&create_event(&owner_did, "3kjcolab", 100, &repo_did, &subject_did)) 84 + .apply(&create_event( 85 + &owner_did, 86 + "3kjcolab", 87 + 100, 88 + &repo_did, 89 + &subject_did, 90 + )) 78 91 .await 79 92 .expect("apply"); 80 93 assert_eq!(outcome, HandlerOutcome::Applied); ··· 98 111 // collaborator record claiming to grant push access on someone 99 112 // else's repo. Our handler rejects it — only the owner of the 100 113 // referenced repo can add collaborators. 101 - common::runtime().block_on(async { 102 - let pool = common::shared_pool().await; 114 + crate::common::runtime().block_on(async { 115 + let pool = crate::common::shared_pool().await; 103 116 let cursor_id = unique_cursor_id("non-owner"); 104 117 let dispatcher = dispatcher_with_handler(pool.clone(), cursor_id.clone()); 105 118 106 119 let (repo_did, _owner_did) = fresh_repo(&pool).await; 107 - let impostor_did = format!("did:test:impostor-{}", common::unique_repo_did()); 108 - let target_subject_did = format!("did:test:also-impostor-{}", common::unique_repo_did()); 120 + let impostor_did = format!("did:test:impostor-{}", crate::common::unique_repo_did()); 121 + let target_subject_did = format!( 122 + "did:test:also-impostor-{}", 123 + crate::common::unique_repo_did() 124 + ); 109 125 110 126 let outcome = dispatcher 111 127 .apply(&create_event( ··· 135 151 136 152 #[test] 137 153 fn update_overwrites_existing_collaborator_row() { 138 - common::runtime().block_on(async { 139 - let pool = common::shared_pool().await; 154 + crate::common::runtime().block_on(async { 155 + let pool = crate::common::shared_pool().await; 140 156 let cursor_id = unique_cursor_id("update"); 141 157 let dispatcher = dispatcher_with_handler(pool.clone(), cursor_id.clone()); 142 158 143 159 let (repo_did, owner_did) = fresh_repo(&pool).await; 144 - let first_subject_did = format!("did:test:friend-a-{}", common::unique_repo_did()); 145 - let second_subject_did = format!("did:test:friend-b-{}", common::unique_repo_did()); 160 + let first_subject_did = format!("did:test:friend-a-{}", crate::common::unique_repo_did()); 161 + let second_subject_did = format!("did:test:friend-b-{}", crate::common::unique_repo_did()); 146 162 147 163 dispatcher 148 - .apply(&create_event(&owner_did, "3kjmutate", 100, &repo_did, &first_subject_did)) 164 + .apply(&create_event( 165 + &owner_did, 166 + "3kjmutate", 167 + 100, 168 + &repo_did, 169 + &first_subject_did, 170 + )) 149 171 .await 150 172 .expect("create"); 151 - assert!(is_collaborator(&pool, &repo_did, &first_subject_did).await.unwrap()); 152 - 153 - let mut update = create_event( 154 - &owner_did, 155 - "3kjmutate", 156 - 200, 157 - &repo_did, 158 - &second_subject_did, 173 + assert!( 174 + is_collaborator(&pool, &repo_did, &first_subject_did) 175 + .await 176 + .unwrap() 159 177 ); 178 + 179 + let mut update = create_event(&owner_did, "3kjmutate", 200, &repo_did, &second_subject_did); 160 180 update.operation = CommitOperation::Update; 161 181 dispatcher.apply(&update).await.expect("update"); 162 182 ··· 177 197 178 198 #[test] 179 199 fn delete_removes_collaborator_row() { 180 - common::runtime().block_on(async { 181 - let pool = common::shared_pool().await; 200 + crate::common::runtime().block_on(async { 201 + let pool = crate::common::shared_pool().await; 182 202 let cursor_id = unique_cursor_id("delete"); 183 203 let dispatcher = dispatcher_with_handler(pool.clone(), cursor_id.clone()); 184 204 185 205 let (repo_did, owner_did) = fresh_repo(&pool).await; 186 - let subject_did = format!("did:test:friend-{}", common::unique_repo_did()); 206 + let subject_did = format!("did:test:friend-{}", crate::common::unique_repo_did()); 187 207 188 208 dispatcher 189 - .apply(&create_event(&owner_did, "3kjrevoke", 100, &repo_did, &subject_did)) 209 + .apply(&create_event( 210 + &owner_did, 211 + "3kjrevoke", 212 + 100, 213 + &repo_did, 214 + &subject_did, 215 + )) 190 216 .await 191 217 .expect("create"); 192 - assert!(is_collaborator(&pool, &repo_did, &subject_did).await.unwrap()); 218 + assert!( 219 + is_collaborator(&pool, &repo_did, &subject_did) 220 + .await 221 + .unwrap() 222 + ); 193 223 194 224 let delete_event = CommitEvent { 195 225 did: owner_did.clone(), ··· 218 248 // we can't honor the new state — but the owner has expressed 219 249 // intent to walk away from the prior link, so the original row 220 250 // (collaborator on repo A) must be deleted. 221 - common::runtime().block_on(async { 222 - let pool = common::shared_pool().await; 251 + crate::common::runtime().block_on(async { 252 + let pool = crate::common::shared_pool().await; 223 253 let cursor_id = unique_cursor_id("implicit-revoke"); 224 254 let dispatcher = dispatcher_with_handler(pool.clone(), cursor_id.clone()); 225 255 226 256 let (repo_a_did, owner_did) = fresh_repo(&pool).await; 227 - let friend_did = format!("did:test:friend-{}", common::unique_repo_did()); 257 + let friend_did = format!("did:test:friend-{}", crate::common::unique_repo_did()); 228 258 229 259 // Initial valid grant. 230 260 dispatcher 231 - .apply(&create_event(&owner_did, "3kjrevoke", 100, &repo_a_did, &friend_did)) 261 + .apply(&create_event( 262 + &owner_did, 263 + "3kjrevoke", 264 + 100, 265 + &repo_a_did, 266 + &friend_did, 267 + )) 232 268 .await 233 269 .expect("create"); 234 270 assert!( ··· 239 275 ); 240 276 241 277 // Now Mallory's repo. Owner_A is NOT its owner. 242 - let mallory_repo_did = common::unique_repo_did(); 243 - let mallory_did = format!("did:test:mallory-{}", common::unique_repo_did()); 278 + let mallory_repo_did = crate::common::unique_repo_did(); 279 + let mallory_did = format!("did:test:mallory-{}", crate::common::unique_repo_did()); 244 280 ensure_repo( 245 281 &pool, 246 282 &mallory_repo_did, 247 283 &mallory_did, 248 284 "mallorys-repo", 249 - common::PLACEHOLDER_RKEY, 285 + crate::common::PLACEHOLDER_RKEY, 250 286 ) 251 287 .await 252 288 .expect("seed mallory repo"); 253 289 254 290 // Owner_A updates their record to point at Mallory's repo. 255 - let mut update = create_event( 256 - &owner_did, 257 - "3kjrevoke", 258 - 200, 259 - &mallory_repo_did, 260 - &friend_did, 261 - ); 291 + let mut update = create_event(&owner_did, "3kjrevoke", 200, &mallory_repo_did, &friend_did); 262 292 update.operation = CommitOperation::Update; 263 293 dispatcher.apply(&update).await.expect("apply update"); 264 294 ··· 284 314 // never heard of. The handler can't even look up an owner — but 285 315 // the record decoded cleanly, so the user has unambiguously 286 316 // expressed intent. Revoke the prior link. 287 - common::runtime().block_on(async { 288 - let pool = common::shared_pool().await; 317 + crate::common::runtime().block_on(async { 318 + let pool = crate::common::shared_pool().await; 289 319 let cursor_id = unique_cursor_id("implicit-revoke-unknown"); 290 320 let dispatcher = dispatcher_with_handler(pool.clone(), cursor_id.clone()); 291 321 292 322 let (repo_a_did, owner_did) = fresh_repo(&pool).await; 293 - let friend_did = format!("did:test:friend-{}", common::unique_repo_did()); 323 + let friend_did = format!("did:test:friend-{}", crate::common::unique_repo_did()); 294 324 295 325 dispatcher 296 - .apply(&create_event(&owner_did, "3kjghostrevoke", 100, &repo_a_did, &friend_did)) 326 + .apply(&create_event( 327 + &owner_did, 328 + "3kjghostrevoke", 329 + 100, 330 + &repo_a_did, 331 + &friend_did, 332 + )) 297 333 .await 298 334 .expect("create"); 299 - assert!(is_collaborator(&pool, &repo_a_did, &friend_did).await.unwrap()); 335 + assert!( 336 + is_collaborator(&pool, &repo_a_did, &friend_did) 337 + .await 338 + .unwrap() 339 + ); 300 340 301 - let phantom_repo = format!("did:test:phantom-{}", common::unique_repo_did()); 341 + let phantom_repo = format!("did:test:phantom-{}", crate::common::unique_repo_did()); 302 342 let mut update = create_event( 303 343 &owner_did, 304 344 "3kjghostrevoke", ··· 325 365 // a buggy update that they'll retry. Do NOT revoke; just skip. 326 366 // This is the discriminator between "user expressed intent we 327 367 // can't honor" (revoke) and "user input is ambiguous" (skip). 328 - common::runtime().block_on(async { 329 - let pool = common::shared_pool().await; 368 + crate::common::runtime().block_on(async { 369 + let pool = crate::common::shared_pool().await; 330 370 let cursor_id = unique_cursor_id("malformed-update"); 331 371 let dispatcher = dispatcher_with_handler(pool.clone(), cursor_id.clone()); 332 372 333 373 let (repo_a_did, owner_did) = fresh_repo(&pool).await; 334 - let friend_did = format!("did:test:friend-{}", common::unique_repo_did()); 374 + let friend_did = format!("did:test:friend-{}", crate::common::unique_repo_did()); 335 375 336 376 dispatcher 337 - .apply(&create_event(&owner_did, "3kjprotect", 100, &repo_a_did, &friend_did)) 377 + .apply(&create_event( 378 + &owner_did, 379 + "3kjprotect", 380 + 100, 381 + &repo_a_did, 382 + &friend_did, 383 + )) 338 384 .await 339 385 .expect("create"); 340 - assert!(is_collaborator(&pool, &repo_a_did, &friend_did).await.unwrap()); 386 + assert!( 387 + is_collaborator(&pool, &repo_a_did, &friend_did) 388 + .await 389 + .unwrap() 390 + ); 341 391 342 392 let malformed_update = CommitEvent { 343 393 did: owner_did.clone(), ··· 360 410 361 411 #[test] 362 412 fn unknown_repo_collaboration_is_skipped() { 363 - common::runtime().block_on(async { 364 - let pool = common::shared_pool().await; 413 + crate::common::runtime().block_on(async { 414 + let pool = crate::common::shared_pool().await; 365 415 let cursor_id = unique_cursor_id("unknown"); 366 416 let dispatcher = dispatcher_with_handler(pool.clone(), cursor_id.clone()); 367 417 368 - let phantom_repo_did = format!("did:test:phantom-{}", common::unique_repo_did()); 369 - let author_did = format!("did:test:author-{}", common::unique_repo_did()); 370 - let subject_did = format!("did:test:friend-{}", common::unique_repo_did()); 418 + let phantom_repo_did = format!("did:test:phantom-{}", crate::common::unique_repo_did()); 419 + let author_did = format!("did:test:author-{}", crate::common::unique_repo_did()); 420 + let subject_did = format!("did:test:friend-{}", crate::common::unique_repo_did()); 371 421 372 422 let outcome = dispatcher 373 423 .apply(&create_event( ··· 395 445 396 446 #[test] 397 447 fn malformed_record_skips_and_advances() { 398 - common::runtime().block_on(async { 399 - let pool = common::shared_pool().await; 448 + crate::common::runtime().block_on(async { 449 + let pool = crate::common::shared_pool().await; 400 450 let cursor_id = unique_cursor_id("malformed"); 401 451 let dispatcher = dispatcher_with_handler(pool.clone(), cursor_id.clone()); 402 452
+45
mnemosyne_tests/tests/integration/main.rs
··· 1 + //! Single integration test binary. 2 + //! 3 + //! Each module in this directory used to be its own top-level 4 + //! `tests/foo.rs`, which cargo compiled into its own statically 5 + //! linked binary — ~22 binaries, each pulling in the full crate 6 + //! graph (sqlx, tokio, bollard, gix, reqwest, mnemosyne_protocol, 7 + //! mnemosyne_ssh, mnemosyne_xrpc, ...). With stale hashed copies in 8 + //! `target/debug/deps/` from every prior build, that alone reached 9 + //! tens of gigabytes. 10 + //! 11 + //! Consolidating them into one binary collapses all of that into a 12 + //! single link, at the cost of needing this declarations file and 13 + //! one extra level of module path. The pattern is the one Aleksey 14 + //! Kladov describes in 15 + //! <https://matklad.github.io/2021/02/27/delete-cargo-integration-tests.html>. 16 + //! 17 + //! Test paths visible to nextest become 18 + //! `mnemosyne_tests::integration::<module>::<test>` (was 19 + //! `mnemosyne_tests::<module>::<test>`). The `.config/nextest.toml` 20 + //! filters were updated in lockstep. 21 + 22 + mod common; 23 + 24 + mod ingest_dispatch; 25 + mod ingest_public_key; 26 + mod ingest_repo; 27 + mod ingest_repo_collaborator; 28 + mod properties; 29 + mod protocol_clone; 30 + mod protocol_edge_cases; 31 + mod protocol_info_refs; 32 + mod protocol_pack; 33 + mod protocol_pkt; 34 + mod protocol_push_concurrent; 35 + mod protocol_push_roundtrip; 36 + mod ssh_auth; 37 + mod ssh_push; 38 + mod ssh_push_parity; 39 + mod storage_dedup; 40 + mod stress; 41 + mod stress_concurrent_push; 42 + mod xrpc_browse; 43 + mod xrpc_endpoints; 44 + mod xrpc_history; 45 + mod xrpc_languages;
mnemosyne_tests/tests/properties.proptest-regressions mnemosyne_tests/tests/proptest-regressions/properties.txt
+11 -13
mnemosyne_tests/tests/properties.rs mnemosyne_tests/tests/integration/properties.rs
··· 1 1 //! Property tests: random scripts against PgBackend must match FileBackend. 2 2 3 - mod common; 4 - 5 - use common::storage_strategies::{ 6 - mixed_script, object_script, ref_script, to_concrete, AbstractCmd, 3 + use crate::common::storage_strategies::{ 4 + AbstractCmd, mixed_script, object_script, ref_script, to_concrete, 7 5 }; 8 6 use gix_hash::ObjectId; 9 7 use mnemosyne_git::FileBackend; 10 - use mnemosyne_harness::{DualRunner, Divergence, Outcome}; 8 + use mnemosyne_harness::{Divergence, DualRunner, Outcome}; 11 9 use mnemosyne_postgres::PgBackend; 12 10 use proptest::prelude::*; 13 11 ··· 56 54 /// between FileBackend and PgBackend for every random script. 57 55 #[test] 58 56 fn parity_objects_only(script in object_script(120)) { 59 - let result = common::runtime().block_on(async { 60 - let (_tmp, file, pg) = common::fresh_backends().await; 57 + let result = crate::common::runtime().block_on(async { 58 + let (_tmp, file, pg) = crate::common::fresh_backends().await; 61 59 let runner = DualRunner::new(file, pg); 62 60 execute(&runner, &script).await 63 61 }); ··· 68 66 /// Scripts are seeded with an initial write so refs have a target. 69 67 #[test] 70 68 fn parity_refs(script in ref_script(120)) { 71 - let result = common::runtime().block_on(async { 72 - let (_tmp, file, pg) = common::fresh_backends().await; 69 + let result = crate::common::runtime().block_on(async { 70 + let (_tmp, file, pg) = crate::common::fresh_backends().await; 73 71 let runner = DualRunner::new(file, pg); 74 72 execute(&runner, &script).await 75 73 }); ··· 79 77 /// Mixed scripts: any operation, any order. 80 78 #[test] 81 79 fn parity_mixed(script in mixed_script(200)) { 82 - let result = common::runtime().block_on(async { 83 - let (_tmp, file, pg) = common::fresh_backends().await; 80 + let result = crate::common::runtime().block_on(async { 81 + let (_tmp, file, pg) = crate::common::fresh_backends().await; 84 82 let runner = DualRunner::new(file, pg); 85 83 execute(&runner, &script).await 86 84 }); ··· 97 95 use gix_object::Kind; 98 96 use mnemosyne_harness::Command; 99 97 100 - common::runtime().block_on(async { 101 - let (_tmp, file, pg) = common::fresh_backends().await; 98 + crate::common::runtime().block_on(async { 99 + let (_tmp, file, pg) = crate::common::fresh_backends().await; 102 100 let runner = DualRunner::new(file, pg); 103 101 104 102 let (oa, _, div) = runner
mnemosyne_tests/tests/protocol_clone.proptest-regressions mnemosyne_tests/tests/proptest-regressions/protocol_clone.txt
+26 -24
mnemosyne_tests/tests/protocol_clone.rs mnemosyne_tests/tests/integration/protocol_clone.rs
··· 12 12 //! advertisement, packfile encoding, side-band-64k chunking), the gix 13 13 //! client will reject the response and the test fails. 14 14 15 - mod common; 16 - 17 - use std::sync::atomic::AtomicBool; 18 15 use std::sync::Arc; 16 + use std::sync::atomic::AtomicBool; 19 17 20 - use axum::routing::{get, post}; 18 + use crate::common::protocol_strategies::{ObjectGraph, ProtocolRefSet, graph_with_refs}; 21 19 use axum::Router; 22 - use common::protocol_strategies::{graph_with_refs, ObjectGraph, ProtocolRefSet}; 20 + use axum::routing::{get, post}; 23 21 use gix_hash::{Kind as HashKind, ObjectId}; 24 22 use mnemosyne_git::{ObjectStore, RefStore}; 25 23 use mnemosyne_postgres::PgBackend; 26 - use mnemosyne_protocol::{info_refs, upload_pack, RepoState}; 24 + use mnemosyne_protocol::{RepoState, info_refs, upload_pack}; 27 25 use proptest::prelude::*; 28 26 use tokio::net::TcpListener; 29 - 30 27 31 28 fn cases_default(default: u32) -> u32 { 32 29 std::env::var("PROPTEST_CASES") ··· 37 34 38 35 async fn seed(backend: &PgBackend, graph: &ObjectGraph, refs: &ProtocolRefSet) { 39 36 for (_oid, kind, bytes) in &graph.objects { 40 - backend.write(*kind, bytes).await.expect("write seed object"); 37 + backend 38 + .write(*kind, bytes) 39 + .await 40 + .expect("write seed object"); 41 41 } 42 42 backend 43 43 .commit_ref_transaction(refs.edits.clone()) ··· 93 93 94 94 #[test] 95 95 fn end_to_end_clone((graph, refs) in graph_with_refs()) { 96 - let result: anyhow::Result<()> = common::runtime().block_on(async { 97 - let pool = common::shared_pool().await; 96 + let result: anyhow::Result<()> = crate::common::runtime().block_on(async { 97 + let pool = crate::common::shared_pool().await; 98 98 let backend = Arc::new(PgBackend::with_pool( 99 - pool, common::unique_repo_did(), HashKind::Sha1, 99 + pool, crate::common::unique_repo_did(), HashKind::Sha1, 100 100 )); 101 101 seed(&backend, &graph, &refs).await; 102 102 ··· 203 203 /// pack-gen (entry order, hash trailer) and ref advertisement order. 204 204 #[test] 205 205 fn idempotent_reclone((graph, refs) in graph_with_refs()) { 206 - let result: anyhow::Result<()> = common::runtime().block_on(async { 207 - let pool = common::shared_pool().await; 206 + let result: anyhow::Result<()> = crate::common::runtime().block_on(async { 207 + let pool = crate::common::shared_pool().await; 208 208 let backend = Arc::new(PgBackend::with_pool( 209 - pool, common::unique_repo_did(), HashKind::Sha1, 209 + pool, crate::common::unique_repo_did(), HashKind::Sha1, 210 210 )); 211 211 seed(&backend, &graph, &refs).await; 212 212 ··· 274 274 /// because gix's default refspec dropped it). 275 275 #[test] 276 276 fn dump_cloned_refs_for_inspection() { 277 - use common::protocol_strategies::{object_graph, ref_set}; 277 + use crate::common::protocol_strategies::{object_graph, ref_set}; 278 278 use proptest::strategy::{Strategy, ValueTree}; 279 279 use proptest::test_runner::TestRunner; 280 280 ··· 285 285 .unwrap() 286 286 .current(); 287 287 288 - common::runtime().block_on(async { 289 - let pool = common::shared_pool().await; 288 + crate::common::runtime().block_on(async { 289 + let pool = crate::common::shared_pool().await; 290 290 let backend = Arc::new(PgBackend::with_pool( 291 291 pool, 292 - common::unique_repo_did(), 292 + crate::common::unique_repo_did(), 293 293 HashKind::Sha1, 294 294 )); 295 295 seed(&backend, &graph, &refs).await; ··· 305 305 let dest = tmp.path().join("clone.git"); 306 306 let url = format!("http://{addr}/"); 307 307 308 - let cloned = 309 - tokio::task::spawn_blocking(move || clone_bare(url, dest)) 310 - .await 311 - .unwrap() 312 - .unwrap(); 308 + let cloned = tokio::task::spawn_blocking(move || clone_bare(url, dest)) 309 + .await 310 + .unwrap() 311 + .unwrap(); 313 312 314 313 eprintln!("--- refs struct ---"); 315 314 eprintln!("{:#?}", refs); 316 - eprintln!("--- seeded ref set (expected_advertised: {}) ---", refs.expected_advertised.len()); 315 + eprintln!( 316 + "--- seeded ref set (expected_advertised: {}) ---", 317 + refs.expected_advertised.len() 318 + ); 317 319 for (name, oid) in &refs.expected_advertised { 318 320 eprintln!(" {name} -> {oid}", name = name.as_bstr()); 319 321 }
+8 -13
mnemosyne_tests/tests/protocol_edge_cases.rs mnemosyne_tests/tests/integration/protocol_edge_cases.rs
··· 9 9 //! for in-process tower-test, and exercises the same path real clients 10 10 //! traverse. 11 11 12 - mod common; 13 - 14 12 use std::sync::Arc; 15 13 16 - use axum::routing::{get, post}; 17 14 use axum::Router; 15 + use axum::routing::{get, post}; 18 16 use gix_hash::Kind as HashKind; 19 17 use mnemosyne_postgres::PgBackend; 20 - use mnemosyne_protocol::{info_refs, pkt, upload_pack, RepoState}; 18 + use mnemosyne_protocol::{RepoState, info_refs, pkt, upload_pack}; 21 19 use tokio::net::TcpListener; 22 - 23 20 24 21 fn build_app(state: RepoState) -> Router { 25 22 Router::new() ··· 39 36 } 40 37 41 38 async fn make_state() -> RepoState { 42 - let pool = common::shared_pool().await; 39 + let pool = crate::common::shared_pool().await; 43 40 let backend = Arc::new(PgBackend::with_pool( 44 41 pool, 45 - common::unique_repo_did(), 42 + crate::common::unique_repo_did(), 46 43 HashKind::Sha1, 47 44 )); 48 45 RepoState { ··· 61 58 let (addr, abort) = serve_app(state).await; 62 59 let client = reqwest::Client::new(); 63 60 let resp = client 64 - .get(format!("http://{addr}/info/refs?service=git-something-bogus")) 61 + .get(format!( 62 + "http://{addr}/info/refs?service=git-something-bogus" 63 + )) 65 64 .send() 66 65 .await 67 66 .unwrap(); ··· 113 112 let (addr, abort) = serve_app(state).await; 114 113 115 114 let mut body = Vec::new(); 116 - pkt::write_text( 117 - &mut body, 118 - b"want deadbeefcafebabe1234567890abcdef12345678", 119 - ) 120 - .unwrap(); 115 + pkt::write_text(&mut body, b"want deadbeefcafebabe1234567890abcdef12345678").unwrap(); 121 116 pkt::write_flush(&mut body).unwrap(); 122 117 pkt::write_text(&mut body, b"done").unwrap(); 123 118
+6 -13
mnemosyne_tests/tests/protocol_info_refs.rs mnemosyne_tests/tests/integration/protocol_info_refs.rs
··· 12 12 //! (c) when HEAD is symbolic, `symref=HEAD:<target>` is advertised; 13 13 //! (d) every seeded ref's (name, advertised-oid) appears exactly once. 14 14 15 - mod common; 16 - 17 15 use std::collections::BTreeSet; 18 16 use std::sync::Arc; 19 17 18 + use crate::common::protocol_strategies::{ObjectGraph, ProtocolRefSet, graph_with_refs}; 20 19 use bstr::{BString, ByteSlice}; 21 - use common::protocol_strategies::{graph_with_refs, ObjectGraph, ProtocolRefSet}; 22 20 use gix_hash::{Kind as HashKind, ObjectId}; 23 21 use gix_packetline::PacketLineRef; 24 22 use mnemosyne_git::{ObjectStore, RefStore}; 25 23 use mnemosyne_postgres::PgBackend; 26 24 use mnemosyne_protocol::{ 27 - info_refs::{build_info_refs, Service}, 28 25 RepoState, 26 + info_refs::{Service, build_info_refs}, 29 27 }; 30 28 use proptest::prelude::*; 31 - 32 29 33 30 fn cases_default(default: u32) -> u32 { 34 31 std::env::var("PROPTEST_CASES") ··· 67 64 fn frame_len(buf: &[u8]) -> usize { 68 65 let prefix = &buf[..4]; 69 66 let n = u16::from_str_radix(std::str::from_utf8(prefix).unwrap(), 16).unwrap(); 70 - if n < 4 { 71 - 4 72 - } else { 73 - n as usize 74 - } 67 + if n < 4 { 4 } else { n as usize } 75 68 } 76 69 77 70 /// Iterate pkt-line frames over a buffer, yielding each `PacketLineRef` ··· 166 159 167 160 #[test] 168 161 fn info_refs_advertises_all_seeded((graph, refs) in graph_with_refs()) { 169 - let result = common::runtime().block_on(async { 170 - let pool = common::shared_pool().await; 171 - let backend = Arc::new(PgBackend::with_pool(pool, common::unique_repo_did(), HashKind::Sha1)); 162 + let result = crate::common::runtime().block_on(async { 163 + let pool = crate::common::shared_pool().await; 164 + let backend = Arc::new(PgBackend::with_pool(pool, crate::common::unique_repo_did(), HashKind::Sha1)); 172 165 seed(&backend, &graph, &refs).await; 173 166 174 167 let state = RepoState {
mnemosyne_tests/tests/protocol_pack.proptest-regressions mnemosyne_tests/tests/proptest-regressions/protocol_pack.txt
+8 -8
mnemosyne_tests/tests/protocol_pack.rs mnemosyne_tests/tests/integration/protocol_pack.rs
··· 9 9 //! parity is exercised by the end-to-end clone proptest in 10 10 //! `protocol_clone.rs` (task 13). 11 11 12 - mod common; 13 - 14 12 use std::collections::HashMap; 15 13 use std::io::Cursor; 16 14 use std::sync::Arc; 17 15 18 - use common::protocol_strategies::{object_graph, ObjectGraph}; 16 + use crate::common::protocol_strategies::{ObjectGraph, object_graph}; 19 17 use gix_hash::{Kind as HashKind, ObjectId}; 20 18 use gix_object::Kind; 21 19 use gix_pack::data::input; ··· 23 21 use mnemosyne_postgres::PgBackend; 24 22 use mnemosyne_protocol::{find_adapter::PgFind, pack::make_pack}; 25 23 use proptest::prelude::*; 26 - 27 24 28 25 fn cases_default(default: u32) -> u32 { 29 26 std::env::var("PROPTEST_CASES") ··· 34 31 35 32 async fn seed_graph(backend: &PgBackend, graph: &ObjectGraph) { 36 33 for (_oid, kind, bytes) in &graph.objects { 37 - backend.write(*kind, bytes).await.expect("write seed object"); 34 + backend 35 + .write(*kind, bytes) 36 + .await 37 + .expect("write seed object"); 38 38 } 39 39 } 40 40 ··· 70 70 71 71 #[test] 72 72 fn pack_roundtrips_over_object_graph(graph in object_graph()) { 73 - let result = common::runtime().block_on(async { 74 - let pool = common::shared_pool().await; 75 - let backend = Arc::new(PgBackend::with_pool(pool, common::unique_repo_did(), HashKind::Sha1)); 73 + let result = crate::common::runtime().block_on(async { 74 + let pool = crate::common::shared_pool().await; 75 + let backend = Arc::new(PgBackend::with_pool(pool, crate::common::unique_repo_did(), HashKind::Sha1)); 76 76 seed_graph(&backend, &graph).await; 77 77 78 78 let wants = vec![graph.head_commit];
+5 -6
mnemosyne_tests/tests/protocol_pkt.rs mnemosyne_tests/tests/integration/protocol_pkt.rs
··· 51 51 fn frame_len(buf: &[u8]) -> usize { 52 52 let prefix = &buf[..4]; 53 53 let n = u16::from_str_radix(std::str::from_utf8(prefix).unwrap(), 16).unwrap(); 54 - if n < 4 { 55 - 4 56 - } else { 57 - n as usize 58 - } 54 + if n < 4 { 4 } else { n as usize } 59 55 } 60 56 61 57 fn decode_all(buf: &[u8]) -> Vec<AbstractFrame> { ··· 137 133 let payload = vec![0_u8; pkt::MAX_DATA_LEN + 1]; 138 134 let mut buf = Vec::new(); 139 135 let result = pkt::write_data(&mut buf, &payload); 140 - assert!(result.is_err(), "encoder should reject payload > MAX_DATA_LEN"); 136 + assert!( 137 + result.is_err(), 138 + "encoder should reject payload > MAX_DATA_LEN" 139 + ); 141 140 }
+8 -11
mnemosyne_tests/tests/protocol_push_concurrent.rs mnemosyne_tests/tests/integration/protocol_push_concurrent.rs
··· 12 12 //! it's the load-bearing guard for the "stateless, load-balanced" 13 13 //! design claim. 14 14 15 - mod common; 16 - 17 15 use std::path::Path; 18 16 use std::process::Command; 19 17 use std::sync::Arc; ··· 137 135 /// Clone from `url` into `dir`, edit README, and commit. Returns the new 138 136 /// commit's oid (parsed from `git rev-parse HEAD`). 139 137 fn clone_and_commit(url: &str, dir: &Path, payload: &str) -> ObjectId { 140 - let (ok, err) = run_git(dir.parent().unwrap(), &["clone", url, dir.to_str().unwrap()]); 138 + let (ok, err) = run_git( 139 + dir.parent().unwrap(), 140 + &["clone", url, dir.to_str().unwrap()], 141 + ); 141 142 assert!(ok, "clone failed: {err}"); 142 143 143 144 std::fs::write(dir.join("README.md"), payload).unwrap(); ··· 170 171 return; 171 172 } 172 173 173 - let pool = common::shared_pool().await; 174 - let repo_did = common::unique_repo_did(); 174 + let pool = crate::common::shared_pool().await; 175 + let repo_did = crate::common::unique_repo_did(); 175 176 let owner_did = "did:test:push-concurrent-owner".to_string(); 176 177 let repo_name = format!("repo-{}", &repo_did[9..]); 177 178 mnemosyne_postgres::ensure_repo( ··· 179 180 &repo_did, 180 181 &owner_did, 181 182 &repo_name, 182 - common::PLACEHOLDER_RKEY, 183 + crate::common::PLACEHOLDER_RKEY, 183 184 ) 184 185 .await 185 186 .expect("ensure_repo"); 186 - let backend = Arc::new(PgBackend::with_pool( 187 - pool, 188 - repo_did.clone(), 189 - HashKind::Sha1, 190 - )); 187 + let backend = Arc::new(PgBackend::with_pool(pool, repo_did.clone(), HashKind::Sha1)); 191 188 192 189 let initial_oid = seed_initial_commit(&backend).await; 193 190
+9 -8
mnemosyne_tests/tests/protocol_push_roundtrip.rs mnemosyne_tests/tests/integration/protocol_push_roundtrip.rs
··· 18 18 //! ingester whose object ordering depends on a hash-table iteration 19 19 //! that's stable only for one tree shape. 20 20 21 - mod common; 22 - 23 21 use std::path::Path; 24 22 use std::process::Command; 25 23 use std::sync::Arc; 26 24 25 + use crate::common::protocol_strategies::{ObjectGraph, ProtocolRefSet, graph_with_refs}; 27 26 use axum::Router; 28 27 use axum::routing::{get, post}; 29 - use common::protocol_strategies::{ObjectGraph, ProtocolRefSet, graph_with_refs}; 30 28 use gix_hash::{Kind as HashKind, ObjectId}; 31 29 use mnemosyne_git::{ObjectStore, RefStore}; 32 30 use mnemosyne_postgres::PgBackend; ··· 61 59 62 60 async fn seed(backend: &PgBackend, graph: &ObjectGraph, refs: &ProtocolRefSet) { 63 61 for (_oid, kind, bytes) in &graph.objects { 64 - backend.write(*kind, bytes).await.expect("write seed object"); 62 + backend 63 + .write(*kind, bytes) 64 + .await 65 + .expect("write seed object"); 65 66 } 66 67 backend 67 68 .commit_ref_transaction(refs.edits.clone()) ··· 118 119 return Ok(()); 119 120 } 120 121 121 - let result: anyhow::Result<()> = common::runtime().block_on(async { 122 - let pool = common::shared_pool().await; 123 - let repo_did = common::unique_repo_did(); 122 + let result: anyhow::Result<()> = crate::common::runtime().block_on(async { 123 + let pool = crate::common::shared_pool().await; 124 + let repo_did = crate::common::unique_repo_did(); 124 125 let owner_did = "did:test:push-roundtrip-owner".to_string(); 125 126 let repo_name = format!("repo-{}", &repo_did[9..]); 126 127 mnemosyne_postgres::ensure_repo( 127 - &pool, &repo_did, &owner_did, &repo_name, common::PLACEHOLDER_RKEY, 128 + &pool, &repo_did, &owner_did, &repo_name, crate::common::PLACEHOLDER_RKEY, 128 129 ).await.map_err(|e| anyhow::anyhow!("ensure_repo: {e}"))?; 129 130 130 131 let backend = Arc::new(PgBackend::with_pool(
+30 -16
mnemosyne_tests/tests/ssh_auth.rs mnemosyne_tests/tests/integration/ssh_auth.rs
··· 13 13 //! DID prefix so a sweep of `public_keys` rows for these tests can 14 14 //! distinguish them from other suite seeds. 15 15 16 - mod common; 17 - 18 16 use std::sync::Arc; 19 17 use std::time::Duration; 20 18 ··· 26 24 use sqlx::PgPool; 27 25 28 26 fn ephemeral_key() -> PrivateKey { 29 - PrivateKey::random(&mut rand::rng(), Algorithm::Ed25519) 30 - .expect("generate ephemeral key") 27 + PrivateKey::random(&mut rand::rng(), Algorithm::Ed25519).expect("generate ephemeral key") 31 28 } 32 29 33 30 fn ephemeral_server_config() -> Arc<ServerConfig> { ··· 85 82 // identical prefix (`ssh-ed25519 AAAAC3NzaC1lZDI1NTE5…`) so a 86 83 // hash of the key's leading bytes would collide between two 87 84 // distinct ed25519 keys. A monotonic counter avoids that. 88 - static RKEY_COUNTER: std::sync::atomic::AtomicU64 = 89 - std::sync::atomic::AtomicU64::new(0); 85 + static RKEY_COUNTER: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0); 90 86 let n = RKEY_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed); 91 87 let rkey = format!("test-rkey-{n}"); 92 88 sqlx::query( ··· 104 100 105 101 #[test] 106 102 fn registered_key_authenticates() { 107 - common::runtime().block_on(async { 108 - let pool = common::shared_pool().await; 103 + crate::common::runtime().block_on(async { 104 + let pool = crate::common::shared_pool().await; 109 105 let client_key = ephemeral_key(); 110 106 let pubkey = client_key.public_key().clone(); 111 - let did = format!("did:test:ssh-auth:registered:{}", common::unique_repo_did()); 107 + let did = format!( 108 + "did:test:ssh-auth:registered:{}", 109 + crate::common::unique_repo_did() 110 + ); 112 111 seed_key(&pool, &did, &pubkey, "2026-05-15T00:00:00Z").await; 113 112 114 113 let (addr, handle) = spawn_auth_server(pool.clone()).await; ··· 127 126 128 127 #[test] 129 128 fn unregistered_key_is_rejected() { 130 - common::runtime().block_on(async { 131 - let pool = common::shared_pool().await; 129 + crate::common::runtime().block_on(async { 130 + let pool = crate::common::shared_pool().await; 132 131 // Generate a key but do NOT seed it. Auth must fail. 133 132 let stranger = ephemeral_key(); 134 133 ··· 149 148 // Same DID, two different keys, both seeded. Either should 150 149 // authenticate; the auth lookup keys on the offered bytes, not 151 150 // on whose DID is registered. 152 - common::runtime().block_on(async { 153 - let pool = common::shared_pool().await; 154 - let did = format!("did:test:ssh-auth:rotated:{}", common::unique_repo_did()); 151 + crate::common::runtime().block_on(async { 152 + let pool = crate::common::shared_pool().await; 153 + let did = format!( 154 + "did:test:ssh-auth:rotated:{}", 155 + crate::common::unique_repo_did() 156 + ); 155 157 let key_a = ephemeral_key(); 156 158 let key_b = ephemeral_key(); 157 - seed_key(&pool, &did, &key_a.public_key().clone(), "2026-05-15T00:00:00Z").await; 158 - seed_key(&pool, &did, &key_b.public_key().clone(), "2026-05-15T00:00:01Z").await; 159 + seed_key( 160 + &pool, 161 + &did, 162 + &key_a.public_key().clone(), 163 + "2026-05-15T00:00:00Z", 164 + ) 165 + .await; 166 + seed_key( 167 + &pool, 168 + &did, 169 + &key_b.public_key().clone(), 170 + "2026-05-15T00:00:01Z", 171 + ) 172 + .await; 159 173 160 174 let (addr, handle) = spawn_auth_server(pool).await; 161 175
+91 -62
mnemosyne_tests/tests/ssh_push.rs mnemosyne_tests/tests/integration/ssh_push.rs
··· 10 10 //! to warrant separate test infrastructure. This smoke test pins the 11 11 //! routing contract without that overhead. 12 12 13 - mod common; 14 - 15 13 use std::sync::Arc; 16 14 use std::time::Duration; 17 15 ··· 20 18 use gix_ref::transaction::{Change, LogChange, PreviousValue, RefEdit}; 21 19 use gix_ref::{FullName, Target}; 22 20 use mnemosyne_git::{ObjectStore, RefStore}; 23 - use mnemosyne_postgres::{ensure_repo, PgBackend}; 21 + use mnemosyne_postgres::{PgBackend, ensure_repo}; 24 22 use mnemosyne_ssh::keys::ssh_key::PublicKey; 25 23 use mnemosyne_ssh::keys::{Algorithm, PrivateKey, PrivateKeyWithHashAlg}; 24 + use mnemosyne_ssh::russh::ChannelMsg; 26 25 use mnemosyne_ssh::russh::client; 27 - use mnemosyne_ssh::russh::ChannelMsg; 28 26 use mnemosyne_ssh::{Server, ServerConfig}; 29 27 use sqlx::PgPool; 30 28 ··· 33 31 use mnemosyne_protocol::PostReceiveBroadcaster; 34 32 35 33 fn ephemeral_key() -> PrivateKey { 36 - PrivateKey::random(&mut rand::rng(), Algorithm::Ed25519) 37 - .expect("generate ephemeral key") 34 + PrivateKey::random(&mut rand::rng(), Algorithm::Ed25519).expect("generate ephemeral key") 38 35 } 39 36 40 37 fn ephemeral_server_config() -> Arc<ServerConfig> { ··· 105 102 106 103 #[test] 107 104 fn ssh_push_can_delete_a_ref() { 108 - common::runtime().block_on(async { 109 - let pool = common::shared_pool().await; 110 - let repo_did = common::unique_repo_did(); 105 + crate::common::runtime().block_on(async { 106 + let pool = crate::common::shared_pool().await; 107 + let repo_did = crate::common::unique_repo_did(); 111 108 let owner_did = "did:test:ssh-push-owner".to_string(); 112 109 let repo_name = format!("repo-{}", &repo_did[9..]); 113 - ensure_repo(&pool, &repo_did, &owner_did, &repo_name, common::PLACEHOLDER_RKEY) 114 - .await 115 - .expect("ensure_repo"); 110 + ensure_repo( 111 + &pool, 112 + &repo_did, 113 + &owner_did, 114 + &repo_name, 115 + crate::common::PLACEHOLDER_RKEY, 116 + ) 117 + .await 118 + .expect("ensure_repo"); 116 119 let backend = PgBackend::with_pool(pool.clone(), repo_did.clone(), HashKind::Sha1); 117 120 118 121 // Seed: a blob + a ref pointing at it. Delete-only push doesn't ··· 163 166 .expect("authenticate"); 164 167 assert!(auth.success(), "auth failed"); 165 168 166 - let mut channel = session 167 - .channel_open_session() 168 - .await 169 - .expect("open session"); 169 + let mut channel = session.channel_open_session().await.expect("open session"); 170 170 171 171 // exec the receive-pack with the repo arg as a bare DID. 172 172 let exec_cmd = format!("git-receive-pack {repo_did}"); ··· 238 238 // Bob is neither the owner nor a registered collaborator. Push 239 239 // should fail with a non-zero exit, and the seeded ref must 240 240 // remain in place. 241 - common::runtime().block_on(async { 242 - let pool = common::shared_pool().await; 243 - let repo_did = common::unique_repo_did(); 241 + crate::common::runtime().block_on(async { 242 + let pool = crate::common::shared_pool().await; 243 + let repo_did = crate::common::unique_repo_did(); 244 244 let alice_did = "did:test:authz-alice".to_string(); 245 245 let bob_did = "did:test:authz-bob".to_string(); 246 246 let repo_name = format!("repo-{}", &repo_did[9..]); 247 - ensure_repo(&pool, &repo_did, &alice_did, &repo_name, common::PLACEHOLDER_RKEY) 248 - .await 249 - .expect("ensure_repo"); 247 + ensure_repo( 248 + &pool, 249 + &repo_did, 250 + &alice_did, 251 + &repo_name, 252 + crate::common::PLACEHOLDER_RKEY, 253 + ) 254 + .await 255 + .expect("ensure_repo"); 250 256 let backend = PgBackend::with_pool(pool.clone(), repo_did.clone(), HashKind::Sha1); 251 257 252 258 let blob_oid = backend ··· 280 286 .await 281 287 .expect("connect"); 282 288 let auth = session 283 - .authenticate_publickey("anyone", PrivateKeyWithHashAlg::new(Arc::new(bob_key), None)) 289 + .authenticate_publickey( 290 + "anyone", 291 + PrivateKeyWithHashAlg::new(Arc::new(bob_key), None), 292 + ) 284 293 .await 285 294 .expect("authenticate"); 286 295 assert!(auth.success(), "Bob's key is registered so auth succeeds"); ··· 340 349 // record published by Alice had been ingested). Bob's key is 341 350 // registered. Bob's push should succeed — the receive_pack authz 342 351 // check passes by collaborator lookup. 343 - common::runtime().block_on(async { 344 - let pool = common::shared_pool().await; 345 - let repo_did = common::unique_repo_did(); 352 + crate::common::runtime().block_on(async { 353 + let pool = crate::common::shared_pool().await; 354 + let repo_did = crate::common::unique_repo_did(); 346 355 let alice_did = "did:test:collab-authz-alice".to_string(); 347 356 let bob_did = "did:test:collab-authz-bob".to_string(); 348 357 let repo_name = format!("repo-{}", &repo_did[9..]); 349 - ensure_repo(&pool, &repo_did, &alice_did, &repo_name, common::PLACEHOLDER_RKEY) 350 - .await 351 - .expect("ensure_repo"); 358 + ensure_repo( 359 + &pool, 360 + &repo_did, 361 + &alice_did, 362 + &repo_name, 363 + crate::common::PLACEHOLDER_RKEY, 364 + ) 365 + .await 366 + .expect("ensure_repo"); 352 367 let backend = PgBackend::with_pool(pool.clone(), repo_did.clone(), HashKind::Sha1); 353 368 354 369 let blob_oid = backend ··· 395 410 .await 396 411 .expect("connect"); 397 412 let auth = session 398 - .authenticate_publickey("anyone", PrivateKeyWithHashAlg::new(Arc::new(bob_key), None)) 413 + .authenticate_publickey( 414 + "anyone", 415 + PrivateKeyWithHashAlg::new(Arc::new(bob_key), None), 416 + ) 399 417 .await 400 418 .expect("authenticate"); 401 419 assert!(auth.success()); ··· 454 472 // (repo_a, bob) must NOT satisfy authz on repo B. Catches the 455 473 // "we accidentally dropped the repo_did = $1 from the SQL" class 456 474 // of bug. 457 - common::runtime().block_on(async { 458 - let pool = common::shared_pool().await; 459 - let repo_a_did = common::unique_repo_did(); 460 - let repo_b_did = common::unique_repo_did(); 475 + crate::common::runtime().block_on(async { 476 + let pool = crate::common::shared_pool().await; 477 + let repo_a_did = crate::common::unique_repo_did(); 478 + let repo_b_did = crate::common::unique_repo_did(); 461 479 let alice_did = "did:test:cross-scope-alice".to_string(); 462 480 let bob_did = "did:test:cross-scope-bob".to_string(); 463 481 ensure_repo( ··· 465 483 &repo_a_did, 466 484 &alice_did, 467 485 &format!("repo-a-{}", &repo_a_did[9..]), 468 - common::PLACEHOLDER_RKEY, 486 + crate::common::PLACEHOLDER_RKEY, 469 487 ) 470 488 .await 471 489 .expect("seed repo A"); ··· 474 492 &repo_b_did, 475 493 &alice_did, 476 494 &format!("repo-b-{}", &repo_b_did[9..]), 477 - common::PLACEHOLDER_RKEY, 495 + crate::common::PLACEHOLDER_RKEY, 478 496 ) 479 497 .await 480 498 .expect("seed repo B"); ··· 584 602 // reject the push with a 403 Forbidden rather than silently 585 603 // mutating state. Mirrors the Go reference's "push goes via SSH 586 604 // only" policy at the handler layer. 587 - common::runtime().block_on(async { 605 + crate::common::runtime().block_on(async { 606 + use axum::Router; 588 607 use axum::body::Body; 589 608 use axum::http::{Request, StatusCode}; 590 609 use axum::routing::post; 591 - use axum::Router; 592 610 use http_body_util::BodyExt; 593 - use mnemosyne_protocol::{receive_pack, RepoState}; 611 + use mnemosyne_protocol::{RepoState, receive_pack}; 594 612 use std::sync::Arc as StdArc; 595 613 use tower::ServiceExt; 596 614 597 - let pool = common::shared_pool().await; 598 - let repo_did = common::unique_repo_did(); 615 + let pool = crate::common::shared_pool().await; 616 + let repo_did = crate::common::unique_repo_did(); 599 617 let owner_did = "did:test:http-deny-owner".to_string(); 600 618 let repo_name = format!("repo-{}", &repo_did[9..]); 601 - ensure_repo(&pool, &repo_did, &owner_did, &repo_name, common::PLACEHOLDER_RKEY) 602 - .await 603 - .expect("ensure_repo"); 619 + ensure_repo( 620 + &pool, 621 + &repo_did, 622 + &owner_did, 623 + &repo_name, 624 + crate::common::PLACEHOLDER_RKEY, 625 + ) 626 + .await 627 + .expect("ensure_repo"); 604 628 let backend = StdArc::new(PgBackend::with_pool( 605 629 pool.clone(), 606 630 repo_did.clone(), ··· 684 708 685 709 #[test] 686 710 fn ssh_push_fires_post_receive_event() { 687 - common::runtime().block_on(async { 688 - let pool = common::shared_pool().await; 689 - let repo_did = common::unique_repo_did(); 711 + crate::common::runtime().block_on(async { 712 + let pool = crate::common::shared_pool().await; 713 + let repo_did = crate::common::unique_repo_did(); 690 714 let owner_did = "did:test:ssh-event-owner".to_string(); 691 715 let repo_name = format!("repo-{}", &repo_did[9..]); 692 - ensure_repo(&pool, &repo_did, &owner_did, &repo_name, common::PLACEHOLDER_RKEY) 693 - .await 694 - .expect("ensure_repo"); 716 + ensure_repo( 717 + &pool, 718 + &repo_did, 719 + &owner_did, 720 + &repo_name, 721 + crate::common::PLACEHOLDER_RKEY, 722 + ) 723 + .await 724 + .expect("ensure_repo"); 695 725 let backend = PgBackend::with_pool(pool.clone(), repo_did.clone(), HashKind::Sha1); 696 726 697 727 let blob_oid = backend ··· 733 763 let mut session = client::connect(client_config, addr, TestClient) 734 764 .await 735 765 .expect("connect"); 736 - assert!(session 737 - .authenticate_publickey( 738 - "anyone", 739 - PrivateKeyWithHashAlg::new(Arc::new(client_key), None), 740 - ) 741 - .await 742 - .expect("authenticate") 743 - .success()); 766 + assert!( 767 + session 768 + .authenticate_publickey( 769 + "anyone", 770 + PrivateKeyWithHashAlg::new(Arc::new(client_key), None), 771 + ) 772 + .await 773 + .expect("authenticate") 774 + .success() 775 + ); 744 776 745 - let mut channel = session 746 - .channel_open_session() 747 - .await 748 - .expect("open session"); 777 + let mut channel = session.channel_open_session().await.expect("open session"); 749 778 channel 750 779 .exec(true, format!("git-receive-pack {repo_did}")) 751 780 .await
mnemosyne_tests/tests/ssh_push_parity.proptest-regressions mnemosyne_tests/tests/proptest-regressions/ssh_push_parity.txt
+14 -15
mnemosyne_tests/tests/ssh_push_parity.rs mnemosyne_tests/tests/integration/ssh_push_parity.rs
··· 18 18 //! The container is shared across cases via OnceCell; per-case 19 19 //! overhead is the SSH server start + `docker exec` git invocations. 20 20 21 - mod common; 22 - 23 21 use std::sync::Arc; 24 22 use std::time::Duration; 25 23 26 - use common::git_ssh_container; 27 - use common::protocol_strategies::{graph_with_refs, ObjectGraph, ProtocolRefSet}; 24 + use crate::common::git_ssh_container; 25 + use crate::common::protocol_strategies::{ObjectGraph, ProtocolRefSet, graph_with_refs}; 28 26 use gix_hash::{Kind as HashKind, ObjectId}; 29 27 use mnemosyne_git::{ObjectStore, RefStore}; 30 - use mnemosyne_postgres::{ensure_repo, PgBackend}; 28 + use mnemosyne_postgres::{PgBackend, ensure_repo}; 31 29 use mnemosyne_ssh::keys::{Algorithm, PrivateKey}; 32 30 use mnemosyne_ssh::{Server as SshServer, ServerConfig}; 33 31 use proptest::prelude::*; ··· 42 40 43 41 async fn seed(backend: &PgBackend, graph: &ObjectGraph, refs: &ProtocolRefSet) { 44 42 for (_oid, kind, bytes) in &graph.objects { 45 - backend.write(*kind, bytes).await.expect("write seed object"); 43 + backend 44 + .write(*kind, bytes) 45 + .await 46 + .expect("write seed object"); 46 47 } 47 48 backend 48 49 .commit_ref_transaction(refs.edits.clone()) ··· 50 51 .expect("commit seed refs"); 51 52 } 52 53 53 - async fn spawn_ssh_server( 54 - pool: PgPool, 55 - ) -> (std::net::SocketAddr, tokio::task::JoinHandle<()>) { 54 + async fn spawn_ssh_server(pool: PgPool) -> (std::net::SocketAddr, tokio::task::JoinHandle<()>) { 56 55 // Bind to 0.0.0.0 so the container can reach us via 57 56 // host.docker.internal. localhost binding would be invisible. 58 57 let listener = tokio::net::TcpListener::bind("0.0.0.0:0") ··· 60 59 .expect("bind ssh ephemeral"); 61 60 let addr = listener.local_addr().expect("local_addr"); 62 61 63 - let host_key = PrivateKey::random(&mut rand::rng(), Algorithm::Ed25519) 64 - .expect("generate host key"); 62 + let host_key = 63 + PrivateKey::random(&mut rand::rng(), Algorithm::Ed25519).expect("generate host key"); 65 64 let config = Arc::new(ServerConfig { 66 65 keys: vec![host_key], 67 66 inactivity_timeout: Some(Duration::from_secs(30)), ··· 109 108 110 109 #[test] 111 110 fn ssh_push_then_verify_round_trips((graph, refs) in graph_with_refs()) { 112 - let result: anyhow::Result<()> = common::runtime().block_on(async { 113 - let pool = common::shared_pool().await; 114 - let repo_did = common::unique_repo_did(); 111 + let result: anyhow::Result<()> = crate::common::runtime().block_on(async { 112 + let pool = crate::common::shared_pool().await; 113 + let repo_did = crate::common::unique_repo_did(); 115 114 let owner_did = "did:test:ssh-parity-owner".to_string(); 116 115 let repo_name = format!("repo-{}", &repo_did[9..]); 117 - ensure_repo(&pool, &repo_did, &owner_did, &repo_name, common::PLACEHOLDER_RKEY) 116 + ensure_repo(&pool, &repo_did, &owner_did, &repo_name, crate::common::PLACEHOLDER_RKEY) 118 117 .await 119 118 .map_err(|e| anyhow::anyhow!("ensure_repo: {e}"))?; 120 119 // Register the key under the owner DID — receive_pack's
+17 -17
mnemosyne_tests/tests/storage_dedup.rs mnemosyne_tests/tests/integration/storage_dedup.rs
··· 8 8 //! storing the same bytes would mean two `objects` rows; with dedup, 9 9 //! only inclusions multiply. 10 10 11 - mod common; 12 - 13 11 use gix_hash::Kind as HashKind; 14 12 use gix_object::Kind as ObjKind; 15 13 use mnemosyne_git::ObjectStore; ··· 17 15 18 16 #[test] 19 17 fn identical_blob_in_two_repos_is_stored_once() { 20 - common::runtime().block_on(async { 21 - let pool = common::shared_pool().await; 22 - let repo_a = common::unique_repo_did(); 23 - let repo_b = common::unique_repo_did(); 18 + crate::common::runtime().block_on(async { 19 + let pool = crate::common::shared_pool().await; 20 + let repo_a = crate::common::unique_repo_did(); 21 + let repo_b = crate::common::unique_repo_did(); 24 22 25 23 let backend_a = PgBackend::with_pool(pool.clone(), repo_a.clone(), HashKind::Sha1); 26 24 let backend_b = PgBackend::with_pool(pool.clone(), repo_b.clone(), HashKind::Sha1); ··· 38 36 39 37 // Content-addressing: same bytes → same OID. The whole dedup 40 38 // story only works because git already guarantees this. 41 - assert_eq!(oid_a, oid_b, "content-addressing should yield identical OIDs"); 39 + assert_eq!( 40 + oid_a, oid_b, 41 + "content-addressing should yield identical OIDs" 42 + ); 42 43 43 - let objects_rows: i64 = 44 - sqlx::query_scalar("SELECT COUNT(*) FROM objects WHERE oid = $1") 45 - .bind(oid_a.as_bytes()) 46 - .fetch_one(&pool) 47 - .await 48 - .unwrap(); 44 + let objects_rows: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM objects WHERE oid = $1") 45 + .bind(oid_a.as_bytes()) 46 + .fetch_one(&pool) 47 + .await 48 + .unwrap(); 49 49 assert_eq!(objects_rows, 1, "dedup pool should store the bytes once"); 50 50 51 51 let inclusion_rows: i64 = sqlx::query_scalar( ··· 72 72 73 73 #[test] 74 74 fn repo_without_inclusion_cannot_see_dedup_pool_object() { 75 - common::runtime().block_on(async { 76 - let pool = common::shared_pool().await; 77 - let repo_a = common::unique_repo_did(); 78 - let repo_b = common::unique_repo_did(); 75 + crate::common::runtime().block_on(async { 76 + let pool = crate::common::shared_pool().await; 77 + let repo_a = crate::common::unique_repo_did(); 78 + let repo_b = crate::common::unique_repo_did(); 79 79 80 80 let backend_a = PgBackend::with_pool(pool.clone(), repo_a, HashKind::Sha1); 81 81 let backend_b = PgBackend::with_pool(pool.clone(), repo_b, HashKind::Sha1);
+7 -6
mnemosyne_tests/tests/stress.rs mnemosyne_tests/tests/integration/stress.rs
··· 4 4 //! rather than many small ones — exercises sustained throughput and 5 5 //! catches state-accumulation bugs that small scripts wouldn't surface. 6 6 7 - mod common; 8 - 9 - use common::storage_strategies::{mixed_cmd, to_concrete, AbstractCmd}; 7 + use crate::common::storage_strategies::{AbstractCmd, mixed_cmd, to_concrete}; 10 8 use gix_hash::ObjectId; 11 9 use mnemosyne_harness::{Divergence, DualRunner, Outcome}; 12 10 use proptest::strategy::{Strategy, ValueTree}; ··· 34 32 script.push(tree.current()); 35 33 } 36 34 37 - common::runtime().block_on(async { 38 - let (_tmp, file, pg) = common::fresh_backends().await; 35 + crate::common::runtime().block_on(async { 36 + let (_tmp, file, pg) = crate::common::fresh_backends().await; 39 37 let dual = DualRunner::new(file, pg); 40 38 41 39 let mut writes: Vec<ObjectId> = Vec::new(); ··· 55 53 } 56 54 } 57 55 58 - eprintln!("stress: completed {stress_len} commands, {} writes", writes.len()); 56 + eprintln!( 57 + "stress: completed {stress_len} commands, {} writes", 58 + writes.len() 59 + ); 59 60 }); 60 61 }
+25 -14
mnemosyne_tests/tests/stress_concurrent_push.rs mnemosyne_tests/tests/integration/stress_concurrent_push.rs
··· 38 38 39 39 #![allow(unreachable_pub)] 40 40 41 - mod common; 42 - 43 41 use std::io::Write as _; 44 42 use std::sync::Arc; 45 43 use std::time::{Duration, Instant}; ··· 222 220 } 223 221 224 222 async fn make_pool_for_stress(pool_size: u32) -> (PgPool, impl Drop) { 225 - common::ensure_docker_host(); 223 + crate::common::ensure_docker_host(); 226 224 let container = TcPostgres::default().start().await.unwrap(); 227 225 let port = container.get_host_port_ipv4(5432).await.unwrap(); 228 226 let url = format!("postgres://postgres:postgres@127.0.0.1:{port}/postgres"); ··· 379 377 // (newline, NUL, or end-of-body). Without this we'd match 380 378 // `refs/heads/main` against `refs/heads/main-foo`. 381 379 let after = space_idx + needle_bytes.len(); 382 - let terminator_ok = after >= body.len() 383 - || matches!(body[after], b'\n' | b'\0'); 380 + let terminator_ok = after >= body.len() || matches!(body[after], b'\n' | b'\0'); 384 381 if terminator_ok && space_idx >= 40 { 385 382 let oid_slice = &body[space_idx - 40..space_idx]; 386 383 if let Ok(oid) = ObjectId::from_hex(oid_slice) { ··· 430 427 let repo_did = format!("did:stress:{}", std::process::id()); 431 428 let owner_did = "did:test:stress-owner".to_string(); 432 429 let repo_name = format!("stress-{}", std::process::id()); 433 - mnemosyne_postgres::ensure_repo( 434 - &pool, &repo_did, &owner_did, &repo_name, "stress-rkey", 435 - ) 436 - .await 437 - .expect("ensure_repo"); 430 + mnemosyne_postgres::ensure_repo(&pool, &repo_did, &owner_did, &repo_name, "stress-rkey") 431 + .await 432 + .expect("ensure_repo"); 438 433 let backend = Arc::new(PgBackend::with_pool( 439 434 pool.clone(), 440 435 repo_did.clone(), ··· 556 551 println!("pool size: {pool_size}"); 557 552 println!("duration target: {duration_secs}s"); 558 553 println!("duration actual: {:.2}s", stress_elapsed.as_secs_f64()); 559 - println!("mode: {}", if same_ref { "same-ref (contention + CAS retry)" } else { "independent refs" }); 554 + println!( 555 + "mode: {}", 556 + if same_ref { 557 + "same-ref (contention + CAS retry)" 558 + } else { 559 + "independent refs" 560 + } 561 + ); 560 562 println!("----"); 561 563 println!("successful pushes: {total_successes}"); 562 564 println!("throughput: {throughput:.1} pushes/sec"); 563 565 println!("CAS retries: {total_cas}"); 564 566 println!("other errors: {total_other}"); 565 567 println!("----"); 566 - println!("latency p50: {} ms", percentile(&all_lats, 50.0) / 1000); 567 - println!("latency p95: {} ms", percentile(&all_lats, 95.0) / 1000); 568 - println!("latency p99: {} ms", percentile(&all_lats, 99.0) / 1000); 568 + println!( 569 + "latency p50: {} ms", 570 + percentile(&all_lats, 50.0) / 1000 571 + ); 572 + println!( 573 + "latency p95: {} ms", 574 + percentile(&all_lats, 95.0) / 1000 575 + ); 576 + println!( 577 + "latency p99: {} ms", 578 + percentile(&all_lats, 99.0) / 1000 579 + ); 569 580 println!( 570 581 "latency max: {} ms", 571 582 all_lats.last().copied().unwrap_or(0) / 1000,
+24 -22
mnemosyne_tests/tests/xrpc_browse.rs mnemosyne_tests/tests/integration/xrpc_browse.rs
··· 6 6 //! verify the response shape matches the lexicon and the contents match 7 7 //! what we seeded. 8 8 9 - mod common; 10 - 11 9 use std::io::Read; 12 10 13 11 use axum::body::Body; ··· 17 15 use gix_actor::Signature; 18 16 use gix_hash::{Kind as HashKind, ObjectId}; 19 17 use gix_object::tree::EntryKind; 20 - use gix_object::{tree, Commit, Kind as ObjKind, Tree, WriteTo}; 18 + use gix_object::{Commit, Kind as ObjKind, Tree, WriteTo, tree}; 21 19 use gix_ref::transaction::{Change, LogChange, PreviousValue, RefEdit}; 22 20 use gix_ref::{FullName, Target}; 23 21 use http_body_util::BodyExt; 24 22 use mnemosyne_git::{ObjectStore, RefStore}; 25 - use mnemosyne_postgres::{ensure_repo, PgBackend}; 26 - use mnemosyne_xrpc::{router, XrpcState}; 23 + use mnemosyne_postgres::{PgBackend, ensure_repo}; 24 + use mnemosyne_xrpc::{XrpcState, router}; 27 25 use serde_json::Value; 28 26 use sqlx::PgPool; 29 27 use tar::Archive; ··· 36 34 } 37 35 38 36 async fn setup() -> BrowseFixture { 39 - let pool = common::shared_pool().await; 40 - let repo_did = common::unique_repo_did(); 37 + let pool = crate::common::shared_pool().await; 38 + let repo_did = crate::common::unique_repo_did(); 41 39 let owner_did = "did:test:browse-owner".to_string(); 42 40 let repo_name = format!("repo-{}", &repo_did[9..]); 43 41 ensure_repo( ··· 45 43 &repo_did, 46 44 &owner_did, 47 45 &repo_name, 48 - common::PLACEHOLDER_RKEY, 46 + crate::common::PLACEHOLDER_RKEY, 49 47 ) 50 48 .await 51 49 .expect("ensure_repo"); ··· 56 54 let files: Vec<(String, Vec<u8>)> = vec![ 57 55 ("README.md".to_string(), b"# the readme\n\nhello\n".to_vec()), 58 56 ("alpha.txt".to_string(), b"alpha contents".to_vec()), 59 - ("data.bin".to_string(), vec![0x00, 0x01, 0x02, 0xff, 0x00, 0xab]), 57 + ( 58 + "data.bin".to_string(), 59 + vec![0x00, 0x01, 0x02, 0xff, 0x00, 0xab], 60 + ), 60 61 ]; 61 62 62 63 let mut entries: Vec<tree::Entry> = Vec::new(); ··· 126 127 .await 127 128 .expect("commit refs"); 128 129 129 - BrowseFixture { pool, repo_did, files } 130 + BrowseFixture { 131 + pool, 132 + repo_did, 133 + files, 134 + } 130 135 } 131 136 132 137 fn state(pool: PgPool) -> XrpcState { ··· 163 168 164 169 #[test] 165 170 fn tree_lists_seeded_entries() { 166 - common::runtime().block_on(async { 171 + crate::common::runtime().block_on(async { 167 172 let f = setup().await; 168 173 let uri = format!( 169 174 "/sh.tangled.repo.tree?repo={}&ref=main", ··· 195 200 // README is inlined. 196 201 let readme = &json["readme"]; 197 202 assert_eq!(readme["filename"].as_str(), Some("README.md")); 198 - assert!(readme["contents"] 199 - .as_str() 200 - .unwrap() 201 - .contains("the readme")); 203 + assert!(readme["contents"].as_str().unwrap().contains("the readme")); 202 204 }); 203 205 } 204 206 205 207 #[test] 206 208 fn blob_returns_utf8_for_text() { 207 - common::runtime().block_on(async { 209 + crate::common::runtime().block_on(async { 208 210 let f = setup().await; 209 211 let uri = format!( 210 212 "/sh.tangled.repo.blob?repo={}&ref=main&path=alpha.txt", ··· 221 223 222 224 #[test] 223 225 fn blob_base64_encodes_binary() { 224 - common::runtime().block_on(async { 226 + crate::common::runtime().block_on(async { 225 227 let f = setup().await; 226 228 let uri = format!( 227 229 "/sh.tangled.repo.blob?repo={}&ref=main&path=data.bin", ··· 232 234 assert_eq!(json["encoding"].as_str(), Some("base64")); 233 235 assert_eq!(json["isBinary"].as_bool(), Some(true)); 234 236 235 - use base64::engine::general_purpose::STANDARD as B64; 236 237 use base64::Engine; 238 + use base64::engine::general_purpose::STANDARD as B64; 237 239 let decoded = B64.decode(json["content"].as_str().unwrap()).unwrap(); 238 240 assert_eq!(decoded, vec![0x00, 0x01, 0x02, 0xff, 0x00, 0xab]); 239 241 }); ··· 241 243 242 244 #[test] 243 245 fn blob_raw_mode_returns_bytes_directly() { 244 - common::runtime().block_on(async { 246 + crate::common::runtime().block_on(async { 245 247 let f = setup().await; 246 248 let uri = format!( 247 249 "/sh.tangled.repo.blob?repo={}&ref=main&path=alpha.txt&raw=true", ··· 255 257 256 258 #[test] 257 259 fn archive_tar_gz_round_trips_every_blob() { 258 - common::runtime().block_on(async { 260 + crate::common::runtime().block_on(async { 259 261 let f = setup().await; 260 262 let uri = format!( 261 263 "/sh.tangled.repo.archive?repo={}&ref=main", ··· 287 289 288 290 #[test] 289 291 fn tree_reports_path_not_found_for_missing_segment() { 290 - common::runtime().block_on(async { 292 + crate::common::runtime().block_on(async { 291 293 let f = setup().await; 292 294 let uri = format!( 293 295 "/sh.tangled.repo.tree?repo={}&ref=main&path=nonexistent/dir", ··· 301 303 302 304 #[test] 303 305 fn tree_reports_ref_not_found_for_unknown_ref() { 304 - common::runtime().block_on(async { 306 + crate::common::runtime().block_on(async { 305 307 let f = setup().await; 306 308 let uri = format!( 307 309 "/sh.tangled.repo.tree?repo={}&ref=not-a-real-branch",
+48 -39
mnemosyne_tests/tests/xrpc_endpoints.rs mnemosyne_tests/tests/integration/xrpc_endpoints.rs
··· 5 5 //! — no port binding, no real network. Asserts cover happy paths plus the 6 6 //! error responses we want to keep stable (status code + `error` tag). 7 7 //! 8 - //! Tests use `common::runtime().block_on(...)` rather than `#[tokio::test]` 8 + //! Tests use `crate::common::runtime().block_on(...)` rather than `#[tokio::test]` 9 9 //! so they share a long-lived runtime with the testcontainer pool — the 10 10 //! per-test runtimes created by `#[tokio::test]` shut down while the 11 11 //! shared pool's background tasks are still active and cause spurious 12 12 //! `PoolTimedOut` and "context being shutdown" errors. 13 - 14 - mod common; 15 13 16 14 use axum::body::Body; 17 15 use axum::http::{Request, StatusCode}; ··· 22 20 use gix_ref::{FullName, Target}; 23 21 use http_body_util::BodyExt; 24 22 use mnemosyne_git::{ObjectStore, RefStore}; 25 - use mnemosyne_postgres::{ensure_repo, PgBackend}; 26 - use mnemosyne_xrpc::{router, XrpcState}; 23 + use mnemosyne_postgres::{PgBackend, ensure_repo}; 24 + use mnemosyne_xrpc::{XrpcState, router}; 27 25 use serde_json::Value; 28 26 use sqlx::PgPool; 29 27 use tower::ServiceExt; ··· 39 37 } 40 38 41 39 async fn setup() -> Fixture { 42 - let pool = common::shared_pool().await; 43 - let repo_did = common::unique_repo_did(); 40 + let pool = crate::common::shared_pool().await; 41 + let repo_did = crate::common::unique_repo_did(); 44 42 let owner_did = "did:test:owner".to_string(); 45 43 let repo_name = format!("repo-{}", &repo_did[9..]); 46 44 ensure_repo( ··· 48 46 &repo_did, 49 47 &owner_did, 50 48 &repo_name, 51 - common::PLACEHOLDER_RKEY, 49 + crate::common::PLACEHOLDER_RKEY, 52 50 ) 53 51 .await 54 52 .expect("ensure_repo"); ··· 125 123 } 126 124 127 125 fn run<F: std::future::Future<Output = ()>>(fut: F) { 128 - common::runtime().block_on(fut); 126 + crate::common::runtime().block_on(fut); 129 127 } 130 128 131 129 // --------------------------------------------------------------------------- ··· 135 133 #[test] 136 134 fn knot_version_returns_crate_version() { 137 135 run(async { 138 - let pool = common::shared_pool().await; 136 + let pool = crate::common::shared_pool().await; 139 137 let (status, body) = serve(state(pool, None), "/sh.tangled.knot.version").await; 140 138 assert_eq!(status, StatusCode::OK); 141 139 assert!(body["version"].is_string()); ··· 150 148 #[test] 151 149 fn owner_returns_configured_did() { 152 150 run(async { 153 - let pool = common::shared_pool().await; 151 + let pool = crate::common::shared_pool().await; 154 152 let (status, body) = serve( 155 153 state(pool, Some("did:test:knot-operator".to_string())), 156 154 "/sh.tangled.owner", ··· 164 162 #[test] 165 163 fn owner_unset_returns_owner_not_found() { 166 164 run(async { 167 - let pool = common::shared_pool().await; 165 + let pool = crate::common::shared_pool().await; 168 166 let (status, body) = serve(state(pool, None), "/sh.tangled.owner").await; 169 167 assert_eq!(status, StatusCode::INTERNAL_SERVER_ERROR); 170 168 assert_eq!(body["error"], "OwnerNotFound"); ··· 179 177 fn get_default_branch_happy_path() { 180 178 run(async { 181 179 let fx = setup().await; 182 - let uri = format!( 183 - "/sh.tangled.repo.getDefaultBranch?repo={}", 184 - fx.repo_did 185 - ); 180 + let uri = format!("/sh.tangled.repo.getDefaultBranch?repo={}", fx.repo_did); 186 181 let (status, body) = serve(state(fx.pool, None), &uri).await; 187 182 assert_eq!(status, StatusCode::OK, "body={body}"); 188 183 assert_eq!(body["name"], "main"); ··· 194 189 #[test] 195 190 fn get_default_branch_unknown_repo_returns_repo_not_found() { 196 191 run(async { 197 - let pool = common::shared_pool().await; 192 + let pool = crate::common::shared_pool().await; 198 193 let (status, body) = serve( 199 194 state(pool, None), 200 195 "/sh.tangled.repo.getDefaultBranch?repo=did:test:does-not-exist", ··· 208 203 #[test] 209 204 fn get_default_branch_non_did_repo_returns_invalid_request() { 210 205 run(async { 211 - let pool = common::shared_pool().await; 206 + let pool = crate::common::shared_pool().await; 212 207 let (status, body) = serve( 213 208 state(pool, None), 214 209 "/sh.tangled.repo.getDefaultBranch?repo=not-a-did", ··· 259 254 fn branch_returns_short_hash_and_is_default() { 260 255 run(async { 261 256 let fx = setup().await; 262 - let uri = format!( 263 - "/sh.tangled.repo.branch?repo={}&name=main", 264 - fx.repo_did 265 - ); 257 + let uri = format!("/sh.tangled.repo.branch?repo={}&name=main", fx.repo_did); 266 258 let (status, body) = serve(state(fx.pool, None), &uri).await; 267 259 assert_eq!(status, StatusCode::OK, "body={body}"); 268 260 let hash = fx.tip.to_hex().to_string(); ··· 277 269 fn branch_missing_returns_branch_not_found() { 278 270 run(async { 279 271 let fx = setup().await; 280 - let uri = format!( 281 - "/sh.tangled.repo.branch?repo={}&name=nope", 282 - fx.repo_did 283 - ); 272 + let uri = format!("/sh.tangled.repo.branch?repo={}&name=nope", fx.repo_did); 284 273 let (status, body) = serve(state(fx.pool, None), &uri).await; 285 274 assert_eq!(status, StatusCode::NOT_FOUND); 286 275 assert_eq!(body["error"], "BranchNotFound"); ··· 360 349 assert_eq!(body["repoDid"], fx.repo_did); 361 350 assert_eq!(body["ownerDid"], fx.owner_did); 362 351 // Until AT Proto ingest lands, every fixture seeds the placeholder. 363 - assert_eq!(body["rkey"], common::PLACEHOLDER_RKEY); 352 + assert_eq!(body["rkey"], crate::common::PLACEHOLDER_RKEY); 364 353 }); 365 354 } 366 355 367 356 #[test] 368 357 fn describe_repo_unknown_did_returns_repo_not_found() { 369 358 run(async { 370 - let pool = common::shared_pool().await; 359 + let pool = crate::common::shared_pool().await; 371 360 let uri = "/sh.tangled.repo.describeRepo?repoDid=did:test:does-not-exist"; 372 361 let (status, body) = serve(state(pool, None), uri).await; 373 362 assert_eq!(status, StatusCode::NOT_FOUND, "body={body}"); ··· 378 367 #[test] 379 368 fn describe_repo_rejects_non_did_parameter() { 380 369 run(async { 381 - let pool = common::shared_pool().await; 370 + let pool = crate::common::shared_pool().await; 382 371 let uri = "/sh.tangled.repo.describeRepo?repoDid=not-a-did"; 383 372 let (status, body) = serve(state(pool, None), uri).await; 384 373 assert_eq!(status, StatusCode::BAD_REQUEST, "body={body}"); ··· 427 416 fn list_keys_orders_by_created_at_desc() { 428 417 let _guard = LIST_KEYS_LOCK.lock().unwrap(); 429 418 run(async { 430 - let pool = common::shared_pool().await; 419 + let pool = crate::common::shared_pool().await; 431 420 clear_keys(&pool).await; 432 421 seed_keys( 433 422 &pool, 434 423 &[ 435 - ("did:test:alice", "ssh-ed25519 AAAA...alice", "2026-05-10T12:00:00Z"), 436 - ("did:test:bob", "ssh-ed25519 AAAA...bob", "2026-05-12T12:00:00Z"), 437 - ("did:test:carol", "ssh-ed25519 AAAA...carol", "2026-05-11T12:00:00Z"), 424 + ( 425 + "did:test:alice", 426 + "ssh-ed25519 AAAA...alice", 427 + "2026-05-10T12:00:00Z", 428 + ), 429 + ( 430 + "did:test:bob", 431 + "ssh-ed25519 AAAA...bob", 432 + "2026-05-12T12:00:00Z", 433 + ), 434 + ( 435 + "did:test:carol", 436 + "ssh-ed25519 AAAA...carol", 437 + "2026-05-11T12:00:00Z", 438 + ), 438 439 ], 439 440 ) 440 441 .await; ··· 457 458 fn list_keys_paginates_via_cursor() { 458 459 let _guard = LIST_KEYS_LOCK.lock().unwrap(); 459 460 run(async { 460 - let pool = common::shared_pool().await; 461 + let pool = crate::common::shared_pool().await; 461 462 clear_keys(&pool).await; 462 463 seed_keys( 463 464 &pool, ··· 470 471 ) 471 472 .await; 472 473 473 - let (_, page1) = serve(state(pool.clone(), None), "/sh.tangled.knot.listKeys?limit=2").await; 474 + let (_, page1) = serve( 475 + state(pool.clone(), None), 476 + "/sh.tangled.knot.listKeys?limit=2", 477 + ) 478 + .await; 474 479 let keys1 = page1["keys"].as_array().unwrap(); 475 480 assert_eq!(keys1.len(), 2); 476 481 assert_eq!(keys1[0]["did"], "did:test:d"); ··· 494 499 fn list_keys_empty_table_returns_empty_array() { 495 500 let _guard = LIST_KEYS_LOCK.lock().unwrap(); 496 501 run(async { 497 - let pool = common::shared_pool().await; 502 + let pool = crate::common::shared_pool().await; 498 503 clear_keys(&pool).await; 499 504 let (status, body) = serve(state(pool, None), "/sh.tangled.knot.listKeys").await; 500 505 assert_eq!(status, StatusCode::OK); ··· 507 512 fn list_keys_rejects_negative_cursor() { 508 513 // No lock needed: this test never touches table contents. 509 514 run(async { 510 - let pool = common::shared_pool().await; 515 + let pool = crate::common::shared_pool().await; 511 516 let (status, body) = serve(state(pool, None), "/sh.tangled.knot.listKeys?cursor=-1").await; 512 517 assert_eq!(status, StatusCode::BAD_REQUEST, "body={body}"); 513 518 assert_eq!(body["error"], "InvalidRequest"); ··· 518 523 fn list_keys_emits_lexicon_wire_shape() { 519 524 let _guard = LIST_KEYS_LOCK.lock().unwrap(); 520 525 run(async { 521 - let pool = common::shared_pool().await; 526 + let pool = crate::common::shared_pool().await; 522 527 clear_keys(&pool).await; 523 528 seed_keys( 524 529 &pool, 525 - &[("did:test:lex", "ssh-ed25519 AAAA...lex", "2026-05-14T15:30:00Z")], 530 + &[( 531 + "did:test:lex", 532 + "ssh-ed25519 AAAA...lex", 533 + "2026-05-14T15:30:00Z", 534 + )], 526 535 ) 527 536 .await; 528 537
+35 -36
mnemosyne_tests/tests/xrpc_history.rs mnemosyne_tests/tests/integration/xrpc_history.rs
··· 6 6 //! cursor pagination, path filtering, the Go-shaped Signature wire 7 7 //! format, and the documented error cases. 8 8 9 - mod common; 10 - 11 9 use axum::body::Body; 12 10 use axum::http::{Request, StatusCode}; 13 11 use bstr::ByteSlice; ··· 15 13 use gix_date::Time; 16 14 use gix_hash::{Kind as HashKind, ObjectId}; 17 15 use gix_object::tree::EntryKind; 18 - use gix_object::{tree, Commit, Kind as ObjKind, Tree, WriteTo}; 16 + use gix_object::{Commit, Kind as ObjKind, Tree, WriteTo, tree}; 19 17 use gix_ref::transaction::{Change, LogChange, PreviousValue, RefEdit}; 20 18 use gix_ref::{FullName, Target}; 21 19 use http_body_util::BodyExt; 22 20 use mnemosyne_git::{ObjectStore, RefStore}; 23 - use mnemosyne_postgres::{ensure_repo, PgBackend}; 24 - use mnemosyne_xrpc::{router, XrpcState}; 21 + use mnemosyne_postgres::{PgBackend, ensure_repo}; 22 + use mnemosyne_xrpc::{XrpcState, router}; 25 23 use serde_json::Value; 26 24 use sqlx::PgPool; 27 25 use tower::ServiceExt; ··· 34 32 } 35 33 36 34 async fn setup() -> LogFixture { 37 - let pool = common::shared_pool().await; 38 - let repo_did = common::unique_repo_did(); 35 + let pool = crate::common::shared_pool().await; 36 + let repo_did = crate::common::unique_repo_did(); 39 37 let owner_did = "did:test:log-owner".to_string(); 40 38 let repo_name = format!("repo-{}", &repo_did[9..]); 41 39 ensure_repo( ··· 43 41 &repo_did, 44 42 &owner_did, 45 43 &repo_name, 46 - common::PLACEHOLDER_RKEY, 44 + crate::common::PLACEHOLDER_RKEY, 47 45 ) 48 46 .await 49 47 .expect("ensure_repo"); ··· 59 57 .write(ObjKind::Blob, b"intro v1\n") 60 58 .await 61 59 .expect("write intro v1"); 62 - let docs_tree_v1 = 63 - write_tree(&backend, &[("intro.md", intro_v1, EntryKind::Blob)]).await; 60 + let docs_tree_v1 = write_tree(&backend, &[("intro.md", intro_v1, EntryKind::Blob)]).await; 64 61 let root_c0 = write_tree( 65 62 &backend, 66 63 &[ ··· 76 73 .write(ObjKind::Blob, b"intro v2\n") 77 74 .await 78 75 .expect("write intro v2"); 79 - let docs_tree_v2 = 80 - write_tree(&backend, &[("intro.md", intro_v2, EntryKind::Blob)]).await; 76 + let docs_tree_v2 = write_tree(&backend, &[("intro.md", intro_v2, EntryKind::Blob)]).await; 81 77 let root_c1 = write_tree( 82 78 &backend, 83 79 &[ ··· 156 152 } 157 153 158 154 fn t(seconds: i64) -> Time { 159 - Time { 160 - seconds, 161 - offset: 0, 162 - } 155 + Time { seconds, offset: 0 } 163 156 } 164 157 165 - async fn write_tree( 166 - backend: &PgBackend, 167 - entries: &[(&str, ObjectId, EntryKind)], 168 - ) -> ObjectId { 158 + async fn write_tree(backend: &PgBackend, entries: &[(&str, ObjectId, EntryKind)]) -> ObjectId { 169 159 let mut tree_entries: Vec<tree::Entry> = entries 170 160 .iter() 171 161 .map(|(name, oid, kind)| tree::Entry { ··· 246 236 247 237 #[test] 248 238 fn log_returns_commits_newest_first() { 249 - common::runtime().block_on(async { 239 + crate::common::runtime().block_on(async { 250 240 let f = setup().await; 251 241 let uri = format!( 252 242 "/sh.tangled.repo.log?repo={}&ref=main", ··· 270 260 271 261 #[test] 272 262 fn log_respects_limit() { 273 - common::runtime().block_on(async { 263 + crate::common::runtime().block_on(async { 274 264 let f = setup().await; 275 265 let uri = format!( 276 266 "/sh.tangled.repo.log?repo={}&ref=main&limit=2", ··· 287 277 288 278 #[test] 289 279 fn log_paginates_via_cursor() { 290 - common::runtime().block_on(async { 280 + crate::common::runtime().block_on(async { 291 281 let f = setup().await; 292 282 // Skip the 2 newest, expect c1 then c0 293 283 let uri = format!( ··· 299 289 300 290 let commits = json["commits"].as_array().unwrap(); 301 291 assert_eq!(commits.len(), 2); 302 - assert_eq!(commits[0]["hash"].as_str(), Some(hex(f.commits[1]).as_str())); 303 - assert_eq!(commits[1]["hash"].as_str(), Some(hex(f.commits[0]).as_str())); 292 + assert_eq!( 293 + commits[0]["hash"].as_str(), 294 + Some(hex(f.commits[1]).as_str()) 295 + ); 296 + assert_eq!( 297 + commits[1]["hash"].as_str(), 298 + Some(hex(f.commits[0]).as_str()) 299 + ); 304 300 assert_eq!(json["page"].as_u64(), Some(2)); 305 301 }); 306 302 } 307 303 308 304 #[test] 309 305 fn log_filters_by_path() { 310 - common::runtime().block_on(async { 306 + crate::common::runtime().block_on(async { 311 307 let f = setup().await; 312 308 // docs/intro.md changed in c0 (create) and c1 (modify) only. 313 309 let uri = format!( ··· 319 315 320 316 let commits = json["commits"].as_array().unwrap(); 321 317 assert_eq!(commits.len(), 2, "expected only c1 and c0"); 322 - assert_eq!(commits[0]["hash"].as_str(), Some(hex(f.commits[1]).as_str())); 323 - assert_eq!(commits[1]["hash"].as_str(), Some(hex(f.commits[0]).as_str())); 318 + assert_eq!( 319 + commits[0]["hash"].as_str(), 320 + Some(hex(f.commits[1]).as_str()) 321 + ); 322 + assert_eq!( 323 + commits[1]["hash"].as_str(), 324 + Some(hex(f.commits[0]).as_str()) 325 + ); 324 326 assert_eq!(json["description"].as_str(), Some("docs/intro.md")); 325 327 }); 326 328 } 327 329 328 330 #[test] 329 331 fn log_signature_uses_go_capital_field_names() { 330 - common::runtime().block_on(async { 332 + crate::common::runtime().block_on(async { 331 333 let f = setup().await; 332 334 let uri = format!( 333 335 "/sh.tangled.repo.log?repo={}&ref=main&limit=1", ··· 350 352 351 353 #[test] 352 354 fn log_emits_deprecated_aliases_this_and_parent() { 353 - common::runtime().block_on(async { 355 + crate::common::runtime().block_on(async { 354 356 let f = setup().await; 355 357 let uri = format!( 356 358 "/sh.tangled.repo.log?repo={}&ref=main&limit=1", ··· 364 366 assert_eq!(commit["parent"].as_str(), Some(hex(f.commits[2]).as_str())); 365 367 let parent_hashes = commit["parent_hashes"].as_array().unwrap(); 366 368 assert_eq!(parent_hashes.len(), 1); 367 - assert_eq!( 368 - parent_hashes[0].as_str(), 369 - Some(hex(f.commits[2]).as_str()) 370 - ); 369 + assert_eq!(parent_hashes[0].as_str(), Some(hex(f.commits[2]).as_str())); 371 370 }); 372 371 } 373 372 374 373 #[test] 375 374 fn log_returns_ref_not_found_for_unknown_ref() { 376 - common::runtime().block_on(async { 375 + crate::common::runtime().block_on(async { 377 376 let f = setup().await; 378 377 let uri = format!( 379 378 "/sh.tangled.repo.log?repo={}&ref=does-not-exist", ··· 387 386 388 387 #[test] 389 388 fn log_rejects_non_integer_cursor() { 390 - common::runtime().block_on(async { 389 + crate::common::runtime().block_on(async { 391 390 let f = setup().await; 392 391 let uri = format!( 393 392 "/sh.tangled.repo.log?repo={}&ref=main&cursor=abc",
+60 -41
mnemosyne_tests/tests/xrpc_languages.rs mnemosyne_tests/tests/integration/xrpc_languages.rs
··· 10 10 //! - `notes.md` → Markdown, category Prose → NOT detected 11 11 //! - `image.bin` → binary blob (NUL bytes) → filtered by is_binary 12 12 13 - mod common; 14 - 15 13 use axum::body::Body; 16 14 use axum::http::{Request, StatusCode}; 17 15 use bstr::ByteSlice; ··· 19 17 use gix_date::Time; 20 18 use gix_hash::{Kind as HashKind, ObjectId}; 21 19 use gix_object::tree::EntryKind; 22 - use gix_object::{tree, Commit, Kind as ObjKind, Tree, WriteTo}; 20 + use gix_object::{Commit, Kind as ObjKind, Tree, WriteTo, tree}; 23 21 use gix_ref::transaction::{Change, LogChange, PreviousValue, RefEdit}; 24 22 use gix_ref::{FullName, Target}; 25 23 use http_body_util::BodyExt; 26 24 use mnemosyne_git::{ObjectStore, RefStore}; 27 - use mnemosyne_postgres::{ensure_repo, PgBackend}; 28 - use mnemosyne_xrpc::{router, XrpcState}; 25 + use mnemosyne_postgres::{PgBackend, ensure_repo}; 26 + use mnemosyne_xrpc::{XrpcState, router}; 29 27 use serde_json::Value; 30 28 use sqlx::PgPool; 31 29 use tower::ServiceExt; ··· 36 34 } 37 35 38 36 async fn setup() -> LangFixture { 39 - let pool = common::shared_pool().await; 40 - let repo_did = common::unique_repo_did(); 37 + let pool = crate::common::shared_pool().await; 38 + let repo_did = crate::common::unique_repo_did(); 41 39 let owner_did = "did:test:lang-owner".to_string(); 42 40 let repo_name = format!("repo-{}", &repo_did[9..]); 43 41 ensure_repo( ··· 45 43 &repo_did, 46 44 &owner_did, 47 45 &repo_name, 48 - common::PLACEHOLDER_RKEY, 46 + crate::common::PLACEHOLDER_RKEY, 49 47 ) 50 48 .await 51 49 .expect("ensure_repo"); ··· 81 79 .expect("write binary"); 82 80 83 81 // Build a nested src/ tree containing lib.rs, plus the rest at root. 84 - let src_tree = build_tree( 85 - &backend, 86 - &[("lib.rs", rust_oid, EntryKind::Blob)], 87 - ) 88 - .await; 82 + let src_tree = build_tree(&backend, &[("lib.rs", rust_oid, EntryKind::Blob)]).await; 89 83 90 84 let root_tree = build_tree( 91 85 &backend, ··· 103 97 let sig = Signature { 104 98 name: "Tester".into(), 105 99 email: "test@example.com".into(), 106 - time: Time { seconds: 100, offset: 0 }, 100 + time: Time { 101 + seconds: 100, 102 + offset: 0, 103 + }, 107 104 }; 108 105 let commit = Commit { 109 106 tree: root_tree, ··· 150 147 LangFixture { pool, repo_did } 151 148 } 152 149 153 - async fn build_tree( 154 - backend: &PgBackend, 155 - entries: &[(&str, ObjectId, EntryKind)], 156 - ) -> ObjectId { 150 + async fn build_tree(backend: &PgBackend, entries: &[(&str, ObjectId, EntryKind)]) -> ObjectId { 157 151 let mut tree_entries: Vec<tree::Entry> = entries 158 152 .iter() 159 153 .map(|(name, oid, kind)| tree::Entry { ··· 163 157 }) 164 158 .collect(); 165 159 tree_entries.sort(); 166 - let tree = Tree { entries: tree_entries }; 160 + let tree = Tree { 161 + entries: tree_entries, 162 + }; 167 163 let mut bytes = Vec::new(); 168 164 tree.write_to(&mut bytes).expect("encode tree"); 169 165 backend ··· 199 195 200 196 #[test] 201 197 fn languages_detects_programming_and_markup() { 202 - common::runtime().block_on(async { 198 + crate::common::runtime().block_on(async { 203 199 let f = setup().await; 204 200 let uri = format!( 205 201 "/sh.tangled.repo.languages?repo={}&ref=main", ··· 220 216 assert!(names.contains("CSS"), "CSS missing: {names:?}"); 221 217 222 218 // JSON is Data, Markdown is Prose — both excluded by gengo. 223 - assert!(!names.contains("JSON"), "JSON should be filtered (Data): {names:?}"); 219 + assert!( 220 + !names.contains("JSON"), 221 + "JSON should be filtered (Data): {names:?}" 222 + ); 224 223 assert!( 225 224 !names.contains("Markdown"), 226 225 "Markdown should be filtered (Prose): {names:?}" ··· 230 229 231 230 #[test] 232 231 fn languages_percentages_sum_to_100_within_rounding() { 233 - common::runtime().block_on(async { 232 + crate::common::runtime().block_on(async { 234 233 let f = setup().await; 235 234 let uri = format!( 236 235 "/sh.tangled.repo.languages?repo={}&ref=main", ··· 255 254 256 255 #[test] 257 256 fn languages_reports_total_size_and_distinct_count() { 258 - common::runtime().block_on(async { 257 + crate::common::runtime().block_on(async { 259 258 let f = setup().await; 260 259 let uri = format!( 261 260 "/sh.tangled.repo.languages?repo={}&ref=main", ··· 267 266 let total_files = json["totalFiles"].as_u64().expect("totalFiles present"); 268 267 assert!(total_size > 0); 269 268 // 3 distinct detected languages: Rust, Python, CSS. 270 - assert_eq!(total_files, 3, "expected 3 distinct languages, got {total_files}"); 269 + assert_eq!( 270 + total_files, 3, 271 + "expected 3 distinct languages, got {total_files}" 272 + ); 271 273 }); 272 274 } 273 275 274 276 #[test] 275 277 fn languages_empty_when_only_filtered_content() { 276 - common::runtime().block_on(async { 277 - let pool = common::shared_pool().await; 278 - let repo_did = common::unique_repo_did(); 278 + crate::common::runtime().block_on(async { 279 + let pool = crate::common::shared_pool().await; 280 + let repo_did = crate::common::unique_repo_did(); 279 281 let owner_did = "did:test:lang-empty-owner".to_string(); 280 282 let repo_name = format!("repo-{}", &repo_did[9..]); 281 283 ensure_repo( ··· 283 285 &repo_did, 284 286 &owner_did, 285 287 &repo_name, 286 - common::PLACEHOLDER_RKEY, 288 + crate::common::PLACEHOLDER_RKEY, 287 289 ) 288 290 .await 289 291 .expect("ensure_repo"); ··· 310 312 let sig = Signature { 311 313 name: "Tester".into(), 312 314 email: "test@example.com".into(), 313 - time: Time { seconds: 100, offset: 0 }, 315 + time: Time { 316 + seconds: 100, 317 + offset: 0, 318 + }, 314 319 }; 315 320 let commit = Commit { 316 321 tree: root, ··· 366 371 .unwrap_or(true), 367 372 "expected empty languages array, got: {json}", 368 373 ); 369 - assert!(json.get("totalSize").is_none(), "totalSize should be omitted"); 370 - assert!(json.get("totalFiles").is_none(), "totalFiles should be omitted"); 374 + assert!( 375 + json.get("totalSize").is_none(), 376 + "totalSize should be omitted" 377 + ); 378 + assert!( 379 + json.get("totalFiles").is_none(), 380 + "totalFiles should be omitted" 381 + ); 371 382 }); 372 383 } 373 384 374 385 #[test] 375 386 fn languages_reports_ref_not_found_for_unknown_ref() { 376 - common::runtime().block_on(async { 387 + crate::common::runtime().block_on(async { 377 388 let f = setup().await; 378 389 let uri = format!( 379 390 "/sh.tangled.repo.languages?repo={}&ref=does-not-exist", ··· 387 398 388 399 #[test] 389 400 fn languages_first_request_populates_cache() { 390 - common::runtime().block_on(async { 401 + crate::common::runtime().block_on(async { 391 402 let f = setup().await; 392 403 let uri = format!( 393 404 "/sh.tangled.repo.languages?repo={}&ref=main", ··· 413 424 .fetch_one(&f.pool) 414 425 .await 415 426 .unwrap(); 416 - assert_eq!(post_count, 1, "cache should have one row after first request"); 427 + assert_eq!( 428 + post_count, 1, 429 + "cache should have one row after first request" 430 + ); 417 431 }); 418 432 } 419 433 ··· 426 440 // identical responses, exactly one row in the cache, no errors" — 427 441 // the unit test in mnemosyne_xrpc::singleflight covers the "exactly 428 442 // one compute" property directly via a counter. 429 - common::runtime().block_on(async { 443 + crate::common::runtime().block_on(async { 430 444 let f = setup().await; 431 445 let uri = format!( 432 446 "/sh.tangled.repo.languages?repo={}&ref=main", ··· 438 452 for _ in 0..concurrency { 439 453 let state = state(f.pool.clone()); 440 454 let uri = uri.clone(); 441 - handles.push(tokio::spawn(async move { 442 - serve_json(state, &uri).await 443 - })); 455 + handles.push(tokio::spawn(async move { serve_json(state, &uri).await })); 444 456 } 445 457 446 458 let mut first_body: Option<Value> = None; ··· 465 477 466 478 #[test] 467 479 fn languages_second_request_serves_identical_payload_from_cache() { 468 - common::runtime().block_on(async { 480 + crate::common::runtime().block_on(async { 469 481 let f = setup().await; 470 482 let uri = format!( 471 483 "/sh.tangled.repo.languages?repo={}&ref=main", ··· 493 505 .unwrap(); 494 506 495 507 let (status2, body2) = serve_json(state(f.pool.clone()), &uri).await; 496 - assert_eq!(status2, StatusCode::OK, "cache hit should still succeed after blobs wiped"); 497 - assert_eq!(body1, body2, "second response should be byte-identical to first"); 508 + assert_eq!( 509 + status2, 510 + StatusCode::OK, 511 + "cache hit should still succeed after blobs wiped" 512 + ); 513 + assert_eq!( 514 + body1, body2, 515 + "second response should be byte-identical to first" 516 + ); 498 517 }); 499 518 }