From 6f3714800e32e4369df36086c23e82aee6a2f384 Mon Sep 17 00:00:00 2001 From: "J.C. Jones" Date: Mon, 18 May 2026 13:20:14 -0700 Subject: [PATCH 1/7] Migrate from Trillium [part 9B]: Rewrite test infrastructure + remove all trillium deps Replace the entire trillium-based test harness with axum-native equivalents and remove every trillium dependency from the workspace. Are you ready for a PARTY? This one removes Trillium. Say goodnight. test-support/* rewrite: - TestRequest/TestResponse types using tower::ServiceExt::oneshot() - ClientLogs rewritten as axum middleware capturing request/response bodies - ApiMocks rewritten with host-based axum::Router dispatch - User injection via X-Integration-Testing-User header (was in the trillium conn state) src/ crate cleanup: - Delete handler::origin_router, handler::proxy, ErrorHandler, and the DivviupApi trillium Handler struct - Delete inject_integration_testing_user middleware (the header-based inject_test_header_user is sufficient) - Rewrote src/api_mocks/* tests/integration/*: - All response_json() calls are now synchronous (body eagerly consumed) Here we see the final destruction of the trillium alliance, and the end of its insignificant rebellion against our dependency tree. I've left some more cleanup for Part 10, including refactoring 'with_state' to 'with_user' in ~100 callsites, and the restoration of remaining HTTP metrics. --- Cargo.lock | 620 +----------------- Cargo.toml | 16 +- client/Cargo.toml | 5 +- .../integration/basic_client_behavior.rs | 8 +- client/tests/integration/harness.rs | 33 +- deny.toml | 10 +- src/api_mocks.rs | 92 ++- src/api_mocks/aggregator_api.rs | 127 ++-- src/api_mocks/auth0.rs | 45 +- src/api_mocks/postmark.rs | 12 +- src/handler.rs | 129 +--- src/handler/error.rs | 10 - src/handler/origin_router.rs | 72 -- src/handler/proxy.rs | 131 ---- src/lib.rs | 2 +- test-support/Cargo.toml | 15 +- test-support/src/api_mocks.rs | 24 +- test-support/src/client_logs.rs | 158 +++-- test-support/src/fixtures.rs | 6 +- test-support/src/lib.rs | 446 ++++++++++--- tests/integration/accounts.rs | 82 +-- tests/integration/admin_queue.rs | 16 +- tests/integration/aggregator_client.rs | 57 +- tests/integration/aggregators.rs | 190 +++--- tests/integration/api_tokens.rs | 52 +- tests/integration/assets.rs | 8 +- tests/integration/auth.rs | 16 +- tests/integration/collector_credentials.rs | 52 +- tests/integration/jobs.rs | 2 +- tests/integration/memberships.rs | 50 +- tests/integration/tasks.rs | 184 +++--- tests/integration/tls_smoke_test.rs | 78 ++- tests/integration/users.rs | 12 +- 33 files changed, 1087 insertions(+), 1673 deletions(-) delete mode 100644 src/handler/origin_router.rs delete mode 100644 src/handler/proxy.rs diff --git a/Cargo.lock b/Cargo.lock index a5c3c512a..79c33bf39 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -222,31 +222,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "async-channel" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" -dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-compat" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f68a707c1feb095d8c07f8a65b9f506b117d30af431cab89374357de7c11461b" -dependencies = [ - "futures-core", - "futures-io", - "once_cell", - "pin-project-lite", - "tokio", -] - [[package]] name = "async-compression" version = "0.4.6" @@ -263,23 +238,13 @@ dependencies = [ "zstd-safe", ] -[[package]] -name = "async-dup" -version = "1.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2886ab563af5038f79ec016dd7b87947ed138b794e8dd64992962c9cca0411" -dependencies = [ - "async-lock", - "futures-io", -] - [[package]] name = "async-lock" version = "3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" dependencies = [ - "event-listener 5.4.1", + "event-listener", "event-listener-strategy", "pin-project-lite", ] @@ -317,15 +282,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "async_cell" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447ab28afbb345f5408b120702a44e5529ebf90b1796ec76e9528df8e288e6c2" -dependencies = [ - "loom", -] - [[package]] name = "atoi" version = "2.0.0" @@ -675,12 +631,6 @@ dependencies = [ "shlex", ] -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - [[package]] name = "cfg-if" version = "1.0.0" @@ -803,16 +753,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" -[[package]] -name = "colored" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" -dependencies = [ - "lazy_static", - "windows-sys 0.59.0", -] - [[package]] name = "colored" version = "3.1.1" @@ -822,16 +762,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "combine" -version = "4.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" -dependencies = [ - "bytes", - "memchr", -] - [[package]] name = "concurrent-queue" version = "2.5.0" @@ -925,16 +855,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "core-foundation" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -1096,19 +1016,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "dashmap" -version = "5.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" -dependencies = [ - "cfg-if", - "hashbrown 0.14.3", - "lock_api", - "once_cell", - "parking_lot_core", -] - [[package]] name = "data-encoding" version = "2.9.0" @@ -1244,6 +1151,7 @@ dependencies = [ "thiserror 2.0.18", "time", "tokio", + "tokio-rustls", "tokio-util", "tower 0.5.3", "tower-http", @@ -1253,16 +1161,6 @@ dependencies = [ "tracing-log", "tracing-stackdriver", "tracing-subscriber", - "trillium", - "trillium-api", - "trillium-client", - "trillium-http", - "trillium-logger", - "trillium-macros 0.0.6", - "trillium-router", - "trillium-rustls", - "trillium-testing", - "trillium-tokio", "typenum", "url", "uuid", @@ -1276,7 +1174,7 @@ dependencies = [ "anyhow", "base64 0.22.1", "clap", - "colored 3.1.1", + "colored", "const_format", "divviup-client", "email_address", @@ -1302,6 +1200,7 @@ dependencies = [ name = "divviup-client" version = "0.5.0-pre.1" dependencies = [ + "axum 0.8.9", "base64 0.22.1", "divviup-api", "divviup-client", @@ -1322,10 +1221,6 @@ dependencies = [ "thiserror 2.0.18", "time", "tokio", - "trillium", - "trillium-http", - "trillium-testing", - "trillium-tokio", "url", "uuid", ] @@ -1460,17 +1355,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "event-listener" -version = "4.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - [[package]] name = "event-listener" version = "5.4.1" @@ -1488,7 +1372,7 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ - "event-listener 5.4.1", + "event-listener", "pin-project-lite", ] @@ -1675,17 +1559,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "futures-rustls" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f2f12607f92c69b12ed746fabf9ca4f5c482cba46679c1a75b874ed7c26adb" -dependencies = [ - "futures-io", - "rustls", - "rustls-pki-types", -] - [[package]] name = "futures-sink" version = "0.3.31" @@ -1716,21 +1589,6 @@ dependencies = [ "slab", ] -[[package]] -name = "generator" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52f04ae4152da20c76fe800fa48659201d5cf627c5149ca0b707b69d7eef6cf9" -dependencies = [ - "cc", - "cfg-if", - "libc", - "log", - "rustversion", - "windows-link 0.2.1", - "windows-result", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -2085,7 +1943,7 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", - "webpki-roots 0.26.1", + "webpki-roots", ] [[package]] @@ -2493,28 +2351,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "jni" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" -dependencies = [ - "cesu8", - "cfg-if", - "combine", - "jni-sys", - "log", - "thiserror 1.0.69", - "walkdir", - "windows-sys 0.45.0", -] - -[[package]] -name = "jni-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" - [[package]] name = "jobserver" version = "0.1.32" @@ -2612,19 +2448,6 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" -[[package]] -name = "loom" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" -dependencies = [ - "cfg-if", - "generator", - "scoped-tls", - "tracing", - "tracing-subscriber", -] - [[package]] name = "lru-slab" version = "0.1.2" @@ -2931,12 +2754,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" -[[package]] -name = "openssl-probe" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" - [[package]] name = "opentelemetry" version = "0.27.1" @@ -3247,15 +3064,6 @@ dependencies = [ "portable-atomic", ] -[[package]] -name = "portpicker" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be97d76faf1bfab666e1375477b23fde79eccf0276e9b63b92a39d676a889ba9" -dependencies = [ - "rand 0.8.5", -] - [[package]] name = "powerfmt" version = "0.2.0" @@ -3724,7 +3532,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots 0.26.1", + "webpki-roots", "windows-registry", ] @@ -3782,26 +3590,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "rlimit" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3560f70f30a0f16d11d01ed078a07740fe6b489667abc7c7b029155d9f21c3d8" -dependencies = [ - "libc", -] - -[[package]] -name = "routefinder" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0971d3c8943a6267d6bd0d782fdc4afa7593e7381a92a3df950ff58897e066b5" -dependencies = [ - "memchr", - "smartcow", - "smartstring", -] - [[package]] name = "rsa" version = "0.9.10" @@ -3884,18 +3672,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rustls-native-certs" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" -dependencies = [ - "openssl-probe", - "rustls-pki-types", - "schannel", - "security-framework", -] - [[package]] name = "rustls-pemfile" version = "2.2.0" @@ -3915,33 +3691,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rustls-platform-verifier" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" -dependencies = [ - "core-foundation", - "core-foundation-sys", - "jni", - "log", - "once_cell", - "rustls", - "rustls-native-certs", - "rustls-platform-verifier-android", - "rustls-webpki", - "security-framework", - "security-framework-sys", - "webpki-root-certs", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustls-platform-verifier-android" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84e217e7fdc8466b5b35d30f8c0a30febd29173df4a3a0c2115d306b9c4117ad" - [[package]] name = "rustls-webpki" version = "0.103.13" @@ -3966,30 +3715,6 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "schannel" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "scopeguard" version = "1.2.0" @@ -4180,29 +3905,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "security-framework" -version = "3.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" -dependencies = [ - "bitflags 2.11.0", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "semver" version = "1.0.21" @@ -4335,16 +4037,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "signal-hook" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" -dependencies = [ - "libc", - "signal-hook-registry", -] - [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -4354,18 +4046,6 @@ dependencies = [ "libc", ] -[[package]] -name = "signal-hook-tokio" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213241f76fb1e37e27de3b6aa1b068a2c333233b59cca6634f634b80a27ecf1e" -dependencies = [ - "futures-core", - "libc", - "signal-hook", - "tokio", -] - [[package]] name = "signature" version = "2.2.0" @@ -4382,12 +4062,6 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" -[[package]] -name = "size" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fed904c7fb2856d868b92464fc8fa597fce366edea1a9cbfaa8cb5fe080bd6d" - [[package]] name = "slab" version = "0.4.9" @@ -4497,7 +4171,7 @@ dependencies = [ "crc", "crossbeam-queue", "either", - "event-listener 5.4.1", + "event-listener", "futures-core", "futures-intrusive", "futures-io", @@ -4522,7 +4196,7 @@ dependencies = [ "tracing", "url", "uuid", - "webpki-roots 0.26.1", + "webpki-roots", ] [[package]] @@ -4698,7 +4372,7 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66c82d03d16a1e591756e978782ce4bc4300f83048b57d44c5600dafa7337019" dependencies = [ - "event-listener 5.4.1", + "event-listener", "futures-lite", "pin-project-lite", ] @@ -4819,9 +4493,13 @@ dependencies = [ name = "test-support" version = "0.5.0-pre.1" dependencies = [ + "async-trait", + "axum 0.8.9", "base64 0.22.1", + "bytes", "divviup-api", "fastrand", + "http-body-util", "pretty_assertions", "querystrong", "rand 0.8.5", @@ -4833,16 +4511,11 @@ dependencies = [ "thiserror 2.0.18", "time", "tokio", + "tokio-util", + "tower 0.5.3", "tracing", "tracing-log", "tracing-subscriber", - "trillium", - "trillium-client", - "trillium-http", - "trillium-macros 0.0.6", - "trillium-rustls", - "trillium-testing", - "trillium-tokio", "url", "uuid", ] @@ -5366,44 +5039,6 @@ dependencies = [ "trillium-http", ] -[[package]] -name = "trillium-api" -version = "0.2.0-rc.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ac0b2a746f70736c3b1c78078d361d71a1705121f2b134a791d50de2a4f78d" -dependencies = [ - "log", - "mime", - "serde", - "serde_json", - "serde_path_to_error", - "thiserror 1.0.69", - "trillium", - "trillium-macros 0.0.6", -] - -[[package]] -name = "trillium-client" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6977db194636278cb7b7397887066885e35a040c7e16312c10e29780372a6c5" -dependencies = [ - "crossbeam-queue", - "dashmap", - "encoding_rs", - "futures-lite", - "httparse", - "log", - "memchr", - "mime", - "serde", - "serde_json", - "size", - "thiserror 1.0.69", - "trillium-http", - "trillium-server-common", -] - [[package]] name = "trillium-http" version = "0.3.17" @@ -5413,43 +5048,17 @@ dependencies = [ "encoding_rs", "futures-lite", "hashbrown 0.14.3", - "http", "httparse", "httpdate", "log", "memchr", "mime", - "serde", "smallvec", "smartcow", "smartstring", "stopper", "thiserror 1.0.69", - "trillium-macros 0.0.6", -] - -[[package]] -name = "trillium-logger" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da5e9b6c08a27d991b4a9c73dd7276c6f181fd4dee7218723b0fbd7fca3a1659" -dependencies = [ - "colored 2.2.0", - "log", - "size", - "time", - "trillium", -] - -[[package]] -name = "trillium-macros" -version = "0.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "916054381183f0cfed7604bf7de2044a760624a50d26eef5492468fb73083bbb" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", + "trillium-macros", ] [[package]] @@ -5463,89 +5072,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "trillium-router" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a7aed20d63101d7dcd165fd047141423009a7f4ccfc75db5b875312d8127dbe" -dependencies = [ - "log", - "routefinder", - "trillium", -] - -[[package]] -name = "trillium-rustls" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0501210eedcb4eae5d4a48c7855632f4be7fac5f57c7c772f5b1e598d6db53ff" -dependencies = [ - "futures-rustls", - "log", - "rustls-pemfile", - "rustls-platform-verifier", - "trillium-server-common", - "webpki-roots 1.0.6", -] - -[[package]] -name = "trillium-server-common" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a96faa60ceaf4b575886eb7d2ad3df4371acf67e0a4489585cccd4ff18966103" -dependencies = [ - "async-trait", - "async_cell", - "event-listener 4.0.3", - "futures-lite", - "log", - "pin-project-lite", - "rlimit", - "trillium", - "trillium-http", - "url", -] - -[[package]] -name = "trillium-testing" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6c5d4d9d6f6844131f166cbe4d779894f09f9b846945ab2024272bcd6e198c" -dependencies = [ - "async-channel", - "async-dup", - "cfg-if", - "dashmap", - "fastrand", - "futures-lite", - "once_cell", - "portpicker", - "trillium", - "trillium-http", - "trillium-macros 0.0.6", - "trillium-server-common", - "trillium-tokio", - "url", -] - -[[package]] -name = "trillium-tokio" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "160f07d12cc798d7de6a65e2279bb445f0606ecc4a9c594315ffff556ab7968b" -dependencies = [ - "async-compat", - "log", - "signal-hook", - "signal-hook-tokio", - "tokio", - "tokio-stream", - "trillium", - "trillium-http", - "trillium-macros 0.0.5", - "trillium-server-common", -] - [[package]] name = "try-lock" version = "0.2.5" @@ -5714,16 +5240,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - [[package]] name = "want" version = "0.3.1" @@ -5844,15 +5360,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webpki-root-certs" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "webpki-roots" version = "0.26.1" @@ -5862,15 +5369,6 @@ dependencies = [ "rustls-pki-types", ] -[[package]] -name = "webpki-roots" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "whoami" version = "1.5.1" @@ -5897,15 +5395,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" -dependencies = [ - "winapi", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -5962,15 +5451,6 @@ dependencies = [ "windows-link 0.1.3", ] -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.48.0" @@ -5989,15 +5469,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - [[package]] name = "windows-sys" version = "0.61.2" @@ -6007,21 +5478,6 @@ dependencies = [ "windows-link 0.2.1", ] -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[package]] name = "windows-targets" version = "0.48.5" @@ -6070,12 +5526,6 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -6094,12 +5544,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -6118,12 +5562,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -6154,12 +5592,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -6178,12 +5610,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -6202,12 +5628,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -6226,12 +5646,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.48.5" diff --git a/Cargo.toml b/Cargo.toml index 769f91260..d87c08605 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,12 +26,7 @@ default-run = "divviup_api_bin" [features] default = [] -api-mocks = ["dep:trillium-testing"] integration-testing = [] -# Enables a non-production axum middleware that reads an `X-Integration-Testing-User` -# header and injects the decoded [`User`] into request extensions. Strictly for -# use by the test harness (`test-support`); never enable in deployed builds. -# TODO: fold into `integration-testing` in Part 9 (test-support rewrite). test-header-injection = [] otlp-trace = ["opentelemetry/trace", "opentelemetry-otlp", "opentelemetry_sdk/trace"] @@ -70,7 +65,6 @@ time = { version = "0.3.41", features = ["serde", "serde-well-known"] } tokio = { version = "1.47.1", features = ["full"] } tokio-util = { version = "0.7", features = ["rt"] } tracing = "0.1.41" -trillium = "0.2.20" tracing-chrome = "0.7.2" tracing-log = "0.2.0" tracing-stackdriver = "0.10.0" @@ -80,15 +74,6 @@ tracing-subscriber = { version = "0.3.19", features = [ "std", "fmt", ] } -trillium-api = { version = "0.2.0-rc.12", default-features = false } -trillium-client = { version = "0.6.2", features = ["json"] } -trillium-http = { version = "0.3.14", features = ["http-compat-1", "serde"] } -trillium-logger = "0.4.5" -trillium-macros = "0.0.6" -trillium-router = "0.4.1" -trillium-rustls = "0.9.0" -trillium-testing = { version = "0.7.0", optional = true } -trillium-tokio = "0.4.0" typenum = "1.18.0" url = "2.5.2" uuid = { version = "1.16.0", features = ["v4", "fast-rng", "serde"] } @@ -120,6 +105,7 @@ features = [ rcgen = "0.14.3" regex = "1.11.1" test-support.workspace = true +tokio-rustls = "0.26" [build-dependencies] rustc_version = "0.4.1" diff --git a/client/Cargo.toml b/client/Cargo.toml index e74262d6d..bbd01329a 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -31,13 +31,10 @@ num-bigint = "0.4.6" num-rational = "0.4.2" [dev-dependencies] +axum = "0.8" divviup-api.workspace = true fastrand = "2.4.1" futures-lite = "2.6.1" test-support.workspace = true tokio = { version = "1", features = ["net"] } -trillium = "0.2.20" -trillium-http = "0.3.14" -trillium-testing = { version = "0.7.0", features = ["tokio"] } -trillium-tokio = "0.4.0" divviup-client = { path = ".", features = ["admin"] } diff --git a/client/tests/integration/basic_client_behavior.rs b/client/tests/integration/basic_client_behavior.rs index 9f22fe71c..337a2fbc0 100644 --- a/client/tests/integration/basic_client_behavior.rs +++ b/client/tests/integration/basic_client_behavior.rs @@ -16,13 +16,17 @@ async fn default_headers( ); assert_eq!( - log.request_headers.get_str(KnownHeaderName::Accept), + log.request_headers + .get(headers::ACCEPT) + .and_then(|v| v.to_str().ok()), Some(CONTENT_TYPE) ); assert!(log .request_headers - .get_str(KnownHeaderName::Authorization) + .get(headers::AUTHORIZATION) + .unwrap() + .to_str() .unwrap() .starts_with("Bearer ")); } diff --git a/client/tests/integration/harness.rs b/client/tests/integration/harness.rs index 87b3859d7..cedacd721 100644 --- a/client/tests/integration/harness.rs +++ b/client/tests/integration/harness.rs @@ -1,28 +1,20 @@ pub use divviup_client::DivviupClient; use std::{future::Future, net::Ipv6Addr, process::Termination}; -use test_support::tracing::install_test_trace_subscriber; use tokio::net::TcpListener; -use trillium_http::Stopper; use url::Url; pub use std::sync::Arc; pub use test_support::*; -async fn spawn_test_server(app: impl trillium::Handler) -> (Url, Stopper) { +async fn spawn_test_server(router: axum::Router) -> Url { let listener = TcpListener::bind((Ipv6Addr::LOCALHOST, 0)).await.unwrap(); let port = listener.local_addr().unwrap().port(); - let stopper = Stopper::new(); - - tokio::spawn( - trillium_tokio::config() - .without_signals() - .with_stopper(stopper.clone()) - .with_prebound_server(listener) - .run_async(app), - ); - - let url = Url::parse(&format!("http://[::1]:{port}/")).unwrap(); - (url, stopper) + tokio::spawn(async move { + axum::serve(listener, router) + .await + .expect("test server error"); + }); + Url::parse(&format!("http://[::1]:{port}/")).unwrap() } pub fn with_configured_client(f: F) -> Out @@ -43,12 +35,17 @@ where Out: Termination, { with_client_logs(move |app, _api_logs| async move { - install_test_trace_subscriber(); let client_logs = ClientLogs::default(); + let router = app + .router() + .clone() + .layer(axum::middleware::from_fn_with_state( + client_logs.clone(), + client_logs::client_logs_middleware, + )); + let base_url = spawn_test_server(router).await; let app = Arc::new(app); - let (base_url, _stopper) = spawn_test_server((client_logs.clone(), app.clone())).await; - let account = fixtures::account(&app).await; let (api_token, token) = ApiToken::build(&account); api_token.insert(app.db()).await.unwrap(); diff --git a/deny.toml b/deny.toml index a37781a91..8fd00996c 100644 --- a/deny.toml +++ b/deny.toml @@ -5,12 +5,10 @@ all-features = true [advisories] version = 2 ignore = [ - # All these must be retired when we finish migrating to Axum - "RUSTSEC-2025-0056", # Used by console-subscriber and trillium - "RUSTSEC-2025-0141", # Used by async-session and trillium - "RUSTSEC-2024-0437", # Used by trillium and old prometheus - "RUSTSEC-2025-0134", # Used by trillium and old requwest (which has to be old b/c trillium) - "RUSTSEC-2026-0097", # Used by trillium and old prometheus + "RUSTSEC-2025-0056", # Used by console-subscriber + "RUSTSEC-2024-0437", # protobuf 2.28.0 via opentelemetry-prometheus → prometheus + "RUSTSEC-2025-0134", # rustls-pemfile 2.2.0 via reqwest + "RUSTSEC-2026-0097", # rand 0.8.5 via prometheus ] [bans] diff --git a/src/api_mocks.rs b/src/api_mocks.rs index f221847dc..4706d0848 100644 --- a/src/api_mocks.rs +++ b/src/api_mocks.rs @@ -1,11 +1,11 @@ -use crate::handler::origin_router; -use trillium::Handler; -use trillium_http::KnownHeaderName; -use trillium_logger::{ - formatters::{method, request_header, status, url}, - logger, +use axum::{ + extract::{Request, State}, + response::IntoResponse, + Router, }; -use trillium_macros::Handler; +use std::{collections::HashMap, sync::Arc}; +use tower::ServiceExt; +use url::Url; pub mod aggregator_api; pub mod auth0; @@ -17,26 +17,68 @@ fn random_chars(n: usize) -> String { .collect() } -#[derive(Handler, Debug)] -pub struct ApiMocks(Box); +#[derive(Clone)] +struct HostMap { + routers: HashMap, + fallback: Router, +} + +#[derive(Debug)] +pub struct ApiMocks { + router: Router, +} impl ApiMocks { pub fn new(postmark_url: &str, auth0_url: &str) -> Self { - Self(Box::new(( - logger().with_formatter(( - "[mock] ", - method, - " ", - request_header(KnownHeaderName::Host), - " ", - url, - " ", - status, - )), - origin_router() - .with_handler(postmark_url, postmark::mock()) - .with_handler(auth0_url, auth0::mock(auth0_url)), - aggregator_api::mock(), - ))) + let mut routers = HashMap::new(); + routers.insert(extract_host(postmark_url), postmark::mock()); + routers.insert(extract_host(auth0_url), auth0::mock(auth0_url)); + + let host_map = Arc::new(HostMap { + routers, + fallback: aggregator_api::mock(), + }); + + let router = Router::new() + .fallback(dispatch_by_host) + .with_state(host_map); + + Self { router } + } + + pub fn into_router(self) -> Router { + self.router } } + +// NB: Url::host_str() strips IPv6 brackets, but the Host header keeps them. +// Current callers only pass hostname URLs, so this doesn't bite today. +fn extract_host(url: &str) -> String { + Url::parse(url) + .ok() + .and_then(|u| { + u.host_str().map(|h| match u.port() { + Some(p) => format!("{h}:{p}"), + None => h.to_string(), + }) + }) + .unwrap_or_default() + .to_lowercase() +} + +async fn dispatch_by_host(State(host_map): State>, req: Request) -> impl IntoResponse { + let host = req + .headers() + .get("host") + .and_then(|h| h.to_str().ok()) + .unwrap_or("") + .to_lowercase(); + + let router = host_map + .routers + .get(&host) + .unwrap_or(&host_map.fallback) + .clone(); + + router.oneshot(req).await.into_response() +} diff --git a/src/api_mocks/aggregator_api.rs b/src/api_mocks/aggregator_api.rs index 6102d9508..4e6054b79 100644 --- a/src/api_mocks/aggregator_api.rs +++ b/src/api_mocks/aggregator_api.rs @@ -7,54 +7,43 @@ use crate::{ }, entity::aggregator::{Feature, Features}, }; +use axum::{ + extract::{Path, Query, Request}, + http::{header, StatusCode}, + middleware::{self, Next}, + response::{IntoResponse, Response}, + routing, Json, Router, +}; use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine}; -use querystrong::QueryStrong; use rand::random; +use serde::Deserialize; use sha2::{Digest, Sha256}; use std::iter::repeat_with; -use trillium::{Conn, Handler, Status}; -use trillium_api::{api, Json, State}; -use trillium_http::KnownHeaderName; -use trillium_router::{router, RouterConnExt}; use uuid::Uuid; pub const BAD_BEARER_TOKEN: &str = "badbearertoken"; -pub fn mock() -> impl Handler { - ( - bearer_token_check, - router() - .get( - "/", - api(|_: &mut Conn, _: ()| async move { - Json(AggregatorApiConfig { - dap_url: format!("https://dap.{}.example", random_chars(5)) - .parse() - .unwrap(), - role: random(), - vdafs: Default::default(), - query_types: Default::default(), - protocol: random(), - features: Features::from_iter([Feature::TokenHash]), - }) - }), - ) - .post("/tasks", api(post_task)) - .get("/tasks/:task_id", api(get_task)) - .patch("/tasks/:task_id", api(patch_task)) - .get("/task_ids", api(task_ids)) - .delete("/tasks/:task_id", Status::Ok) - .get( - "/tasks/:task_id/metrics/uploads", - api(get_task_upload_metrics), - ), - ) +pub fn mock() -> Router { + Router::new() + .route("/", routing::get(get_config)) + .route("/tasks", routing::post(post_task)) + .route( + "/tasks/{task_id}", + routing::get(get_task).patch(patch_task).delete(delete_task), + ) + .route("/task_ids", routing::get(task_ids)) + .route( + "/tasks/{task_id}/metrics/uploads", + routing::get(get_task_upload_metrics), + ) + .layer(middleware::from_fn(bearer_token_check)) } -async fn bearer_token_check(conn: Conn) -> Conn { - let token_is_valid = conn - .request_headers() - .get_str(KnownHeaderName::Authorization) +async fn bearer_token_check(request: Request, next: Next) -> Response { + let token_is_valid = request + .headers() + .get(header::AUTHORIZATION) + .and_then(|v| v.to_str().ok()) .is_some_and(|s| match s.split_once(' ') { Some(("Bearer", BAD_BEARER_TOKEN)) => false, Some(("Bearer", _)) => true, @@ -62,13 +51,26 @@ async fn bearer_token_check(conn: Conn) -> Conn { }); if token_is_valid { - conn + next.run(request).await } else { - conn.with_status(Status::Unauthorized).halt() + StatusCode::UNAUTHORIZED.into_response() } } -async fn get_task_upload_metrics(_: &mut Conn, (): ()) -> Json { +async fn get_config() -> Json { + Json(AggregatorApiConfig { + dap_url: format!("https://dap.{}.example", random_chars(5)) + .parse() + .unwrap(), + role: random(), + vdafs: Default::default(), + query_types: Default::default(), + protocol: random(), + features: Features::from_iter([Feature::TokenHash]), + }) +} + +async fn get_task_upload_metrics() -> Json { Json(TaskUploadMetrics { interval_collected: fastrand::u64(..1000), report_decode_failure: fastrand::u64(..1000), @@ -81,8 +83,7 @@ async fn get_task_upload_metrics(_: &mut Conn, (): ()) -> Json Json { - let task_id = conn.param("task_id").unwrap(); +async fn get_task(Path(task_id): Path) -> Json { Json(TaskResponse { task_id: task_id.parse().unwrap(), peer_aggregator_endpoint: "https://_".parse().unwrap(), @@ -103,15 +104,14 @@ async fn get_task(conn: &mut Conn, (): ()) -> Json { }) } -async fn post_task( - _: &mut Conn, - Json(task_create): Json, -) -> (State, Json) { - (State(task_create.clone()), Json(task_response(task_create))) +async fn post_task(Json(task_create): Json) -> Json { + Json(task_response(task_create)) } -async fn patch_task(conn: &mut Conn, Json(patch): Json) -> Json { - let task_id = conn.param("task_id").unwrap(); +async fn patch_task( + Path(task_id): Path, + Json(patch): Json, +) -> Json { Json(TaskResponse { task_id: task_id.parse().unwrap(), peer_aggregator_endpoint: "https://_".parse().unwrap(), @@ -132,6 +132,10 @@ async fn patch_task(conn: &mut Conn, Json(patch): Json) -> Json StatusCode { + StatusCode::OK +} + pub fn task_response(task_create: TaskCreate) -> TaskResponse { let task_id = TaskId::try_from( Sha256::digest(URL_SAFE_NO_PAD.decode(task_create.vdaf_verify_key).unwrap()).as_slice(), @@ -167,27 +171,30 @@ pub fn random_hpke_config() -> HpkeConfig { ) } -async fn task_ids(conn: &mut Conn, (): ()) -> Result, Status> { - let query = - QueryStrong::parse_strict(conn.querystring()).map_err(|_| Status::InternalServerError)?; - match query.get_str("pagination_token") { - None => Ok(Json(TaskIds { +#[derive(Deserialize)] +struct TaskIdsQuery { + pagination_token: Option, +} + +async fn task_ids(Query(query): Query) -> Json { + match query.pagination_token.as_deref() { + None => Json(TaskIds { task_ids: repeat_with(|| Uuid::new_v4().to_string()) .take(10) .collect(), pagination_token: Some("second".into()), - })), + }), - Some("second") => Ok(Json(TaskIds { + Some("second") => Json(TaskIds { task_ids: repeat_with(|| Uuid::new_v4().to_string()) .take(10) .collect(), pagination_token: Some("last".into()), - })), + }), - _ => Ok(Json(TaskIds { + _ => Json(TaskIds { task_ids: repeat_with(|| Uuid::new_v4().to_string()).take(5).collect(), pagination_token: None, - })), + }), } } diff --git a/src/api_mocks/auth0.rs b/src/api_mocks/auth0.rs index be273d073..bb9b153ad 100644 --- a/src/api_mocks/auth0.rs +++ b/src/api_mocks/auth0.rs @@ -1,33 +1,36 @@ use super::random_chars; use crate::{clients::auth0_client::Token, User}; +use axum::{routing, Json, Router}; use serde_json::json; -use trillium::Handler; -use trillium_api::{Halt, Json}; -use trillium_router::router; -pub fn mock(auth0_url: &str) -> impl Handler { - ( - router() - .get("/userinfo", Json(User::for_integration_testing())) - .post( - "/oauth/token", +pub fn mock(auth0_url: &str) -> Router { + let auth0_url = auth0_url.to_owned(); + Router::new() + .route( + "/userinfo", + routing::get(|| async { Json(User::for_integration_testing()) }), + ) + .route( + "/oauth/token", + routing::post(|| async { Json(Token { access_token: "access token".into(), expires_in: 60, scope: "".into(), token_type: "bearer".into(), - }), - ) - .post( - "/api/v2/users", - Json(json!({ "user_id": random_chars(10) })), - ) - .post( - "/api/v2/tickets/password-change", + }) + }), + ) + .route( + "/api/v2/users", + routing::post(|| async { Json(json!({ "user_id": random_chars(10) })) }), + ) + .route( + "/api/v2/tickets/password-change", + routing::post(move || async move { Json(json!({ "ticket": format!("{auth0_url}/password_tickets/{}", random_chars(10)) - })), - ), - Halt, - ) + })) + }), + ) } diff --git a/src/api_mocks/postmark.rs b/src/api_mocks/postmark.rs index 43a6c18ae..5c97f2744 100644 --- a/src/api_mocks/postmark.rs +++ b/src/api_mocks/postmark.rs @@ -1,7 +1,9 @@ +use axum::{routing, Json, Router}; use serde_json::json; -use trillium::Handler; -use trillium_api::{Halt, Json}; -use trillium_router::router; -pub fn mock() -> impl Handler { - (router().post("/email/withTemplate", Json(json!({}))), Halt) + +pub fn mock() -> Router { + Router::new().route( + "/email/withTemplate", + routing::post(|| async { Json(json!({})) }), + ) } diff --git a/src/handler.rs b/src/handler.rs index 1ce066378..30423a36f 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -6,13 +6,8 @@ pub(crate) mod custom_mime_types; pub(crate) mod error; pub(crate) mod extract; pub(crate) mod oauth2; -// TODO: remove origin_router in Part 9/10 (used by api_mocks) -pub(crate) mod origin_router; pub(crate) mod session_store; -// TODO: remove proxy in Part 9 (only kept for DivviupApi test shim) -pub(crate) mod proxy; - use crate::{ clients::{Auth0Client, HttpClient}, routes::{axum_routes, health_check}, @@ -24,23 +19,15 @@ use axum::{ routing, }; use cors::axum_cors_layer; -use error::ErrorHandler; use oauth2::OauthClient; -// TODO: remove proxy + trillium imports in Part 9 (test-support rewrite) -use proxy::AxumProxy; use session_store::axum_session_layer; -use std::{net::Ipv6Addr, sync::Arc}; -use tokio::net::TcpListener; -use tokio_util::sync::CancellationToken; +use std::sync::Arc; use tower::ServiceBuilder; use tower_http::{ compression::CompressionLayer, set_header::SetResponseHeaderLayer, trace::TraceLayer, }; -use trillium::{Handler, Info}; -use trillium_macros::Handler; pub use error::Error; -pub use origin_router::origin_router; /// Shared state for the Axum application. #[derive(Clone, Debug)] @@ -124,7 +111,7 @@ pub async fn build_app(config: Config) -> BuiltApp { client: config.client.clone(), }; - // TODO(Part 9): add OpenTelemetry HTTP metrics middleware. The deleted + // TODO(Part 10): add OpenTelemetry HTTP metrics middleware. The deleted // trillium-opentelemetry handler provided http.server.* histograms and // optional OTLP per-request spans; TraceLayer only emits tracing events. let middleware = ServiceBuilder::new() @@ -162,109 +149,9 @@ pub async fn build_app(config: Config) -> BuiltApp { BuiltApp { router, db, config } } -// --------------------------------------------------------------------------- -// Test-only shim: DivviupApi -// -// test-support constructs a DivviupApi, calls .db()/.config()/.init(), and -// passes it to trillium_testing's .run_async(&app). This shim keeps that -// working by spawning the Axum router on a loopback port and proxying via -// AxumProxy. -// -// TODO: remove in Part 9 (test-support rewrite) -// --------------------------------------------------------------------------- - -#[derive(Handler, Debug)] -pub struct DivviupApi { - #[handler(except = init)] - handler: Box, - db: Db, - config: Arc, -} - -impl DivviupApi { - pub async fn init(&mut self, info: &mut Info) { - *info.server_description_mut() = format!("divviup-api {}", env!("CARGO_PKG_VERSION")); - self.handler.init(info).await - } - - pub async fn new(config: Config) -> Self { - let app = build_app(config).await; - - let axum_listener = TcpListener::bind((Ipv6Addr::LOCALHOST, 0)) - .await - .expect("failed to bind Axum listener on IPv6 loopback"); - let axum_addr = axum_listener - .local_addr() - .expect("failed to get Axum listener address"); - tokio::spawn(async move { - if let Err(e) = axum::serve(axum_listener, app.router).await { - log::error!("axum server error: {e}"); - } - }); - - let proxy = AxumProxy::new(axum_addr); - - Self { - handler: Box::new(( - #[cfg(feature = "test-header-injection")] - inject_test_user_trillium, - proxy, - ErrorHandler, - )), - db: app.db, - config: app.config, - } - } - - pub fn db(&self) -> &Db { - &self.db - } - - pub fn config(&self) -> &Config { - &self.config - } - - pub fn crypter(&self) -> &crate::Crypter { - &self.config.crypter - } -} - -// TODO: remove in Part 9 (test-support rewrite) -// NOTE: the CancellationToken created here is never cancelled, so workers -// spawned from this Queue would run forever. This is fine because callers -// only use perform_one_queue_job(), never spawn_workers(). -impl From<&DivviupApi> for crate::Queue { - fn from(app: &DivviupApi) -> Self { - Self::new(app.db(), app.config(), CancellationToken::new()) - } -} - -impl AsRef for DivviupApi { - fn as_ref(&self) -> &Db { - &self.db - } -} - -/// Trillium-side test shim: if a `User` was injected via `.with_state()` -/// (legacy test pattern), serialize it into the `X-Integration-Testing-User` -/// header so the proxy forwards it to Axum. -// TODO: remove in Part 9 (test-support rewrite — tests will set the header directly) -#[cfg(feature = "test-header-injection")] -async fn inject_test_user_trillium(mut conn: trillium::Conn) -> trillium::Conn { - if let Some(json) = conn - .state::() - .and_then(|u| serde_json::to_string(u).ok()) - { - conn.request_headers_mut() - .insert("x-integration-testing-user", json); - } - conn -} - /// Axum middleware that unconditionally injects an admin -/// [`User`](crate::User) into every request. This is the Axum equivalent of -/// the Trillium `state(User::for_integration_testing())` that was in the old -/// `api()` handler chain. +/// [`User`](crate::User) into every request, unless one is already present +/// (e.g. from the `test-header-injection` middleware). /// /// Only compiled under `--features integration-testing` (enabled by /// `compose.dev.override.yaml`). Never compiled into deployed builds. @@ -273,9 +160,11 @@ async fn inject_integration_testing_user( mut request: axum::extract::Request, next: axum::middleware::Next, ) -> axum::response::Response { - request - .extensions_mut() - .insert(crate::User::for_integration_testing()); + if request.extensions().get::().is_none() { + request + .extensions_mut() + .insert(crate::User::for_integration_testing()); + } next.run(request).await } diff --git a/src/handler/error.rs b/src/handler/error.rs index f12dd9574..3e6a5a30d 100644 --- a/src/handler/error.rs +++ b/src/handler/error.rs @@ -7,16 +7,6 @@ use serde_json::json; use std::sync::Arc; use validator::ValidationErrors; -// TODO: remove in Part 9 (test-support rewrite) — ErrorHandler is only kept -// in the DivviupApi test shim's handler tuple. -pub struct ErrorHandler; -#[trillium::async_trait] -impl trillium::Handler for ErrorHandler { - async fn run(&self, conn: trillium::Conn) -> trillium::Conn { - conn - } -} - /// API-layer errors surfaced by extractors and content-type negotiation. #[derive(thiserror::Error, Debug, Clone)] pub enum ApiError { diff --git a/src/handler/origin_router.rs b/src/handler/origin_router.rs deleted file mode 100644 index 261ed456b..000000000 --- a/src/handler/origin_router.rs +++ /dev/null @@ -1,72 +0,0 @@ -// TODO: remove in Part 9/10 (kept for api_mocks) -use std::collections::HashMap; -use trillium::{Conn, Handler, Info}; - -fn origin_host(conn: &Conn) -> String { - conn.inner().host().unwrap_or_default().to_lowercase() -} - -#[derive(Default, Debug)] -pub struct OriginRouter { - map: HashMap>, -} - -impl OriginRouter { - fn handler(&self, conn: &Conn) -> Option<&dyn Handler> { - let host = origin_host(conn); - self.map.get(&host).map(|boxed_handler| &**boxed_handler) - } - - /// Construct a new OriginRouter. - pub fn new() -> Self { - Self::default() - } - - /// Add a handler to this origin router at the specified exact origin, returning self. - /// See also [`add_handler`]. - pub fn with_handler(mut self, origin: &str, handler: impl Handler) -> Self { - self.add_handler(origin, handler); - self - } - - /// Add a handler to this origin router at the specified exact origin. - /// See also [`with_handler`] for chainability. - pub fn add_handler(&mut self, origin: &str, handler: impl Handler) { - let key = origin - .to_lowercase() - .trim_end_matches('/') - .trim_start_matches("https://") - .trim_start_matches("http://") - .to_owned(); - self.map.insert(key, Box::new(handler)); - } -} - -#[trillium::async_trait] -impl Handler for OriginRouter { - async fn run(&self, conn: Conn) -> Conn { - if let Some(handler) = self.handler(&conn) { - handler.run(conn).await - } else { - conn - } - } - - async fn before_send(&self, conn: Conn) -> Conn { - if let Some(handler) = self.handler(&conn) { - handler.before_send(conn).await - } else { - conn - } - } - - async fn init(&mut self, info: &mut Info) { - for value in self.map.values_mut() { - value.init(info).await - } - } -} - -pub fn origin_router() -> OriginRouter { - OriginRouter::new() -} diff --git a/src/handler/proxy.rs b/src/handler/proxy.rs deleted file mode 100644 index 98ddee8d2..000000000 --- a/src/handler/proxy.rs +++ /dev/null @@ -1,131 +0,0 @@ -//! Temporary reverse proxy handler that forwards unmatched Trillium requests to -//! the local Axum server. This exists only during the incremental migration and -//! will be removed once all routes have been moved to Axum. - -use reqwest::{ - header::{HeaderName, CONNECTION, HOST, TE, TRAILER, TRANSFER_ENCODING}, - redirect, -}; -use std::net::SocketAddr; -use trillium::{Conn, Handler, Status}; - -/// A Trillium [`Handler`] that proxies unhalted requests to a local Axum server. -#[derive(Debug)] -pub struct AxumProxy { - upstream: String, - client: reqwest::Client, -} - -impl AxumProxy { - pub fn new(addr: SocketAddr) -> Self { - Self { - upstream: format!("http://[::1]:{}", addr.port()), - client: reqwest::Client::builder() - .no_proxy() - .redirect(redirect::Policy::none()) - .build() - .expect("failed to build proxy HTTP client"), - } - } -} - -/// Hop-by-hop headers that should not be forwarded through the proxy (RFC 7230 §6.1). -const UNPROXYABLE_HEADERS: [HeaderName; 5] = [HOST, TRANSFER_ENCODING, CONNECTION, TE, TRAILER]; - -#[trillium::async_trait] -impl Handler for AxumProxy { - async fn run(&self, mut conn: Conn) -> Conn { - // Only proxy requests that haven't been handled by earlier handlers. - if conn.status().is_some() || conn.is_halted() { - return conn; - } - - let method = conn.method(); - let path = conn.path(); - let querystring = conn.querystring(); - - let url = if querystring.is_empty() { - format!("{}{}", self.upstream, path) - } else { - format!("{}{}?{}", self.upstream, path, querystring) - }; - - let reqwest_method = match reqwest::Method::from_bytes(method.as_ref().as_bytes()) { - Ok(m) => m, - Err(_) => return conn.with_status(Status::BadRequest).halt(), - }; - - let mut builder = self.client.request(reqwest_method, &url); - - // Preserve the original Host for downstream host-based routing (e.g. assets). - if let Some(original_host) = conn.request_headers().get_str("host") { - builder = builder.header("x-forwarded-host", original_host); - } - - // Forward request headers, filtering out hop-by-hop headers. - for (name, values) in conn.request_headers() { - let header_name = match HeaderName::from_bytes(name.as_ref().as_bytes()) { - Ok(h) => h, - Err(_) => continue, - }; - if UNPROXYABLE_HEADERS.contains(&header_name) { - continue; - } - for value in values.iter() { - if let Some(s) = value.as_str() { - builder = builder.header(&header_name, s); - } - } - } - - // Forward the request body. Note: no size limit is enforced here; - // the Trillium API layer (trillium-api) enforces a 1 MiB limit before - // requests reach this handler, so it's fine for the migration window. - let body = conn.request_body().await.read_bytes().await; - match body { - Ok(bytes) if !bytes.is_empty() => { - builder = builder.body(bytes); - } - Err(e) => { - log::error!("axum proxy error reading request body: {e}"); - return conn.with_status(Status::BadRequest).halt(); - } - _ => {} - } - - let resp = match builder.send().await { - Ok(resp) => resp, - Err(e) => { - log::error!("axum proxy error: {e}"); - return conn.with_status(Status::BadGateway).halt(); - } - }; - - let status = resp.status().as_u16(); - let resp_headers = resp.headers().clone(); - let body = match resp.bytes().await { - Ok(b) => b, - Err(e) => { - log::error!("axum proxy error reading response: {e}"); - return conn.with_status(Status::BadGateway).halt(); - } - }; - - let mut conn = conn.with_status(status).halt(); - - for (name, value) in resp_headers.iter() { - if UNPROXYABLE_HEADERS.contains(name) { - continue; - } - if let Ok(v) = value.to_str() { - conn.response_headers_mut() - .append(name.as_str().to_owned(), v.to_owned()); - } - } - - // This copies the response body; we could avoid it by streaming via - // resp.into_body() + Body::new_streaming, but it's not worth the extra - // plumbing for a temporary shim. - conn.with_body(body.to_vec()) - } -} diff --git a/src/lib.rs b/src/lib.rs index 06dbf9b55..b00d6231b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,7 +26,7 @@ pub use config::{Config, ConfigError, FeatureFlags}; pub use crypter::Crypter; pub use db::Db; pub use handler::{ - build_app, custom_mime_types::DIVVIUP_API_MEDIA_TYPE, AxumAppState, BuiltApp, DivviupApi, Error, + build_app, custom_mime_types::DIVVIUP_API_MEDIA_TYPE, AxumAppState, BuiltApp, Error, }; pub use opentelemetry; pub use permissions::{AdminPermissionsActor, Permissions, PermissionsActor}; diff --git a/test-support/Cargo.toml b/test-support/Cargo.toml index 30cdac5b5..20c252cb3 100644 --- a/test-support/Cargo.toml +++ b/test-support/Cargo.toml @@ -6,11 +6,12 @@ publish = false license.workspace = true [dependencies] +async-trait = "0.1" +axum = "0.8" +bytes = "1" fastrand = "2.4.1" +http-body-util = "0.1" time = "0.3.47" -trillium = "0.2.20" -trillium-macros = "0.0.6" -trillium-testing = { version = "0.7.0", features = ["tokio"] } divviup-api = { workspace = true, features = ["test-header-injection"] } serde = "1.0.228" serde_json = "1.0.149" @@ -23,12 +24,10 @@ tracing-subscriber = { version = "0.3.23", features = [ "std", "fmt", ] } -trillium-client = { version = "0.6.2", features = ["json"] } -trillium-http = "0.3.14" -trillium-rustls = "0.9.0" -trillium-tokio = "0.4.0" reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "json"] } -tokio = { version = "1", features = ["net"] } +tokio = { version = "1", features = ["net", "rt"] } +tokio-util = { version = "0.7", features = ["rt"] } +tower = { version = "0.5", features = ["util"] } url = "2.5.8" uuid = { version = "1.20.0", features = ["v4", "fast-rng", "serde"] } sea-orm = { version = "1.1.20", features = ["sqlx-sqlite"] } diff --git a/test-support/src/api_mocks.rs b/test-support/src/api_mocks.rs index 2c18e514a..0d1954b48 100644 --- a/test-support/src/api_mocks.rs +++ b/test-support/src/api_mocks.rs @@ -1,10 +1,9 @@ -use super::{ClientLogs, AUTH0_URL, POSTMARK_URL}; -use trillium_macros::Handler; +use super::{client_logs::client_logs_middleware, ClientLogs, AUTH0_URL, POSTMARK_URL}; +use axum::{middleware, Router}; -#[derive(Handler, Debug)] +#[derive(Debug)] pub struct ApiMocks { - #[handler] - handler: (ClientLogs, divviup_api::api_mocks::ApiMocks), + router: Router, client_logs: ClientLogs, } @@ -18,11 +17,14 @@ impl ApiMocks { pub fn new() -> Self { let client_logs = ClientLogs::default(); + let inner = divviup_api::api_mocks::ApiMocks::new(POSTMARK_URL, AUTH0_URL); + let router = inner.into_router().layer(middleware::from_fn_with_state( + client_logs.clone(), + client_logs_middleware, + )); + Self { - handler: ( - client_logs.clone(), - divviup_api::api_mocks::ApiMocks::new(POSTMARK_URL, AUTH0_URL), - ), + router, client_logs, } } @@ -30,4 +32,8 @@ impl ApiMocks { pub fn client_logs(&self) -> ClientLogs { self.client_logs.clone() } + + pub fn into_router(self) -> Router { + self.router + } } diff --git a/test-support/src/client_logs.rs b/test-support/src/client_logs.rs index ea2423d4d..e67fc0452 100644 --- a/test-support/src/client_logs.rs +++ b/test-support/src/client_logs.rs @@ -1,84 +1,49 @@ +use axum::{ + body::Body, + extract::{Request, State}, + http::{HeaderMap, Method, StatusCode}, + middleware::Next, + response::Response, +}; use divviup_api::clients::ORIGINAL_URL_HEADER; +use http_body_util::BodyExt; use serde::Deserialize; use std::{ - fmt::{Display, Formatter, Result}, + fmt::{Display, Formatter, Result as FmtResult}, sync::{Arc, RwLock}, }; -use trillium::{async_trait, Body, Conn, Handler, Headers, Method, StateSet, Status}; use url::Url; #[derive(Debug, Clone)] pub struct LoggedConn { pub url: Url, pub method: Method, + pub request_headers: HeaderMap, + pub request_body: Option, pub response_body: Option, - pub response_status: Status, - pub request_headers: Headers, - pub response_headers: Headers, - pub state: Arc, + pub response_status: StatusCode, + pub response_headers: HeaderMap, } impl LoggedConn { pub fn response_json<'a: 'de, 'de, T: Deserialize<'de>>(&'a self) -> T { serde_json::from_str(self.response_body.as_ref().unwrap()).expect("deserialization error") } -} -impl From<&mut Conn> for LoggedConn { - fn from(conn: &mut Conn) -> Self { - let url = conn - .request_headers() - .get_str(ORIGINAL_URL_HEADER.as_str()) - .and_then(|s| Url::parse(s).ok()) - .unwrap_or_else(|| { - Url::parse(&format!( - "{}://{}{}{}", - if conn.is_secure() { "https" } else { "http" }, - conn.inner().host().unwrap(), - conn.path(), - match conn.querystring() { - "" => "".into(), - q => format!("?{q}"), - } - )) - .unwrap() - }); - - let state = Arc::new(std::mem::take(conn.inner_mut().state_mut())); - - let request_headers = match state.get() { - Some(OriginalRequestHeaders(headers)) => headers.clone(), - None => conn.request_headers().clone(), - }; - - Self { - url, - state, - method: conn.method(), - response_body: conn - .inner() - .response_body() - .and_then(Body::static_bytes) - .map(|s| String::from_utf8_lossy(s).to_string()), - response_status: conn.status().unwrap_or(Status::NotFound), - request_headers, - response_headers: conn.response_headers().clone(), - } + pub fn request_json(&self) -> T { + serde_json::from_str(self.request_body.as_ref().unwrap()).expect("deserialization error") } } impl Display for LoggedConn { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - f.write_fmt(format_args!( - "{} {}: {}", - self.method, self.url, self.response_status - )) + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "{} {}: {}", self.method, self.url, self.response_status) } } #[derive(Debug, Default, Clone)] pub struct ClientLogs { - pub(super) logged_conns: Arc>>, + logged_conns: Arc>>, } impl ClientLogs { @@ -109,20 +74,77 @@ impl ClientLogs { } } -#[derive(Debug)] -struct OriginalRequestHeaders(Headers); - -#[async_trait] -impl Handler for ClientLogs { - async fn run(&self, conn: Conn) -> Conn { - let request_headers = conn.request_headers().clone(); - conn.with_state(OriginalRequestHeaders(request_headers)) - } - async fn before_send(&self, mut conn: Conn) -> Conn { - self.logged_conns - .write() - .unwrap() - .push(LoggedConn::from(&mut conn)); - conn +fn reconstruct_url(req: &Request) -> Url { + if let Some(original) = req + .headers() + .get(ORIGINAL_URL_HEADER.as_str()) + .and_then(|v| v.to_str().ok()) + { + if let Ok(url) = Url::parse(original) { + return url; + } } + + let host = req + .headers() + .get("host") + .and_then(|h| h.to_str().ok()) + .unwrap_or("unknown"); + let path = req.uri().path(); + let query = req.uri().query(); + let url_str = match query { + Some(q) => format!("https://{host}{path}?{q}"), + None => format!("https://{host}{path}"), + }; + Url::parse(&url_str) + .unwrap_or_else(|e| panic!("reconstruct_url: malformed Host or path: {url_str}: {e}")) +} + +pub async fn client_logs_middleware( + State(logs): State, + request: Request, + next: Next, +) -> Response { + let method = request.method().clone(); + let request_headers = request.headers().clone(); + let url = reconstruct_url(&request); + + let (parts, body) = request.into_parts(); + let body_bytes = body + .collect() + .await + .expect("failed to read request body") + .to_bytes(); + let request_body = if body_bytes.is_empty() { + None + } else { + String::from_utf8(body_bytes.to_vec()).ok() + }; + let request = Request::from_parts(parts, Body::from(body_bytes)); + + let response = next.run(request).await; + + let (parts, body) = response.into_parts(); + let response_bytes = body + .collect() + .await + .expect("failed to read response body") + .to_bytes(); + let response_body = if response_bytes.is_empty() { + None + } else { + String::from_utf8(response_bytes.to_vec()).ok() + }; + + logs.logged_conns.write().unwrap().push(LoggedConn { + url, + method, + request_headers, + request_body, + response_body, + response_status: parts.status, + response_headers: parts.headers.clone(), + }); + + Response::from_parts(parts, Body::from(response_bytes)) } diff --git a/test-support/src/fixtures.rs b/test-support/src/fixtures.rs index d2aa487e2..35c1733c9 100644 --- a/test-support/src/fixtures.rs +++ b/test-support/src/fixtures.rs @@ -9,6 +9,7 @@ use divviup_api::{ }; use rand::random; use uuid::Uuid; +use HeaderValue; pub fn user() -> User { User { @@ -201,7 +202,10 @@ pub async fn aggregator(app: &DivviupApi, account: Option<&Account>) -> Aggregat pub async fn api_token(app: &DivviupApi, account: &Account) -> (ApiToken, HeaderValue) { let (api_token, token) = ApiToken::build(account); let api_token = api_token.insert(app.db()).await.unwrap(); - (api_token, format!("Bearer {token}").into()) + ( + api_token, + HeaderValue::from_str(&format!("Bearer {token}")).unwrap(), + ) } pub async fn admin_token(app: &DivviupApi) -> HeaderValue { diff --git a/test-support/src/lib.rs b/test-support/src/lib.rs index f31a87a87..be77326ca 100644 --- a/test-support/src/lib.rs +++ b/test-support/src/lib.rs @@ -9,10 +9,21 @@ #![allow(clippy::cargo_common_metadata)] #![allow(clippy::multiple_crate_versions)] +use axum::{ + body::Body, + extract::Request, + http::header::{self, HeaderName, HeaderValue}, + middleware::from_fn_with_state, + response::Response, + serve, Router, +}; use divviup_api::{ clients::{aggregator_client::api_types, HttpClient}, + handler::BuiltApp, Config, Crypter, Db, }; +use http_body_util::BodyExt; +use reqwest::Client; use serde::{de::DeserializeOwned, Serialize}; use std::{ error::Error, @@ -20,13 +31,14 @@ use std::{ iter::repeat_with, net::{Ipv6Addr, SocketAddr}, process::Termination, + sync::Arc, }; -use tokio::net::TcpListener; +use tokio::{net::TcpListener, runtime}; +use tokio_util::sync::CancellationToken; +use tower::ServiceExt; use tracing::install_test_trace_subscriber; -use trillium::Handler; -use trillium_http::HeaderValue; -use trillium_testing::TestConn; +pub use axum::http::{header as headers, HeaderMap, Method, StatusCode}; pub use base64::{ engine::general_purpose::{STANDARD, URL_SAFE_NO_PAD}, Engine, @@ -34,7 +46,7 @@ pub use base64::{ pub use divviup_api::{ entity::{self, *}, queue::{Job, Queue}, - DivviupApi, User, + User, }; pub use pretty_assertions::{assert_eq, assert_ne, assert_str_eq}; pub use querystrong::QueryStrong; @@ -45,16 +57,28 @@ pub use sea_orm::{ pub use serde_json::{json, Value}; pub use test_harness::test; pub use time::OffsetDateTime; -pub use trillium::{Conn, KnownHeaderName, Method, Status}; -pub use trillium_testing::prelude::*; pub use url::Url; pub type TestResult = Result<(), Box>; +pub trait IntoTestStatus { + fn into_status(self) -> StatusCode; +} +impl IntoTestStatus for u16 { + fn into_status(self) -> StatusCode { + StatusCode::from_u16(self).expect("invalid status code") + } +} +impl IntoTestStatus for StatusCode { + fn into_status(self) -> StatusCode { + self + } +} + pub mod fixtures; pub mod tracing; -mod client_logs; +pub mod client_logs; pub use client_logs::{ClientLogs, LoggedConn}; mod api_mocks; @@ -87,21 +111,20 @@ pub async fn set_up_schema(db: &Db) { set_up_schema_for(&schema, db, CollectorCredentials).await; } -pub async fn config(api_mocks: impl Handler) -> Config { +pub async fn config(mock_router: Router) -> Config { let listener = TcpListener::bind((Ipv6Addr::LOCALHOST, 0u16)) .await .expect("failed to bind mock server"); let mock_addr = listener.local_addr().unwrap(); let mock_base = format!("http://[::1]:{}", mock_addr.port()); - let stopper = trillium_http::Stopper::new(); - let config_for_server = trillium_tokio::config() - .without_signals() - .with_stopper(stopper) - .with_prebound_server(listener); - tokio::spawn(config_for_server.run_async(api_mocks)); + tokio::spawn(async move { + serve(listener, mock_router) + .await + .expect("mock server error"); + }); - let reqwest_client = reqwest::Client::builder() + let reqwest_client = Client::builder() .no_proxy() .build() .expect("failed to build reqwest client"); @@ -138,27 +161,68 @@ pub async fn config(api_mocks: impl Handler) -> Config { } } -pub async fn with_db(f: F) -> Out -where - F: FnOnce(Db) -> Fut, - Fut: Future, - Out: Termination, -{ - block_on(async move { - let db = Db::connect("sqlite::memory").await; - set_up_schema(&db).await; - f(db).await - }) +#[derive(Debug)] +pub struct DivviupApi { + router: Router, + db: Db, + config: Arc, +} + +impl DivviupApi { + pub fn router(&self) -> &Router { + &self.router + } + + pub fn db(&self) -> &Db { + &self.db + } + + pub fn config(&self) -> &Config { + &self.config + } + + pub fn crypter(&self) -> &Crypter { + &self.config.crypter + } +} + +impl From<&DivviupApi> for Queue { + fn from(app: &DivviupApi) -> Self { + Self::new(app.db(), app.config(), CancellationToken::new()) + } +} + +impl AsRef for DivviupApi { + fn as_ref(&self) -> &Db { + &self.db + } } pub async fn build_test_app() -> (DivviupApi, ClientLogs) { install_test_trace_subscriber(); let api_mocks = ApiMocks::new(); let client_logs = api_mocks.client_logs(); - let mut app = DivviupApi::new(config(api_mocks).await).await; - set_up_schema(app.db()).await; - let mut info = "testing".into(); - app.init(&mut info).await; + let BuiltApp { router, db, config } = + divviup_api::build_app(config(api_mocks.into_router()).await).await; + set_up_schema(&db).await; + let app = DivviupApi { router, db, config }; + (app, client_logs) +} + +/// Build a test app using a custom mock `Router` instead of the default [`ApiMocks`]. +/// The provided router is wrapped with client-log middleware so all outbound +/// aggregator API requests are captured in the returned [`ClientLogs`]. +pub async fn build_test_app_with_mock(mock: Router) -> (DivviupApi, ClientLogs) { + install_test_trace_subscriber(); + let client_logs = ClientLogs::default(); + let mock_with_logs = mock.layer(from_fn_with_state( + client_logs.clone(), + client_logs::client_logs_middleware, + )); + let BuiltApp { router, db, config } = + divviup_api::build_app(config(mock_with_logs).await).await; + set_up_schema(&db).await; + let app = DivviupApi { router, db, config }; (app, client_logs) } @@ -178,7 +242,7 @@ where pub fn with_client_logs(f: F) -> Out where F: FnOnce(DivviupApi, ClientLogs) -> Fut, - Fut: Future + Send + 'static, + Fut: Future, Out: Termination, { block_on(async move { @@ -187,99 +251,301 @@ where }) } +pub fn block_on, T>(future: F) -> T { + runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap() + .block_on(future) +} + pub const APP_CONTENT_TYPE: &str = "application/vnd.divviup+json;version=0.1"; -#[macro_export] -macro_rules! assert_not_found { - ($conn:expr) => { - assert_eq!($conn.status().unwrap_or(Status::NotFound), Status::NotFound); - assert_eq!($conn.take_response_body_string().unwrap_or_default(), ""); - }; +#[derive(Debug)] +pub struct TestRequest { + method: Method, + uri: String, + headers: HeaderMap, + body: Vec, } -#[trillium::async_trait] -pub trait TestingJsonExt { - async fn response_json(&mut self) -> T; - fn with_request_json(self, t: T) -> Self; +pub fn get(path: impl Into) -> TestRequest { + TestRequest::new(Method::GET, path) } -#[trillium::async_trait] -impl TestingJsonExt for TestConn { - async fn response_json(&mut self) -> T { - assert_eq!( - self.response_headers() - .get_str(KnownHeaderName::ContentType) - .unwrap(), - APP_CONTENT_TYPE - ); +pub fn post(path: impl Into) -> TestRequest { + TestRequest::new(Method::POST, path) +} - let body = self - .take_response_body() - .expect("no body was set") - .into_bytes() - .await - .expect("could not read body"); +pub fn put(path: impl Into) -> TestRequest { + TestRequest::new(Method::PUT, path) +} + +pub fn patch(path: impl Into) -> TestRequest { + TestRequest::new(Method::PATCH, path) +} - serde_json::from_slice(&body).expect("could not deserialize body") +pub fn delete(path: impl Into) -> TestRequest { + TestRequest::new(Method::DELETE, path) +} + +impl TestRequest { + fn new(method: Method, uri: impl Into) -> Self { + Self { + method, + uri: uri.into(), + headers: HeaderMap::new(), + body: Vec::new(), + } } - fn with_request_json(self, t: T) -> Self { - self.with_request_body(serde_json::to_string(&t).unwrap()) + pub fn with_request_header(mut self, name: N, value: V) -> Self + where + N: TryInto, + N::Error: std::fmt::Debug, + V: TryInto, + V::Error: std::fmt::Debug, + { + let name = name.try_into().expect("invalid header name"); + let value = value.try_into().expect("invalid header value"); + self.headers.insert(name, value); + self } -} -pub trait TestExt { - fn with_api_headers(self) -> Self; - fn with_api_host(self) -> Self; - fn with_app_host(self) -> Self; - fn with_auth_header(self, token: HeaderValue) -> Self; - fn with_user(self, user: &User) -> Self; -} + pub fn with_request_body(mut self, body: impl Into) -> Self { + self.body = body.into().into_bytes(); + self + } -impl TestExt for TestConn { - fn with_api_host(self) -> Self { - self.with_request_header(KnownHeaderName::Host, "api.example") - .secure() + pub fn with_request_json(self, t: T) -> Self { + self.with_request_body(serde_json::to_string(&t).unwrap()) } - fn with_app_host(self) -> Self { - self.with_request_header(KnownHeaderName::Host, "app.example") - .secure() + pub fn with_api_host(self) -> Self { + self.with_request_header(header::HOST, "api.example") } - fn with_api_headers(self) -> Self { - if self.method() == Method::Get { + pub fn with_app_host(self) -> Self { + self.with_request_header(header::HOST, "app.example") + } + + pub fn with_api_headers(self) -> Self { + (if self.method == Method::GET { self } else { - self.with_request_header(KnownHeaderName::ContentType, APP_CONTENT_TYPE) - } - .with_request_header(KnownHeaderName::Accept, APP_CONTENT_TYPE) + self.with_request_header(header::CONTENT_TYPE, APP_CONTENT_TYPE) + }) + .with_request_header(header::ACCEPT, APP_CONTENT_TYPE) .with_api_host() } - fn with_auth_header(self, token: HeaderValue) -> Self { - self.with_request_header(KnownHeaderName::Authorization, token) + pub fn with_auth_header(self, token: HeaderValue) -> Self { + self.with_request_header(header::AUTHORIZATION, token) } - /// Simulate an authenticated session for routes that have migrated to the - /// Axum side. The header is read by an axum middleware gated on the - /// `test-header-injection` feature; it is not forwarded to or honored by - /// anything in production builds. - fn with_user(self, user: &User) -> Self { + pub fn with_user(self, user: &User) -> Self { self.with_request_header( - "X-Integration-Testing-User", + "x-integration-testing-user", serde_json::to_string(user).unwrap(), ) } + + /// In Part 10, refactor all callers from `.with_state(user)` to `.with_user(&user)`. + pub fn with_state(self, user: User) -> Self { + self.with_user(&user) + } + + pub fn method(&self) -> Method { + self.method.clone() + } + + pub async fn run_async(self, app: &DivviupApi) -> TestResponse { + let body = if self.body.is_empty() { + Body::empty() + } else { + Body::from(self.body) + }; + + let mut builder = Request::builder().method(self.method).uri(&self.uri); + for (name, value) in &self.headers { + builder = builder.header(name, value); + } + let request = builder.body(body).expect("failed to build request"); + let response = app + .router + .clone() + .oneshot(request) + .await + .expect("oneshot request failed"); + TestResponse::from_response(response).await + } +} + +#[derive(Debug)] +pub struct TestResponse { + status: StatusCode, + headers: HeaderMap, + body: bytes::Bytes, +} + +impl TestResponse { + async fn from_response(response: Response) -> Self { + let status = response.status(); + let headers = response.headers().clone(); + let body = response + .into_body() + .collect() + .await + .expect("failed to read response body") + .to_bytes(); + Self { + status, + headers, + body, + } + } + + pub fn status(&self) -> StatusCode { + self.status + } + + pub fn response_headers(&self) -> &HeaderMap { + &self.headers + } + + pub fn response_json(&self) -> T { + assert_eq!( + self.headers + .get(header::CONTENT_TYPE) + .and_then(|v| v.to_str().ok()), + Some(APP_CONTENT_TYPE), + "expected Content-Type {APP_CONTENT_TYPE}" + ); + + serde_json::from_slice(&self.body).expect("could not deserialize response body") + } + + pub fn response_body_string(&self) -> Option { + if self.body.is_empty() { + None + } else { + String::from_utf8(self.body.to_vec()).ok() + } + } + + pub fn header_str(&self, name: impl AsRef) -> Option<&str> { + self.headers + .get(name.as_ref()) + .and_then(|v| v.to_str().ok()) + } +} + +// These are reimplementations of the Trillium assertion macros atop Axum. In Part 10 +// we can decide whether to keep these or refactor the tests, but they're used heavily. + +#[macro_export] +macro_rules! assert_ok { + ($conn:expr) => {{ + let ref __conn = $conn; + assert_eq!(__conn.status(), $crate::StatusCode::OK); + }}; + + ($conn:expr, $body:expr $(, $header_name:expr => $header_val:expr)*) => {{ + let ref __conn = $conn; + assert_eq!(__conn.status(), $crate::StatusCode::OK); + assert_eq!( + __conn.response_body_string().unwrap_or_default(), + $body + ); + $( + assert_eq!( + __conn.header_str($header_name), + Some($header_val), + concat!("expected header ", stringify!($header_name)), + ); + )* + }}; +} + +#[macro_export] +macro_rules! assert_not_found { + ($conn:expr) => {{ + let ref __conn = $conn; + assert_eq!(__conn.status(), $crate::StatusCode::NOT_FOUND); + assert_eq!(__conn.response_body_string().unwrap_or_default(), ""); + }}; +} + +#[macro_export] +macro_rules! assert_response { + ($conn:expr, $status:expr) => {{ + let ref __conn = $conn; + let expected = $crate::IntoTestStatus::into_status($status); + assert_eq!(__conn.status(), expected); + }}; + + ($conn:expr, $status:expr, $body:expr $(, $header_name:expr => $header_val:expr)*) => {{ + let ref __conn = $conn; + let expected = $crate::IntoTestStatus::into_status($status); + assert_eq!(__conn.status(), expected); + assert_eq!( + __conn.response_body_string().unwrap_or_default(), + $body + ); + $( + assert_eq!( + __conn.header_str($header_name), + Some($header_val), + concat!("expected header ", stringify!($header_name)), + ); + )* + }}; +} + +#[macro_export] +macro_rules! assert_status { + ($conn:expr, $status:expr) => {{ + let ref __conn = $conn; + let expected = $crate::IntoTestStatus::into_status($status); + assert_eq!(__conn.status(), expected); + }}; +} + +#[macro_export] +macro_rules! assert_body_contains { + ($conn:expr, $pattern:expr) => {{ + let ref __conn = $conn; + let body = __conn.response_body_string().unwrap_or_default(); + assert!( + body.contains($pattern), + "expected body to contain {:?}, got {:?}", + $pattern, + body + ); + }}; +} + +#[macro_export] +macro_rules! assert_headers { + ($conn:expr, $($header_name:expr => $header_val:expr),+ $(,)?) => {{ + let ref __conn = $conn; + $( + assert_eq!( + __conn.header_str($header_name), + Some($header_val), + concat!("expected header ", stringify!($header_name)), + ); + )+ + }}; } -#[trillium::async_trait] +#[async_trait::async_trait] pub trait Reload: Sized { async fn reload(&self, db: &impl ConnectionTrait) -> Result, DbErr>; } macro_rules! impl_reload { ($model:ty, $entity:ty) => { - #[trillium::async_trait] + #[async_trait::async_trait] impl Reload for $model { async fn reload(&self, db: &impl ConnectionTrait) -> Result, DbErr> { <$entity>::find_by_id(self.id.clone()).one(db).await diff --git a/tests/integration/accounts.rs b/tests/integration/accounts.rs index 011b764ba..a7bc5bd65 100644 --- a/tests/integration/accounts.rs +++ b/tests/integration/accounts.rs @@ -7,14 +7,14 @@ mod index { let (user, account, ..) = fixtures::member(&app).await; let _other_account = fixtures::account(&app).await; - let mut conn = get("/api/accounts") + let conn = get("/api/accounts") .with_api_headers() .with_state(user) .run_async(&app) .await; assert_ok!(conn); - let accounts: Vec = conn.response_json().await; + let accounts: Vec = conn.response_json(); assert_eq!(accounts, vec![account]); Ok(()) } @@ -23,13 +23,13 @@ mod index { async fn as_admin(app: DivviupApi) -> TestResult { let (user, account, ..) = fixtures::admin(&app).await; let other_account = fixtures::account(&app).await; - let mut conn = get("/api/accounts") + let conn = get("/api/accounts") .with_api_headers() .with_state(user) .run_async(&app) .await; - let accounts: Vec = conn.response_json().await; + let accounts: Vec = conn.response_json(); assert_eq!(accounts, vec![account, other_account]); Ok(()) } @@ -39,12 +39,12 @@ mod index { let account = fixtures::admin_account(&app).await; let other_account = fixtures::account(&app).await; let (_, header) = fixtures::api_token(&app, &account).await; - let mut conn = get("/api/accounts") + let conn = get("/api/accounts") .with_api_headers() - .with_request_header(KnownHeaderName::Authorization, header) + .with_request_header(headers::AUTHORIZATION, header) .run_async(&app) .await; - let accounts: Vec = conn.response_json().await; + let accounts: Vec = conn.response_json(); assert_eq!(accounts, vec![account, other_account]); Ok(()) @@ -55,12 +55,12 @@ mod index { let account = fixtures::account(&app).await; fixtures::account(&app).await; let (_, header) = fixtures::api_token(&app, &account).await; - let mut conn = get("/api/accounts") + let conn = get("/api/accounts") .with_api_headers() - .with_request_header(KnownHeaderName::Authorization, header) + .with_request_header(headers::AUTHORIZATION, header) .run_async(&app) .await; - let accounts: Vec = conn.response_json().await; + let accounts: Vec = conn.response_json(); assert_eq!(accounts, vec![account]); Ok(()) @@ -74,7 +74,7 @@ mod index { token.delete(app.db()).await?; let conn = get("/api/accounts") .with_api_headers() - .with_request_header(KnownHeaderName::Authorization, header) + .with_request_header(headers::AUTHORIZATION, header) .run_async(&app) .await; assert_response!(conn, 403); @@ -88,14 +88,14 @@ mod show { #[test(harness = set_up)] async fn as_a_member(app: DivviupApi) -> TestResult { let (user, account, ..) = fixtures::member(&app).await; - let mut conn = get(format!("/api/accounts/{}", account.id)) + let conn = get(format!("/api/accounts/{}", account.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; assert_ok!(conn); - let account_response: Account = conn.response_json().await; + let account_response: Account = conn.response_json(); assert_eq!(account_response, account); Ok(()) @@ -121,14 +121,14 @@ mod show { let (user, ..) = fixtures::admin(&app).await; let other_account = fixtures::account(&app).await; - let mut conn = get(format!("/api/accounts/{}", other_account.id)) + let conn = get(format!("/api/accounts/{}", other_account.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; assert_ok!(conn); - let account: Account = conn.response_json().await; + let account: Account = conn.response_json(); assert_eq!(account, other_account); @@ -140,12 +140,12 @@ mod show { let account = fixtures::admin_account(&app).await; let other_account = fixtures::account(&app).await; let (_, header) = fixtures::api_token(&app, &account).await; - let mut conn = get(format!("/api/accounts/{}", other_account.id)) + let conn = get(format!("/api/accounts/{}", other_account.id)) .with_api_headers() - .with_request_header(KnownHeaderName::Authorization, header) + .with_request_header(headers::AUTHORIZATION, header) .run_async(&app) .await; - let response: Account = conn.response_json().await; + let response: Account = conn.response_json(); assert_eq!(response, other_account); Ok(()) @@ -156,13 +156,13 @@ mod show { let account = fixtures::account(&app).await; fixtures::account(&app).await; let (_, header) = fixtures::api_token(&app, &account).await; - let mut conn = get(format!("/api/accounts/{}", account.id)) + let conn = get(format!("/api/accounts/{}", account.id)) .with_api_headers() - .with_request_header(KnownHeaderName::Authorization, header) + .with_request_header(headers::AUTHORIZATION, header) .run_async(&app) .await; - let response: Account = conn.response_json().await; + let response: Account = conn.response_json(); assert_eq!(response, account); Ok(()) } @@ -174,7 +174,7 @@ mod show { let (_, header) = fixtures::api_token(&app, &account).await; let conn = get(format!("/api/accounts/{}", other_account.id)) .with_api_headers() - .with_request_header(KnownHeaderName::Authorization, header) + .with_request_header(headers::AUTHORIZATION, header) .run_async(&app) .await; assert_response!(conn, 403); @@ -205,14 +205,14 @@ mod create { #[test(harness = set_up)] async fn valid(app: DivviupApi) -> TestResult { let user = fixtures::user(); - let mut conn = post("/api/accounts") + let conn = post("/api/accounts") .with_api_headers() .with_state(user.clone()) .with_request_json(json!({ "name": "some account name" })) .run_async(&app) .await; assert_response!(conn, 202); - let account: Account = conn.response_json().await; + let account: Account = conn.response_json(); assert_eq!(account.name, "some account name"); let accounts = Accounts::find().all(app.db()).await?; @@ -230,7 +230,7 @@ mod create { #[test(harness = set_up)] async fn invalid(app: DivviupApi) -> TestResult { let user = fixtures::user(); - let mut conn = post("/api/accounts") + let conn = post("/api/accounts") .with_api_headers() .with_state(user.clone()) .with_request_json(json!({ "name": "" })) @@ -238,7 +238,7 @@ mod create { .await; assert_response!(conn, 400); - let errors: Value = conn.response_json().await; + let errors: Value = conn.response_json(); assert!(errors.get("name").is_some()); let accounts = Accounts::find().all(app.db()).await?; assert_eq!(accounts.len(), 0); @@ -251,14 +251,14 @@ mod create { async fn admin_token(app: DivviupApi) -> TestResult { let token = fixtures::admin_token(&app).await; let name = fixtures::random_name(); - let mut conn = post("/api/accounts") + let conn = post("/api/accounts") .with_api_headers() .with_auth_header(token) .with_request_json(json!({ "name": name })) .run_async(&app) .await; assert_response!(conn, 202); - let account: Account = conn.response_json().await; + let account: Account = conn.response_json(); assert_eq!(&account.name, &name); assert_eq!(account.reload(app.db()).await?.unwrap().name, name); @@ -290,7 +290,7 @@ mod update { async fn as_a_member(app: DivviupApi) -> TestResult { let (user, account, ..) = fixtures::member(&app).await; - let mut conn = patch(format!("/api/accounts/{}", account.id)) + let conn = patch(format!("/api/accounts/{}", account.id)) .with_api_headers() .with_request_json(json!({ "name": "new name" })) .with_state(user) @@ -298,7 +298,7 @@ mod update { .await; assert_response!(conn, 202); - let response: Account = conn.response_json().await; + let response: Account = conn.response_json(); assert_eq!(&response.name, "new name"); assert_eq!(account.reload(app.db()).await?.unwrap().name, "new name"); @@ -326,7 +326,7 @@ mod update { let (user, ..) = fixtures::admin(&app).await; let other_account = fixtures::account(&app).await; - let mut conn = patch(format!("/api/accounts/{}", other_account.id)) + let conn = patch(format!("/api/accounts/{}", other_account.id)) .with_api_headers() .with_request_json(json!({ "name": "new name" })) .with_state(user) @@ -334,7 +334,7 @@ mod update { .await; assert_response!(conn, 202); - let account: Account = conn.response_json().await; + let account: Account = conn.response_json(); assert_eq!(&account.name, "new name"); assert_eq!(account.reload(app.db()).await?.unwrap().name, "new name"); @@ -345,7 +345,7 @@ mod update { #[test(harness = set_up)] async fn invalid(app: DivviupApi) -> TestResult { let (user, account, ..) = fixtures::member(&app).await; - let mut conn = patch(format!("/api/accounts/{}", account.id)) + let conn = patch(format!("/api/accounts/{}", account.id)) .with_api_headers() .with_request_json(json!({ "name": "" })) .with_state(user) @@ -353,7 +353,7 @@ mod update { .await; assert_response!(conn, 400); - let errors: Value = conn.response_json().await; + let errors: Value = conn.response_json(); assert!(errors.get("name").is_some()); Ok(()) @@ -364,15 +364,15 @@ mod update { let account = fixtures::account(&app).await; let (_, header) = fixtures::api_token(&app, &account).await; let name = fixtures::random_name(); - let mut conn = patch(format!("/api/accounts/{}", account.id)) + let conn = patch(format!("/api/accounts/{}", account.id)) .with_api_headers() - .with_request_header(KnownHeaderName::Authorization, header) + .with_request_header(headers::AUTHORIZATION, header) .with_request_json(json!({ "name": &name })) .run_async(&app) .await; assert_response!(conn, 202); - let response: Account = conn.response_json().await; + let response: Account = conn.response_json(); assert_eq!(&response.name, &name); assert_eq!(account.reload(app.db()).await?.unwrap().name, name); @@ -388,7 +388,7 @@ mod update { let name = fixtures::random_name(); let conn = patch(format!("/api/accounts/{}", other_account.id)) .with_api_headers() - .with_request_header(KnownHeaderName::Authorization, header) + .with_request_header(headers::AUTHORIZATION, header) .with_request_json(json!({ "name": &name })) .run_async(&app) .await; @@ -403,15 +403,15 @@ mod update { let other_account = fixtures::account(&app).await; let (_, header) = fixtures::api_token(&app, &account).await; let name = fixtures::random_name(); - let mut conn = patch(format!("/api/accounts/{}", other_account.id)) + let conn = patch(format!("/api/accounts/{}", other_account.id)) .with_api_headers() - .with_request_header(KnownHeaderName::Authorization, header) + .with_request_header(headers::AUTHORIZATION, header) .with_request_json(json!({ "name": &name })) .run_async(&app) .await; assert_response!(conn, 202); - let response: Account = conn.response_json().await; + let response: Account = conn.response_json(); assert_eq!(&response.name, &name); assert_eq!(other_account.reload(app.db()).await?.unwrap().name, name); diff --git a/tests/integration/admin_queue.rs b/tests/integration/admin_queue.rs index 5a2d7fdff..c25f866dc 100644 --- a/tests/integration/admin_queue.rs +++ b/tests/integration/admin_queue.rs @@ -10,16 +10,13 @@ mod index { let queue_item = Job::new_invitation_flow(&fixtures::build_membership(&app).await) .insert(app.db()) .await?; - let mut conn = get("/api/admin/queue") + let conn = get("/api/admin/queue") .with_api_headers() .with_state(admin) .run_async(&app) .await; assert_ok!(conn); - assert_eq!( - conn.response_json::>().await, - vec![queue_item] - ); + assert_eq!(conn.response_json::>(), vec![queue_item]); Ok(()) } @@ -55,24 +52,21 @@ mod index { .with_state(admin.clone()) .run_async(&app) .await - .response_json() - .await; + .response_json(); let pending: Vec = get("/api/admin/queue?status=pending") .with_api_headers() .with_state(admin.clone()) .run_async(&app) .await - .response_json() - .await; + .response_json(); let failed: Vec = get("/api/admin/queue?status=failed") .with_api_headers() .with_state(admin) .run_async(&app) .await - .response_json() - .await; + .response_json(); assert!(success.iter().all(|item| item.status == JobStatus::Success)); assert_eq!(success.len(), 1); diff --git a/tests/integration/aggregator_client.rs b/tests/integration/aggregator_client.rs index db7314451..c4f11fa44 100644 --- a/tests/integration/aggregator_client.rs +++ b/tests/integration/aggregator_client.rs @@ -14,14 +14,18 @@ async fn get_task_ids(app: DivviupApi, client_logs: ClientLogs) -> TestResult { let logs = client_logs.logs(); assert!(logs.iter().all(|log| { log.request_headers - .get_str(KnownHeaderName::Accept) + .get(headers::ACCEPT) + .unwrap() + .to_str() .unwrap() == "application/vnd.janus.aggregator+json;version=0.1" })); assert!(logs.iter().all(|log| { log.request_headers - .get_str(KnownHeaderName::Authorization) + .get(headers::AUTHORIZATION) + .unwrap() + .to_str() .unwrap() == format!("Bearer {}", aggregator.bearer_token(app.crypter()).unwrap()) })); @@ -48,13 +52,17 @@ async fn get_task_metrics(app: DivviupApi, client_logs: ClientLogs) -> TestResul let log = client_logs.last(); assert_eq!( log.request_headers - .get_str(KnownHeaderName::Accept) + .get(headers::ACCEPT) + .unwrap() + .to_str() .unwrap(), "application/vnd.janus.aggregator+json;version=0.1" ); assert_eq!( log.request_headers - .get_str(KnownHeaderName::Authorization) + .get(headers::AUTHORIZATION) + .unwrap() + .to_str() .unwrap(), &format!("Bearer {}", aggregator.bearer_token(app.crypter()).unwrap()) ); @@ -81,13 +89,17 @@ async fn get_config(app: DivviupApi, client_logs: ClientLogs) -> TestResult { let log = client_logs.last(); assert_eq!( log.request_headers - .get_str(KnownHeaderName::Accept) + .get(headers::ACCEPT) + .unwrap() + .to_str() .unwrap(), "application/vnd.janus.aggregator+json;version=0.1" ); assert_eq!( log.request_headers - .get_str(KnownHeaderName::Authorization) + .get(headers::AUTHORIZATION) + .unwrap() + .to_str() .unwrap(), "Bearer token" ); @@ -109,8 +121,9 @@ async fn get_config_bad_token(app: DivviupApi) -> TestResult { } mod prefixes { - use divviup_api::{clients::aggregator_client::TaskUploadMetrics, handler::origin_router}; - use trillium_router::router; + use divviup_api::clients::aggregator_client::TaskUploadMetrics; + + use axum::Router; use super::{assert_eq, test, *}; @@ -120,23 +133,15 @@ mod prefixes { Fut: std::future::Future>> + Send + 'static, { block_on(async move { - let client_logs = ClientLogs::default(); let prefix = fixtures::random_name(); + let mock = Router::new().nest(&format!("/{prefix}"), aggregator_api::mock()); + let (app, client_logs) = build_test_app_with_mock(mock).await; + let api_url = Url::parse(&format!( - "https://api.{}.divviup.org/{prefix}", + "https://api.{}.divviup.org/{prefix}/", fixtures::random_name() )) .unwrap(); - let api_mocks = ( - trillium_logger::logger(), - client_logs.clone(), - origin_router().with_handler( - &api_url.origin().ascii_serialization(), - router().all(format!("/{prefix}/*"), aggregator_api::mock()), - ), - ); - let mut app = DivviupApi::new(config(api_mocks).await).await; - set_up_schema(app.db()).await; let mut aggregator = fixtures::aggregator(&app, None).await.into_active_model(); aggregator.encrypted_bearer_token = ActiveValue::Set( app.crypter() @@ -148,8 +153,6 @@ mod prefixes { ); aggregator.api_url = ActiveValue::Set(api_url.into()); let aggregator = aggregator.update(app.db()).await.unwrap(); - let mut info = "testing".into(); - app.init(&mut info).await; f(app, client_logs, aggregator).await }) .unwrap() @@ -171,9 +174,9 @@ mod prefixes { .map(|l| l.url.as_ref()) .collect::>(), vec![ - format!("{}/task_ids", aggregator.api_url), - format!("{}/task_ids?pagination_token=second", aggregator.api_url), - format!("{}/task_ids?pagination_token=last", aggregator.api_url) + format!("{}task_ids", aggregator.api_url), + format!("{}task_ids?pagination_token=second", aggregator.api_url), + format!("{}task_ids?pagination_token=last", aggregator.api_url) ] ); assert_eq!(client_logs.logs().len(), 3); @@ -193,10 +196,10 @@ mod prefixes { client_logs.last().response_json::(), metrics ); - assert_eq!(client_logs.last().method, Method::Get); + assert_eq!(client_logs.last().method, Method::GET); assert_eq!( client_logs.last().url.as_ref(), - format!("{}/tasks/fake-task-id/metrics/uploads", aggregator.api_url) + format!("{}tasks/fake-task-id/metrics/uploads", aggregator.api_url) ); assert_eq!(client_logs.logs().len(), 1); Ok(()) diff --git a/tests/integration/aggregators.rs b/tests/integration/aggregators.rs index 7a5a1fdb2..487a298f2 100644 --- a/tests/integration/aggregators.rs +++ b/tests/integration/aggregators.rs @@ -15,14 +15,14 @@ mod index { let aggregator1 = fixtures::aggregator(&app, Some(&account)).await; let aggregator2 = fixtures::aggregator(&app, Some(&account)).await; - let mut conn = get(format!("/api/accounts/{}/aggregators", account.id)) + let conn = get(format!("/api/accounts/{}/aggregators", account.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; assert_ok!(conn); - let aggregators: Vec = conn.response_json().await; + let aggregators: Vec = conn.response_json(); assert_same_json_representation( &aggregators, &vec![shared_aggregator, aggregator1, aggregator2], @@ -40,14 +40,14 @@ mod index { aggregator1.tombstone().update(app.db()).await?; let aggregator2 = fixtures::aggregator(&app, Some(&account)).await; - let mut conn = get(format!("/api/accounts/{}/aggregators", account.id)) + let conn = get(format!("/api/accounts/{}/aggregators", account.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; assert_ok!(conn); - let aggregators: Vec = conn.response_json().await; + let aggregators: Vec = conn.response_json(); assert_same_json_representation(&aggregators, &vec![aggregator2]); Ok(()) } @@ -62,14 +62,14 @@ mod index { aggregator1.tombstone().update(app.db()).await?; let aggregator2 = fixtures::aggregator(&app, Some(&account)).await; - let mut conn = get(format!("/api/accounts/{}/aggregators", account.id)) + let conn = get(format!("/api/accounts/{}/aggregators", account.id)) .with_api_headers() .with_state(admin) .run_async(&app) .await; assert_ok!(conn); - let aggregators: Vec = conn.response_json().await; + let aggregators: Vec = conn.response_json(); assert_same_json_representation(&vec![aggregator2], &aggregators); Ok(()) } @@ -103,7 +103,7 @@ mod index { fixtures::aggregator(&app, Some(&account)).await; fixtures::aggregator(&app, Some(&account)).await; - let mut conn = get("/api/accounts/not-an-account/aggregators") + let conn = get("/api/accounts/not-an-account/aggregators") .with_api_headers() .with_state(user) .run_async(&app) @@ -123,14 +123,14 @@ mod index { let aggregator1 = fixtures::aggregator(&app, Some(&account)).await; let aggregator2 = fixtures::aggregator(&app, Some(&account)).await; - let mut conn = get(format!("/api/accounts/{}/aggregators", account.id)) + let conn = get(format!("/api/accounts/{}/aggregators", account.id)) .with_api_headers() .with_state(admin) .run_async(&app) .await; assert_ok!(conn); - let aggregators: Vec = conn.response_json().await; + let aggregators: Vec = conn.response_json(); assert_same_json_representation( &aggregators, &vec![shared_aggregator, aggregator1, aggregator2], @@ -149,14 +149,14 @@ mod index { let aggregator1 = fixtures::aggregator(&app, Some(&account)).await; let aggregator2 = fixtures::aggregator(&app, Some(&account)).await; - let mut conn = get(format!("/api/accounts/{}/aggregators", account.id)) + let conn = get(format!("/api/accounts/{}/aggregators", account.id)) .with_api_headers() - .with_request_header(KnownHeaderName::Authorization, token) + .with_request_header(headers::AUTHORIZATION, token) .run_async(&app) .await; assert_ok!(conn); - let aggregators: Vec = conn.response_json().await; + let aggregators: Vec = conn.response_json(); assert_same_json_representation( &aggregators, &vec![shared_aggregator, aggregator1, aggregator2], @@ -177,14 +177,14 @@ mod index { let admin_token = fixtures::admin_token(&app).await; - let mut conn = get(format!("/api/accounts/{}/aggregators", account.id)) + let conn = get(format!("/api/accounts/{}/aggregators", account.id)) .with_api_headers() - .with_request_header(KnownHeaderName::Authorization, admin_token) + .with_request_header(headers::AUTHORIZATION, admin_token) .run_async(&app) .await; assert_ok!(conn); - let aggregators: Vec = conn.response_json().await; + let aggregators: Vec = conn.response_json(); assert_same_json_representation( &aggregators, &vec![shared_aggregator, aggregator1, aggregator2], @@ -206,7 +206,7 @@ mod index { let conn = get(format!("/api/accounts/{}/aggregators", account.id)) .with_api_headers() - .with_request_header(KnownHeaderName::Authorization, token) + .with_request_header(headers::AUTHORIZATION, token) .run_async(&app) .await; @@ -230,14 +230,14 @@ mod shared_aggregator_index { let (user, account, ..) = fixtures::member(&app).await; fixtures::aggregator(&app, Some(&account)).await; - let mut conn = get("/api/aggregators") + let conn = get("/api/aggregators") .with_api_headers() .with_state(user) .run_async(&app) .await; assert_ok!(conn); - let aggregators: Vec = conn.response_json().await; + let aggregators: Vec = conn.response_json(); assert_same_json_representation( &aggregators, &vec![shared_aggregator1, shared_aggregator2], @@ -257,14 +257,14 @@ mod shared_aggregator_index { let (_, token) = fixtures::api_token(&app, &account).await; fixtures::aggregator(&app, Some(&account)).await; - let mut conn = get("/api/aggregators") + let conn = get("/api/aggregators") .with_api_headers() .with_auth_header(token) .run_async(&app) .await; assert_ok!(conn); - let aggregators: Vec = conn.response_json().await; + let aggregators: Vec = conn.response_json(); assert_same_json_representation( &aggregators, &vec![shared_aggregator1, shared_aggregator2], @@ -294,14 +294,14 @@ mod create { let (user, account, ..) = fixtures::member(&app).await; let new_aggregator = fixtures::new_aggregator(); - let mut conn = post(format!("/api/accounts/{}/aggregators", account.id)) + let conn = post(format!("/api/accounts/{}/aggregators", account.id)) .with_api_headers() .with_state(user) .with_request_json(new_aggregator.clone()) .run_async(&app) .await; assert_response!(conn, 201); - let aggregator: Aggregator = conn.response_json().await; + let aggregator: Aggregator = conn.response_json(); let aggregator_config: AggregatorApiConfig = client_logs.last().response_json(); @@ -329,14 +329,14 @@ mod create { let mut new_aggregator = fixtures::new_aggregator(); new_aggregator.is_first_party = Some(true); - let mut conn = post(format!("/api/accounts/{}/aggregators", account.id)) + let conn = post(format!("/api/accounts/{}/aggregators", account.id)) .with_api_headers() .with_state(user) .with_request_json(new_aggregator) .run_async(&app) .await; assert_response!(conn, 201); - let aggregator: Aggregator = conn.response_json().await; + let aggregator: Aggregator = conn.response_json(); assert!(!aggregator.is_first_party); assert!(!aggregator.reload(app.db()).await?.unwrap().is_first_party); @@ -347,7 +347,7 @@ mod create { async fn invalid(app: DivviupApi) -> TestResult { let (user, account, ..) = fixtures::member(&app).await; - let mut conn = post(format!("/api/accounts/{}/aggregators", account.id)) + let conn = post(format!("/api/accounts/{}/aggregators", account.id)) .with_api_headers() .with_state(user) .with_request_json(json!({ @@ -358,7 +358,7 @@ mod create { .await; assert_response!(conn, 400); - let error: Value = conn.response_json().await; + let error: Value = conn.response_json(); assert!(error.get("name").is_some()); assert!(error.get("api_url").is_some()); Ok(()) @@ -389,7 +389,7 @@ mod create { let user = fixtures::user(); let aggregator_count_before = Aggregators::find().count(app.db()).await?; - let mut conn = post("/api/accounts/does-not-exist/aggregators") + let conn = post("/api/accounts/does-not-exist/aggregators") .with_api_headers() .with_state(user) .with_request_json(fixtures::new_aggregator()) @@ -407,7 +407,7 @@ mod create { async fn admin_not_member(app: DivviupApi) -> TestResult { let (admin, ..) = fixtures::admin(&app).await; let account = fixtures::account(&app).await; - let mut conn = post(format!("/api/accounts/{}/aggregators", account.id)) + let conn = post(format!("/api/accounts/{}/aggregators", account.id)) .with_api_headers() .with_state(admin) .with_request_json(fixtures::new_aggregator()) @@ -415,7 +415,7 @@ mod create { .await; assert_response!(conn, 201); - let aggregator: Aggregator = conn.response_json().await; + let aggregator: Aggregator = conn.response_json(); assert!(!aggregator.is_first_party); let aggregator_from_db = aggregator.reload(app.db()).await?.unwrap(); @@ -428,7 +428,7 @@ mod create { async fn admin_token(app: DivviupApi) -> TestResult { let token = fixtures::admin_token(&app).await; let account = fixtures::account(&app).await; - let mut conn = post(format!("/api/accounts/{}/aggregators", account.id)) + let conn = post(format!("/api/accounts/{}/aggregators", account.id)) .with_api_headers() .with_auth_header(token) .with_request_json(fixtures::new_aggregator()) @@ -436,7 +436,7 @@ mod create { .await; assert_response!(conn, 201); - let aggregator: Aggregator = conn.response_json().await; + let aggregator: Aggregator = conn.response_json(); assert!(!aggregator.is_first_party); let aggregator_from_db = aggregator.reload(app.db()).await?.unwrap(); @@ -449,7 +449,7 @@ mod create { async fn member_token(app: DivviupApi) -> TestResult { let account = fixtures::account(&app).await; let (_, token) = fixtures::api_token(&app, &account).await; - let mut conn = post(format!("/api/accounts/{}/aggregators", account.id)) + let conn = post(format!("/api/accounts/{}/aggregators", account.id)) .with_api_headers() .with_auth_header(token) .with_request_json(fixtures::new_aggregator()) @@ -457,7 +457,7 @@ mod create { .await; assert_response!(conn, 201); - let aggregator: Aggregator = conn.response_json().await; + let aggregator: Aggregator = conn.response_json(); assert!(!aggregator.is_first_party); let aggregator_from_db = aggregator.reload(app.db()).await?.unwrap(); @@ -494,15 +494,15 @@ mod create { new_aggregator.bearer_token = Some(BAD_BEARER_TOKEN.to_string()); let aggregator_count_before = Aggregators::find().count(app.db()).await?; - let mut conn = post(format!("/api/accounts/{}/aggregators", account.id)) + let conn = post(format!("/api/accounts/{}/aggregators", account.id)) .with_api_headers() .with_state(user) .with_request_json(new_aggregator.clone()) .run_async(&app) .await; assert_response!(conn, 400); - assert_eq!(client_logs.last().response_status, Status::Unauthorized); - let error: Value = conn.response_json().await; + assert_eq!(client_logs.last().response_status, StatusCode::UNAUTHORIZED); + let error: Value = conn.response_json(); assert!(error.get("bearer_token").is_some()); let aggregator_count_after = Aggregators::find().count(app.db()).await?; assert_eq!(aggregator_count_before, aggregator_count_after); @@ -518,13 +518,13 @@ mod show { async fn as_member(app: DivviupApi) -> TestResult { let (user, account, ..) = fixtures::member(&app).await; let aggregator = fixtures::aggregator(&app, Some(&account)).await; - let mut conn = get(format!("/api/aggregators/{}", aggregator.id)) + let conn = get(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json().await; + let response_aggregator: Aggregator = conn.response_json(); assert_same_json_representation(&response_aggregator, &aggregator); Ok(()) } @@ -533,13 +533,13 @@ mod show { async fn shared_aggregator(app: DivviupApi) -> TestResult { let (user, _account, ..) = fixtures::member(&app).await; let aggregator = fixtures::aggregator(&app, None).await; - let mut conn = get(format!("/api/aggregators/{}", aggregator.id)) + let conn = get(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json().await; + let response_aggregator: Aggregator = conn.response_json(); assert_same_json_representation(&response_aggregator, &aggregator); Ok(()) } @@ -563,13 +563,13 @@ mod show { let (admin, ..) = fixtures::admin(&app).await; let account = fixtures::account(&app).await; let aggregator = fixtures::aggregator(&app, Some(&account)).await; - let mut conn = get(format!("/api/aggregators/{}", aggregator.id)) + let conn = get(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_state(admin) .run_async(&app) .await; assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json().await; + let response_aggregator: Aggregator = conn.response_json(); assert_same_json_representation(&response_aggregator, &aggregator); Ok(()) } @@ -577,7 +577,7 @@ mod show { #[test(harness = set_up)] async fn nonexistant_aggregator(app: DivviupApi) -> TestResult { let user = fixtures::user(); - let mut conn = get("/api/aggregators/some-made-up-id") + let conn = get("/api/aggregators/some-made-up-id") .with_api_headers() .with_state(user) .run_async(&app) @@ -595,13 +595,13 @@ mod show { .update(app.db()) .await?; - let mut conn = get(format!("/api/aggregators/{}", aggregator.id)) + let conn = get(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json().await; + let response_aggregator: Aggregator = conn.response_json(); assert_same_json_representation(&response_aggregator, &aggregator); Ok(()) } @@ -615,13 +615,13 @@ mod show { .update(app.db()) .await?; - let mut conn = get(format!("/api/aggregators/{}", aggregator.id)) + let conn = get(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json().await; + let response_aggregator: Aggregator = conn.response_json(); assert_same_json_representation(&response_aggregator, &aggregator); Ok(()) } @@ -635,13 +635,13 @@ mod show { .tombstone() .update(app.db()) .await?; - let mut conn = get(format!("/api/aggregators/{}", aggregator.id)) + let conn = get(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_state(admin) .run_async(&app) .await; assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json().await; + let response_aggregator: Aggregator = conn.response_json(); assert_same_json_representation(&response_aggregator, &aggregator); Ok(()) } @@ -654,13 +654,13 @@ mod show { .tombstone() .update(app.db()) .await?; - let mut conn = get(format!("/api/aggregators/{}", aggregator.id)) + let conn = get(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_state(admin) .run_async(&app) .await; assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json().await; + let response_aggregator: Aggregator = conn.response_json(); assert_same_json_representation(&response_aggregator, &aggregator); Ok(()) } @@ -670,13 +670,13 @@ mod show { let token = fixtures::admin_token(&app).await; let account = fixtures::account(&app).await; let aggregator = fixtures::aggregator(&app, Some(&account)).await; - let mut conn = get(format!("/api/aggregators/{}", aggregator.id)) + let conn = get(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; assert_ok!(conn); - let response: Aggregator = conn.response_json().await; + let response: Aggregator = conn.response_json(); assert_same_json_representation(&aggregator, &response); Ok(()) } @@ -686,13 +686,13 @@ mod show { let account = fixtures::account(&app).await; let (_, token) = fixtures::api_token(&app, &account).await; let aggregator = fixtures::aggregator(&app, Some(&account)).await; - let mut conn = get(format!("/api/aggregators/{}", aggregator.id)) + let conn = get(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; assert_ok!(conn); - let response: Aggregator = conn.response_json().await; + let response: Aggregator = conn.response_json(); assert_same_json_representation(&aggregator, &response); Ok(()) } @@ -730,7 +730,7 @@ mod update { let new_name = format!("new name {}", fixtures::random_name()); let new_bearer_token = fixtures::random_name(); - let mut conn = patch(format!("/api/aggregators/{}", aggregator.id)) + let conn = patch(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_request_json(json!({ "name": &new_name, "bearer_token": &new_bearer_token })) .with_state(user) @@ -742,11 +742,13 @@ mod update { client_logs .last() .request_headers - .get_str(KnownHeaderName::Authorization) + .get(headers::AUTHORIZATION) + .unwrap() + .to_str() .unwrap(), format!("Bearer {new_bearer_token}") ); - let response_aggregator: Aggregator = conn.response_json().await; + let response_aggregator: Aggregator = conn.response_json(); assert_eq!(response_aggregator.name, new_name); let reloaded = aggregator.reload(app.db()).await?.unwrap(); assert_eq!(reloaded.name, new_name); @@ -761,7 +763,7 @@ mod update { let aggregator = fixtures::aggregator(&app, Some(&account)).await; let original_bearer_token = aggregator.encrypted_bearer_token.clone(); - let mut conn = patch(format!("/api/aggregators/{}", aggregator.id)) + let conn = patch(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_request_json(json!({ "bearer_token": &BAD_BEARER_TOKEN })) .with_state(user) @@ -773,7 +775,9 @@ mod update { client_logs .last() .request_headers - .get_str(KnownHeaderName::Authorization) + .get(headers::AUTHORIZATION) + .unwrap() + .to_str() .unwrap(), format!("Bearer {BAD_BEARER_TOKEN}") ); @@ -786,8 +790,8 @@ mod update { .encrypted_bearer_token, original_bearer_token ); - assert_eq!(client_logs.last().response_status, Status::Unauthorized); - let errors: Value = conn.response_json().await; + assert_eq!(client_logs.last().response_status, StatusCode::UNAUTHORIZED); + let errors: Value = conn.response_json(); assert!(errors.get("bearer_token").is_some()); Ok(()) @@ -798,14 +802,14 @@ mod update { let (user, account, ..) = fixtures::member(&app).await; let aggregator = fixtures::aggregator(&app, Some(&account)).await; - let mut conn = patch(format!("/api/aggregators/{}", aggregator.id)) + let conn = patch(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_request_json(json!({ "name": "" })) .with_state(user) .run_async(&app) .await; assert_response!(conn, 400); - let errors: Value = conn.response_json().await; + let errors: Value = conn.response_json(); assert!(errors.get("name").is_some()); assert_eq!( @@ -860,7 +864,7 @@ mod update { let aggregator = fixtures::aggregator(&app, None).await; let new_name = format!("new name {}", fixtures::random_name()); - let mut conn = patch(format!("/api/aggregators/{}", aggregator.id)) + let conn = patch(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_request_json(json!({ "name": &new_name })) .with_state(admin) @@ -868,7 +872,7 @@ mod update { .await; assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json().await; + let response_aggregator: Aggregator = conn.response_json(); assert_eq!(response_aggregator.name, new_name); assert_eq!(aggregator.reload(app.db()).await?.unwrap().name, new_name); Ok(()) @@ -881,14 +885,14 @@ mod update { let aggregator = fixtures::aggregator(&app, Some(&account)).await; let new_name = format!("new name {}", fixtures::random_name()); - let mut conn = patch(format!("/api/aggregators/{}", aggregator.id)) + let conn = patch(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_request_json(json!({ "name": &new_name })) .with_state(admin) .run_async(&app) .await; assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json().await; + let response_aggregator: Aggregator = conn.response_json(); assert_eq!(response_aggregator.name, new_name); assert_eq!(aggregator.reload(app.db()).await?.unwrap().name, new_name); @@ -898,7 +902,7 @@ mod update { #[test(harness = set_up)] async fn nonexistant_aggregator(app: DivviupApi) -> TestResult { let user = fixtures::user(); - let mut conn = patch("/api/aggregators/not-an-aggregator-id") + let conn = patch("/api/aggregators/not-an-aggregator-id") .with_api_headers() .with_request_json(json!({ "name": "irrelevant" })) .with_state(user) @@ -917,14 +921,14 @@ mod update { .update(app.db()) .await?; let name = fixtures::random_name(); - let mut conn = patch(format!("/api/aggregators/{}", aggregator.id)) + let conn = patch(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_request_json(json!({ "name": name })) .with_state(user) .run_async(&app) .await; assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json().await; + let response_aggregator: Aggregator = conn.response_json(); assert_eq!(response_aggregator.name, name); assert_eq!(aggregator.reload(app.db()).await?.unwrap().name, name); Ok(()) @@ -959,14 +963,14 @@ mod update { .update(app.db()) .await?; let name = fixtures::random_name(); - let mut conn = patch(format!("/api/aggregators/{}", aggregator.id)) + let conn = patch(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_request_json(json!({ "name": name })) .with_state(admin) .run_async(&app) .await; assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json().await; + let response_aggregator: Aggregator = conn.response_json(); assert_eq!(response_aggregator.name, name); assert_eq!(aggregator.reload(app.db()).await?.unwrap().name, name); Ok(()) @@ -981,7 +985,7 @@ mod update { .update(app.db()) .await?; let name = fixtures::random_name(); - let mut conn = patch(format!("/api/aggregators/{}", aggregator.id)) + let conn = patch(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_request_json(json!({ "name": name })) .with_state(admin) @@ -989,7 +993,7 @@ mod update { .await; assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json().await; + let response_aggregator: Aggregator = conn.response_json(); assert_eq!(response_aggregator.name, name); assert_eq!(aggregator.reload(app.db()).await?.unwrap().name, name); Ok(()) @@ -1001,7 +1005,7 @@ mod update { let account = fixtures::account(&app).await; let aggregator = fixtures::aggregator(&app, Some(&account)).await; let name = fixtures::random_name(); - let mut conn = patch(format!("/api/aggregators/{}", aggregator.id)) + let conn = patch(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_auth_header(token) .with_request_json(json!({ "name": name })) @@ -1009,7 +1013,7 @@ mod update { .await; assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json().await; + let response_aggregator: Aggregator = conn.response_json(); assert_eq!(response_aggregator.name, name); assert_eq!(aggregator.reload(app.db()).await?.unwrap().name, name); Ok(()) @@ -1021,14 +1025,14 @@ mod update { let (_, token) = fixtures::api_token(&app, &account).await; let aggregator = fixtures::aggregator(&app, Some(&account)).await; let name = fixtures::random_name(); - let mut conn = patch(format!("/api/aggregators/{}", aggregator.id)) + let conn = patch(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_auth_header(token) .with_request_json(json!({ "name": name })) .run_async(&app) .await; assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json().await; + let response_aggregator: Aggregator = conn.response_json(); assert_eq!(response_aggregator.name, name); assert_eq!(aggregator.reload(app.db()).await?.unwrap().name, name); Ok(()) @@ -1071,25 +1075,25 @@ mod update { aggregator.features = ActiveValue::Set(Features::from_iter::<[Feature; 0]>([]).into()); let aggregator = aggregator.update(app.db()).await?; - let mut conn = get(format!("/api/aggregators/{}", aggregator.id)) + let conn = get(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_state(user.clone()) .run_async(&app) .await; assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json().await; + let response_aggregator: Aggregator = conn.response_json(); assert!(response_aggregator.query_types.is_empty()); assert!(response_aggregator.vdafs.is_empty()); assert!(response_aggregator.features.is_empty()); - let mut conn = patch(format!("/api/aggregators/{}", aggregator.id)) + let conn = patch(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_request_json(json!({})) .with_state(user) .run_async(&app) .await; assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json().await; + let response_aggregator: Aggregator = conn.response_json(); assert_eq!( response_aggregator.query_types, before_aggregator.query_types @@ -1278,7 +1282,7 @@ mod shared_create { async fn as_admin(app: DivviupApi, client_logs: ClientLogs) -> TestResult { let (admin, ..) = fixtures::admin(&app).await; let new_aggregator = fixtures::new_aggregator(); - let mut conn = post("/api/aggregators") + let conn = post("/api/aggregators") .with_request_json(&new_aggregator) .with_api_headers() .with_state(admin) @@ -1286,7 +1290,7 @@ mod shared_create { .await; assert_response!(conn, 201); - let aggregator: Aggregator = conn.response_json().await; + let aggregator: Aggregator = conn.response_json(); let aggregator_config: AggregatorApiConfig = client_logs.last().response_json(); assert!(aggregator.account_id.is_none()); @@ -1314,14 +1318,14 @@ mod shared_create { let (admin, ..) = fixtures::admin(&app).await; let mut new_aggregator = fixtures::new_aggregator(); new_aggregator.is_first_party = Some(true); - let mut conn = post("/api/aggregators") + let conn = post("/api/aggregators") .with_api_headers() .with_state(admin) .with_request_json(new_aggregator) .run_async(&app) .await; assert_response!(conn, 201); - let aggregator: Aggregator = conn.response_json().await; + let aggregator: Aggregator = conn.response_json(); assert!(aggregator.is_first_party); assert!(aggregator.reload(app.db()).await?.unwrap().is_first_party); @@ -1333,14 +1337,14 @@ mod shared_create { let (admin, ..) = fixtures::admin(&app).await; let mut new_aggregator = fixtures::new_aggregator(); new_aggregator.is_first_party = Some(false); - let mut conn = post("/api/aggregators") + let conn = post("/api/aggregators") .with_api_headers() .with_state(admin) .with_request_json(new_aggregator) .run_async(&app) .await; assert_response!(conn, 201); - let aggregator: Aggregator = conn.response_json().await; + let aggregator: Aggregator = conn.response_json(); assert!(!aggregator.is_first_party); assert!(!aggregator.reload(app.db()).await?.unwrap().is_first_party); @@ -1353,7 +1357,7 @@ mod shared_create { let new_aggregator = fixtures::new_aggregator(); let aggregator_count_before = Aggregators::find().count(app.db()).await?; - let mut conn = post("/api/aggregators") + let conn = post("/api/aggregators") .with_request_json(&new_aggregator) .with_api_headers() .with_state(admin) @@ -1374,7 +1378,7 @@ mod shared_create { let new_aggregator = fixtures::new_aggregator(); let aggregator_count_before = Aggregators::find().count(app.db()).await?; - let mut conn = post("/api/aggregators") + let conn = post("/api/aggregators") .with_request_json(&new_aggregator) .with_api_headers() .with_auth_header(token) @@ -1392,13 +1396,13 @@ mod shared_create { let token = fixtures::admin_token(&app).await; let new_aggregator = fixtures::new_aggregator(); let aggregator_count_before = Aggregators::find().count(app.db()).await?; - let mut conn = post("/api/aggregators") + let conn = post("/api/aggregators") .with_request_json(&new_aggregator) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - let aggregator: Aggregator = conn.response_json().await; + let aggregator: Aggregator = conn.response_json(); assert_eq!(aggregator.name, new_aggregator.name.unwrap()); let aggregator_count_after = Aggregators::find().count(app.db()).await?; assert_eq!(aggregator_count_after, aggregator_count_before + 1); diff --git a/tests/integration/api_tokens.rs b/tests/integration/api_tokens.rs index dd6f0c7ab..59edb5c53 100644 --- a/tests/integration/api_tokens.rs +++ b/tests/integration/api_tokens.rs @@ -13,14 +13,14 @@ mod index { let (deleted, _) = fixtures::api_token(&app, &account).await; let _deleted = deleted.tombstone().update(app.db()).await.unwrap(); - let mut conn = get(format!("/api/accounts/{}/api_tokens", account.id)) + let conn = get(format!("/api/accounts/{}/api_tokens", account.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; assert_ok!(conn); - let api_tokens: Vec = conn.response_json().await; + let api_tokens: Vec = conn.response_json(); assert_same_json_representation(&api_tokens, &vec![token2, token1]); Ok(()) } @@ -52,7 +52,7 @@ mod index { fixtures::api_token(&app, &account).await; fixtures::api_token(&app, &account).await; - let mut conn = get("/api/accounts/not-an-account/api_tokens") + let conn = get("/api/accounts/not-an-account/api_tokens") .with_api_headers() .with_state(user) .run_async(&app) @@ -70,14 +70,14 @@ mod index { let (api_token1, _) = fixtures::api_token(&app, &account).await; let (api_token2, _) = fixtures::api_token(&app, &account).await; - let mut conn = get(format!("/api/accounts/{}/api_tokens", account.id)) + let conn = get(format!("/api/accounts/{}/api_tokens", account.id)) .with_api_headers() .with_state(admin) .run_async(&app) .await; assert_ok!(conn); - let api_tokens: Vec = conn.response_json().await; + let api_tokens: Vec = conn.response_json(); assert_same_json_representation(&api_tokens, &vec![api_token2, api_token1]); Ok(()) } @@ -89,14 +89,14 @@ mod index { let (api_token1, _) = fixtures::api_token(&app, &account).await; let (api_token2, _) = fixtures::api_token(&app, &account).await; - let mut conn = get(format!("/api/accounts/{}/api_tokens", account.id)) + let conn = get(format!("/api/accounts/{}/api_tokens", account.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; assert_ok!(conn); - let api_tokens: Vec = conn.response_json().await; + let api_tokens: Vec = conn.response_json(); assert_same_json_representation(&api_tokens, &vec![api_token2, api_token1]); Ok(()) } @@ -107,14 +107,14 @@ mod index { let (api_token1, token) = fixtures::api_token(&app, &account).await; let (api_token2, _) = fixtures::api_token(&app, &account).await; - let mut conn = get(format!("/api/accounts/{}/api_tokens", account.id)) + let conn = get(format!("/api/accounts/{}/api_tokens", account.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; assert_ok!(conn); - let api_tokens: Vec = conn.response_json().await; + let api_tokens: Vec = conn.response_json(); assert_same_json_representation( &api_tokens, &vec![api_token2, api_token1.reload(app.db()).await?.unwrap()], @@ -148,13 +148,13 @@ mod create { async fn success(app: DivviupApi) -> TestResult { let (user, account, ..) = fixtures::member(&app).await; - let mut conn = post(format!("/api/accounts/{}/api_tokens", account.id)) + let conn = post(format!("/api/accounts/{}/api_tokens", account.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; assert_response!(conn, 201); - let mut api_token: ApiToken = conn.response_json().await; + let mut api_token: ApiToken = conn.response_json(); let api_token_from_db = api_token.reload(app.db()).await?.unwrap(); let (api_token_from_token, account_from_token) = ApiTokens::load_and_check(&api_token.token.take().unwrap(), app.db()) @@ -191,7 +191,7 @@ mod create { let user = fixtures::user(); let api_token_count_before = ApiTokens::find().count(app.db()).await?; - let mut conn = post("/api/accounts/does-not-exist/api_tokens") + let conn = post("/api/accounts/does-not-exist/api_tokens") .with_api_headers() .with_state(user) .run_async(&app) @@ -208,14 +208,14 @@ mod create { async fn admin_not_member(app: DivviupApi) -> TestResult { let (admin, ..) = fixtures::admin(&app).await; let account = fixtures::account(&app).await; - let mut conn = post(format!("/api/accounts/{}/api_tokens", account.id)) + let conn = post(format!("/api/accounts/{}/api_tokens", account.id)) .with_api_headers() .with_state(admin) .run_async(&app) .await; assert_response!(conn, 201); - let mut api_token: ApiToken = conn.response_json().await; + let mut api_token: ApiToken = conn.response_json(); let api_token_from_db = api_token.reload(app.db()).await?.unwrap(); let (api_token_from_token, account_from_token) = ApiTokens::load_and_check(&api_token.token.take().unwrap(), app.db()) @@ -232,14 +232,14 @@ mod create { async fn admin_token(app: DivviupApi) -> TestResult { let token = fixtures::admin_token(&app).await; let account = fixtures::account(&app).await; - let mut conn = post(format!("/api/accounts/{}/api_tokens", account.id)) + let conn = post(format!("/api/accounts/{}/api_tokens", account.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; assert_response!(conn, 201); - let mut api_token: ApiToken = conn.response_json().await; + let mut api_token: ApiToken = conn.response_json(); let api_token_from_db = api_token.reload(app.db()).await?.unwrap(); let (api_token_from_token, account_from_token) = ApiTokens::load_and_check(&api_token.token.take().unwrap(), app.db()) @@ -256,14 +256,14 @@ mod create { async fn member_token(app: DivviupApi) -> TestResult { let account = fixtures::account(&app).await; let (_, token) = fixtures::api_token(&app, &account).await; - let mut conn = post(format!("/api/accounts/{}/api_tokens", account.id)) + let conn = post(format!("/api/accounts/{}/api_tokens", account.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; assert_response!(conn, 201); - let mut api_token: ApiToken = conn.response_json().await; + let mut api_token: ApiToken = conn.response_json(); let api_token_from_db = api_token.reload(app.db()).await?.unwrap(); let (api_token_from_token, account_from_token) = ApiTokens::load_and_check(&api_token.token.take().unwrap(), app.db()) @@ -430,14 +430,14 @@ mod update { let (user, account, ..) = fixtures::member(&app).await; let (api_token, _) = fixtures::api_token(&app, &account).await; let name = fixtures::random_name(); - let mut conn = patch(format!("/api/api_tokens/{}", api_token.id)) + let conn = patch(format!("/api/api_tokens/{}", api_token.id)) .with_api_headers() .with_request_json(json!({ "name": name })) .with_state(user) .run_async(&app) .await; assert_status!(conn, 200); - let response: ApiToken = conn.response_json().await; + let response: ApiToken = conn.response_json(); assert_eq!(response.name.unwrap(), name); assert_eq!( api_token.reload(app.db()).await?.unwrap().name.unwrap(), @@ -471,14 +471,14 @@ mod update { let account = fixtures::account(&app).await; let (api_token, _) = fixtures::api_token(&app, &account).await; let name = fixtures::random_name(); - let mut conn = patch(format!("/api/api_tokens/{}", api_token.id)) + let conn = patch(format!("/api/api_tokens/{}", api_token.id)) .with_api_headers() .with_request_json(json!({ "name": name })) .with_state(admin) .run_async(&app) .await; assert_status!(conn, 200); - let response: ApiToken = conn.response_json().await; + let response: ApiToken = conn.response_json(); assert_eq!(response.name.unwrap(), name); assert_eq!( api_token.reload(app.db()).await?.unwrap().name.unwrap(), @@ -494,14 +494,14 @@ mod update { let account = fixtures::account(&app).await; let (api_token, _) = fixtures::api_token(&app, &account).await; let name = fixtures::random_name(); - let mut conn = patch(format!("/api/api_tokens/{}", api_token.id)) + let conn = patch(format!("/api/api_tokens/{}", api_token.id)) .with_api_headers() .with_request_json(json!({ "name": name })) .with_auth_header(token) .run_async(&app) .await; assert_status!(conn, 200); - let response: ApiToken = conn.response_json().await; + let response: ApiToken = conn.response_json(); assert_eq!(response.name.unwrap(), name); assert_eq!( api_token.reload(app.db()).await?.unwrap().name.unwrap(), @@ -516,14 +516,14 @@ mod update { let account = fixtures::account(&app).await; let (api_token, token) = fixtures::api_token(&app, &account).await; let name = fixtures::random_name(); - let mut conn = patch(format!("/api/api_tokens/{}", api_token.id)) + let conn = patch(format!("/api/api_tokens/{}", api_token.id)) .with_api_headers() .with_request_json(json!({ "name": name })) .with_auth_header(token) .run_async(&app) .await; assert_status!(conn, 200); - let response: ApiToken = conn.response_json().await; + let response: ApiToken = conn.response_json(); assert_eq!(response.name.unwrap(), name); assert_eq!( api_token.reload(app.db()).await?.unwrap().name.unwrap(), diff --git a/tests/integration/assets.rs b/tests/integration/assets.rs index 79419914f..329b57d6c 100644 --- a/tests/integration/assets.rs +++ b/tests/integration/assets.rs @@ -46,23 +46,23 @@ async fn api_url(app: DivviupApi) -> TestResult { #[test(harness = set_up)] async fn missing_asset_is_not_cached(app: DivviupApi) -> TestResult { - let mut conn = get("/assets/does-not-exist.js") + let conn = get("/assets/does-not-exist.js") .with_app_host() .run_async(&app) .await; - assert_not_found!(&mut conn); + assert_not_found!(&conn); assert_headers!(&conn, "cache-control" => "no-cache"); Ok(()) } #[test(harness = set_up)] async fn static_files(app: DivviupApi) -> TestResult { - let mut html_conn = get("/").with_app_host().run_async(&app).await; + let html_conn = get("/").with_app_host().run_async(&app).await; assert_ok!(&html_conn); assert_headers!(&html_conn, "cache-control" => "no-cache"); - let html = html_conn.take_response_body_string().unwrap(); + let html = html_conn.response_body_string().unwrap(); let regex = regex::Regex::new(r#"script type="module" crossorigin src="([^"]+)""#).unwrap(); let js_path = ®ex.captures_iter(&html).next().unwrap()[1]; diff --git a/tests/integration/auth.rs b/tests/integration/auth.rs index 448f2c36f..70975d120 100644 --- a/tests/integration/auth.rs +++ b/tests/integration/auth.rs @@ -54,11 +54,7 @@ mod login { let conn = get("/login").with_api_host().run_async(&app).await; let auth_base = app.config().auth_url.join("/authorize")?; assert_status!(conn, 302); - let location = conn - .inner() - .response_headers() - .get_str(KnownHeaderName::Location) - .unwrap(); + let location = conn.header_str(headers::LOCATION.as_str()).unwrap(); assert!(location.starts_with(auth_base.as_ref())); let url = Url::parse(location)?; let query = QueryStrong::parse_strict(url.query().unwrap()).unwrap(); @@ -80,17 +76,13 @@ mod login { .with_user(&user) .run_async(&app) .await; - assert_response!(conn, 302, "", "Location" => app.config().app_url.as_ref()); + assert_response!(conn, 302, "", headers::LOCATION => app.config().app_url.as_ref()); Ok(()) } } #[test(harness = set_up)] async fn logout(app: DivviupApi) -> TestResult { - // Session destruction (Set-Cookie clearing the session cookie) is not - // exercised here: the test harness proxy doesn't propagate cookies, so - // we can't round-trip a live session. Cookie clearing is tower-sessions' - // responsibility; this test only asserts the redirect contract. let user = fixtures::user(); let conn = get("/logout") .with_api_host() @@ -98,10 +90,10 @@ async fn logout(app: DivviupApi) -> TestResult { .run_async(&app) .await; + // TODO(Part 10): verify the session is destroyed (was `conn.session().is_destroyed()`) assert_response!(conn, 302); let location: Url = conn - .response_headers() - .get_str(KnownHeaderName::Location) + .header_str(headers::LOCATION.as_str()) .unwrap() .parse()?; diff --git a/tests/integration/collector_credentials.rs b/tests/integration/collector_credentials.rs index 3ff64df6e..e5e144f30 100644 --- a/tests/integration/collector_credentials.rs +++ b/tests/integration/collector_credentials.rs @@ -16,7 +16,7 @@ mod index { let deleted = fixtures::collector_credential(&app, &account).await; let _deleted = deleted.tombstone().update(app.db()).await.unwrap(); - let mut conn = get(format!( + let conn = get(format!( "/api/accounts/{}/collector_credentials", account.id )) @@ -26,7 +26,7 @@ mod index { .await; assert_ok!(conn); - let collector_credentials: Vec = conn.response_json().await; + let collector_credentials: Vec = conn.response_json(); assert_same_json_representation( &collector_credentials, &vec![collector_credential1, collector_credential2], @@ -64,7 +64,7 @@ mod index { fixtures::collector_credential(&app, &account).await; fixtures::collector_credential(&app, &account).await; - let mut conn = get("/api/accounts/not-an-account/collector_credentials") + let conn = get("/api/accounts/not-an-account/collector_credentials") .with_api_headers() .with_state(user) .run_async(&app) @@ -82,7 +82,7 @@ mod index { let collector_credential1 = fixtures::collector_credential(&app, &account).await; let collector_credential2 = fixtures::collector_credential(&app, &account).await; - let mut conn = get(format!( + let conn = get(format!( "/api/accounts/{}/collector_credentials", account.id )) @@ -92,7 +92,7 @@ mod index { .await; assert_ok!(conn); - let collector_credentials: Vec = conn.response_json().await; + let collector_credentials: Vec = conn.response_json(); assert_same_json_representation( &collector_credentials, &vec![collector_credential1, collector_credential2], @@ -107,7 +107,7 @@ mod index { let collector_credential1 = fixtures::collector_credential(&app, &account).await; let collector_credential2 = fixtures::collector_credential(&app, &account).await; - let mut conn = get(format!( + let conn = get(format!( "/api/accounts/{}/collector_credentials", account.id )) @@ -117,7 +117,7 @@ mod index { .await; assert_ok!(conn); - let collector_credentials: Vec = conn.response_json().await; + let collector_credentials: Vec = conn.response_json(); assert_same_json_representation( &collector_credentials, &vec![collector_credential1, collector_credential2], @@ -132,7 +132,7 @@ mod index { let collector_credential1 = fixtures::collector_credential(&app, &account).await; let collector_credential2 = fixtures::collector_credential(&app, &account).await; - let mut conn = get(format!( + let conn = get(format!( "/api/accounts/{}/collector_credentials", account.id )) @@ -142,7 +142,7 @@ mod index { .await; assert_ok!(conn); - let collector_credentials: Vec = conn.response_json().await; + let collector_credentials: Vec = conn.response_json(); assert_same_json_representation( &collector_credentials, &vec![collector_credential1, collector_credential2], @@ -187,7 +187,7 @@ mod create { let (user, account, ..) = fixtures::member(&app).await; let hpke_config = random_hpke_config(); - let mut conn = post(format!( + let conn = post(format!( "/api/accounts/{}/collector_credentials", account.id )) @@ -197,7 +197,7 @@ mod create { .run_async(&app) .await; assert_response!(conn, 201); - let mut collector_credential: CollectorCredential = conn.response_json().await; + let mut collector_credential: CollectorCredential = conn.response_json(); let token = collector_credential.token.take(); assert!(token.is_some()); assert!(collector_credential.token_hash.is_some()); @@ -240,7 +240,7 @@ mod create { let collector_credential_count_before = CollectorCredentials::find().count(app.db()).await?; - let mut conn = post("/api/accounts/does-not-exist/collector_credentials") + let conn = post("/api/accounts/does-not-exist/collector_credentials") .with_api_headers() .with_state(user) .with_request_json(valid_collector_credential_json(random_hpke_config())) @@ -261,7 +261,7 @@ mod create { async fn admin_not_member(app: DivviupApi) -> TestResult { let (admin, ..) = fixtures::admin(&app).await; let account = fixtures::account(&app).await; - let mut conn = post(format!( + let conn = post(format!( "/api/accounts/{}/collector_credentials", account.id )) @@ -272,7 +272,7 @@ mod create { .await; assert_response!(conn, 201); - let mut collector_credential: CollectorCredential = conn.response_json().await; + let mut collector_credential: CollectorCredential = conn.response_json(); let token = collector_credential.token.take(); assert!(token.is_some()); assert!(collector_credential.token_hash.is_some()); @@ -285,7 +285,7 @@ mod create { async fn admin_token(app: DivviupApi) -> TestResult { let token = fixtures::admin_token(&app).await; let account = fixtures::account(&app).await; - let mut conn = post(format!( + let conn = post(format!( "/api/accounts/{}/collector_credentials", account.id )) @@ -296,7 +296,7 @@ mod create { .await; assert_response!(conn, 201); - let mut collector_credential: CollectorCredential = conn.response_json().await; + let mut collector_credential: CollectorCredential = conn.response_json(); let token = collector_credential.token.take(); assert!(token.is_some()); assert!(collector_credential.token_hash.is_some()); @@ -309,7 +309,7 @@ mod create { async fn member_token(app: DivviupApi) -> TestResult { let account = fixtures::account(&app).await; let (_, token) = fixtures::api_token(&app, &account).await; - let mut conn = post(format!( + let conn = post(format!( "/api/accounts/{}/collector_credentials", account.id )) @@ -320,7 +320,7 @@ mod create { .await; assert_response!(conn, 201); - let mut collector_credential: CollectorCredential = conn.response_json().await; + let mut collector_credential: CollectorCredential = conn.response_json(); let token = collector_credential.token.take(); assert!(token.is_some()); assert!(collector_credential.token_hash.is_some()); @@ -528,7 +528,7 @@ mod update { let (user, account, ..) = fixtures::member(&app).await; let collector_credential = fixtures::collector_credential(&app, &account).await; let name = fixtures::random_name(); - let mut conn = patch(format!( + let conn = patch(format!( "/api/collector_credentials/{}", collector_credential.id )) @@ -538,7 +538,7 @@ mod update { .run_async(&app) .await; assert_status!(conn, 200); - let response: CollectorCredential = conn.response_json().await; + let response: CollectorCredential = conn.response_json(); assert_eq!(response.name.unwrap(), name); assert_eq!( collector_credential @@ -583,7 +583,7 @@ mod update { let account = fixtures::account(&app).await; let collector_credential = fixtures::collector_credential(&app, &account).await; let name = fixtures::random_name(); - let mut conn = patch(format!( + let conn = patch(format!( "/api/collector_credentials/{}", collector_credential.id )) @@ -593,7 +593,7 @@ mod update { .run_async(&app) .await; assert_status!(conn, 200); - let response: CollectorCredential = conn.response_json().await; + let response: CollectorCredential = conn.response_json(); assert_eq!(response.name.unwrap(), name); assert_eq!( collector_credential @@ -614,7 +614,7 @@ mod update { let account = fixtures::account(&app).await; let collector_credential = fixtures::collector_credential(&app, &account).await; let name = fixtures::random_name(); - let mut conn = patch(format!( + let conn = patch(format!( "/api/collector_credentials/{}", collector_credential.id )) @@ -624,7 +624,7 @@ mod update { .run_async(&app) .await; assert_status!(conn, 200); - let response: CollectorCredential = conn.response_json().await; + let response: CollectorCredential = conn.response_json(); assert_eq!(response.name.unwrap(), name); assert_eq!( collector_credential @@ -645,7 +645,7 @@ mod update { let (_, token) = fixtures::api_token(&app, &account).await; let collector_credential = fixtures::collector_credential(&app, &account).await; let name = fixtures::random_name(); - let mut conn = patch(format!( + let conn = patch(format!( "/api/collector_credentials/{}", collector_credential.id )) @@ -655,7 +655,7 @@ mod update { .run_async(&app) .await; assert_status!(conn, 200); - let response: CollectorCredential = conn.response_json().await; + let response: CollectorCredential = conn.response_json(); assert_eq!(response.name.unwrap(), name); assert_eq!( collector_credential diff --git a/tests/integration/jobs.rs b/tests/integration/jobs.rs index 515df17cf..cf4aaf98b 100644 --- a/tests/integration/jobs.rs +++ b/tests/integration/jobs.rs @@ -77,7 +77,7 @@ async fn send_email(app: DivviupApi, client_logs: ClientLogs) -> TestResult { assert!(job.perform(&app.config().into(), app.db()).await?.is_none()); let reset_request = client_logs.logs().last().unwrap().clone(); - assert_eq!(reset_request.method, Method::Post); + assert_eq!(reset_request.method, Method::POST); assert_eq!( reset_request.url, app.config() diff --git a/tests/integration/memberships.rs b/tests/integration/memberships.rs index d0b180d94..90765db75 100644 --- a/tests/integration/memberships.rs +++ b/tests/integration/memberships.rs @@ -8,7 +8,7 @@ mod create { #[test(harness = set_up)] async fn success(app: DivviupApi) -> TestResult { let (user, account, ..) = fixtures::member(&app).await; - let mut conn = post(format!("/api/accounts/{}/memberships", account.id)) + let conn = post(format!("/api/accounts/{}/memberships", account.id)) .with_api_headers() .with_request_json(json!({ "user_email": "someone.else@example.com" })) .with_state(user) @@ -17,7 +17,7 @@ mod create { assert_response!(conn, 201); - let membership: Membership = conn.response_json().await; + let membership: Membership = conn.response_json(); assert_eq!(membership.user_email, "someone.else@example.com"); assert_eq!(membership.account_id, account.id); let membership_id = membership.id; @@ -40,7 +40,7 @@ mod create { let membership_count_before = Memberships::find().count(app.db()).await?; - let mut conn = post(format!("/api/accounts/{}/memberships", account.id)) + let conn = post(format!("/api/accounts/{}/memberships", account.id)) .with_api_headers() .with_request_json(json!({ "user_email": other_user.email })) .with_state(user) @@ -54,7 +54,7 @@ mod create { Memberships::find().count(app.db()).await? ); - let membership: Membership = conn.response_json().await; + let membership: Membership = conn.response_json(); assert_eq!(membership.user_email, other_user.email); assert_eq!(membership.account_id, account.id); assert!(membership.reload(app.db()).await?.is_some()); @@ -67,7 +67,7 @@ mod create { let (user, account, ..) = fixtures::member(&app).await; let membership_count_before = Memberships::find().count(app.db()).await?; - let mut conn = post(format!("/api/accounts/{}/memberships", account.id)) + let conn = post(format!("/api/accounts/{}/memberships", account.id)) .with_api_headers() .with_request_json(json!({ "user_email": "not a valid email" })) .with_state(user) @@ -76,7 +76,7 @@ mod create { assert_response!(conn, 400); - let errors: Value = conn.response_json().await; + let errors: Value = conn.response_json(); assert!(errors.get("user_email").is_some()); assert_eq!( @@ -109,7 +109,7 @@ mod create { .with_state(user) .run_async(&app) .await; - assert_eq!(conn.status().unwrap_or(Status::NotFound), 404); + assert_status!(conn, 404); Ok(()) } @@ -117,7 +117,7 @@ mod create { async fn admin_not_member(app: DivviupApi) -> TestResult { let (user, ..) = fixtures::admin(&app).await; let account = fixtures::account(&app).await; - let mut conn = post(format!("/api/accounts/{}/memberships", account.id)) + let conn = post(format!("/api/accounts/{}/memberships", account.id)) .with_api_headers() .with_request_json(json!({ "user_email": "someone.else@example.com" })) .with_state(user) @@ -126,7 +126,7 @@ mod create { assert_response!(conn, 201); - let membership: Membership = conn.response_json().await; + let membership: Membership = conn.response_json(); assert_eq!(membership.user_email, "someone.else@example.com"); assert_eq!(membership.account_id, account.id); assert!(membership.reload(app.db()).await?.is_some()); @@ -139,14 +139,14 @@ mod create { let token = fixtures::admin_token(&app).await; let account = fixtures::account(&app).await; let email = format!("{}@example.com", fixtures::random_name()); - let mut conn = post(format!("/api/accounts/{}/memberships", account.id)) + let conn = post(format!("/api/accounts/{}/memberships", account.id)) .with_api_headers() .with_request_json(json!({ "user_email": email })) .with_auth_header(token) .run_async(&app) .await; assert_response!(conn, 201); - let membership: Membership = conn.response_json().await; + let membership: Membership = conn.response_json(); assert_eq!(membership.user_email, email); assert_eq!(membership.account_id, account.id); assert!(membership.reload(app.db()).await?.is_some()); @@ -158,14 +158,14 @@ mod create { let account = fixtures::account(&app).await; let (_, token) = fixtures::api_token(&app, &account).await; let email = format!("{}@example.com", fixtures::random_name()); - let mut conn = post(format!("/api/accounts/{}/memberships", account.id)) + let conn = post(format!("/api/accounts/{}/memberships", account.id)) .with_api_headers() .with_request_json(json!({ "user_email": email })) .with_auth_header(token) .run_async(&app) .await; assert_response!(conn, 201); - let membership: Membership = conn.response_json().await; + let membership: Membership = conn.response_json(); assert_eq!(membership.user_email, email); assert_eq!(membership.account_id, account.id); assert!(membership.reload(app.db()).await?.is_some()); @@ -202,13 +202,13 @@ mod index { let (user, account, ..) = fixtures::member(&app).await; fixtures::membership(&app, &account, &fixtures::user()).await; fixtures::membership(&app, &account, &fixtures::user()).await; - let mut conn = get(format!("/api/accounts/{}/memberships", account.id)) + let conn = get(format!("/api/accounts/{}/memberships", account.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; assert_ok!(conn); - let memberships: Vec = conn.response_json().await; + let memberships: Vec = conn.response_json(); assert_eq!(memberships.len(), 3); Ok(()) } @@ -229,7 +229,7 @@ mod index { #[test(harness = set_up)] async fn nonexistant_account(app: DivviupApi) -> TestResult { let (user, ..) = fixtures::member(&app).await; - let mut conn = get("/api/accounts/not-an-id/memberships") + let conn = get("/api/accounts/not-an-id/memberships") .with_api_headers() .with_state(user) .run_async(&app) @@ -245,13 +245,13 @@ mod index { fixtures::membership(&app, &account, &fixtures::user()).await; fixtures::membership(&app, &account, &fixtures::user()).await; - let mut conn = get(format!("/api/accounts/{}/memberships", account.id)) + let conn = get(format!("/api/accounts/{}/memberships", account.id)) .with_api_headers() .with_state(admin) .run_async(&app) .await; assert_ok!(conn); - let memberships: Vec = conn.response_json().await; + let memberships: Vec = conn.response_json(); assert_eq!(memberships.len(), 2); Ok(()) @@ -264,13 +264,13 @@ mod index { fixtures::membership(&app, &account, &fixtures::user()).await; fixtures::membership(&app, &account, &fixtures::user()).await; - let mut conn = get(format!("/api/accounts/{}/memberships", account.id)) + let conn = get(format!("/api/accounts/{}/memberships", account.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; assert_ok!(conn); - let memberships: Vec = conn.response_json().await; + let memberships: Vec = conn.response_json(); assert_eq!(memberships.len(), 2); Ok(()) } @@ -282,13 +282,13 @@ mod index { fixtures::membership(&app, &account, &fixtures::user()).await; fixtures::membership(&app, &account, &fixtures::user()).await; - let mut conn = get(format!("/api/accounts/{}/memberships", account.id)) + let conn = get(format!("/api/accounts/{}/memberships", account.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; assert_ok!(conn); - let memberships: Vec = conn.response_json().await; + let memberships: Vec = conn.response_json(); assert_eq!(memberships.len(), 2); Ok(()) } @@ -335,7 +335,7 @@ mod delete { let (user, ..) = fixtures::member(&app).await; let account = fixtures::account(&app).await; let other_membership = fixtures::membership(&app, &account, &fixtures::user()).await; - let mut conn = delete(format!("/api/memberships/{}", other_membership.id)) + let conn = delete(format!("/api/memberships/{}", other_membership.id)) .with_api_headers() .with_state(user) .run_async(&app) @@ -348,7 +348,7 @@ mod delete { #[test(harness = set_up)] async fn nonexistant_id(app: DivviupApi) -> TestResult { let (user, ..) = fixtures::member(&app).await; - let mut conn = delete("/api/memberships/876b2071-9da8-4bda-bd4c-8d42a3ae7d90") + let conn = delete("/api/memberships/876b2071-9da8-4bda-bd4c-8d42a3ae7d90") .with_api_headers() .with_state(user) .run_async(&app) @@ -427,7 +427,7 @@ mod delete { let account = fixtures::account(&app).await; let membership = fixtures::membership(&app, &account, &fixtures::user()).await; let count_before = Memberships::find().count(app.db()).await?; - let mut conn = delete(format!("/api/memberships/{}", membership.id)) + let conn = delete(format!("/api/memberships/{}", membership.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) diff --git a/tests/integration/tasks.rs b/tests/integration/tasks.rs index d44f84f61..9ebeb9428 100644 --- a/tests/integration/tasks.rs +++ b/tests/integration/tasks.rs @@ -12,13 +12,13 @@ mod index { am.deleted_at = ActiveValue::Set(Some(OffsetDateTime::now_utc())); let _ = am.update(app.db()).await?; - let mut conn = get(format!("/api/accounts/{}/tasks", account.id)) + let conn = get(format!("/api/accounts/{}/tasks", account.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; assert_ok!(conn); - let tasks: Vec = conn.response_json().await; + let tasks: Vec = conn.response_json(); assert!(tasks.is_empty()); Ok(()) } @@ -32,14 +32,14 @@ mod index { let task1 = fixtures::task(&app, &account).await; let task2 = fixtures::task(&app, &account).await; - let mut conn = get(format!("/api/accounts/{}/tasks", account.id)) + let conn = get(format!("/api/accounts/{}/tasks", account.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; assert_ok!(conn); - let tasks: Vec = conn.response_json().await; + let tasks: Vec = conn.response_json(); assert_eq!(vec![task1, task2], tasks); Ok(()) } @@ -70,7 +70,7 @@ mod index { fixtures::task(&app, &account).await; fixtures::task(&app, &account).await; - let mut conn = get("/api/accounts/not-an-account/tasks") + let conn = get("/api/accounts/not-an-account/tasks") .with_api_headers() .with_state(user) .run_async(&app) @@ -89,14 +89,14 @@ mod index { let task1 = fixtures::task(&app, &account).await; let task2 = fixtures::task(&app, &account).await; - let mut conn = get(format!("/api/accounts/{}/tasks", account.id)) + let conn = get(format!("/api/accounts/{}/tasks", account.id)) .with_api_headers() .with_state(admin) .run_async(&app) .await; assert_ok!(conn); - let tasks: Vec = conn.response_json().await; + let tasks: Vec = conn.response_json(); assert_eq!(vec![task1, task2], tasks); Ok(()) } @@ -108,14 +108,14 @@ mod index { let task1 = fixtures::task(&app, &account).await; let task2 = fixtures::task(&app, &account).await; - let mut conn = get(format!("/api/accounts/{}/tasks", account.id)) + let conn = get(format!("/api/accounts/{}/tasks", account.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; assert_ok!(conn); - let tasks: Vec = conn.response_json().await; + let tasks: Vec = conn.response_json(); assert_eq!(vec![task1, task2], tasks); Ok(()) } @@ -127,14 +127,14 @@ mod index { let task1 = fixtures::task(&app, &account).await; let task2 = fixtures::task(&app, &account).await; - let mut conn = get(format!("/api/accounts/{}/tasks", account.id)) + let conn = get(format!("/api/accounts/{}/tasks", account.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; assert_ok!(conn); - let tasks: Vec = conn.response_json().await; + let tasks: Vec = conn.response_json(); assert_eq!(vec![task1, task2], tasks); Ok(()) } @@ -188,7 +188,7 @@ mod create { let (leader, helper) = fixtures::aggregator_pair(&app, &account).await; let collector_credential = fixtures::collector_credential(&app, &account).await; - let mut conn = post(format!("/api/accounts/{}/tasks", account.id)) + let conn = post(format!("/api/accounts/{}/tasks", account.id)) .with_api_headers() .with_state(user) .with_request_json(valid_task_json(&collector_credential, &leader, &helper)) @@ -199,8 +199,8 @@ mod create { let [helper_provisioning, leader_provisioning] = &logs[..] else { panic!("expected exactly two requests"); }; - let helper_task_create = helper_provisioning.state.get::().unwrap(); - let leader_task_create = leader_provisioning.state.get::().unwrap(); + let helper_task_create: TaskCreate = helper_provisioning.request_json(); + let leader_task_create: TaskCreate = leader_provisioning.request_json(); assert_eq!( leader_task_create .collector_auth_token_hash @@ -212,7 +212,7 @@ mod create { assert!(helper_task_create.collector_auth_token_hash.is_none()); assert_response!(conn, 201); - let task: Task = conn.response_json().await; + let task: Task = conn.response_json(); assert_eq!(task.leader_aggregator_id, leader.id); assert_eq!(task.helper_aggregator_id, helper.id); @@ -237,7 +237,7 @@ mod create { leader.features = ActiveValue::Set(Features::default().into()); let leader = leader.update(app.db()).await?; - let mut conn = post(format!("/api/accounts/{}/tasks", account.id)) + let conn = post(format!("/api/accounts/{}/tasks", account.id)) .with_api_headers() .with_state(user) .with_request_json(valid_task_json(&collector_credential, &leader, &helper)) @@ -248,14 +248,14 @@ mod create { let [helper_provisioning, leader_provisioning] = &logs[..] else { panic!("expected exactly two requests"); }; - let helper_task_create = helper_provisioning.state.get::().unwrap(); - let leader_task_create = leader_provisioning.state.get::().unwrap(); + let helper_task_create: TaskCreate = helper_provisioning.request_json(); + let leader_task_create: TaskCreate = leader_provisioning.request_json(); assert!(leader_task_create.collector_auth_token_hash.is_none()); assert!(helper_task_create.collector_auth_token_hash.is_none()); assert_response!(conn, 201); - let task: Task = conn.response_json().await; + let task: Task = conn.response_json(); assert_eq!(task.leader_aggregator_id, leader.id); assert_eq!(task.helper_aggregator_id, helper.id); @@ -277,15 +277,15 @@ mod create { let collector_credential = fixtures::collector_credential(&app, &account).await; let leader = leader.tombstone().update(app.db()).await.unwrap(); - let mut conn = post(format!("/api/accounts/{}/tasks", account.id)) + let conn = post(format!("/api/accounts/{}/tasks", account.id)) .with_api_headers() .with_state(user) .with_request_json(valid_task_json(&collector_credential, &leader, &helper)) .run_async(&app) .await; - assert_response!(conn, 400); - let error: Value = conn.response_json().await; + assert_response!(conn, StatusCode::BAD_REQUEST); + let error: Value = conn.response_json(); assert!(error.get("leader_aggregator_id").is_some()); assert!(client_logs.is_empty()); Ok(()) @@ -301,15 +301,15 @@ mod create { let collector_credential = fixtures::collector_credential(&app, &account).await; let helper = helper.tombstone().update(app.db()).await.unwrap(); - let mut conn = post(format!("/api/accounts/{}/tasks", account.id)) + let conn = post(format!("/api/accounts/{}/tasks", account.id)) .with_api_headers() .with_state(user) .with_request_json(valid_task_json(&collector_credential, &leader, &helper)) .run_async(&app) .await; - assert_response!(conn, 400); - let error: Value = conn.response_json().await; + assert_response!(conn, StatusCode::BAD_REQUEST); + let error: Value = conn.response_json(); assert!(error.get("helper_aggregator_id").is_some()); assert!(client_logs.is_empty()); Ok(()) @@ -319,7 +319,7 @@ mod create { async fn invalid(app: DivviupApi) -> TestResult { let (user, account, ..) = fixtures::member(&app).await; - let mut conn = post(format!("/api/accounts/{}/tasks", account.id)) + let conn = post(format!("/api/accounts/{}/tasks", account.id)) .with_api_headers() .with_state(user) .with_request_json(json!({ @@ -332,8 +332,8 @@ mod create { .run_async(&app) .await; - assert_response!(conn, 400); - let error: Value = conn.response_json().await; + assert_response!(conn, StatusCode::BAD_REQUEST); + let error: Value = conn.response_json(); assert!(error.get("vdaf").is_some()); assert!(error.get("min_batch_size").is_some()); assert!(error.get("time_precision_seconds").is_some()); @@ -367,7 +367,7 @@ mod create { let (leader, helper) = fixtures::aggregator_pair(&app, &account).await; let collector_credential = fixtures::collector_credential(&app, &account).await; - let mut conn = post("/api/accounts/does-not-exist/tasks") + let conn = post("/api/accounts/does-not-exist/tasks") .with_api_headers() .with_state(user) .with_request_json(valid_task_json(&collector_credential, &leader, &helper)) @@ -386,7 +386,7 @@ mod create { let (leader, helper) = fixtures::aggregator_pair(&app, &account).await; let collector_credential = fixtures::collector_credential(&app, &account).await; - let mut conn = post(format!("/api/accounts/{}/tasks", account.id)) + let conn = post(format!("/api/accounts/{}/tasks", account.id)) .with_api_headers() .with_state(admin) .with_request_json(valid_task_json(&collector_credential, &leader, &helper)) @@ -394,7 +394,7 @@ mod create { .await; assert_response!(conn, 201); - let task: Task = conn.response_json().await; + let task: Task = conn.response_json(); assert!(task.reload(app.db()).await?.is_some()); Ok(()) } @@ -406,7 +406,7 @@ mod create { let (leader, helper) = fixtures::aggregator_pair(&app, &account).await; let collector_credential = fixtures::collector_credential(&app, &account).await; - let mut conn = post(format!("/api/accounts/{}/tasks", account.id)) + let conn = post(format!("/api/accounts/{}/tasks", account.id)) .with_api_headers() .with_auth_header(token) .with_request_json(valid_task_json(&collector_credential, &leader, &helper)) @@ -414,7 +414,7 @@ mod create { .await; assert_response!(conn, 201); - let task: Task = conn.response_json().await; + let task: Task = conn.response_json(); assert!(task.reload(app.db()).await?.is_some()); Ok(()) } @@ -426,7 +426,7 @@ mod create { let (leader, helper) = fixtures::aggregator_pair(&app, &account).await; let collector_credential = fixtures::collector_credential(&app, &account).await; let count_before = Tasks::find().count(app.db()).await?; - let mut conn = post(format!("/api/accounts/{}/tasks", account.id)) + let conn = post(format!("/api/accounts/{}/tasks", account.id)) .with_api_headers() .with_auth_header(token) .with_request_json(valid_task_json(&collector_credential, &leader, &helper)) @@ -435,7 +435,7 @@ mod create { let count_after = Tasks::find().count(app.db()).await?; assert_response!(conn, 201); assert_eq!(count_before + 1, count_after); - let task: Task = conn.response_json().await; + let task: Task = conn.response_json(); assert!(task.reload(app.db()).await?.is_some()); Ok(()) } @@ -466,23 +466,20 @@ mod create { mod show { use super::{assert_eq, test, *}; - use divviup_api::{ - entity::aggregator::{Feature, Features}, - FeatureFlags, - }; + use divviup_api::entity::aggregator::{Feature, Features}; use time::Duration; #[test(harness = set_up)] async fn as_member(app: DivviupApi) -> TestResult { let (user, account, ..) = fixtures::member(&app).await; let task = fixtures::task(&app, &account).await; - let mut conn = get(format!("/api/tasks/{}", task.id)) + let conn = get(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; assert_ok!(conn); - let response_task: Task = conn.response_json().await; + let response_task: Task = conn.response_json(); assert_eq!(response_task, task); Ok(()) } @@ -500,7 +497,7 @@ mod show { leader.update(app.db()).await?; let leader = task.leader_aggregator(app.db()).await?; - let mut conn = get(format!("/api/tasks/{}", task.id)) + let conn = get(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_state(user.clone()) .run_async(&app) @@ -517,17 +514,17 @@ mod show { ); let metrics: TaskUploadMetrics = aggregator_api_request.response_json(); - let response_task: Task = conn.response_json().await; + let response_task: Task = conn.response_json(); assert_eq!(metrics, response_task); assert!(response_task.updated_at > task.updated_at); - let mut conn = get(format!("/api/tasks/{}", task.id)) + let conn = get(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - let second_response_task: Task = conn.response_json().await; + let second_response_task: Task = conn.response_json(); assert_eq!(metrics, second_response_task); assert_eq!(second_response_task.updated_at, response_task.updated_at); @@ -535,7 +532,10 @@ mod show { } #[test(harness = with_client_logs)] - async fn metrics_refresh_disabled(app: DivviupApi, client_logs: ClientLogs) -> TestResult { + async fn no_metrics_refresh_without_aggregator_features( + app: DivviupApi, + client_logs: ClientLogs, + ) -> TestResult { let (user, account, ..) = fixtures::member(&app).await; let task = fixtures::task(&app, &account).await; let mut task = task.into_active_model(); @@ -544,10 +544,6 @@ mod show { let conn = get(format!("/api/tasks/{}", task.id)) .with_api_headers() - .with_state(FeatureFlags { - metrics_refresh_enabled: false, - ssrf_validation_enabled: false, - }) .with_state(user.clone()) .run_async(&app) .await; @@ -578,13 +574,13 @@ mod show { let (admin, ..) = fixtures::admin(&app).await; let account = fixtures::account(&app).await; let task = fixtures::task(&app, &account).await; - let mut conn = get(format!("/api/tasks/{}", task.id)) + let conn = get(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_state(admin) .run_async(&app) .await; assert_ok!(conn); - let response_task: Task = conn.response_json().await; + let response_task: Task = conn.response_json(); assert_eq!(response_task, task); Ok(()) } @@ -606,13 +602,13 @@ mod show { let token = fixtures::admin_token(&app).await; let account = fixtures::account(&app).await; let task = fixtures::task(&app, &account).await; - let mut conn = get(format!("/api/tasks/{}", task.id)) + let conn = get(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; assert_ok!(conn); - let response_task: Task = conn.response_json().await; + let response_task: Task = conn.response_json(); assert_eq!(response_task, task); Ok(()) } @@ -622,13 +618,13 @@ mod show { let account = fixtures::account(&app).await; let (_, token) = fixtures::api_token(&app, &account).await; let task = fixtures::task(&app, &account).await; - let mut conn = get(format!("/api/tasks/{}", task.id)) + let conn = get(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; assert_ok!(conn); - let response_task: Task = conn.response_json().await; + let response_task: Task = conn.response_json(); assert_eq!(response_task, task); Ok(()) } @@ -660,14 +656,14 @@ mod update { let task = fixtures::task(&app, &account).await; let new_name = format!("new name {}", fixtures::random_name()); - let mut conn = patch(format!("/api/tasks/{}", task.id)) + let conn = patch(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_request_json(json!({ "name": &new_name })) .with_state(user) .run_async(&app) .await; assert_ok!(conn); - let response_task: Task = conn.response_json().await; + let response_task: Task = conn.response_json(); assert_eq!(response_task.name, new_name); let task_reload = task.reload(app.db()).await?.unwrap(); @@ -685,14 +681,14 @@ mod update { let (user, account, ..) = fixtures::member(&app).await; let task = fixtures::task(&app, &account).await; - let mut conn = patch(format!("/api/tasks/{}", task.id)) + let conn = patch(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_request_json(json!({ "name": "" })) .with_state(user) .run_async(&app) .await; - assert_response!(conn, 400); - let errors: Value = conn.response_json().await; + assert_response!(conn, StatusCode::BAD_REQUEST); + let errors: Value = conn.response_json(); assert!(errors.get("name").is_some()); assert_eq!( @@ -710,14 +706,14 @@ mod update { // Set the expiration. let now = OffsetDateTime::now_utc(); - let mut conn = patch(format!("/api/tasks/{}", task.id)) + let conn = patch(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_request_json(json!({ "expiration": now.format(&Rfc3339).unwrap() })) .with_state(user.clone()) .run_async(&app) .await; assert_ok!(conn); - let response_task: Task = conn.response_json().await; + let response_task: Task = conn.response_json(); assert_eq!(response_task.expiration, Some(now)); let task_reload = task.reload(app.db()).await?.unwrap(); @@ -731,14 +727,14 @@ mod update { ); // Unset the expiration. - let mut conn = patch(format!("/api/tasks/{}", task.id)) + let conn = patch(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_request_json(json!({ "expiration": null })) .with_state(user.clone()) .run_async(&app) .await; assert_ok!(conn); - let response_task: Task = conn.response_json().await; + let response_task: Task = conn.response_json(); assert_eq!(response_task.expiration, None); let task_reload = task.reload(app.db()).await?.unwrap(); @@ -754,7 +750,7 @@ mod update { // Set both name and expiration. let now = OffsetDateTime::now_utc(); let new_name = format!("new name {}", fixtures::random_name()); - let mut conn = patch(format!("/api/tasks/{}", task.id)) + let conn = patch(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_request_json( json!({ "expiration": now.format(&Rfc3339).unwrap(), "name": new_name}), @@ -763,7 +759,7 @@ mod update { .run_async(&app) .await; assert_ok!(conn); - let response_task: Task = conn.response_json().await; + let response_task: Task = conn.response_json(); assert_eq!(response_task.expiration, Some(now)); assert_eq!(response_task.name, new_name); let task_reload = task.reload(app.db()).await?.unwrap(); @@ -784,7 +780,7 @@ mod update { .with_state(user) .run_async(&app) .await; - assert_eq!(conn.status(), Some(Status::BadRequest)); + assert_response!(conn, StatusCode::BAD_REQUEST); // Task should be unchanged. let task_reload = task.reload(app.db()).await?.unwrap(); @@ -821,14 +817,14 @@ mod update { let task = fixtures::task(&app, &account).await; let new_name = format!("new name {}", fixtures::random_name()); - let mut conn = patch(format!("/api/tasks/{}", task.id)) + let conn = patch(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_request_json(json!({ "name": &new_name })) .with_state(admin) .run_async(&app) .await; assert_ok!(conn); - let response_task: Task = conn.response_json().await; + let response_task: Task = conn.response_json(); assert_eq!(response_task.name, new_name); assert_eq!(task.reload(app.db()).await?.unwrap().name, new_name); Ok(()) @@ -854,14 +850,14 @@ mod update { let task = fixtures::task(&app, &account).await; let new_name = format!("new name {}", fixtures::random_name()); - let mut conn = patch(format!("/api/tasks/{}", task.id)) + let conn = patch(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_request_json(json!({ "name": &new_name })) .with_auth_header(token) .run_async(&app) .await; assert_ok!(conn); - let response_task: Task = conn.response_json().await; + let response_task: Task = conn.response_json(); assert_eq!(response_task.name, new_name); assert_eq!(task.reload(app.db()).await?.unwrap().name, new_name); Ok(()) @@ -874,14 +870,14 @@ mod update { let task = fixtures::task(&app, &account).await; let new_name = format!("new name {}", fixtures::random_name()); - let mut conn = patch(format!("/api/tasks/{}", task.id)) + let conn = patch(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_request_json(json!({ "name": &new_name })) .with_auth_header(token) .run_async(&app) .await; assert_ok!(conn); - let response_task: Task = conn.response_json().await; + let response_task: Task = conn.response_json(); assert_eq!(response_task.name, new_name); assert_eq!(task.reload(app.db()).await?.unwrap().name, new_name); Ok(()) @@ -909,9 +905,8 @@ mod update { } mod delete { - use divviup_api::DivviupApi; + use axum::Router; use janus_messages::Time as JanusTime; - use test_support::tracing::install_test_trace_subscriber; use super::{assert_eq, test, *}; @@ -947,7 +942,7 @@ mod delete { .with_state(user.clone()) .run_async(&app) .await; - assert_status!(conn, Status::NoContent); + assert_status!(conn, StatusCode::NO_CONTENT); let task_reload = task.reload(app.db()).await?.unwrap(); assert!(task_reload.expiration.is_some()); assert!(task_reload.expiration <= Some(OffsetDateTime::now_utc())); @@ -962,7 +957,7 @@ mod delete { .with_state(user) .run_async(&app) .await; - assert_status!(conn, Status::NoContent); + assert_status!(conn, StatusCode::NO_CONTENT); let task_reload_2 = task.reload(app.db()).await?.unwrap(); assert_eq!(task_reload.deleted_at, task_reload_2.deleted_at); assert_eq!(client_logs_len, client_logs.len()); @@ -971,20 +966,9 @@ mod delete { #[tokio::test] async fn force() -> TestResult { - install_test_trace_subscriber(); - let client_logs = ClientLogs::default(); - let mut app = DivviupApi::new( - config(( - client_logs.clone(), - // Stub out aggregator API mocks to simulate a FUBAR aggregator. - |conn: Conn| async move { conn.with_status(Status::InternalServerError) }, - )) - .await, - ) - .await; - set_up_schema(app.db()).await; - let mut info = "testing".into(); - app.init(&mut info).await; + // Stub out aggregator API mocks to simulate a FUBAR aggregator. + let mock = Router::new().fallback(|| async { StatusCode::INTERNAL_SERVER_ERROR }); + let (app, client_logs) = build_test_app_with_mock(mock).await; let (user, account, ..) = fixtures::member(&app).await; let task = fixtures::task(&app, &account).await; @@ -994,7 +978,7 @@ mod delete { .with_state(user.clone()) .run_async(&app) .await; - assert_status!(conn, Status::NoContent); + assert_status!(conn, StatusCode::NO_CONTENT); let task_reload = task.reload(app.db()).await?.unwrap(); assert!(task_reload.expiration.is_some()); assert!(task_reload.expiration <= Some(OffsetDateTime::now_utc())); @@ -1005,8 +989,8 @@ mod delete { let [leader, helper] = &logs[..] else { panic!("expected exactly two requests"); }; - assert_eq!(leader.response_status, Status::InternalServerError); - assert_eq!(helper.response_status, Status::InternalServerError); + assert_eq!(leader.response_status, StatusCode::INTERNAL_SERVER_ERROR); + assert_eq!(helper.response_status, StatusCode::INTERNAL_SERVER_ERROR); Ok(()) } @@ -1024,7 +1008,7 @@ mod delete { .with_state(user.clone()) .run_async(&app) .await; - assert_status!(conn, Status::NoContent); + assert_status!(conn, StatusCode::NO_CONTENT); let task_reload = task.reload(app.db()).await?.unwrap(); assert!(task_reload.expiration.is_some()); assert!(task_reload.expiration <= Some(OffsetDateTime::now_utc())); @@ -1047,7 +1031,7 @@ mod delete { .with_state(user) .run_async(&app) .await; - assert_status!(conn, Status::NoContent); + assert_status!(conn, StatusCode::NO_CONTENT); let task_reload = task.reload(app.db()).await?.unwrap(); // Shouldn't re-expire an already expired task. assert_eq!(task_reload.expiration, task.expiration); @@ -1086,7 +1070,7 @@ mod delete { .with_state(admin) .run_async(&app) .await; - assert_status!(conn, Status::NoContent); + assert_status!(conn, StatusCode::NO_CONTENT); let task_reload = task.reload(app.db()).await?.unwrap(); assert!(task_reload.expiration.is_some()); assert!(task_reload.expiration <= Some(OffsetDateTime::now_utc())); @@ -1118,7 +1102,7 @@ mod delete { .with_auth_header(token) .run_async(&app) .await; - assert_status!(conn, Status::NoContent); + assert_status!(conn, StatusCode::NO_CONTENT); let task_reload = task.reload(app.db()).await?.unwrap(); assert!(task_reload.expiration.is_some()); assert!(task_reload.expiration <= Some(OffsetDateTime::now_utc())); @@ -1138,7 +1122,7 @@ mod delete { .with_auth_header(token) .run_async(&app) .await; - assert_status!(conn, Status::NoContent); + assert_status!(conn, StatusCode::NO_CONTENT); let task_reload = task.reload(app.db()).await?.unwrap(); assert!(task_reload.expiration.is_some()); assert!(task_reload.expiration <= Some(OffsetDateTime::now_utc())); diff --git a/tests/integration/tls_smoke_test.rs b/tests/integration/tls_smoke_test.rs index 312355596..a403c7266 100644 --- a/tests/integration/tls_smoke_test.rs +++ b/tests/integration/tls_smoke_test.rs @@ -1,45 +1,59 @@ use rcgen::generate_simple_self_signed; -use test_support::{assert_eq, *}; -use tokio::{net::TcpListener, spawn}; -use trillium_client::Client; -use trillium_http::Stopper; -use trillium_rustls::{rustls::RootCertStore, RustlsAcceptor, RustlsConfig}; -use trillium_tokio::ClientConfig; +use rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer}; +use std::sync::Arc; +use tokio::{ + io::{AsyncReadExt, AsyncWriteExt}, + net::TcpListener, +}; +use tokio_rustls::TlsAcceptor; #[tokio::test] async fn https_connection() { // Choose aws-lc-rs as the default rustls crypto provider. This is what's currently enabled by // the default Cargo feature. Specifying a default provider here prevents runtime errors if // another dependency also enables the ring feature. - let _ = trillium_rustls::rustls::crypto::aws_lc_rs::default_provider().install_default(); + let _ = rustls::crypto::aws_lc_rs::default_provider().install_default(); let self_signed = generate_simple_self_signed(["localhost".into()]).unwrap(); - let stopper = Stopper::new(); - let listener = TcpListener::bind("localhost:0").await.unwrap(); - let local_addr = listener.local_addr().unwrap(); - let server_config = trillium_tokio::config() - .with_acceptor(RustlsAcceptor::from_single_cert( - self_signed.cert.pem().as_bytes(), - self_signed.signing_key.serialize_pem().as_bytes(), - )) - .without_signals() - .with_stopper(stopper.clone()) - .with_prebound_server(listener); - spawn(server_config.run_async(|conn: Conn| async { conn.ok("") })); - - let mut root_store = RootCertStore::empty(); - root_store.add_parsable_certificates([self_signed.cert.der().clone()]); - - let client = Client::new(RustlsConfig::new( - trillium_rustls::rustls::ClientConfig::builder() - .with_root_certificates(root_store) - .with_no_client_auth(), - ClientConfig::default(), + let cert_der = CertificateDer::from(self_signed.cert.der().to_vec()); + let key_der = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from( + self_signed.signing_key.serialize_der(), )); - let url = format!("https://localhost:{}/", local_addr.port()); - let conn = client.get(url).await.unwrap(); - assert_status!(conn, 200); - stopper.stop(); + let mut server_config = rustls::ServerConfig::builder() + .with_no_client_auth() + .with_single_cert(vec![cert_der.clone()], key_der) + .unwrap(); + server_config.alpn_protocols = vec![b"http/1.1".to_vec()]; + let tls_acceptor = TlsAcceptor::from(Arc::new(server_config)); + + // Bind to "localhost" (not Ipv6Addr::LOCALHOST) so the address matches the + // self-signed certificate's SAN and the reqwest client's hostname validation. + let listener = TcpListener::bind("localhost:0").await.unwrap(); + let port = listener.local_addr().unwrap().port(); + + tokio::spawn(async move { + let (tcp_stream, _) = listener.accept().await.unwrap(); + let mut tls_stream = tls_acceptor.accept(tcp_stream).await.unwrap(); + let mut buf = vec![0u8; 4096]; + let _ = tls_stream.read(&mut buf).await.unwrap(); + let response = b"HTTP/1.1 200 OK\r\ncontent-length: 0\r\nconnection: close\r\n\r\n"; + tls_stream.write_all(response).await.unwrap(); + tls_stream.shutdown().await.unwrap(); + }); + + let root_cert = reqwest::tls::Certificate::from_der(cert_der.as_ref()).unwrap(); + let client = reqwest::Client::builder() + .add_root_certificate(root_cert) + .http1_only() + .build() + .unwrap(); + + let resp = client + .get(format!("https://localhost:{port}/")) + .send() + .await + .unwrap(); + assert_eq!(resp.status(), 200); } diff --git a/tests/integration/users.rs b/tests/integration/users.rs index 2606e6bfb..63a53a92d 100644 --- a/tests/integration/users.rs +++ b/tests/integration/users.rs @@ -6,13 +6,13 @@ mod get_users_me { #[test(harness = set_up)] async fn as_a_logged_in_user(app: DivviupApi) -> TestResult { let user = fixtures::user(); - let mut conn = get("/api/users/me") + let conn = get("/api/users/me") .with_api_headers() .with_state(user.clone()) .run_async(&app) .await; - let mut response_user: User = conn.response_json().await; + let mut response_user: User = conn.response_json(); assert!(!response_user.is_admin()); response_user.admin = None; // for equality comparison @@ -23,13 +23,13 @@ mod get_users_me { #[test(harness = set_up)] async fn as_an_admin(app: DivviupApi) -> TestResult { let (admin, ..) = fixtures::admin(&app).await; - let mut conn = get("/api/users/me") + let conn = get("/api/users/me") .with_api_headers() .with_state(admin.clone()) .run_async(&app) .await; - let mut response_user: User = conn.response_json().await; + let mut response_user: User = conn.response_json(); assert!(response_user.is_admin()); response_user.admin = None; // for equality comparison @@ -40,13 +40,13 @@ mod get_users_me { #[test(harness = set_up)] async fn as_integration_testing_user(app: DivviupApi) -> TestResult { let user = User::for_integration_testing(); - let mut conn = get("/api/users/me") + let conn = get("/api/users/me") .with_api_headers() .with_state(user.clone()) .run_async(&app) .await; - let response_user: User = conn.response_json().await; + let response_user: User = conn.response_json(); assert!(response_user.is_admin()); assert_eq!(user, response_user); From 59da00f96ff0a691aa8f9e0f6e30370f684e1f8f Mon Sep 17 00:00:00 2001 From: "J.C. Jones" Date: Mon, 18 May 2026 16:15:14 -0700 Subject: [PATCH 2/7] Use parts.extensions instead of header injection for user injection --- .github/workflows/rust.yml | 2 -- Cargo.toml | 1 - src/handler.rs | 29 ++--------------------------- test-support/Cargo.toml | 2 +- test-support/src/lib.rs | 15 +++++++++------ 5 files changed, 12 insertions(+), 37 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index a075d0133..207c19f84 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -40,8 +40,6 @@ jobs: continue-on-error: true - name: Format run: cargo fmt --all -- --check - - name: check +api-mocks - run: cargo check --features api-mocks --locked - name: check +otlp-trace run: cargo check --features otlp-trace --locked - name: check cli +default +admin diff --git a/Cargo.toml b/Cargo.toml index d87c08605..b154810c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,6 @@ default-run = "divviup_api_bin" [features] default = [] integration-testing = [] -test-header-injection = [] otlp-trace = ["opentelemetry/trace", "opentelemetry-otlp", "opentelemetry_sdk/trace"] [dependencies] diff --git a/src/handler.rs b/src/handler.rs index 30423a36f..0c3c8239d 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -128,9 +128,6 @@ pub async fn build_app(config: Config) -> BuiltApp { #[cfg(feature = "integration-testing")] let middleware = middleware.layer(axum::middleware::from_fn(inject_integration_testing_user)); - #[cfg(feature = "test-header-injection")] - let middleware = middleware.layer(axum::middleware::from_fn(inject_test_header_user)); - #[cfg(assets)] let middleware = middleware.layer(axum::middleware::from_fn_with_state( assets::AssetConfig::new(&config.api_url, &config.app_url), @@ -149,9 +146,8 @@ pub async fn build_app(config: Config) -> BuiltApp { BuiltApp { router, db, config } } -/// Axum middleware that unconditionally injects an admin -/// [`User`](crate::User) into every request, unless one is already present -/// (e.g. from the `test-header-injection` middleware). +/// Axum middleware that injects an admin [`User`](crate::User) into every +/// request that doesn't already have one in extensions. /// /// Only compiled under `--features integration-testing` (enabled by /// `compose.dev.override.yaml`). Never compiled into deployed builds. @@ -167,24 +163,3 @@ async fn inject_integration_testing_user( } next.run(request).await } - -/// Axum middleware that reads an `X-Integration-Testing-User` header and -/// injects the decoded [`User`](crate::User) into request extensions. -/// Used by `test-support` to impersonate specific users in tests. -/// -/// Only compiled under `--features test-header-injection` (enabled by -/// `test-support`). Never compiled into deployed builds. -#[cfg(feature = "test-header-injection")] -async fn inject_test_header_user( - mut request: axum::extract::Request, - next: axum::middleware::Next, -) -> axum::response::Response { - if let Some(user) = request - .headers() - .get("x-integration-testing-user") - .and_then(|v| serde_json::from_slice::(v.as_bytes()).ok()) - { - request.extensions_mut().insert(user); - } - next.run(request).await -} diff --git a/test-support/Cargo.toml b/test-support/Cargo.toml index 20c252cb3..68a10124f 100644 --- a/test-support/Cargo.toml +++ b/test-support/Cargo.toml @@ -12,7 +12,7 @@ bytes = "1" fastrand = "2.4.1" http-body-util = "0.1" time = "0.3.47" -divviup-api = { workspace = true, features = ["test-header-injection"] } +divviup-api = { workspace = true } serde = "1.0.228" serde_json = "1.0.149" thiserror = "2.0.18" diff --git a/test-support/src/lib.rs b/test-support/src/lib.rs index be77326ca..f9c9b9c4c 100644 --- a/test-support/src/lib.rs +++ b/test-support/src/lib.rs @@ -267,6 +267,7 @@ pub struct TestRequest { uri: String, headers: HeaderMap, body: Vec, + user: Option, } pub fn get(path: impl Into) -> TestRequest { @@ -296,6 +297,7 @@ impl TestRequest { uri: uri.into(), headers: HeaderMap::new(), body: Vec::new(), + user: None, } } @@ -343,11 +345,9 @@ impl TestRequest { self.with_request_header(header::AUTHORIZATION, token) } - pub fn with_user(self, user: &User) -> Self { - self.with_request_header( - "x-integration-testing-user", - serde_json::to_string(user).unwrap(), - ) + pub fn with_user(mut self, user: &User) -> Self { + self.user = Some(user.clone()); + self } /// In Part 10, refactor all callers from `.with_state(user)` to `.with_user(&user)`. @@ -370,7 +370,10 @@ impl TestRequest { for (name, value) in &self.headers { builder = builder.header(name, value); } - let request = builder.body(body).expect("failed to build request"); + let mut request = builder.body(body).expect("failed to build request"); + if let Some(user) = self.user { + request.extensions_mut().insert(user); + } let response = app .router .clone() From 616fd33b5b809043cad5606560ccb5ac02131217 Mon Sep 17 00:00:00 2001 From: "J.C. Jones" Date: Tue, 19 May 2026 08:25:27 -0700 Subject: [PATCH 3/7] Review updates --- src/clients.rs | 8 +++++--- src/handler.rs | 5 +++++ src/handler/oauth2.rs | 2 +- src/queue/job.rs | 4 +--- src/user.rs | 5 ++--- tests/integration/auth.rs | 8 ++++++-- 6 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/clients.rs b/src/clients.rs index c0f8c7a82..c17bee1a7 100644 --- a/src/clients.rs +++ b/src/clients.rs @@ -37,6 +37,10 @@ impl HttpClient { } } + pub(crate) fn reqwest_client(&self) -> &reqwest::Client { + &self.inner + } + pub fn with_base(mut self, url: impl Into) -> Self { let mut url = url.into(); if !url.path().ends_with('/') { @@ -139,7 +143,6 @@ impl HttpClient { #[derive(Debug)] pub struct HttpStatusNotSuccess { - pub method: String, pub url: Url, pub status: Option, pub body: String, @@ -147,7 +150,7 @@ pub struct HttpStatusNotSuccess { #[derive(thiserror::Error, Debug)] pub enum ClientError { - #[error("unexpected http status {} {} {:?}: {}", .0.method, .0.url, .0.status, .0.body)] + #[error("unexpected http status {} {:?}: {}", .0.url, .0.status, .0.body)] HttpStatusNotSuccess(Box), #[error(transparent)] @@ -178,7 +181,6 @@ impl ResponseExt for reqwest::Response { let body = self.text().await.unwrap_or_default(); Err(ClientError::HttpStatusNotSuccess(Box::new( HttpStatusNotSuccess { - method: String::new(), url, status: Some(status), body, diff --git a/src/handler.rs b/src/handler.rs index 0c3c8239d..2d9d05f27 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -114,6 +114,11 @@ pub async fn build_app(config: Config) -> BuiltApp { // TODO(Part 10): add OpenTelemetry HTTP metrics middleware. The deleted // trillium-opentelemetry handler provided http.server.* histograms and // optional OTLP per-request spans; TraceLayer only emits tracing events. + // + // TODO(Part 10): restore X-Forwarded-For / Forwarded header propagation. + // The deleted trillium-forwarding::Forwarding::trust_always() updated the + // peer IP from proxy headers so trace spans logged the client IP. Without + // it, TraceLayer records the proxy/load-balancer IP instead. let middleware = ServiceBuilder::new() .layer(TraceLayer::new_for_http()) .layer(DefaultBodyLimit::max(MAX_REQUEST_BODY_SIZE)) diff --git a/src/handler/oauth2.rs b/src/handler/oauth2.rs index 0099911a2..44aa63429 100644 --- a/src/handler/oauth2.rs +++ b/src/handler/oauth2.rs @@ -235,7 +235,7 @@ impl OauthClient { .set_token_uri(TokenUrl::from_url(config.token_url.clone())) .set_redirect_uri(RedirectUrl::from_url(config.redirect_url.clone())); - let reqwest_client = reqwest::Client::new(); + let reqwest_client = config.http_client.reqwest_client().clone(); Self(Arc::new(OauthClientInner { oauth_config: config.clone(), diff --git a/src/queue/job.rs b/src/queue/job.rs index b7ebde86d..5e218605f 100644 --- a/src/queue/job.rs +++ b/src/queue/job.rs @@ -29,9 +29,8 @@ pub enum JobError { #[error("{0}")] ClientOther(String), - #[error("unexpected http status {method} {url} {status:?}: {body}")] + #[error("unexpected http status {url} {status:?}: {body}")] HttpStatusNotSuccess { - method: String, url: Url, status: Option, body: String, @@ -57,7 +56,6 @@ impl From for JobError { fn from(value: ClientError) -> Self { match value { ClientError::HttpStatusNotSuccess(e) => Self::HttpStatusNotSuccess { - method: e.method, url: e.url, status: e.status.map(|s| s.as_u16()), body: e.body, diff --git a/src/user.rs b/src/user.rs index a5089b474..459509394 100644 --- a/src/user.rs +++ b/src/user.rs @@ -80,15 +80,14 @@ impl User { Db: FromRef, S: Send + Sync, { - // Cache: return early if already extracted and admin-populated. - // Cleanup after Trillium is removed. + // Return early if already extracted and admin-populated if let Some(user) = parts.extensions.get::() { if user.admin.is_some() { return Ok(Some(user.clone())); } } - // Get a mutable user, preferring extensions and falling back to session. + // Prefer extensions, fall back to session. let mut user = if let Some(user) = parts.extensions.remove::() { user } else { diff --git a/tests/integration/auth.rs b/tests/integration/auth.rs index 70975d120..0f6360a54 100644 --- a/tests/integration/auth.rs +++ b/tests/integration/auth.rs @@ -1,4 +1,4 @@ -use test_support::{assert_eq, test, *}; +use test_support::{assert_eq, entity::session, test, *}; #[test(harness = set_up)] async fn first_use_of_a_token_updates_last_used_at(app: DivviupApi) -> TestResult { @@ -90,8 +90,12 @@ async fn logout(app: DivviupApi) -> TestResult { .run_async(&app) .await; - // TODO(Part 10): verify the session is destroyed (was `conn.session().is_destroyed()`) assert_response!(conn, 302); + + // session.flush() in the logout handler should delete any session that was + // created during the request — verify no sessions remain in the store. + let session_count = session::Entity::find().count(app.db()).await?; + assert_eq!(session_count, 0, "session should be destroyed after logout"); let location: Url = conn .header_str(headers::LOCATION.as_str()) .unwrap() From 4b39e9a773523df8f479aa00fdc099c9053c7d88 Mon Sep 17 00:00:00 2001 From: "J.C. Jones" Date: Wed, 20 May 2026 09:09:43 -0700 Subject: [PATCH 4/7] Review updates for everything but method --- client/tests/integration/harness.rs | 1 + test-support/src/api_mocks.rs | 3 +- test-support/src/client_logs.rs | 13 +- test-support/src/fixtures.rs | 2 +- test-support/src/lib.rs | 61 ++-- tests/integration/accounts.rs | 114 +++---- tests/integration/admin_queue.rs | 10 +- tests/integration/aggregator_client.rs | 28 +- tests/integration/aggregators.rs | 350 ++++++++++----------- tests/integration/api_tokens.rs | 136 ++++---- tests/integration/assets.rs | 6 +- tests/integration/auth.rs | 24 +- tests/integration/collector_credentials.rs | 136 ++++---- tests/integration/memberships.rs | 116 +++---- tests/integration/tasks.rs | 260 +++++++-------- tests/integration/users.rs | 12 +- 16 files changed, 634 insertions(+), 638 deletions(-) diff --git a/client/tests/integration/harness.rs b/client/tests/integration/harness.rs index cedacd721..e6efe1499 100644 --- a/client/tests/integration/harness.rs +++ b/client/tests/integration/harness.rs @@ -35,6 +35,7 @@ where Out: Termination, { with_client_logs(move |app, _api_logs| async move { + install_test_trace_subscriber(); let client_logs = ClientLogs::default(); let router = app .router() diff --git a/test-support/src/api_mocks.rs b/test-support/src/api_mocks.rs index 0d1954b48..4fbdbfaff 100644 --- a/test-support/src/api_mocks.rs +++ b/test-support/src/api_mocks.rs @@ -1,5 +1,6 @@ use super::{client_logs::client_logs_middleware, ClientLogs, AUTH0_URL, POSTMARK_URL}; use axum::{middleware, Router}; +use divviup_api::api_mocks::ApiMocks as DivviupApiMocks; #[derive(Debug)] pub struct ApiMocks { @@ -17,7 +18,7 @@ impl ApiMocks { pub fn new() -> Self { let client_logs = ClientLogs::default(); - let inner = divviup_api::api_mocks::ApiMocks::new(POSTMARK_URL, AUTH0_URL); + let inner = DivviupApiMocks::new(POSTMARK_URL, AUTH0_URL); let router = inner.into_router().layer(middleware::from_fn_with_state( client_logs.clone(), client_logs_middleware, diff --git a/test-support/src/client_logs.rs b/test-support/src/client_logs.rs index e67fc0452..af404b701 100644 --- a/test-support/src/client_logs.rs +++ b/test-support/src/client_logs.rs @@ -90,14 +90,11 @@ fn reconstruct_url(req: &Request) -> Url { .get("host") .and_then(|h| h.to_str().ok()) .unwrap_or("unknown"); - let path = req.uri().path(); - let query = req.uri().query(); - let url_str = match query { - Some(q) => format!("https://{host}{path}?{q}"), - None => format!("https://{host}{path}"), - }; - Url::parse(&url_str) - .unwrap_or_else(|e| panic!("reconstruct_url: malformed Host or path: {url_str}: {e}")) + let mut url = Url::parse(&format!("https://{host}")) + .unwrap_or_else(|e| panic!("reconstruct_url: malformed Host: {host}: {e}")); + url.set_path(req.uri().path()); + url.set_query(req.uri().query()); + url } pub async fn client_logs_middleware( diff --git a/test-support/src/fixtures.rs b/test-support/src/fixtures.rs index 35c1733c9..062259aa8 100644 --- a/test-support/src/fixtures.rs +++ b/test-support/src/fixtures.rs @@ -204,7 +204,7 @@ pub async fn api_token(app: &DivviupApi, account: &Account) -> (ApiToken, Header let api_token = api_token.insert(app.db()).await.unwrap(); ( api_token, - HeaderValue::from_str(&format!("Bearer {token}")).unwrap(), + HeaderValue::try_from(format!("Bearer {token}")).unwrap(), ) } diff --git a/test-support/src/lib.rs b/test-support/src/lib.rs index f9c9b9c4c..df47c4017 100644 --- a/test-support/src/lib.rs +++ b/test-support/src/lib.rs @@ -251,6 +251,9 @@ where }) } +/// Create a single-threaded tokio runtime and run `future` to completion. +/// The `test_harness::test` macro calls harness functions (e.g. `set_up`) +/// from a synchronous context, so this is the async entry point for tests. pub fn block_on, T>(future: F) -> T { runtime::Builder::new_current_thread() .enable_all() @@ -448,21 +451,21 @@ impl TestResponse { #[macro_export] macro_rules! assert_ok { - ($conn:expr) => {{ - let ref __conn = $conn; - assert_eq!(__conn.status(), $crate::StatusCode::OK); + ($resp:expr) => {{ + let ref __resp = $resp; + assert_eq!(__resp.status(), $crate::StatusCode::OK); }}; - ($conn:expr, $body:expr $(, $header_name:expr => $header_val:expr)*) => {{ - let ref __conn = $conn; - assert_eq!(__conn.status(), $crate::StatusCode::OK); + ($resp:expr, $body:expr $(, $header_name:expr => $header_val:expr)*) => {{ + let ref __resp = $resp; + assert_eq!(__resp.status(), $crate::StatusCode::OK); assert_eq!( - __conn.response_body_string().unwrap_or_default(), + __resp.response_body_string().unwrap_or_default(), $body ); $( assert_eq!( - __conn.header_str($header_name), + __resp.header_str($header_name), Some($header_val), concat!("expected header ", stringify!($header_name)), ); @@ -472,32 +475,32 @@ macro_rules! assert_ok { #[macro_export] macro_rules! assert_not_found { - ($conn:expr) => {{ - let ref __conn = $conn; - assert_eq!(__conn.status(), $crate::StatusCode::NOT_FOUND); - assert_eq!(__conn.response_body_string().unwrap_or_default(), ""); + ($resp:expr) => {{ + let ref __resp = $resp; + assert_eq!(__resp.status(), $crate::StatusCode::NOT_FOUND); + assert_eq!(__resp.response_body_string().unwrap_or_default(), ""); }}; } #[macro_export] macro_rules! assert_response { - ($conn:expr, $status:expr) => {{ - let ref __conn = $conn; + ($resp:expr, $status:expr) => {{ + let ref __resp = $resp; let expected = $crate::IntoTestStatus::into_status($status); - assert_eq!(__conn.status(), expected); + assert_eq!(__resp.status(), expected); }}; - ($conn:expr, $status:expr, $body:expr $(, $header_name:expr => $header_val:expr)*) => {{ - let ref __conn = $conn; + ($resp:expr, $status:expr, $body:expr $(, $header_name:expr => $header_val:expr)*) => {{ + let ref __resp = $resp; let expected = $crate::IntoTestStatus::into_status($status); - assert_eq!(__conn.status(), expected); + assert_eq!(__resp.status(), expected); assert_eq!( - __conn.response_body_string().unwrap_or_default(), + __resp.response_body_string().unwrap_or_default(), $body ); $( assert_eq!( - __conn.header_str($header_name), + __resp.header_str($header_name), Some($header_val), concat!("expected header ", stringify!($header_name)), ); @@ -507,18 +510,18 @@ macro_rules! assert_response { #[macro_export] macro_rules! assert_status { - ($conn:expr, $status:expr) => {{ - let ref __conn = $conn; + ($resp:expr, $status:expr) => {{ + let ref __resp = $resp; let expected = $crate::IntoTestStatus::into_status($status); - assert_eq!(__conn.status(), expected); + assert_eq!(__resp.status(), expected); }}; } #[macro_export] macro_rules! assert_body_contains { - ($conn:expr, $pattern:expr) => {{ - let ref __conn = $conn; - let body = __conn.response_body_string().unwrap_or_default(); + ($resp:expr, $pattern:expr) => {{ + let ref __resp = $resp; + let body = __resp.response_body_string().unwrap_or_default(); assert!( body.contains($pattern), "expected body to contain {:?}, got {:?}", @@ -530,11 +533,11 @@ macro_rules! assert_body_contains { #[macro_export] macro_rules! assert_headers { - ($conn:expr, $($header_name:expr => $header_val:expr),+ $(,)?) => {{ - let ref __conn = $conn; + ($resp:expr, $($header_name:expr => $header_val:expr),+ $(,)?) => {{ + let ref __resp = $resp; $( assert_eq!( - __conn.header_str($header_name), + __resp.header_str($header_name), Some($header_val), concat!("expected header ", stringify!($header_name)), ); diff --git a/tests/integration/accounts.rs b/tests/integration/accounts.rs index a7bc5bd65..a35d95c8f 100644 --- a/tests/integration/accounts.rs +++ b/tests/integration/accounts.rs @@ -7,14 +7,14 @@ mod index { let (user, account, ..) = fixtures::member(&app).await; let _other_account = fixtures::account(&app).await; - let conn = get("/api/accounts") + let resp = get("/api/accounts") .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_ok!(conn); - let accounts: Vec = conn.response_json(); + assert_ok!(resp); + let accounts: Vec = resp.response_json(); assert_eq!(accounts, vec![account]); Ok(()) } @@ -23,13 +23,13 @@ mod index { async fn as_admin(app: DivviupApi) -> TestResult { let (user, account, ..) = fixtures::admin(&app).await; let other_account = fixtures::account(&app).await; - let conn = get("/api/accounts") + let resp = get("/api/accounts") .with_api_headers() .with_state(user) .run_async(&app) .await; - let accounts: Vec = conn.response_json(); + let accounts: Vec = resp.response_json(); assert_eq!(accounts, vec![account, other_account]); Ok(()) } @@ -39,12 +39,12 @@ mod index { let account = fixtures::admin_account(&app).await; let other_account = fixtures::account(&app).await; let (_, header) = fixtures::api_token(&app, &account).await; - let conn = get("/api/accounts") + let resp = get("/api/accounts") .with_api_headers() .with_request_header(headers::AUTHORIZATION, header) .run_async(&app) .await; - let accounts: Vec = conn.response_json(); + let accounts: Vec = resp.response_json(); assert_eq!(accounts, vec![account, other_account]); Ok(()) @@ -55,12 +55,12 @@ mod index { let account = fixtures::account(&app).await; fixtures::account(&app).await; let (_, header) = fixtures::api_token(&app, &account).await; - let conn = get("/api/accounts") + let resp = get("/api/accounts") .with_api_headers() .with_request_header(headers::AUTHORIZATION, header) .run_async(&app) .await; - let accounts: Vec = conn.response_json(); + let accounts: Vec = resp.response_json(); assert_eq!(accounts, vec![account]); Ok(()) @@ -72,12 +72,12 @@ mod index { fixtures::account(&app).await; let (token, header) = fixtures::api_token(&app, &account).await; token.delete(app.db()).await?; - let conn = get("/api/accounts") + let resp = get("/api/accounts") .with_api_headers() .with_request_header(headers::AUTHORIZATION, header) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } } @@ -88,14 +88,14 @@ mod show { #[test(harness = set_up)] async fn as_a_member(app: DivviupApi) -> TestResult { let (user, account, ..) = fixtures::member(&app).await; - let conn = get(format!("/api/accounts/{}", account.id)) + let resp = get(format!("/api/accounts/{}", account.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_ok!(conn); - let account_response: Account = conn.response_json(); + assert_ok!(resp); + let account_response: Account = resp.response_json(); assert_eq!(account_response, account); Ok(()) @@ -105,13 +105,13 @@ mod show { async fn not_as_a_member(app: DivviupApi) -> TestResult { let (user, ..) = fixtures::member(&app).await; let other_account = fixtures::account(&app).await; - let conn = get(format!("/api/accounts/{}", other_account.id)) + let resp = get(format!("/api/accounts/{}", other_account.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } @@ -121,14 +121,14 @@ mod show { let (user, ..) = fixtures::admin(&app).await; let other_account = fixtures::account(&app).await; - let conn = get(format!("/api/accounts/{}", other_account.id)) + let resp = get(format!("/api/accounts/{}", other_account.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_ok!(conn); - let account: Account = conn.response_json(); + assert_ok!(resp); + let account: Account = resp.response_json(); assert_eq!(account, other_account); @@ -140,12 +140,12 @@ mod show { let account = fixtures::admin_account(&app).await; let other_account = fixtures::account(&app).await; let (_, header) = fixtures::api_token(&app, &account).await; - let conn = get(format!("/api/accounts/{}", other_account.id)) + let resp = get(format!("/api/accounts/{}", other_account.id)) .with_api_headers() .with_request_header(headers::AUTHORIZATION, header) .run_async(&app) .await; - let response: Account = conn.response_json(); + let response: Account = resp.response_json(); assert_eq!(response, other_account); Ok(()) @@ -156,13 +156,13 @@ mod show { let account = fixtures::account(&app).await; fixtures::account(&app).await; let (_, header) = fixtures::api_token(&app, &account).await; - let conn = get(format!("/api/accounts/{}", account.id)) + let resp = get(format!("/api/accounts/{}", account.id)) .with_api_headers() .with_request_header(headers::AUTHORIZATION, header) .run_async(&app) .await; - let response: Account = conn.response_json(); + let response: Account = resp.response_json(); assert_eq!(response, account); Ok(()) } @@ -172,12 +172,12 @@ mod show { let account = fixtures::account(&app).await; let other_account = fixtures::account(&app).await; let (_, header) = fixtures::api_token(&app, &account).await; - let conn = get(format!("/api/accounts/{}", other_account.id)) + let resp = get(format!("/api/accounts/{}", other_account.id)) .with_api_headers() .with_request_header(headers::AUTHORIZATION, header) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } } @@ -187,13 +187,13 @@ mod create { #[test(harness = set_up)] async fn not_logged_in(app: DivviupApi) -> TestResult { - let conn = post("/api/accounts") + let resp = post("/api/accounts") .with_api_headers() .with_request_json(json!({ "name": "some account name" })) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); let accounts = Accounts::find().all(app.db()).await?; assert_eq!(accounts.len(), 0); let memberships = Memberships::find().all(app.db()).await?; @@ -205,14 +205,14 @@ mod create { #[test(harness = set_up)] async fn valid(app: DivviupApi) -> TestResult { let user = fixtures::user(); - let conn = post("/api/accounts") + let resp = post("/api/accounts") .with_api_headers() .with_state(user.clone()) .with_request_json(json!({ "name": "some account name" })) .run_async(&app) .await; - assert_response!(conn, 202); - let account: Account = conn.response_json(); + assert_response!(resp, 202); + let account: Account = resp.response_json(); assert_eq!(account.name, "some account name"); let accounts = Accounts::find().all(app.db()).await?; @@ -230,15 +230,15 @@ mod create { #[test(harness = set_up)] async fn invalid(app: DivviupApi) -> TestResult { let user = fixtures::user(); - let conn = post("/api/accounts") + let resp = post("/api/accounts") .with_api_headers() .with_state(user.clone()) .with_request_json(json!({ "name": "" })) .run_async(&app) .await; - assert_response!(conn, 400); - let errors: Value = conn.response_json(); + assert_response!(resp, 400); + let errors: Value = resp.response_json(); assert!(errors.get("name").is_some()); let accounts = Accounts::find().all(app.db()).await?; assert_eq!(accounts.len(), 0); @@ -251,14 +251,14 @@ mod create { async fn admin_token(app: DivviupApi) -> TestResult { let token = fixtures::admin_token(&app).await; let name = fixtures::random_name(); - let conn = post("/api/accounts") + let resp = post("/api/accounts") .with_api_headers() .with_auth_header(token) .with_request_json(json!({ "name": name })) .run_async(&app) .await; - assert_response!(conn, 202); - let account: Account = conn.response_json(); + assert_response!(resp, 202); + let account: Account = resp.response_json(); assert_eq!(&account.name, &name); assert_eq!(account.reload(app.db()).await?.unwrap().name, name); @@ -270,14 +270,14 @@ mod create { let account = fixtures::account(&app).await; let (_, token) = fixtures::api_token(&app, &account).await; let name = fixtures::random_name(); - let conn = post("/api/accounts") + let resp = post("/api/accounts") .with_api_headers() .with_auth_header(token) .with_request_json(json!({ "name": name })) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } @@ -290,15 +290,15 @@ mod update { async fn as_a_member(app: DivviupApi) -> TestResult { let (user, account, ..) = fixtures::member(&app).await; - let conn = patch(format!("/api/accounts/{}", account.id)) + let resp = patch(format!("/api/accounts/{}", account.id)) .with_api_headers() .with_request_json(json!({ "name": "new name" })) .with_state(user) .run_async(&app) .await; - assert_response!(conn, 202); - let response: Account = conn.response_json(); + assert_response!(resp, 202); + let response: Account = resp.response_json(); assert_eq!(&response.name, "new name"); assert_eq!(account.reload(app.db()).await?.unwrap().name, "new name"); @@ -309,14 +309,14 @@ mod update { async fn not_as_a_member(app: DivviupApi) -> TestResult { let (user, ..) = fixtures::member(&app).await; let other_account = fixtures::account(&app).await; - let conn = patch(format!("/api/accounts/{}", other_account.id)) + let resp = patch(format!("/api/accounts/{}", other_account.id)) .with_api_headers() .with_request_json(json!({ "name": "new name" })) .with_state(user) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } @@ -326,15 +326,15 @@ mod update { let (user, ..) = fixtures::admin(&app).await; let other_account = fixtures::account(&app).await; - let conn = patch(format!("/api/accounts/{}", other_account.id)) + let resp = patch(format!("/api/accounts/{}", other_account.id)) .with_api_headers() .with_request_json(json!({ "name": "new name" })) .with_state(user) .run_async(&app) .await; - assert_response!(conn, 202); - let account: Account = conn.response_json(); + assert_response!(resp, 202); + let account: Account = resp.response_json(); assert_eq!(&account.name, "new name"); assert_eq!(account.reload(app.db()).await?.unwrap().name, "new name"); @@ -345,15 +345,15 @@ mod update { #[test(harness = set_up)] async fn invalid(app: DivviupApi) -> TestResult { let (user, account, ..) = fixtures::member(&app).await; - let conn = patch(format!("/api/accounts/{}", account.id)) + let resp = patch(format!("/api/accounts/{}", account.id)) .with_api_headers() .with_request_json(json!({ "name": "" })) .with_state(user) .run_async(&app) .await; - assert_response!(conn, 400); - let errors: Value = conn.response_json(); + assert_response!(resp, 400); + let errors: Value = resp.response_json(); assert!(errors.get("name").is_some()); Ok(()) @@ -364,15 +364,15 @@ mod update { let account = fixtures::account(&app).await; let (_, header) = fixtures::api_token(&app, &account).await; let name = fixtures::random_name(); - let conn = patch(format!("/api/accounts/{}", account.id)) + let resp = patch(format!("/api/accounts/{}", account.id)) .with_api_headers() .with_request_header(headers::AUTHORIZATION, header) .with_request_json(json!({ "name": &name })) .run_async(&app) .await; - assert_response!(conn, 202); - let response: Account = conn.response_json(); + assert_response!(resp, 202); + let response: Account = resp.response_json(); assert_eq!(&response.name, &name); assert_eq!(account.reload(app.db()).await?.unwrap().name, name); @@ -386,14 +386,14 @@ mod update { let other_account = fixtures::account(&app).await; let (_, header) = fixtures::api_token(&app, &account).await; let name = fixtures::random_name(); - let conn = patch(format!("/api/accounts/{}", other_account.id)) + let resp = patch(format!("/api/accounts/{}", other_account.id)) .with_api_headers() .with_request_header(headers::AUTHORIZATION, header) .with_request_json(json!({ "name": &name })) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } @@ -403,15 +403,15 @@ mod update { let other_account = fixtures::account(&app).await; let (_, header) = fixtures::api_token(&app, &account).await; let name = fixtures::random_name(); - let conn = patch(format!("/api/accounts/{}", other_account.id)) + let resp = patch(format!("/api/accounts/{}", other_account.id)) .with_api_headers() .with_request_header(headers::AUTHORIZATION, header) .with_request_json(json!({ "name": &name })) .run_async(&app) .await; - assert_response!(conn, 202); - let response: Account = conn.response_json(); + assert_response!(resp, 202); + let response: Account = resp.response_json(); assert_eq!(&response.name, &name); assert_eq!(other_account.reload(app.db()).await?.unwrap().name, name); diff --git a/tests/integration/admin_queue.rs b/tests/integration/admin_queue.rs index c25f866dc..a1ca35648 100644 --- a/tests/integration/admin_queue.rs +++ b/tests/integration/admin_queue.rs @@ -10,13 +10,13 @@ mod index { let queue_item = Job::new_invitation_flow(&fixtures::build_membership(&app).await) .insert(app.db()) .await?; - let conn = get("/api/admin/queue") + let resp = get("/api/admin/queue") .with_api_headers() .with_state(admin) .run_async(&app) .await; - assert_ok!(conn); - assert_eq!(conn.response_json::>(), vec![queue_item]); + assert_ok!(resp); + assert_eq!(resp.response_json::>(), vec![queue_item]); Ok(()) } @@ -28,13 +28,13 @@ mod index { .insert(app.db()) .await?; - let conn = get("/api/admin/queue") + let resp = get("/api/admin/queue") .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_status!(conn, 404); + assert_status!(resp, 404); Ok(()) } diff --git a/tests/integration/aggregator_client.rs b/tests/integration/aggregator_client.rs index c4f11fa44..b3ef20be0 100644 --- a/tests/integration/aggregator_client.rs +++ b/tests/integration/aggregator_client.rs @@ -12,23 +12,17 @@ async fn get_task_ids(app: DivviupApi, client_logs: ClientLogs) -> TestResult { assert_eq!(task_ids.len(), 25); // two pages of 10 plus a final page of 5 let logs = client_logs.logs(); - assert!(logs.iter().all(|log| { - log.request_headers - .get(headers::ACCEPT) - .unwrap() - .to_str() - .unwrap() - == "application/vnd.janus.aggregator+json;version=0.1" - })); - - assert!(logs.iter().all(|log| { - log.request_headers - .get(headers::AUTHORIZATION) - .unwrap() - .to_str() - .unwrap() - == format!("Bearer {}", aggregator.bearer_token(app.crypter()).unwrap()) - })); + let expected_token = format!("Bearer {}", aggregator.bearer_token(app.crypter()).unwrap()); + for log in &logs { + assert_eq!( + log.request_headers.get(headers::ACCEPT).unwrap(), + "application/vnd.janus.aggregator+json;version=0.1" + ); + assert_eq!( + log.request_headers.get(headers::AUTHORIZATION).unwrap(), + expected_token.as_str() + ); + } let queries = logs.iter().map(|log| log.url.query()).collect::>(); assert_eq!( diff --git a/tests/integration/aggregators.rs b/tests/integration/aggregators.rs index 487a298f2..307bc7698 100644 --- a/tests/integration/aggregators.rs +++ b/tests/integration/aggregators.rs @@ -15,14 +15,14 @@ mod index { let aggregator1 = fixtures::aggregator(&app, Some(&account)).await; let aggregator2 = fixtures::aggregator(&app, Some(&account)).await; - let conn = get(format!("/api/accounts/{}/aggregators", account.id)) + let resp = get(format!("/api/accounts/{}/aggregators", account.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_ok!(conn); - let aggregators: Vec = conn.response_json(); + assert_ok!(resp); + let aggregators: Vec = resp.response_json(); assert_same_json_representation( &aggregators, &vec![shared_aggregator, aggregator1, aggregator2], @@ -40,14 +40,14 @@ mod index { aggregator1.tombstone().update(app.db()).await?; let aggregator2 = fixtures::aggregator(&app, Some(&account)).await; - let conn = get(format!("/api/accounts/{}/aggregators", account.id)) + let resp = get(format!("/api/accounts/{}/aggregators", account.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_ok!(conn); - let aggregators: Vec = conn.response_json(); + assert_ok!(resp); + let aggregators: Vec = resp.response_json(); assert_same_json_representation(&aggregators, &vec![aggregator2]); Ok(()) } @@ -62,14 +62,14 @@ mod index { aggregator1.tombstone().update(app.db()).await?; let aggregator2 = fixtures::aggregator(&app, Some(&account)).await; - let conn = get(format!("/api/accounts/{}/aggregators", account.id)) + let resp = get(format!("/api/accounts/{}/aggregators", account.id)) .with_api_headers() .with_state(admin) .run_async(&app) .await; - assert_ok!(conn); - let aggregators: Vec = conn.response_json(); + assert_ok!(resp); + let aggregators: Vec = resp.response_json(); assert_same_json_representation(&vec![aggregator2], &aggregators); Ok(()) } @@ -83,13 +83,13 @@ mod index { fixtures::aggregator(&app, Some(&account)).await; fixtures::aggregator(&app, Some(&account)).await; - let conn = get(format!("/api/accounts/{}/aggregators", account.id)) + let resp = get(format!("/api/accounts/{}/aggregators", account.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } @@ -103,13 +103,13 @@ mod index { fixtures::aggregator(&app, Some(&account)).await; fixtures::aggregator(&app, Some(&account)).await; - let conn = get("/api/accounts/not-an-account/aggregators") + let resp = get("/api/accounts/not-an-account/aggregators") .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_not_found!(conn); + assert_not_found!(resp); Ok(()) } @@ -123,14 +123,14 @@ mod index { let aggregator1 = fixtures::aggregator(&app, Some(&account)).await; let aggregator2 = fixtures::aggregator(&app, Some(&account)).await; - let conn = get(format!("/api/accounts/{}/aggregators", account.id)) + let resp = get(format!("/api/accounts/{}/aggregators", account.id)) .with_api_headers() .with_state(admin) .run_async(&app) .await; - assert_ok!(conn); - let aggregators: Vec = conn.response_json(); + assert_ok!(resp); + let aggregators: Vec = resp.response_json(); assert_same_json_representation( &aggregators, &vec![shared_aggregator, aggregator1, aggregator2], @@ -149,14 +149,14 @@ mod index { let aggregator1 = fixtures::aggregator(&app, Some(&account)).await; let aggregator2 = fixtures::aggregator(&app, Some(&account)).await; - let conn = get(format!("/api/accounts/{}/aggregators", account.id)) + let resp = get(format!("/api/accounts/{}/aggregators", account.id)) .with_api_headers() .with_request_header(headers::AUTHORIZATION, token) .run_async(&app) .await; - assert_ok!(conn); - let aggregators: Vec = conn.response_json(); + assert_ok!(resp); + let aggregators: Vec = resp.response_json(); assert_same_json_representation( &aggregators, &vec![shared_aggregator, aggregator1, aggregator2], @@ -177,14 +177,14 @@ mod index { let admin_token = fixtures::admin_token(&app).await; - let conn = get(format!("/api/accounts/{}/aggregators", account.id)) + let resp = get(format!("/api/accounts/{}/aggregators", account.id)) .with_api_headers() .with_request_header(headers::AUTHORIZATION, admin_token) .run_async(&app) .await; - assert_ok!(conn); - let aggregators: Vec = conn.response_json(); + assert_ok!(resp); + let aggregators: Vec = resp.response_json(); assert_same_json_representation( &aggregators, &vec![shared_aggregator, aggregator1, aggregator2], @@ -204,13 +204,13 @@ mod index { fixtures::aggregator(&app, Some(&account)).await; fixtures::aggregator(&app, Some(&account)).await; - let conn = get(format!("/api/accounts/{}/aggregators", account.id)) + let resp = get(format!("/api/accounts/{}/aggregators", account.id)) .with_api_headers() .with_request_header(headers::AUTHORIZATION, token) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } @@ -230,14 +230,14 @@ mod shared_aggregator_index { let (user, account, ..) = fixtures::member(&app).await; fixtures::aggregator(&app, Some(&account)).await; - let conn = get("/api/aggregators") + let resp = get("/api/aggregators") .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_ok!(conn); - let aggregators: Vec = conn.response_json(); + assert_ok!(resp); + let aggregators: Vec = resp.response_json(); assert_same_json_representation( &aggregators, &vec![shared_aggregator1, shared_aggregator2], @@ -257,14 +257,14 @@ mod shared_aggregator_index { let (_, token) = fixtures::api_token(&app, &account).await; fixtures::aggregator(&app, Some(&account)).await; - let conn = get("/api/aggregators") + let resp = get("/api/aggregators") .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_ok!(conn); - let aggregators: Vec = conn.response_json(); + assert_ok!(resp); + let aggregators: Vec = resp.response_json(); assert_same_json_representation( &aggregators, &vec![shared_aggregator1, shared_aggregator2], @@ -274,12 +274,12 @@ mod shared_aggregator_index { #[test(harness = set_up)] async fn not_logged_in(app: DivviupApi) -> TestResult { - let conn = get("/api/aggregators") + let resp = get("/api/aggregators") .with_api_headers() .run_async(&app) .await; - assert_status!(conn, 403); + assert_status!(resp, 403); Ok(()) } } @@ -294,14 +294,14 @@ mod create { let (user, account, ..) = fixtures::member(&app).await; let new_aggregator = fixtures::new_aggregator(); - let conn = post(format!("/api/accounts/{}/aggregators", account.id)) + let resp = post(format!("/api/accounts/{}/aggregators", account.id)) .with_api_headers() .with_state(user) .with_request_json(new_aggregator.clone()) .run_async(&app) .await; - assert_response!(conn, 201); - let aggregator: Aggregator = conn.response_json(); + assert_response!(resp, 201); + let aggregator: Aggregator = resp.response_json(); let aggregator_config: AggregatorApiConfig = client_logs.last().response_json(); @@ -329,14 +329,14 @@ mod create { let mut new_aggregator = fixtures::new_aggregator(); new_aggregator.is_first_party = Some(true); - let conn = post(format!("/api/accounts/{}/aggregators", account.id)) + let resp = post(format!("/api/accounts/{}/aggregators", account.id)) .with_api_headers() .with_state(user) .with_request_json(new_aggregator) .run_async(&app) .await; - assert_response!(conn, 201); - let aggregator: Aggregator = conn.response_json(); + assert_response!(resp, 201); + let aggregator: Aggregator = resp.response_json(); assert!(!aggregator.is_first_party); assert!(!aggregator.reload(app.db()).await?.unwrap().is_first_party); @@ -347,7 +347,7 @@ mod create { async fn invalid(app: DivviupApi) -> TestResult { let (user, account, ..) = fixtures::member(&app).await; - let conn = post(format!("/api/accounts/{}/aggregators", account.id)) + let resp = post(format!("/api/accounts/{}/aggregators", account.id)) .with_api_headers() .with_state(user) .with_request_json(json!({ @@ -357,8 +357,8 @@ mod create { .run_async(&app) .await; - assert_response!(conn, 400); - let error: Value = conn.response_json(); + assert_response!(resp, 400); + let error: Value = resp.response_json(); assert!(error.get("name").is_some()); assert!(error.get("api_url").is_some()); Ok(()) @@ -370,14 +370,14 @@ mod create { let account = fixtures::account(&app).await; // no membership let aggregator_count_before = Aggregators::find().count(app.db()).await?; - let conn = post(format!("/api/accounts/{}/aggregators", account.id)) + let resp = post(format!("/api/accounts/{}/aggregators", account.id)) .with_api_headers() .with_state(user) .with_request_json(fixtures::new_aggregator()) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); let aggregator_count_after = Aggregators::find().count(app.db()).await?; assert_eq!(aggregator_count_before, aggregator_count_after); @@ -389,14 +389,14 @@ mod create { let user = fixtures::user(); let aggregator_count_before = Aggregators::find().count(app.db()).await?; - let conn = post("/api/accounts/does-not-exist/aggregators") + let resp = post("/api/accounts/does-not-exist/aggregators") .with_api_headers() .with_state(user) .with_request_json(fixtures::new_aggregator()) .run_async(&app) .await; - assert_not_found!(conn); + assert_not_found!(resp); let aggregator_count_after = Aggregators::find().count(app.db()).await?; assert_eq!(aggregator_count_before, aggregator_count_after); @@ -407,15 +407,15 @@ mod create { async fn admin_not_member(app: DivviupApi) -> TestResult { let (admin, ..) = fixtures::admin(&app).await; let account = fixtures::account(&app).await; - let conn = post(format!("/api/accounts/{}/aggregators", account.id)) + let resp = post(format!("/api/accounts/{}/aggregators", account.id)) .with_api_headers() .with_state(admin) .with_request_json(fixtures::new_aggregator()) .run_async(&app) .await; - assert_response!(conn, 201); - let aggregator: Aggregator = conn.response_json(); + assert_response!(resp, 201); + let aggregator: Aggregator = resp.response_json(); assert!(!aggregator.is_first_party); let aggregator_from_db = aggregator.reload(app.db()).await?.unwrap(); @@ -428,15 +428,15 @@ mod create { async fn admin_token(app: DivviupApi) -> TestResult { let token = fixtures::admin_token(&app).await; let account = fixtures::account(&app).await; - let conn = post(format!("/api/accounts/{}/aggregators", account.id)) + let resp = post(format!("/api/accounts/{}/aggregators", account.id)) .with_api_headers() .with_auth_header(token) .with_request_json(fixtures::new_aggregator()) .run_async(&app) .await; - assert_response!(conn, 201); - let aggregator: Aggregator = conn.response_json(); + assert_response!(resp, 201); + let aggregator: Aggregator = resp.response_json(); assert!(!aggregator.is_first_party); let aggregator_from_db = aggregator.reload(app.db()).await?.unwrap(); @@ -449,15 +449,15 @@ mod create { async fn member_token(app: DivviupApi) -> TestResult { let account = fixtures::account(&app).await; let (_, token) = fixtures::api_token(&app, &account).await; - let conn = post(format!("/api/accounts/{}/aggregators", account.id)) + let resp = post(format!("/api/accounts/{}/aggregators", account.id)) .with_api_headers() .with_auth_header(token) .with_request_json(fixtures::new_aggregator()) .run_async(&app) .await; - assert_response!(conn, 201); - let aggregator: Aggregator = conn.response_json(); + assert_response!(resp, 201); + let aggregator: Aggregator = resp.response_json(); assert!(!aggregator.is_first_party); let aggregator_from_db = aggregator.reload(app.db()).await?.unwrap(); @@ -472,14 +472,14 @@ mod create { let account = fixtures::account(&app).await; let aggregator_count_before = Aggregators::find().count(app.db()).await?; - let conn = post(format!("/api/accounts/{}/aggregators", account.id)) + let resp = post(format!("/api/accounts/{}/aggregators", account.id)) .with_api_headers() .with_auth_header(token) .with_request_json(fixtures::new_aggregator()) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); let aggregator_count_after = Aggregators::find().count(app.db()).await?; assert_eq!(aggregator_count_before, aggregator_count_after); @@ -494,15 +494,15 @@ mod create { new_aggregator.bearer_token = Some(BAD_BEARER_TOKEN.to_string()); let aggregator_count_before = Aggregators::find().count(app.db()).await?; - let conn = post(format!("/api/accounts/{}/aggregators", account.id)) + let resp = post(format!("/api/accounts/{}/aggregators", account.id)) .with_api_headers() .with_state(user) .with_request_json(new_aggregator.clone()) .run_async(&app) .await; - assert_response!(conn, 400); + assert_response!(resp, 400); assert_eq!(client_logs.last().response_status, StatusCode::UNAUTHORIZED); - let error: Value = conn.response_json(); + let error: Value = resp.response_json(); assert!(error.get("bearer_token").is_some()); let aggregator_count_after = Aggregators::find().count(app.db()).await?; assert_eq!(aggregator_count_before, aggregator_count_after); @@ -518,13 +518,13 @@ mod show { async fn as_member(app: DivviupApi) -> TestResult { let (user, account, ..) = fixtures::member(&app).await; let aggregator = fixtures::aggregator(&app, Some(&account)).await; - let conn = get(format!("/api/aggregators/{}", aggregator.id)) + let resp = get(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json(); + assert_ok!(resp); + let response_aggregator: Aggregator = resp.response_json(); assert_same_json_representation(&response_aggregator, &aggregator); Ok(()) } @@ -533,13 +533,13 @@ mod show { async fn shared_aggregator(app: DivviupApi) -> TestResult { let (user, _account, ..) = fixtures::member(&app).await; let aggregator = fixtures::aggregator(&app, None).await; - let conn = get(format!("/api/aggregators/{}", aggregator.id)) + let resp = get(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json(); + assert_ok!(resp); + let response_aggregator: Aggregator = resp.response_json(); assert_same_json_representation(&response_aggregator, &aggregator); Ok(()) } @@ -549,12 +549,12 @@ mod show { let user = fixtures::user(); let account = fixtures::account(&app).await; let aggregator = fixtures::aggregator(&app, Some(&account)).await; - let conn = get(format!("/api/aggregators/{}", aggregator.id)) + let resp = get(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } @@ -563,13 +563,13 @@ mod show { let (admin, ..) = fixtures::admin(&app).await; let account = fixtures::account(&app).await; let aggregator = fixtures::aggregator(&app, Some(&account)).await; - let conn = get(format!("/api/aggregators/{}", aggregator.id)) + let resp = get(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_state(admin) .run_async(&app) .await; - assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json(); + assert_ok!(resp); + let response_aggregator: Aggregator = resp.response_json(); assert_same_json_representation(&response_aggregator, &aggregator); Ok(()) } @@ -577,12 +577,12 @@ mod show { #[test(harness = set_up)] async fn nonexistant_aggregator(app: DivviupApi) -> TestResult { let user = fixtures::user(); - let conn = get("/api/aggregators/some-made-up-id") + let resp = get("/api/aggregators/some-made-up-id") .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_not_found!(conn); + assert_not_found!(resp); Ok(()) } @@ -595,13 +595,13 @@ mod show { .update(app.db()) .await?; - let conn = get(format!("/api/aggregators/{}", aggregator.id)) + let resp = get(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json(); + assert_ok!(resp); + let response_aggregator: Aggregator = resp.response_json(); assert_same_json_representation(&response_aggregator, &aggregator); Ok(()) } @@ -615,13 +615,13 @@ mod show { .update(app.db()) .await?; - let conn = get(format!("/api/aggregators/{}", aggregator.id)) + let resp = get(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json(); + assert_ok!(resp); + let response_aggregator: Aggregator = resp.response_json(); assert_same_json_representation(&response_aggregator, &aggregator); Ok(()) } @@ -635,13 +635,13 @@ mod show { .tombstone() .update(app.db()) .await?; - let conn = get(format!("/api/aggregators/{}", aggregator.id)) + let resp = get(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_state(admin) .run_async(&app) .await; - assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json(); + assert_ok!(resp); + let response_aggregator: Aggregator = resp.response_json(); assert_same_json_representation(&response_aggregator, &aggregator); Ok(()) } @@ -654,13 +654,13 @@ mod show { .tombstone() .update(app.db()) .await?; - let conn = get(format!("/api/aggregators/{}", aggregator.id)) + let resp = get(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_state(admin) .run_async(&app) .await; - assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json(); + assert_ok!(resp); + let response_aggregator: Aggregator = resp.response_json(); assert_same_json_representation(&response_aggregator, &aggregator); Ok(()) } @@ -670,13 +670,13 @@ mod show { let token = fixtures::admin_token(&app).await; let account = fixtures::account(&app).await; let aggregator = fixtures::aggregator(&app, Some(&account)).await; - let conn = get(format!("/api/aggregators/{}", aggregator.id)) + let resp = get(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_ok!(conn); - let response: Aggregator = conn.response_json(); + assert_ok!(resp); + let response: Aggregator = resp.response_json(); assert_same_json_representation(&aggregator, &response); Ok(()) } @@ -686,13 +686,13 @@ mod show { let account = fixtures::account(&app).await; let (_, token) = fixtures::api_token(&app, &account).await; let aggregator = fixtures::aggregator(&app, Some(&account)).await; - let conn = get(format!("/api/aggregators/{}", aggregator.id)) + let resp = get(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_ok!(conn); - let response: Aggregator = conn.response_json(); + assert_ok!(resp); + let response: Aggregator = resp.response_json(); assert_same_json_representation(&aggregator, &response); Ok(()) } @@ -703,12 +703,12 @@ mod show { let (_, token) = fixtures::api_token(&app, &other_account).await; let account = fixtures::account(&app).await; let aggregator = fixtures::aggregator(&app, Some(&account)).await; - let conn = get(format!("/api/aggregators/{}", aggregator.id)) + let resp = get(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } } @@ -730,13 +730,13 @@ mod update { let new_name = format!("new name {}", fixtures::random_name()); let new_bearer_token = fixtures::random_name(); - let conn = patch(format!("/api/aggregators/{}", aggregator.id)) + let resp = patch(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_request_json(json!({ "name": &new_name, "bearer_token": &new_bearer_token })) .with_state(user) .run_async(&app) .await; - assert_ok!(conn); + assert_ok!(resp); assert_eq!(client_logs.logs().len(), 1); assert_eq!( client_logs @@ -748,7 +748,7 @@ mod update { .unwrap(), format!("Bearer {new_bearer_token}") ); - let response_aggregator: Aggregator = conn.response_json(); + let response_aggregator: Aggregator = resp.response_json(); assert_eq!(response_aggregator.name, new_name); let reloaded = aggregator.reload(app.db()).await?.unwrap(); assert_eq!(reloaded.name, new_name); @@ -763,13 +763,13 @@ mod update { let aggregator = fixtures::aggregator(&app, Some(&account)).await; let original_bearer_token = aggregator.encrypted_bearer_token.clone(); - let conn = patch(format!("/api/aggregators/{}", aggregator.id)) + let resp = patch(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_request_json(json!({ "bearer_token": &BAD_BEARER_TOKEN })) .with_state(user) .run_async(&app) .await; - assert_status!(conn, 400); + assert_status!(resp, 400); assert_eq!(client_logs.logs().len(), 1); assert_eq!( client_logs @@ -791,7 +791,7 @@ mod update { original_bearer_token ); assert_eq!(client_logs.last().response_status, StatusCode::UNAUTHORIZED); - let errors: Value = conn.response_json(); + let errors: Value = resp.response_json(); assert!(errors.get("bearer_token").is_some()); Ok(()) @@ -802,14 +802,14 @@ mod update { let (user, account, ..) = fixtures::member(&app).await; let aggregator = fixtures::aggregator(&app, Some(&account)).await; - let conn = patch(format!("/api/aggregators/{}", aggregator.id)) + let resp = patch(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_request_json(json!({ "name": "" })) .with_state(user) .run_async(&app) .await; - assert_response!(conn, 400); - let errors: Value = conn.response_json(); + assert_response!(resp, 400); + let errors: Value = resp.response_json(); assert!(errors.get("name").is_some()); assert_eq!( @@ -826,14 +826,14 @@ mod update { let account = fixtures::account(&app).await; let aggregator = fixtures::aggregator(&app, Some(&account)).await; - let conn = patch(format!("/api/aggregators/{}", aggregator.id)) + let resp = patch(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_request_json(json!({ "name": "irrelevant" })) .with_state(user) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); assert_eq!( aggregator.reload(app.db()).await?.unwrap().name, aggregator.name // unchanged @@ -846,14 +846,14 @@ mod update { let user = fixtures::user(); let aggregator = fixtures::aggregator(&app, None).await; let old_name = aggregator.name.clone(); - let conn = patch(format!("/api/aggregators/{}", aggregator.id)) + let resp = patch(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_request_json(json!({ "name": "irrelevant" })) .with_state(user) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); assert_eq!(aggregator.reload(app.db()).await?.unwrap().name, old_name); Ok(()) } @@ -864,15 +864,15 @@ mod update { let aggregator = fixtures::aggregator(&app, None).await; let new_name = format!("new name {}", fixtures::random_name()); - let conn = patch(format!("/api/aggregators/{}", aggregator.id)) + let resp = patch(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_request_json(json!({ "name": &new_name })) .with_state(admin) .run_async(&app) .await; - assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json(); + assert_ok!(resp); + let response_aggregator: Aggregator = resp.response_json(); assert_eq!(response_aggregator.name, new_name); assert_eq!(aggregator.reload(app.db()).await?.unwrap().name, new_name); Ok(()) @@ -885,14 +885,14 @@ mod update { let aggregator = fixtures::aggregator(&app, Some(&account)).await; let new_name = format!("new name {}", fixtures::random_name()); - let conn = patch(format!("/api/aggregators/{}", aggregator.id)) + let resp = patch(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_request_json(json!({ "name": &new_name })) .with_state(admin) .run_async(&app) .await; - assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json(); + assert_ok!(resp); + let response_aggregator: Aggregator = resp.response_json(); assert_eq!(response_aggregator.name, new_name); assert_eq!(aggregator.reload(app.db()).await?.unwrap().name, new_name); @@ -902,13 +902,13 @@ mod update { #[test(harness = set_up)] async fn nonexistant_aggregator(app: DivviupApi) -> TestResult { let user = fixtures::user(); - let conn = patch("/api/aggregators/not-an-aggregator-id") + let resp = patch("/api/aggregators/not-an-aggregator-id") .with_api_headers() .with_request_json(json!({ "name": "irrelevant" })) .with_state(user) .run_async(&app) .await; - assert_not_found!(conn); + assert_not_found!(resp); Ok(()) } @@ -921,14 +921,14 @@ mod update { .update(app.db()) .await?; let name = fixtures::random_name(); - let conn = patch(format!("/api/aggregators/{}", aggregator.id)) + let resp = patch(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_request_json(json!({ "name": name })) .with_state(user) .run_async(&app) .await; - assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json(); + assert_ok!(resp); + let response_aggregator: Aggregator = resp.response_json(); assert_eq!(response_aggregator.name, name); assert_eq!(aggregator.reload(app.db()).await?.unwrap().name, name); Ok(()) @@ -943,13 +943,13 @@ mod update { .update(app.db()) .await?; let name = fixtures::random_name(); - let conn = patch(format!("/api/aggregators/{}", aggregator.id)) + let resp = patch(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_request_json(json!({ "name": name })) .with_state(user) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } @@ -963,14 +963,14 @@ mod update { .update(app.db()) .await?; let name = fixtures::random_name(); - let conn = patch(format!("/api/aggregators/{}", aggregator.id)) + let resp = patch(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_request_json(json!({ "name": name })) .with_state(admin) .run_async(&app) .await; - assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json(); + assert_ok!(resp); + let response_aggregator: Aggregator = resp.response_json(); assert_eq!(response_aggregator.name, name); assert_eq!(aggregator.reload(app.db()).await?.unwrap().name, name); Ok(()) @@ -985,15 +985,15 @@ mod update { .update(app.db()) .await?; let name = fixtures::random_name(); - let conn = patch(format!("/api/aggregators/{}", aggregator.id)) + let resp = patch(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_request_json(json!({ "name": name })) .with_state(admin) .run_async(&app) .await; - assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json(); + assert_ok!(resp); + let response_aggregator: Aggregator = resp.response_json(); assert_eq!(response_aggregator.name, name); assert_eq!(aggregator.reload(app.db()).await?.unwrap().name, name); Ok(()) @@ -1005,15 +1005,15 @@ mod update { let account = fixtures::account(&app).await; let aggregator = fixtures::aggregator(&app, Some(&account)).await; let name = fixtures::random_name(); - let conn = patch(format!("/api/aggregators/{}", aggregator.id)) + let resp = patch(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_auth_header(token) .with_request_json(json!({ "name": name })) .run_async(&app) .await; - assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json(); + assert_ok!(resp); + let response_aggregator: Aggregator = resp.response_json(); assert_eq!(response_aggregator.name, name); assert_eq!(aggregator.reload(app.db()).await?.unwrap().name, name); Ok(()) @@ -1025,14 +1025,14 @@ mod update { let (_, token) = fixtures::api_token(&app, &account).await; let aggregator = fixtures::aggregator(&app, Some(&account)).await; let name = fixtures::random_name(); - let conn = patch(format!("/api/aggregators/{}", aggregator.id)) + let resp = patch(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_auth_header(token) .with_request_json(json!({ "name": name })) .run_async(&app) .await; - assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json(); + assert_ok!(resp); + let response_aggregator: Aggregator = resp.response_json(); assert_eq!(response_aggregator.name, name); assert_eq!(aggregator.reload(app.db()).await?.unwrap().name, name); Ok(()) @@ -1045,13 +1045,13 @@ mod update { let account = fixtures::account(&app).await; let aggregator = fixtures::aggregator(&app, Some(&account)).await; let name_before = aggregator.name.clone(); - let conn = patch(format!("/api/aggregators/{}", aggregator.id)) + let resp = patch(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_auth_header(token) .with_request_json(json!({ "name": fixtures::random_name() })) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); assert_eq!( aggregator.reload(app.db()).await?.unwrap().name, name_before @@ -1075,25 +1075,25 @@ mod update { aggregator.features = ActiveValue::Set(Features::from_iter::<[Feature; 0]>([]).into()); let aggregator = aggregator.update(app.db()).await?; - let conn = get(format!("/api/aggregators/{}", aggregator.id)) + let resp = get(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_state(user.clone()) .run_async(&app) .await; - assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json(); + assert_ok!(resp); + let response_aggregator: Aggregator = resp.response_json(); assert!(response_aggregator.query_types.is_empty()); assert!(response_aggregator.vdafs.is_empty()); assert!(response_aggregator.features.is_empty()); - let conn = patch(format!("/api/aggregators/{}", aggregator.id)) + let resp = patch(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_request_json(json!({})) .with_state(user) .run_async(&app) .await; - assert_ok!(conn); - let response_aggregator: Aggregator = conn.response_json(); + assert_ok!(resp); + let response_aggregator: Aggregator = resp.response_json(); assert_eq!( response_aggregator.query_types, before_aggregator.query_types @@ -1113,12 +1113,12 @@ mod delete { #[ignore] async fn nonexistant_aggregator(app: DivviupApi) -> TestResult { let (user, ..) = fixtures::member(&app).await; - let conn = delete(format!("/api/aggregators/{}", Uuid::new_v4())) + let resp = delete(format!("/api/aggregators/{}", Uuid::new_v4())) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } @@ -1126,12 +1126,12 @@ mod delete { async fn shared_as_admin(app: DivviupApi) -> TestResult { let (admin, ..) = fixtures::admin(&app).await; let aggregator = fixtures::aggregator(&app, None).await; - let conn = delete(format!("/api/aggregators/{}", aggregator.id)) + let resp = delete(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_state(admin) .run_async(&app) .await; - assert_status!(conn, 204); + assert_status!(resp, 204); assert!(aggregator.reload(app.db()).await?.unwrap().is_tombstoned()); Ok(()) @@ -1141,12 +1141,12 @@ mod delete { async fn shared(app: DivviupApi) -> TestResult { let (user, ..) = fixtures::member(&app).await; let aggregator = fixtures::aggregator(&app, None).await; - let conn = delete(format!("/api/aggregators/{}", aggregator.id)) + let resp = delete(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); assert!(!aggregator.reload(app.db()).await?.unwrap().is_tombstoned()); Ok(()) @@ -1156,12 +1156,12 @@ mod delete { async fn as_member(app: DivviupApi) -> TestResult { let (user, account, ..) = fixtures::member(&app).await; let aggregator = fixtures::aggregator(&app, Some(&account)).await; - let conn = delete(format!("/api/aggregators/{}", aggregator.id)) + let resp = delete(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_status!(conn, 204); + assert_status!(resp, 204); assert!(aggregator.reload(app.db()).await?.unwrap().is_tombstoned()); Ok(()) @@ -1172,12 +1172,12 @@ mod delete { let account = fixtures::account(&app).await; let (user, ..) = fixtures::member(&app).await; let aggregator = fixtures::aggregator(&app, Some(&account)).await; - let conn = delete(format!("/api/aggregators/{}", aggregator.id)) + let resp = delete(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); assert!(!aggregator.reload(app.db()).await?.unwrap().is_tombstoned()); Ok(()) @@ -1188,12 +1188,12 @@ mod delete { let (admin, ..) = fixtures::admin(&app).await; let account = fixtures::account(&app).await; let aggregator = fixtures::aggregator(&app, Some(&account)).await; - let conn = delete(format!("/api/aggregators/{}", aggregator.id)) + let resp = delete(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_state(admin) .run_async(&app) .await; - assert_status!(conn, 204); + assert_status!(resp, 204); assert!(aggregator.reload(app.db()).await?.unwrap().is_tombstoned()); Ok(()) @@ -1204,12 +1204,12 @@ mod delete { let token = fixtures::admin_token(&app).await; let account = fixtures::account(&app).await; let aggregator = fixtures::aggregator(&app, Some(&account)).await; - let conn = delete(format!("/api/aggregators/{}", aggregator.id)) + let resp = delete(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_status!(conn, 204); + assert_status!(resp, 204); assert!(aggregator.reload(app.db()).await?.unwrap().is_tombstoned()); Ok(()) } @@ -1219,12 +1219,12 @@ mod delete { let account = fixtures::account(&app).await; let (_, token) = fixtures::api_token(&app, &account).await; let aggregator = fixtures::aggregator(&app, Some(&account)).await; - let conn = delete(format!("/api/aggregators/{}", aggregator.id)) + let resp = delete(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_status!(conn, 204); + assert_status!(resp, 204); assert!(aggregator.reload(app.db()).await?.unwrap().is_tombstoned()); Ok(()) } @@ -1235,12 +1235,12 @@ mod delete { let (_, token) = fixtures::api_token(&app, &other_account).await; let account = fixtures::account(&app).await; let aggregator = fixtures::aggregator(&app, Some(&account)).await; - let conn = delete(format!("/api/aggregators/{}", aggregator.id)) + let resp = delete(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); assert!(!aggregator.reload(app.db()).await?.unwrap().is_tombstoned()); Ok(()) } @@ -1250,12 +1250,12 @@ mod delete { let account = fixtures::account(&app).await; let (_, token) = fixtures::api_token(&app, &account).await; let aggregator = fixtures::aggregator(&app, None).await; - let conn = delete(format!("/api/aggregators/{}", aggregator.id)) + let resp = delete(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); assert!(!aggregator.reload(app.db()).await?.unwrap().is_tombstoned()); Ok(()) } @@ -1264,12 +1264,12 @@ mod delete { async fn admin_token_shared_aggregator(app: DivviupApi) -> TestResult { let token = fixtures::admin_token(&app).await; let aggregator = fixtures::aggregator(&app, None).await; - let conn = delete(format!("/api/aggregators/{}", aggregator.id)) + let resp = delete(format!("/api/aggregators/{}", aggregator.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_status!(conn, 204); + assert_status!(resp, 204); assert!(aggregator.reload(app.db()).await?.unwrap().is_tombstoned()); Ok(()) } @@ -1282,15 +1282,15 @@ mod shared_create { async fn as_admin(app: DivviupApi, client_logs: ClientLogs) -> TestResult { let (admin, ..) = fixtures::admin(&app).await; let new_aggregator = fixtures::new_aggregator(); - let conn = post("/api/aggregators") + let resp = post("/api/aggregators") .with_request_json(&new_aggregator) .with_api_headers() .with_state(admin) .run_async(&app) .await; - assert_response!(conn, 201); - let aggregator: Aggregator = conn.response_json(); + assert_response!(resp, 201); + let aggregator: Aggregator = resp.response_json(); let aggregator_config: AggregatorApiConfig = client_logs.last().response_json(); assert!(aggregator.account_id.is_none()); @@ -1318,14 +1318,14 @@ mod shared_create { let (admin, ..) = fixtures::admin(&app).await; let mut new_aggregator = fixtures::new_aggregator(); new_aggregator.is_first_party = Some(true); - let conn = post("/api/aggregators") + let resp = post("/api/aggregators") .with_api_headers() .with_state(admin) .with_request_json(new_aggregator) .run_async(&app) .await; - assert_response!(conn, 201); - let aggregator: Aggregator = conn.response_json(); + assert_response!(resp, 201); + let aggregator: Aggregator = resp.response_json(); assert!(aggregator.is_first_party); assert!(aggregator.reload(app.db()).await?.unwrap().is_first_party); @@ -1337,14 +1337,14 @@ mod shared_create { let (admin, ..) = fixtures::admin(&app).await; let mut new_aggregator = fixtures::new_aggregator(); new_aggregator.is_first_party = Some(false); - let conn = post("/api/aggregators") + let resp = post("/api/aggregators") .with_api_headers() .with_state(admin) .with_request_json(new_aggregator) .run_async(&app) .await; - assert_response!(conn, 201); - let aggregator: Aggregator = conn.response_json(); + assert_response!(resp, 201); + let aggregator: Aggregator = resp.response_json(); assert!(!aggregator.is_first_party); assert!(!aggregator.reload(app.db()).await?.unwrap().is_first_party); @@ -1357,14 +1357,14 @@ mod shared_create { let new_aggregator = fixtures::new_aggregator(); let aggregator_count_before = Aggregators::find().count(app.db()).await?; - let conn = post("/api/aggregators") + let resp = post("/api/aggregators") .with_request_json(&new_aggregator) .with_api_headers() .with_state(admin) .run_async(&app) .await; - assert_not_found!(conn); + assert_not_found!(resp); let aggregator_count_after = Aggregators::find().count(app.db()).await?; assert_eq!(aggregator_count_before, aggregator_count_after); @@ -1378,14 +1378,14 @@ mod shared_create { let new_aggregator = fixtures::new_aggregator(); let aggregator_count_before = Aggregators::find().count(app.db()).await?; - let conn = post("/api/aggregators") + let resp = post("/api/aggregators") .with_request_json(&new_aggregator) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_not_found!(conn); + assert_not_found!(resp); let aggregator_count_after = Aggregators::find().count(app.db()).await?; assert_eq!(aggregator_count_before, aggregator_count_after); Ok(()) @@ -1396,13 +1396,13 @@ mod shared_create { let token = fixtures::admin_token(&app).await; let new_aggregator = fixtures::new_aggregator(); let aggregator_count_before = Aggregators::find().count(app.db()).await?; - let conn = post("/api/aggregators") + let resp = post("/api/aggregators") .with_request_json(&new_aggregator) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - let aggregator: Aggregator = conn.response_json(); + let aggregator: Aggregator = resp.response_json(); assert_eq!(aggregator.name, new_aggregator.name.unwrap()); let aggregator_count_after = Aggregators::find().count(app.db()).await?; assert_eq!(aggregator_count_after, aggregator_count_before + 1); diff --git a/tests/integration/api_tokens.rs b/tests/integration/api_tokens.rs index 59edb5c53..8f2f4b5f9 100644 --- a/tests/integration/api_tokens.rs +++ b/tests/integration/api_tokens.rs @@ -13,14 +13,14 @@ mod index { let (deleted, _) = fixtures::api_token(&app, &account).await; let _deleted = deleted.tombstone().update(app.db()).await.unwrap(); - let conn = get(format!("/api/accounts/{}/api_tokens", account.id)) + let resp = get(format!("/api/accounts/{}/api_tokens", account.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_ok!(conn); + assert_ok!(resp); - let api_tokens: Vec = conn.response_json(); + let api_tokens: Vec = resp.response_json(); assert_same_json_representation(&api_tokens, &vec![token2, token1]); Ok(()) } @@ -33,13 +33,13 @@ mod index { fixtures::api_token(&app, &account).await; fixtures::api_token(&app, &account).await; - let conn = get(format!("/api/accounts/{}/api_tokens", account.id)) + let resp = get(format!("/api/accounts/{}/api_tokens", account.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } @@ -52,13 +52,13 @@ mod index { fixtures::api_token(&app, &account).await; fixtures::api_token(&app, &account).await; - let conn = get("/api/accounts/not-an-account/api_tokens") + let resp = get("/api/accounts/not-an-account/api_tokens") .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_not_found!(conn); + assert_not_found!(resp); Ok(()) } @@ -70,14 +70,14 @@ mod index { let (api_token1, _) = fixtures::api_token(&app, &account).await; let (api_token2, _) = fixtures::api_token(&app, &account).await; - let conn = get(format!("/api/accounts/{}/api_tokens", account.id)) + let resp = get(format!("/api/accounts/{}/api_tokens", account.id)) .with_api_headers() .with_state(admin) .run_async(&app) .await; - assert_ok!(conn); - let api_tokens: Vec = conn.response_json(); + assert_ok!(resp); + let api_tokens: Vec = resp.response_json(); assert_same_json_representation(&api_tokens, &vec![api_token2, api_token1]); Ok(()) } @@ -89,14 +89,14 @@ mod index { let (api_token1, _) = fixtures::api_token(&app, &account).await; let (api_token2, _) = fixtures::api_token(&app, &account).await; - let conn = get(format!("/api/accounts/{}/api_tokens", account.id)) + let resp = get(format!("/api/accounts/{}/api_tokens", account.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_ok!(conn); - let api_tokens: Vec = conn.response_json(); + assert_ok!(resp); + let api_tokens: Vec = resp.response_json(); assert_same_json_representation(&api_tokens, &vec![api_token2, api_token1]); Ok(()) } @@ -107,14 +107,14 @@ mod index { let (api_token1, token) = fixtures::api_token(&app, &account).await; let (api_token2, _) = fixtures::api_token(&app, &account).await; - let conn = get(format!("/api/accounts/{}/api_tokens", account.id)) + let resp = get(format!("/api/accounts/{}/api_tokens", account.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_ok!(conn); - let api_tokens: Vec = conn.response_json(); + assert_ok!(resp); + let api_tokens: Vec = resp.response_json(); assert_same_json_representation( &api_tokens, &vec![api_token2, api_token1.reload(app.db()).await?.unwrap()], @@ -131,12 +131,12 @@ mod index { fixtures::api_token(&app, &account).await; fixtures::api_token(&app, &account).await; - let conn = get(format!("/api/accounts/{}/api_tokens", account.id)) + let resp = get(format!("/api/accounts/{}/api_tokens", account.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } } @@ -148,13 +148,13 @@ mod create { async fn success(app: DivviupApi) -> TestResult { let (user, account, ..) = fixtures::member(&app).await; - let conn = post(format!("/api/accounts/{}/api_tokens", account.id)) + let resp = post(format!("/api/accounts/{}/api_tokens", account.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_response!(conn, 201); - let mut api_token: ApiToken = conn.response_json(); + assert_response!(resp, 201); + let mut api_token: ApiToken = resp.response_json(); let api_token_from_db = api_token.reload(app.db()).await?.unwrap(); let (api_token_from_token, account_from_token) = ApiTokens::load_and_check(&api_token.token.take().unwrap(), app.db()) @@ -173,13 +173,13 @@ mod create { let account = fixtures::account(&app).await; // no membership let api_token_count_before = ApiTokens::find().count(app.db()).await?; - let conn = post(format!("/api/accounts/{}/api_tokens", account.id)) + let resp = post(format!("/api/accounts/{}/api_tokens", account.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); let api_token_count_after = ApiTokens::find().count(app.db()).await?; assert_eq!(api_token_count_before, api_token_count_after); @@ -191,13 +191,13 @@ mod create { let user = fixtures::user(); let api_token_count_before = ApiTokens::find().count(app.db()).await?; - let conn = post("/api/accounts/does-not-exist/api_tokens") + let resp = post("/api/accounts/does-not-exist/api_tokens") .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_not_found!(conn); + assert_not_found!(resp); let api_token_count_after = ApiTokens::find().count(app.db()).await?; assert_eq!(api_token_count_before, api_token_count_after); @@ -208,14 +208,14 @@ mod create { async fn admin_not_member(app: DivviupApi) -> TestResult { let (admin, ..) = fixtures::admin(&app).await; let account = fixtures::account(&app).await; - let conn = post(format!("/api/accounts/{}/api_tokens", account.id)) + let resp = post(format!("/api/accounts/{}/api_tokens", account.id)) .with_api_headers() .with_state(admin) .run_async(&app) .await; - assert_response!(conn, 201); - let mut api_token: ApiToken = conn.response_json(); + assert_response!(resp, 201); + let mut api_token: ApiToken = resp.response_json(); let api_token_from_db = api_token.reload(app.db()).await?.unwrap(); let (api_token_from_token, account_from_token) = ApiTokens::load_and_check(&api_token.token.take().unwrap(), app.db()) @@ -232,14 +232,14 @@ mod create { async fn admin_token(app: DivviupApi) -> TestResult { let token = fixtures::admin_token(&app).await; let account = fixtures::account(&app).await; - let conn = post(format!("/api/accounts/{}/api_tokens", account.id)) + let resp = post(format!("/api/accounts/{}/api_tokens", account.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_response!(conn, 201); - let mut api_token: ApiToken = conn.response_json(); + assert_response!(resp, 201); + let mut api_token: ApiToken = resp.response_json(); let api_token_from_db = api_token.reload(app.db()).await?.unwrap(); let (api_token_from_token, account_from_token) = ApiTokens::load_and_check(&api_token.token.take().unwrap(), app.db()) @@ -256,14 +256,14 @@ mod create { async fn member_token(app: DivviupApi) -> TestResult { let account = fixtures::account(&app).await; let (_, token) = fixtures::api_token(&app, &account).await; - let conn = post(format!("/api/accounts/{}/api_tokens", account.id)) + let resp = post(format!("/api/accounts/{}/api_tokens", account.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_response!(conn, 201); - let mut api_token: ApiToken = conn.response_json(); + assert_response!(resp, 201); + let mut api_token: ApiToken = resp.response_json(); let api_token_from_db = api_token.reload(app.db()).await?.unwrap(); let (api_token_from_token, account_from_token) = ApiTokens::load_and_check(&api_token.token.take().unwrap(), app.db()) @@ -283,12 +283,12 @@ mod create { let (_, token) = fixtures::api_token(&app, &other_account).await; let account = fixtures::account(&app).await; let count_before = ApiTokens::find().count(app.db()).await?; - let conn = post(format!("/api/accounts/{}/api_tokens", account.id)) + let resp = post(format!("/api/accounts/{}/api_tokens", account.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); let count_after = ApiTokens::find().count(app.db()).await?; assert_eq!(count_before, count_after); Ok(()) @@ -303,12 +303,12 @@ mod delete { #[test(harness = set_up)] async fn nonexistant_api_token(app: DivviupApi) -> TestResult { let (user, ..) = fixtures::member(&app).await; - let conn = delete(format!("/api/api_tokens/{}", Uuid::new_v4())) + let resp = delete(format!("/api/api_tokens/{}", Uuid::new_v4())) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } @@ -316,12 +316,12 @@ mod delete { async fn as_member(app: DivviupApi) -> TestResult { let (user, account, ..) = fixtures::member(&app).await; let (api_token, _) = fixtures::api_token(&app, &account).await; - let conn = delete(format!("/api/api_tokens/{}", api_token.id)) + let resp = delete(format!("/api/api_tokens/{}", api_token.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_status!(conn, 204); + assert_status!(resp, 204); assert!(api_token.reload(app.db()).await?.unwrap().is_tombstoned()); Ok(()) @@ -332,12 +332,12 @@ mod delete { let account = fixtures::account(&app).await; let (user, ..) = fixtures::member(&app).await; let (api_token, ..) = fixtures::api_token(&app, &account).await; - let conn = delete(format!("/api/api_tokens/{}", api_token.id)) + let resp = delete(format!("/api/api_tokens/{}", api_token.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); assert!(!api_token.reload(app.db()).await?.unwrap().is_tombstoned()); Ok(()) @@ -348,12 +348,12 @@ mod delete { let (admin, ..) = fixtures::admin(&app).await; let account = fixtures::account(&app).await; let (api_token, _) = fixtures::api_token(&app, &account).await; - let conn = delete(format!("/api/api_tokens/{}", api_token.id)) + let resp = delete(format!("/api/api_tokens/{}", api_token.id)) .with_api_headers() .with_state(admin) .run_async(&app) .await; - assert_status!(conn, 204); + assert_status!(resp, 204); assert!(api_token.reload(app.db()).await?.unwrap().is_tombstoned()); Ok(()) @@ -364,12 +364,12 @@ mod delete { let token = fixtures::admin_token(&app).await; let account = fixtures::account(&app).await; let (api_token, _) = fixtures::api_token(&app, &account).await; - let conn = delete(format!("/api/api_tokens/{}", api_token.id)) + let resp = delete(format!("/api/api_tokens/{}", api_token.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_status!(conn, 204); + assert_status!(resp, 204); assert!(api_token.reload(app.db()).await?.unwrap().is_tombstoned()); Ok(()) @@ -380,12 +380,12 @@ mod delete { let account = fixtures::account(&app).await; let (_, token) = fixtures::api_token(&app, &account).await; let (api_token, _) = fixtures::api_token(&app, &account).await; - let conn = delete(format!("/api/api_tokens/{}", api_token.id)) + let resp = delete(format!("/api/api_tokens/{}", api_token.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_status!(conn, 204); + assert_status!(resp, 204); assert!(api_token.reload(app.db()).await?.unwrap().is_tombstoned()); Ok(()) } @@ -396,12 +396,12 @@ mod delete { let (_, token) = fixtures::api_token(&app, &other_account).await; let account = fixtures::account(&app).await; let (api_token, _) = fixtures::api_token(&app, &account).await; - let conn = delete(format!("/api/api_tokens/{}", api_token.id)) + let resp = delete(format!("/api/api_tokens/{}", api_token.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); assert!(!api_token.reload(app.db()).await?.unwrap().is_tombstoned()); Ok(()) } @@ -415,13 +415,13 @@ mod update { #[test(harness = set_up)] async fn nonexistant_api_token(app: DivviupApi) -> TestResult { let (user, ..) = fixtures::member(&app).await; - let conn = patch(format!("/api/api_tokens/{}", Uuid::new_v4())) + let resp = patch(format!("/api/api_tokens/{}", Uuid::new_v4())) .with_request_json(json!({ "name": fixtures::random_name() })) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } @@ -430,14 +430,14 @@ mod update { let (user, account, ..) = fixtures::member(&app).await; let (api_token, _) = fixtures::api_token(&app, &account).await; let name = fixtures::random_name(); - let conn = patch(format!("/api/api_tokens/{}", api_token.id)) + let resp = patch(format!("/api/api_tokens/{}", api_token.id)) .with_api_headers() .with_request_json(json!({ "name": name })) .with_state(user) .run_async(&app) .await; - assert_status!(conn, 200); - let response: ApiToken = conn.response_json(); + assert_status!(resp, 200); + let response: ApiToken = resp.response_json(); assert_eq!(response.name.unwrap(), name); assert_eq!( api_token.reload(app.db()).await?.unwrap().name.unwrap(), @@ -452,14 +452,14 @@ mod update { let account = fixtures::account(&app).await; let (user, ..) = fixtures::member(&app).await; let (api_token, ..) = fixtures::api_token(&app, &account).await; - let conn = patch(format!("/api/api_tokens/{}", api_token.id)) + let resp = patch(format!("/api/api_tokens/{}", api_token.id)) .with_api_headers() .with_request_json(json!({ "name": fixtures::random_name() })) .with_state(user) .run_async(&app) .await; let name_before = api_token.name.clone(); - assert_response!(conn, 403); + assert_response!(resp, 403); assert_eq!(api_token.reload(app.db()).await?.unwrap().name, name_before); Ok(()) @@ -471,14 +471,14 @@ mod update { let account = fixtures::account(&app).await; let (api_token, _) = fixtures::api_token(&app, &account).await; let name = fixtures::random_name(); - let conn = patch(format!("/api/api_tokens/{}", api_token.id)) + let resp = patch(format!("/api/api_tokens/{}", api_token.id)) .with_api_headers() .with_request_json(json!({ "name": name })) .with_state(admin) .run_async(&app) .await; - assert_status!(conn, 200); - let response: ApiToken = conn.response_json(); + assert_status!(resp, 200); + let response: ApiToken = resp.response_json(); assert_eq!(response.name.unwrap(), name); assert_eq!( api_token.reload(app.db()).await?.unwrap().name.unwrap(), @@ -494,14 +494,14 @@ mod update { let account = fixtures::account(&app).await; let (api_token, _) = fixtures::api_token(&app, &account).await; let name = fixtures::random_name(); - let conn = patch(format!("/api/api_tokens/{}", api_token.id)) + let resp = patch(format!("/api/api_tokens/{}", api_token.id)) .with_api_headers() .with_request_json(json!({ "name": name })) .with_auth_header(token) .run_async(&app) .await; - assert_status!(conn, 200); - let response: ApiToken = conn.response_json(); + assert_status!(resp, 200); + let response: ApiToken = resp.response_json(); assert_eq!(response.name.unwrap(), name); assert_eq!( api_token.reload(app.db()).await?.unwrap().name.unwrap(), @@ -516,14 +516,14 @@ mod update { let account = fixtures::account(&app).await; let (api_token, token) = fixtures::api_token(&app, &account).await; let name = fixtures::random_name(); - let conn = patch(format!("/api/api_tokens/{}", api_token.id)) + let resp = patch(format!("/api/api_tokens/{}", api_token.id)) .with_api_headers() .with_request_json(json!({ "name": name })) .with_auth_header(token) .run_async(&app) .await; - assert_status!(conn, 200); - let response: ApiToken = conn.response_json(); + assert_status!(resp, 200); + let response: ApiToken = resp.response_json(); assert_eq!(response.name.unwrap(), name); assert_eq!( api_token.reload(app.db()).await?.unwrap().name.unwrap(), @@ -541,13 +541,13 @@ mod update { let account = fixtures::account(&app).await; let (api_token, _) = fixtures::api_token(&app, &account).await; let name = fixtures::random_name(); - let conn = patch(format!("/api/api_tokens/{}", api_token.id)) + let resp = patch(format!("/api/api_tokens/{}", api_token.id)) .with_api_headers() .with_request_json(json!({ "name": name })) .with_auth_header(token) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); assert!(api_token.reload(app.db()).await?.unwrap().name.is_none()); Ok(()) } diff --git a/tests/integration/assets.rs b/tests/integration/assets.rs index 329b57d6c..cb4a21e30 100644 --- a/tests/integration/assets.rs +++ b/tests/integration/assets.rs @@ -46,12 +46,12 @@ async fn api_url(app: DivviupApi) -> TestResult { #[test(harness = set_up)] async fn missing_asset_is_not_cached(app: DivviupApi) -> TestResult { - let conn = get("/assets/does-not-exist.js") + let resp = get("/assets/does-not-exist.js") .with_app_host() .run_async(&app) .await; - assert_not_found!(&conn); - assert_headers!(&conn, "cache-control" => "no-cache"); + assert_not_found!(&resp); + assert_headers!(&resp, "cache-control" => "no-cache"); Ok(()) } diff --git a/tests/integration/auth.rs b/tests/integration/auth.rs index 0f6360a54..fca3e388c 100644 --- a/tests/integration/auth.rs +++ b/tests/integration/auth.rs @@ -25,23 +25,23 @@ async fn deleted_token_cannot_authenticate(app: DivviupApi) -> TestResult { let (api_token, token) = fixtures::api_token(&app, &account).await; // Token works before deletion - let conn = get("/api/accounts") + let resp = get("/api/accounts") .with_api_headers() .with_auth_header(token.clone()) .run_async(&app) .await; - assert_ok!(conn); + assert_ok!(resp); // Delete (tombstone) the token api_token.tombstone().update(app.db()).await.unwrap(); // Deleted token should no longer authenticate - let conn = get("/api/accounts") + let resp = get("/api/accounts") .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_status!(conn, 403); + assert_status!(resp, 403); Ok(()) } @@ -51,10 +51,10 @@ mod login { #[test(harness = set_up)] async fn when_not_already_logged_in(app: DivviupApi) -> TestResult { - let conn = get("/login").with_api_host().run_async(&app).await; + let resp = get("/login").with_api_host().run_async(&app).await; let auth_base = app.config().auth_url.join("/authorize")?; - assert_status!(conn, 302); - let location = conn.header_str(headers::LOCATION.as_str()).unwrap(); + assert_status!(resp, 302); + let location = resp.header_str(headers::LOCATION.as_str()).unwrap(); assert!(location.starts_with(auth_base.as_ref())); let url = Url::parse(location)?; let query = QueryStrong::parse_strict(url.query().unwrap()).unwrap(); @@ -71,12 +71,12 @@ mod login { #[test(harness = set_up)] async fn when_already_logged_in(app: DivviupApi) -> TestResult { let user = fixtures::user(); - let conn = get("/login") + let resp = get("/login") .with_api_host() .with_user(&user) .run_async(&app) .await; - assert_response!(conn, 302, "", headers::LOCATION => app.config().app_url.as_ref()); + assert_response!(resp, 302, "", headers::LOCATION => app.config().app_url.as_ref()); Ok(()) } } @@ -84,19 +84,19 @@ mod login { #[test(harness = set_up)] async fn logout(app: DivviupApi) -> TestResult { let user = fixtures::user(); - let conn = get("/logout") + let resp = get("/logout") .with_api_host() .with_user(&user) .run_async(&app) .await; - assert_response!(conn, 302); + assert_response!(resp, 302); // session.flush() in the logout handler should delete any session that was // created during the request — verify no sessions remain in the store. let session_count = session::Entity::find().count(app.db()).await?; assert_eq!(session_count, 0, "session should be destroyed after logout"); - let location: Url = conn + let location: Url = resp .header_str(headers::LOCATION.as_str()) .unwrap() .parse()?; diff --git a/tests/integration/collector_credentials.rs b/tests/integration/collector_credentials.rs index e5e144f30..b61dab310 100644 --- a/tests/integration/collector_credentials.rs +++ b/tests/integration/collector_credentials.rs @@ -16,7 +16,7 @@ mod index { let deleted = fixtures::collector_credential(&app, &account).await; let _deleted = deleted.tombstone().update(app.db()).await.unwrap(); - let conn = get(format!( + let resp = get(format!( "/api/accounts/{}/collector_credentials", account.id )) @@ -24,9 +24,9 @@ mod index { .with_state(user) .run_async(&app) .await; - assert_ok!(conn); + assert_ok!(resp); - let collector_credentials: Vec = conn.response_json(); + let collector_credentials: Vec = resp.response_json(); assert_same_json_representation( &collector_credentials, &vec![collector_credential1, collector_credential2], @@ -42,7 +42,7 @@ mod index { fixtures::collector_credential(&app, &account).await; fixtures::collector_credential(&app, &account).await; - let conn = get(format!( + let resp = get(format!( "/api/accounts/{}/collector_credentials", account.id )) @@ -51,7 +51,7 @@ mod index { .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } @@ -64,13 +64,13 @@ mod index { fixtures::collector_credential(&app, &account).await; fixtures::collector_credential(&app, &account).await; - let conn = get("/api/accounts/not-an-account/collector_credentials") + let resp = get("/api/accounts/not-an-account/collector_credentials") .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_not_found!(conn); + assert_not_found!(resp); Ok(()) } @@ -82,7 +82,7 @@ mod index { let collector_credential1 = fixtures::collector_credential(&app, &account).await; let collector_credential2 = fixtures::collector_credential(&app, &account).await; - let conn = get(format!( + let resp = get(format!( "/api/accounts/{}/collector_credentials", account.id )) @@ -91,8 +91,8 @@ mod index { .run_async(&app) .await; - assert_ok!(conn); - let collector_credentials: Vec = conn.response_json(); + assert_ok!(resp); + let collector_credentials: Vec = resp.response_json(); assert_same_json_representation( &collector_credentials, &vec![collector_credential1, collector_credential2], @@ -107,7 +107,7 @@ mod index { let collector_credential1 = fixtures::collector_credential(&app, &account).await; let collector_credential2 = fixtures::collector_credential(&app, &account).await; - let conn = get(format!( + let resp = get(format!( "/api/accounts/{}/collector_credentials", account.id )) @@ -116,8 +116,8 @@ mod index { .run_async(&app) .await; - assert_ok!(conn); - let collector_credentials: Vec = conn.response_json(); + assert_ok!(resp); + let collector_credentials: Vec = resp.response_json(); assert_same_json_representation( &collector_credentials, &vec![collector_credential1, collector_credential2], @@ -132,7 +132,7 @@ mod index { let collector_credential1 = fixtures::collector_credential(&app, &account).await; let collector_credential2 = fixtures::collector_credential(&app, &account).await; - let conn = get(format!( + let resp = get(format!( "/api/accounts/{}/collector_credentials", account.id )) @@ -141,8 +141,8 @@ mod index { .run_async(&app) .await; - assert_ok!(conn); - let collector_credentials: Vec = conn.response_json(); + assert_ok!(resp); + let collector_credentials: Vec = resp.response_json(); assert_same_json_representation( &collector_credentials, &vec![collector_credential1, collector_credential2], @@ -159,7 +159,7 @@ mod index { fixtures::collector_credential(&app, &account).await; fixtures::collector_credential(&app, &account).await; - let conn = get(format!( + let resp = get(format!( "/api/accounts/{}/collector_credentials", account.id )) @@ -167,7 +167,7 @@ mod index { .with_auth_header(token) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } } @@ -187,7 +187,7 @@ mod create { let (user, account, ..) = fixtures::member(&app).await; let hpke_config = random_hpke_config(); - let conn = post(format!( + let resp = post(format!( "/api/accounts/{}/collector_credentials", account.id )) @@ -196,8 +196,8 @@ mod create { .with_request_json(valid_collector_credential_json(hpke_config.clone())) .run_async(&app) .await; - assert_response!(conn, 201); - let mut collector_credential: CollectorCredential = conn.response_json(); + assert_response!(resp, 201); + let mut collector_credential: CollectorCredential = resp.response_json(); let token = collector_credential.token.take(); assert!(token.is_some()); assert!(collector_credential.token_hash.is_some()); @@ -214,7 +214,7 @@ mod create { let collector_credential_count_before = CollectorCredentials::find().count(app.db()).await?; - let conn = post(format!( + let resp = post(format!( "/api/accounts/{}/collector_credentials", account.id )) @@ -224,7 +224,7 @@ mod create { .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); let collector_credential_count_after = CollectorCredentials::find().count(app.db()).await?; assert_eq!( collector_credential_count_before, @@ -240,14 +240,14 @@ mod create { let collector_credential_count_before = CollectorCredentials::find().count(app.db()).await?; - let conn = post("/api/accounts/does-not-exist/collector_credentials") + let resp = post("/api/accounts/does-not-exist/collector_credentials") .with_api_headers() .with_state(user) .with_request_json(valid_collector_credential_json(random_hpke_config())) .run_async(&app) .await; - assert_not_found!(conn); + assert_not_found!(resp); let collector_credential_count_after = CollectorCredentials::find().count(app.db()).await?; assert_eq!( collector_credential_count_before, @@ -261,7 +261,7 @@ mod create { async fn admin_not_member(app: DivviupApi) -> TestResult { let (admin, ..) = fixtures::admin(&app).await; let account = fixtures::account(&app).await; - let conn = post(format!( + let resp = post(format!( "/api/accounts/{}/collector_credentials", account.id )) @@ -271,8 +271,8 @@ mod create { .run_async(&app) .await; - assert_response!(conn, 201); - let mut collector_credential: CollectorCredential = conn.response_json(); + assert_response!(resp, 201); + let mut collector_credential: CollectorCredential = resp.response_json(); let token = collector_credential.token.take(); assert!(token.is_some()); assert!(collector_credential.token_hash.is_some()); @@ -285,7 +285,7 @@ mod create { async fn admin_token(app: DivviupApi) -> TestResult { let token = fixtures::admin_token(&app).await; let account = fixtures::account(&app).await; - let conn = post(format!( + let resp = post(format!( "/api/accounts/{}/collector_credentials", account.id )) @@ -295,8 +295,8 @@ mod create { .run_async(&app) .await; - assert_response!(conn, 201); - let mut collector_credential: CollectorCredential = conn.response_json(); + assert_response!(resp, 201); + let mut collector_credential: CollectorCredential = resp.response_json(); let token = collector_credential.token.take(); assert!(token.is_some()); assert!(collector_credential.token_hash.is_some()); @@ -309,7 +309,7 @@ mod create { async fn member_token(app: DivviupApi) -> TestResult { let account = fixtures::account(&app).await; let (_, token) = fixtures::api_token(&app, &account).await; - let conn = post(format!( + let resp = post(format!( "/api/accounts/{}/collector_credentials", account.id )) @@ -319,8 +319,8 @@ mod create { .run_async(&app) .await; - assert_response!(conn, 201); - let mut collector_credential: CollectorCredential = conn.response_json(); + assert_response!(resp, 201); + let mut collector_credential: CollectorCredential = resp.response_json(); let token = collector_credential.token.take(); assert!(token.is_some()); assert!(collector_credential.token_hash.is_some()); @@ -335,7 +335,7 @@ mod create { let (_, token) = fixtures::api_token(&app, &other_account).await; let account = fixtures::account(&app).await; let count_before = CollectorCredentials::find().count(app.db()).await?; - let conn = post(format!( + let resp = post(format!( "/api/accounts/{}/collector_credentials", account.id )) @@ -344,7 +344,7 @@ mod create { .with_request_json(valid_collector_credential_json(random_hpke_config())) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); let count_after = CollectorCredentials::find().count(app.db()).await?; assert_eq!(count_before, count_after); Ok(()) @@ -359,12 +359,12 @@ mod delete { #[test(harness = set_up)] async fn nonexistant_collector_credential(app: DivviupApi) -> TestResult { let (user, ..) = fixtures::member(&app).await; - let conn = delete(format!("/api/collector_credentials/{}", Uuid::new_v4())) + let resp = delete(format!("/api/collector_credentials/{}", Uuid::new_v4())) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } @@ -372,7 +372,7 @@ mod delete { async fn as_member(app: DivviupApi) -> TestResult { let (user, account, ..) = fixtures::member(&app).await; let collector_credential = fixtures::collector_credential(&app, &account).await; - let conn = delete(format!( + let resp = delete(format!( "/api/collector_credentials/{}", collector_credential.id )) @@ -380,7 +380,7 @@ mod delete { .with_state(user) .run_async(&app) .await; - assert_status!(conn, 204); + assert_status!(resp, 204); assert!(collector_credential .reload(app.db()) .await? @@ -395,7 +395,7 @@ mod delete { let account = fixtures::account(&app).await; let (user, ..) = fixtures::member(&app).await; let collector_credential = fixtures::collector_credential(&app, &account).await; - let conn = delete(format!( + let resp = delete(format!( "/api/collector_credentials/{}", collector_credential.id )) @@ -403,7 +403,7 @@ mod delete { .with_state(user) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); assert!(!collector_credential .reload(app.db()) .await? @@ -418,7 +418,7 @@ mod delete { let (admin, ..) = fixtures::admin(&app).await; let account = fixtures::account(&app).await; let collector_credential = fixtures::collector_credential(&app, &account).await; - let conn = delete(format!( + let resp = delete(format!( "/api/collector_credentials/{}", collector_credential.id )) @@ -426,7 +426,7 @@ mod delete { .with_state(admin) .run_async(&app) .await; - assert_status!(conn, 204); + assert_status!(resp, 204); assert!(collector_credential .reload(app.db()) .await? @@ -441,7 +441,7 @@ mod delete { let token = fixtures::admin_token(&app).await; let account = fixtures::account(&app).await; let collector_credential = fixtures::collector_credential(&app, &account).await; - let conn = delete(format!( + let resp = delete(format!( "/api/collector_credentials/{}", collector_credential.id )) @@ -449,7 +449,7 @@ mod delete { .with_auth_header(token) .run_async(&app) .await; - assert_status!(conn, 204); + assert_status!(resp, 204); assert!(collector_credential .reload(app.db()) .await? @@ -464,7 +464,7 @@ mod delete { let account = fixtures::account(&app).await; let (_, token) = fixtures::api_token(&app, &account).await; let collector_credential = fixtures::collector_credential(&app, &account).await; - let conn = delete(format!( + let resp = delete(format!( "/api/collector_credentials/{}", collector_credential.id )) @@ -472,7 +472,7 @@ mod delete { .with_auth_header(token) .run_async(&app) .await; - assert_status!(conn, 204); + assert_status!(resp, 204); assert!(collector_credential .reload(app.db()) .await? @@ -487,7 +487,7 @@ mod delete { let (_, token) = fixtures::api_token(&app, &other_account).await; let account = fixtures::account(&app).await; let collector_credential = fixtures::collector_credential(&app, &account).await; - let conn = delete(format!( + let resp = delete(format!( "/api/collector_credentials/{}", collector_credential.id )) @@ -495,7 +495,7 @@ mod delete { .with_auth_header(token) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); assert!(!collector_credential .reload(app.db()) .await? @@ -513,13 +513,13 @@ mod update { #[test(harness = set_up)] async fn nonexistant_collector_credential(app: DivviupApi) -> TestResult { let (user, ..) = fixtures::member(&app).await; - let conn = patch(format!("/api/collector_credentials/{}", Uuid::new_v4())) + let resp = patch(format!("/api/collector_credentials/{}", Uuid::new_v4())) .with_request_json(json!({ "name": fixtures::random_name() })) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } @@ -528,7 +528,7 @@ mod update { let (user, account, ..) = fixtures::member(&app).await; let collector_credential = fixtures::collector_credential(&app, &account).await; let name = fixtures::random_name(); - let conn = patch(format!( + let resp = patch(format!( "/api/collector_credentials/{}", collector_credential.id )) @@ -537,8 +537,8 @@ mod update { .with_state(user) .run_async(&app) .await; - assert_status!(conn, 200); - let response: CollectorCredential = conn.response_json(); + assert_status!(resp, 200); + let response: CollectorCredential = resp.response_json(); assert_eq!(response.name.unwrap(), name); assert_eq!( collector_credential @@ -558,7 +558,7 @@ mod update { let account = fixtures::account(&app).await; let (user, ..) = fixtures::member(&app).await; let collector_credential = fixtures::collector_credential(&app, &account).await; - let conn = patch(format!( + let resp = patch(format!( "/api/collector_credentials/{}", collector_credential.id )) @@ -568,7 +568,7 @@ mod update { .run_async(&app) .await; let name_before = collector_credential.name.clone(); - assert_response!(conn, 403); + assert_response!(resp, 403); assert_eq!( collector_credential.reload(app.db()).await?.unwrap().name, name_before @@ -583,7 +583,7 @@ mod update { let account = fixtures::account(&app).await; let collector_credential = fixtures::collector_credential(&app, &account).await; let name = fixtures::random_name(); - let conn = patch(format!( + let resp = patch(format!( "/api/collector_credentials/{}", collector_credential.id )) @@ -592,8 +592,8 @@ mod update { .with_state(admin) .run_async(&app) .await; - assert_status!(conn, 200); - let response: CollectorCredential = conn.response_json(); + assert_status!(resp, 200); + let response: CollectorCredential = resp.response_json(); assert_eq!(response.name.unwrap(), name); assert_eq!( collector_credential @@ -614,7 +614,7 @@ mod update { let account = fixtures::account(&app).await; let collector_credential = fixtures::collector_credential(&app, &account).await; let name = fixtures::random_name(); - let conn = patch(format!( + let resp = patch(format!( "/api/collector_credentials/{}", collector_credential.id )) @@ -623,8 +623,8 @@ mod update { .with_auth_header(token) .run_async(&app) .await; - assert_status!(conn, 200); - let response: CollectorCredential = conn.response_json(); + assert_status!(resp, 200); + let response: CollectorCredential = resp.response_json(); assert_eq!(response.name.unwrap(), name); assert_eq!( collector_credential @@ -645,7 +645,7 @@ mod update { let (_, token) = fixtures::api_token(&app, &account).await; let collector_credential = fixtures::collector_credential(&app, &account).await; let name = fixtures::random_name(); - let conn = patch(format!( + let resp = patch(format!( "/api/collector_credentials/{}", collector_credential.id )) @@ -654,8 +654,8 @@ mod update { .with_auth_header(token) .run_async(&app) .await; - assert_status!(conn, 200); - let response: CollectorCredential = conn.response_json(); + assert_status!(resp, 200); + let response: CollectorCredential = resp.response_json(); assert_eq!(response.name.unwrap(), name); assert_eq!( collector_credential @@ -678,7 +678,7 @@ mod update { let collector_credential = fixtures::collector_credential(&app, &account).await; let name_before = collector_credential.name.clone(); let name = fixtures::random_name(); - let conn = patch(format!( + let resp = patch(format!( "/api/collector_credentials/{}", collector_credential.id )) @@ -687,7 +687,7 @@ mod update { .with_auth_header(token) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); assert_eq!( collector_credential.reload(app.db()).await?.unwrap().name, name_before diff --git a/tests/integration/memberships.rs b/tests/integration/memberships.rs index 90765db75..cf377791f 100644 --- a/tests/integration/memberships.rs +++ b/tests/integration/memberships.rs @@ -8,16 +8,16 @@ mod create { #[test(harness = set_up)] async fn success(app: DivviupApi) -> TestResult { let (user, account, ..) = fixtures::member(&app).await; - let conn = post(format!("/api/accounts/{}/memberships", account.id)) + let resp = post(format!("/api/accounts/{}/memberships", account.id)) .with_api_headers() .with_request_json(json!({ "user_email": "someone.else@example.com" })) .with_state(user) .run_async(&app) .await; - assert_response!(conn, 201); + assert_response!(resp, 201); - let membership: Membership = conn.response_json(); + let membership: Membership = resp.response_json(); assert_eq!(membership.user_email, "someone.else@example.com"); assert_eq!(membership.account_id, account.id); let membership_id = membership.id; @@ -40,21 +40,21 @@ mod create { let membership_count_before = Memberships::find().count(app.db()).await?; - let conn = post(format!("/api/accounts/{}/memberships", account.id)) + let resp = post(format!("/api/accounts/{}/memberships", account.id)) .with_api_headers() .with_request_json(json!({ "user_email": other_user.email })) .with_state(user) .run_async(&app) .await; - assert_response!(conn, 200); + assert_response!(resp, 200); assert_eq!( membership_count_before, Memberships::find().count(app.db()).await? ); - let membership: Membership = conn.response_json(); + let membership: Membership = resp.response_json(); assert_eq!(membership.user_email, other_user.email); assert_eq!(membership.account_id, account.id); assert!(membership.reload(app.db()).await?.is_some()); @@ -67,16 +67,16 @@ mod create { let (user, account, ..) = fixtures::member(&app).await; let membership_count_before = Memberships::find().count(app.db()).await?; - let conn = post(format!("/api/accounts/{}/memberships", account.id)) + let resp = post(format!("/api/accounts/{}/memberships", account.id)) .with_api_headers() .with_request_json(json!({ "user_email": "not a valid email" })) .with_state(user) .run_async(&app) .await; - assert_response!(conn, 400); + assert_response!(resp, 400); - let errors: Value = conn.response_json(); + let errors: Value = resp.response_json(); assert!(errors.get("user_email").is_some()); assert_eq!( @@ -90,26 +90,26 @@ mod create { async fn not_member(app: DivviupApi) -> TestResult { let (user, ..) = fixtures::member(&app).await; let account = fixtures::account(&app).await; - let conn = post(format!("/api/accounts/{}/memberships", account.id)) + let resp = post(format!("/api/accounts/{}/memberships", account.id)) .with_api_headers() .with_request_json(json!({ "user_email": "someone.else@example.com" })) .with_state(user) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } #[test(harness = set_up)] async fn nonexistant_account(app: DivviupApi) -> TestResult { let (user, ..) = fixtures::member(&app).await; - let conn = post("/api/accounts/no-account-with-this-id/memberships") + let resp = post("/api/accounts/no-account-with-this-id/memberships") .with_api_headers() .with_request_json(json!({ "user_email": "someone.else@example.com" })) .with_state(user) .run_async(&app) .await; - assert_status!(conn, 404); + assert_status!(resp, 404); Ok(()) } @@ -117,16 +117,16 @@ mod create { async fn admin_not_member(app: DivviupApi) -> TestResult { let (user, ..) = fixtures::admin(&app).await; let account = fixtures::account(&app).await; - let conn = post(format!("/api/accounts/{}/memberships", account.id)) + let resp = post(format!("/api/accounts/{}/memberships", account.id)) .with_api_headers() .with_request_json(json!({ "user_email": "someone.else@example.com" })) .with_state(user) .run_async(&app) .await; - assert_response!(conn, 201); + assert_response!(resp, 201); - let membership: Membership = conn.response_json(); + let membership: Membership = resp.response_json(); assert_eq!(membership.user_email, "someone.else@example.com"); assert_eq!(membership.account_id, account.id); assert!(membership.reload(app.db()).await?.is_some()); @@ -139,14 +139,14 @@ mod create { let token = fixtures::admin_token(&app).await; let account = fixtures::account(&app).await; let email = format!("{}@example.com", fixtures::random_name()); - let conn = post(format!("/api/accounts/{}/memberships", account.id)) + let resp = post(format!("/api/accounts/{}/memberships", account.id)) .with_api_headers() .with_request_json(json!({ "user_email": email })) .with_auth_header(token) .run_async(&app) .await; - assert_response!(conn, 201); - let membership: Membership = conn.response_json(); + assert_response!(resp, 201); + let membership: Membership = resp.response_json(); assert_eq!(membership.user_email, email); assert_eq!(membership.account_id, account.id); assert!(membership.reload(app.db()).await?.is_some()); @@ -158,14 +158,14 @@ mod create { let account = fixtures::account(&app).await; let (_, token) = fixtures::api_token(&app, &account).await; let email = format!("{}@example.com", fixtures::random_name()); - let conn = post(format!("/api/accounts/{}/memberships", account.id)) + let resp = post(format!("/api/accounts/{}/memberships", account.id)) .with_api_headers() .with_request_json(json!({ "user_email": email })) .with_auth_header(token) .run_async(&app) .await; - assert_response!(conn, 201); - let membership: Membership = conn.response_json(); + assert_response!(resp, 201); + let membership: Membership = resp.response_json(); assert_eq!(membership.user_email, email); assert_eq!(membership.account_id, account.id); assert!(membership.reload(app.db()).await?.is_some()); @@ -179,13 +179,13 @@ mod create { let account = fixtures::account(&app).await; let email = format!("{}@example.com", fixtures::random_name()); let count_before = Memberships::find().count(app.db()).await?; - let conn = post(format!("/api/accounts/{}/memberships", account.id)) + let resp = post(format!("/api/accounts/{}/memberships", account.id)) .with_api_headers() .with_request_json(json!({ "user_email": email })) .with_auth_header(token) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); let count_after = Memberships::find().count(app.db()).await?; assert_eq!(count_before, count_after); Ok(()) @@ -202,13 +202,13 @@ mod index { let (user, account, ..) = fixtures::member(&app).await; fixtures::membership(&app, &account, &fixtures::user()).await; fixtures::membership(&app, &account, &fixtures::user()).await; - let conn = get(format!("/api/accounts/{}/memberships", account.id)) + let resp = get(format!("/api/accounts/{}/memberships", account.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_ok!(conn); - let memberships: Vec = conn.response_json(); + assert_ok!(resp); + let memberships: Vec = resp.response_json(); assert_eq!(memberships.len(), 3); Ok(()) } @@ -217,24 +217,24 @@ mod index { async fn not_member(app: DivviupApi) -> TestResult { let (_, account, ..) = fixtures::member(&app).await; let (user, ..) = fixtures::member(&app).await; - let conn = get(format!("/api/accounts/{}/memberships", account.id)) + let resp = get(format!("/api/accounts/{}/memberships", account.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } #[test(harness = set_up)] async fn nonexistant_account(app: DivviupApi) -> TestResult { let (user, ..) = fixtures::member(&app).await; - let conn = get("/api/accounts/not-an-id/memberships") + let resp = get("/api/accounts/not-an-id/memberships") .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_not_found!(conn); + assert_not_found!(resp); Ok(()) } @@ -245,13 +245,13 @@ mod index { fixtures::membership(&app, &account, &fixtures::user()).await; fixtures::membership(&app, &account, &fixtures::user()).await; - let conn = get(format!("/api/accounts/{}/memberships", account.id)) + let resp = get(format!("/api/accounts/{}/memberships", account.id)) .with_api_headers() .with_state(admin) .run_async(&app) .await; - assert_ok!(conn); - let memberships: Vec = conn.response_json(); + assert_ok!(resp); + let memberships: Vec = resp.response_json(); assert_eq!(memberships.len(), 2); Ok(()) @@ -264,13 +264,13 @@ mod index { fixtures::membership(&app, &account, &fixtures::user()).await; fixtures::membership(&app, &account, &fixtures::user()).await; - let conn = get(format!("/api/accounts/{}/memberships", account.id)) + let resp = get(format!("/api/accounts/{}/memberships", account.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_ok!(conn); - let memberships: Vec = conn.response_json(); + assert_ok!(resp); + let memberships: Vec = resp.response_json(); assert_eq!(memberships.len(), 2); Ok(()) } @@ -282,13 +282,13 @@ mod index { fixtures::membership(&app, &account, &fixtures::user()).await; fixtures::membership(&app, &account, &fixtures::user()).await; - let conn = get(format!("/api/accounts/{}/memberships", account.id)) + let resp = get(format!("/api/accounts/{}/memberships", account.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_ok!(conn); - let memberships: Vec = conn.response_json(); + assert_ok!(resp); + let memberships: Vec = resp.response_json(); assert_eq!(memberships.len(), 2); Ok(()) } @@ -301,12 +301,12 @@ mod index { fixtures::membership(&app, &account, &fixtures::user()).await; fixtures::membership(&app, &account, &fixtures::user()).await; - let conn = get(format!("/api/accounts/{}/memberships", account.id)) + let resp = get(format!("/api/accounts/{}/memberships", account.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } } @@ -319,12 +319,12 @@ mod delete { let (user, account, ..) = fixtures::member(&app).await; let other_membership = fixtures::membership(&app, &account, &fixtures::user()).await; fixtures::membership(&app, &account, &fixtures::user()).await; - let conn = delete(format!("/api/memberships/{}", other_membership.id)) + let resp = delete(format!("/api/memberships/{}", other_membership.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_response!(conn, 204); + assert_response!(resp, 204); assert!(other_membership.reload(app.db()).await?.is_none()); Ok(()) @@ -335,12 +335,12 @@ mod delete { let (user, ..) = fixtures::member(&app).await; let account = fixtures::account(&app).await; let other_membership = fixtures::membership(&app, &account, &fixtures::user()).await; - let conn = delete(format!("/api/memberships/{}", other_membership.id)) + let resp = delete(format!("/api/memberships/{}", other_membership.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_not_found!(conn); + assert_not_found!(resp); assert!(other_membership.reload(app.db()).await?.is_some()); Ok(()) } @@ -348,24 +348,24 @@ mod delete { #[test(harness = set_up)] async fn nonexistant_id(app: DivviupApi) -> TestResult { let (user, ..) = fixtures::member(&app).await; - let conn = delete("/api/memberships/876b2071-9da8-4bda-bd4c-8d42a3ae7d90") + let resp = delete("/api/memberships/876b2071-9da8-4bda-bd4c-8d42a3ae7d90") .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_not_found!(conn); + assert_not_found!(resp); Ok(()) } #[test(harness = set_up)] async fn removing_self(app: DivviupApi) -> TestResult { let (user, _, membership) = fixtures::member(&app).await; - let conn = delete(format!("/api/memberships/{}", membership.id)) + let resp = delete(format!("/api/memberships/{}", membership.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_status!(conn, 403); + assert_status!(resp, 403); Ok(()) } @@ -374,12 +374,12 @@ mod delete { let (user, ..) = fixtures::admin(&app).await; let account = fixtures::account(&app).await; let membership = fixtures::membership(&app, &account, &fixtures::user()).await; - let conn = delete(format!("/api/memberships/{}", membership.id)) + let resp = delete(format!("/api/memberships/{}", membership.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_response!(conn, 204); + assert_response!(resp, 204); assert!(membership.reload(app.db()).await?.is_none()); Ok(()) } @@ -390,12 +390,12 @@ mod delete { let account = fixtures::account(&app).await; let membership = fixtures::membership(&app, &account, &fixtures::user()).await; let count_before = Memberships::find().count(app.db()).await?; - let conn = delete(format!("/api/memberships/{}", membership.id)) + let resp = delete(format!("/api/memberships/{}", membership.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_response!(conn, 204); + assert_response!(resp, 204); assert!(membership.reload(app.db()).await?.is_none()); let count_after = Memberships::find().count(app.db()).await?; assert_eq!(count_before - 1, count_after); @@ -408,12 +408,12 @@ mod delete { let (_, token) = fixtures::api_token(&app, &account).await; let membership = fixtures::membership(&app, &account, &fixtures::user()).await; let count_before = Memberships::find().count(app.db()).await?; - let conn = delete(format!("/api/memberships/{}", membership.id)) + let resp = delete(format!("/api/memberships/{}", membership.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_response!(conn, 204); + assert_response!(resp, 204); assert!(membership.reload(app.db()).await?.is_none()); let count_after = Memberships::find().count(app.db()).await?; assert_eq!(count_before - 1, count_after); @@ -427,12 +427,12 @@ mod delete { let account = fixtures::account(&app).await; let membership = fixtures::membership(&app, &account, &fixtures::user()).await; let count_before = Memberships::find().count(app.db()).await?; - let conn = delete(format!("/api/memberships/{}", membership.id)) + let resp = delete(format!("/api/memberships/{}", membership.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_not_found!(conn); + assert_not_found!(resp); assert!(membership.reload(app.db()).await?.is_some()); let count_after = Memberships::find().count(app.db()).await?; assert_eq!(count_before, count_after); diff --git a/tests/integration/tasks.rs b/tests/integration/tasks.rs index 9ebeb9428..2bd20c9cd 100644 --- a/tests/integration/tasks.rs +++ b/tests/integration/tasks.rs @@ -12,13 +12,13 @@ mod index { am.deleted_at = ActiveValue::Set(Some(OffsetDateTime::now_utc())); let _ = am.update(app.db()).await?; - let conn = get(format!("/api/accounts/{}/tasks", account.id)) + let resp = get(format!("/api/accounts/{}/tasks", account.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_ok!(conn); - let tasks: Vec = conn.response_json(); + assert_ok!(resp); + let tasks: Vec = resp.response_json(); assert!(tasks.is_empty()); Ok(()) } @@ -32,14 +32,14 @@ mod index { let task1 = fixtures::task(&app, &account).await; let task2 = fixtures::task(&app, &account).await; - let conn = get(format!("/api/accounts/{}/tasks", account.id)) + let resp = get(format!("/api/accounts/{}/tasks", account.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_ok!(conn); - let tasks: Vec = conn.response_json(); + assert_ok!(resp); + let tasks: Vec = resp.response_json(); assert_eq!(vec![task1, task2], tasks); Ok(()) } @@ -52,13 +52,13 @@ mod index { fixtures::task(&app, &account).await; fixtures::task(&app, &account).await; - let conn = get(format!("/api/accounts/{}/tasks", account.id)) + let resp = get(format!("/api/accounts/{}/tasks", account.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } @@ -70,13 +70,13 @@ mod index { fixtures::task(&app, &account).await; fixtures::task(&app, &account).await; - let conn = get("/api/accounts/not-an-account/tasks") + let resp = get("/api/accounts/not-an-account/tasks") .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_not_found!(conn); + assert_not_found!(resp); Ok(()) } @@ -89,14 +89,14 @@ mod index { let task1 = fixtures::task(&app, &account).await; let task2 = fixtures::task(&app, &account).await; - let conn = get(format!("/api/accounts/{}/tasks", account.id)) + let resp = get(format!("/api/accounts/{}/tasks", account.id)) .with_api_headers() .with_state(admin) .run_async(&app) .await; - assert_ok!(conn); - let tasks: Vec = conn.response_json(); + assert_ok!(resp); + let tasks: Vec = resp.response_json(); assert_eq!(vec![task1, task2], tasks); Ok(()) } @@ -108,14 +108,14 @@ mod index { let task1 = fixtures::task(&app, &account).await; let task2 = fixtures::task(&app, &account).await; - let conn = get(format!("/api/accounts/{}/tasks", account.id)) + let resp = get(format!("/api/accounts/{}/tasks", account.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_ok!(conn); - let tasks: Vec = conn.response_json(); + assert_ok!(resp); + let tasks: Vec = resp.response_json(); assert_eq!(vec![task1, task2], tasks); Ok(()) } @@ -127,14 +127,14 @@ mod index { let task1 = fixtures::task(&app, &account).await; let task2 = fixtures::task(&app, &account).await; - let conn = get(format!("/api/accounts/{}/tasks", account.id)) + let resp = get(format!("/api/accounts/{}/tasks", account.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_ok!(conn); - let tasks: Vec = conn.response_json(); + assert_ok!(resp); + let tasks: Vec = resp.response_json(); assert_eq!(vec![task1, task2], tasks); Ok(()) } @@ -148,13 +148,13 @@ mod index { fixtures::task(&app, &account).await; fixtures::task(&app, &account).await; - let conn = get(format!("/api/accounts/{}/tasks", account.id)) + let resp = get(format!("/api/accounts/{}/tasks", account.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } } @@ -188,7 +188,7 @@ mod create { let (leader, helper) = fixtures::aggregator_pair(&app, &account).await; let collector_credential = fixtures::collector_credential(&app, &account).await; - let conn = post(format!("/api/accounts/{}/tasks", account.id)) + let resp = post(format!("/api/accounts/{}/tasks", account.id)) .with_api_headers() .with_state(user) .with_request_json(valid_task_json(&collector_credential, &leader, &helper)) @@ -211,8 +211,8 @@ mod create { ); assert!(helper_task_create.collector_auth_token_hash.is_none()); - assert_response!(conn, 201); - let task: Task = conn.response_json(); + assert_response!(resp, 201); + let task: Task = resp.response_json(); assert_eq!(task.leader_aggregator_id, leader.id); assert_eq!(task.helper_aggregator_id, helper.id); @@ -237,7 +237,7 @@ mod create { leader.features = ActiveValue::Set(Features::default().into()); let leader = leader.update(app.db()).await?; - let conn = post(format!("/api/accounts/{}/tasks", account.id)) + let resp = post(format!("/api/accounts/{}/tasks", account.id)) .with_api_headers() .with_state(user) .with_request_json(valid_task_json(&collector_credential, &leader, &helper)) @@ -254,8 +254,8 @@ mod create { assert!(leader_task_create.collector_auth_token_hash.is_none()); assert!(helper_task_create.collector_auth_token_hash.is_none()); - assert_response!(conn, 201); - let task: Task = conn.response_json(); + assert_response!(resp, 201); + let task: Task = resp.response_json(); assert_eq!(task.leader_aggregator_id, leader.id); assert_eq!(task.helper_aggregator_id, helper.id); @@ -277,15 +277,15 @@ mod create { let collector_credential = fixtures::collector_credential(&app, &account).await; let leader = leader.tombstone().update(app.db()).await.unwrap(); - let conn = post(format!("/api/accounts/{}/tasks", account.id)) + let resp = post(format!("/api/accounts/{}/tasks", account.id)) .with_api_headers() .with_state(user) .with_request_json(valid_task_json(&collector_credential, &leader, &helper)) .run_async(&app) .await; - assert_response!(conn, StatusCode::BAD_REQUEST); - let error: Value = conn.response_json(); + assert_response!(resp, StatusCode::BAD_REQUEST); + let error: Value = resp.response_json(); assert!(error.get("leader_aggregator_id").is_some()); assert!(client_logs.is_empty()); Ok(()) @@ -301,15 +301,15 @@ mod create { let collector_credential = fixtures::collector_credential(&app, &account).await; let helper = helper.tombstone().update(app.db()).await.unwrap(); - let conn = post(format!("/api/accounts/{}/tasks", account.id)) + let resp = post(format!("/api/accounts/{}/tasks", account.id)) .with_api_headers() .with_state(user) .with_request_json(valid_task_json(&collector_credential, &leader, &helper)) .run_async(&app) .await; - assert_response!(conn, StatusCode::BAD_REQUEST); - let error: Value = conn.response_json(); + assert_response!(resp, StatusCode::BAD_REQUEST); + let error: Value = resp.response_json(); assert!(error.get("helper_aggregator_id").is_some()); assert!(client_logs.is_empty()); Ok(()) @@ -319,7 +319,7 @@ mod create { async fn invalid(app: DivviupApi) -> TestResult { let (user, account, ..) = fixtures::member(&app).await; - let conn = post(format!("/api/accounts/{}/tasks", account.id)) + let resp = post(format!("/api/accounts/{}/tasks", account.id)) .with_api_headers() .with_state(user) .with_request_json(json!({ @@ -332,8 +332,8 @@ mod create { .run_async(&app) .await; - assert_response!(conn, StatusCode::BAD_REQUEST); - let error: Value = conn.response_json(); + assert_response!(resp, StatusCode::BAD_REQUEST); + let error: Value = resp.response_json(); assert!(error.get("vdaf").is_some()); assert!(error.get("min_batch_size").is_some()); assert!(error.get("time_precision_seconds").is_some()); @@ -348,14 +348,14 @@ mod create { let (leader, helper) = fixtures::aggregator_pair(&app, &account).await; let collector_credential = fixtures::collector_credential(&app, &account).await; - let conn = post(format!("/api/accounts/{}/tasks", account.id)) + let resp = post(format!("/api/accounts/{}/tasks", account.id)) .with_api_headers() .with_state(user) .with_request_json(valid_task_json(&collector_credential, &leader, &helper)) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } @@ -367,14 +367,14 @@ mod create { let (leader, helper) = fixtures::aggregator_pair(&app, &account).await; let collector_credential = fixtures::collector_credential(&app, &account).await; - let conn = post("/api/accounts/does-not-exist/tasks") + let resp = post("/api/accounts/does-not-exist/tasks") .with_api_headers() .with_state(user) .with_request_json(valid_task_json(&collector_credential, &leader, &helper)) .run_async(&app) .await; - assert_not_found!(conn); + assert_not_found!(resp); Ok(()) } @@ -386,15 +386,15 @@ mod create { let (leader, helper) = fixtures::aggregator_pair(&app, &account).await; let collector_credential = fixtures::collector_credential(&app, &account).await; - let conn = post(format!("/api/accounts/{}/tasks", account.id)) + let resp = post(format!("/api/accounts/{}/tasks", account.id)) .with_api_headers() .with_state(admin) .with_request_json(valid_task_json(&collector_credential, &leader, &helper)) .run_async(&app) .await; - assert_response!(conn, 201); - let task: Task = conn.response_json(); + assert_response!(resp, 201); + let task: Task = resp.response_json(); assert!(task.reload(app.db()).await?.is_some()); Ok(()) } @@ -406,15 +406,15 @@ mod create { let (leader, helper) = fixtures::aggregator_pair(&app, &account).await; let collector_credential = fixtures::collector_credential(&app, &account).await; - let conn = post(format!("/api/accounts/{}/tasks", account.id)) + let resp = post(format!("/api/accounts/{}/tasks", account.id)) .with_api_headers() .with_auth_header(token) .with_request_json(valid_task_json(&collector_credential, &leader, &helper)) .run_async(&app) .await; - assert_response!(conn, 201); - let task: Task = conn.response_json(); + assert_response!(resp, 201); + let task: Task = resp.response_json(); assert!(task.reload(app.db()).await?.is_some()); Ok(()) } @@ -426,16 +426,16 @@ mod create { let (leader, helper) = fixtures::aggregator_pair(&app, &account).await; let collector_credential = fixtures::collector_credential(&app, &account).await; let count_before = Tasks::find().count(app.db()).await?; - let conn = post(format!("/api/accounts/{}/tasks", account.id)) + let resp = post(format!("/api/accounts/{}/tasks", account.id)) .with_api_headers() .with_auth_header(token) .with_request_json(valid_task_json(&collector_credential, &leader, &helper)) .run_async(&app) .await; let count_after = Tasks::find().count(app.db()).await?; - assert_response!(conn, 201); + assert_response!(resp, 201); assert_eq!(count_before + 1, count_after); - let task: Task = conn.response_json(); + let task: Task = resp.response_json(); assert!(task.reload(app.db()).await?.is_some()); Ok(()) } @@ -450,7 +450,7 @@ mod create { let collector_credential = fixtures::collector_credential(&app, &account).await; let count_before = Tasks::find().count(app.db()).await?; - let conn = post(format!("/api/accounts/{}/tasks", account.id)) + let resp = post(format!("/api/accounts/{}/tasks", account.id)) .with_api_headers() .with_auth_header(token) .with_request_json(valid_task_json(&collector_credential, &leader, &helper)) @@ -459,7 +459,7 @@ mod create { let count_after = Tasks::find().count(app.db()).await?; assert_eq!(count_before, count_after); - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } } @@ -473,13 +473,13 @@ mod show { async fn as_member(app: DivviupApi) -> TestResult { let (user, account, ..) = fixtures::member(&app).await; let task = fixtures::task(&app, &account).await; - let conn = get(format!("/api/tasks/{}", task.id)) + let resp = get(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_ok!(conn); - let response_task: Task = conn.response_json(); + assert_ok!(resp); + let response_task: Task = resp.response_json(); assert_eq!(response_task, task); Ok(()) } @@ -497,12 +497,12 @@ mod show { leader.update(app.db()).await?; let leader = task.leader_aggregator(app.db()).await?; - let conn = get(format!("/api/tasks/{}", task.id)) + let resp = get(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_state(user.clone()) .run_async(&app) .await; - assert_ok!(conn); + assert_ok!(resp); let aggregator_api_request = client_logs.last(); assert_eq!( @@ -514,17 +514,17 @@ mod show { ); let metrics: TaskUploadMetrics = aggregator_api_request.response_json(); - let response_task: Task = conn.response_json(); + let response_task: Task = resp.response_json(); assert_eq!(metrics, response_task); assert!(response_task.updated_at > task.updated_at); - let conn = get(format!("/api/tasks/{}", task.id)) + let resp = get(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - let second_response_task: Task = conn.response_json(); + let second_response_task: Task = resp.response_json(); assert_eq!(metrics, second_response_task); assert_eq!(second_response_task.updated_at, response_task.updated_at); @@ -542,12 +542,12 @@ mod show { task.updated_at = ActiveValue::Set(OffsetDateTime::now_utc() - Duration::minutes(10)); let task = task.update(app.db()).await?; - let conn = get(format!("/api/tasks/{}", task.id)) + let resp = get(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_state(user.clone()) .run_async(&app) .await; - assert_ok!(conn); + assert_ok!(resp); // Ensure the aggregator API was never contacted. assert!(client_logs.logs().is_empty()); @@ -560,12 +560,12 @@ mod show { let user = fixtures::user(); let account = fixtures::account(&app).await; let task = fixtures::task(&app, &account).await; - let conn = get(format!("/api/tasks/{}", task.id)) + let resp = get(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } @@ -574,13 +574,13 @@ mod show { let (admin, ..) = fixtures::admin(&app).await; let account = fixtures::account(&app).await; let task = fixtures::task(&app, &account).await; - let conn = get(format!("/api/tasks/{}", task.id)) + let resp = get(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_state(admin) .run_async(&app) .await; - assert_ok!(conn); - let response_task: Task = conn.response_json(); + assert_ok!(resp); + let response_task: Task = resp.response_json(); assert_eq!(response_task, task); Ok(()) } @@ -588,12 +588,12 @@ mod show { #[test(harness = set_up)] async fn nonexistant_task(app: DivviupApi) -> TestResult { let user = fixtures::user(); - let conn = get("/api/tasks/some-made-up-id") + let resp = get("/api/tasks/some-made-up-id") .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } @@ -602,13 +602,13 @@ mod show { let token = fixtures::admin_token(&app).await; let account = fixtures::account(&app).await; let task = fixtures::task(&app, &account).await; - let conn = get(format!("/api/tasks/{}", task.id)) + let resp = get(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_ok!(conn); - let response_task: Task = conn.response_json(); + assert_ok!(resp); + let response_task: Task = resp.response_json(); assert_eq!(response_task, task); Ok(()) } @@ -618,13 +618,13 @@ mod show { let account = fixtures::account(&app).await; let (_, token) = fixtures::api_token(&app, &account).await; let task = fixtures::task(&app, &account).await; - let conn = get(format!("/api/tasks/{}", task.id)) + let resp = get(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_ok!(conn); - let response_task: Task = conn.response_json(); + assert_ok!(resp); + let response_task: Task = resp.response_json(); assert_eq!(response_task, task); Ok(()) } @@ -635,12 +635,12 @@ mod show { let (_, token) = fixtures::api_token(&app, &other_account).await; let account = fixtures::account(&app).await; let task = fixtures::task(&app, &account).await; - let conn = get(format!("/api/tasks/{}", task.id)) + let resp = get(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } } @@ -656,14 +656,14 @@ mod update { let task = fixtures::task(&app, &account).await; let new_name = format!("new name {}", fixtures::random_name()); - let conn = patch(format!("/api/tasks/{}", task.id)) + let resp = patch(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_request_json(json!({ "name": &new_name })) .with_state(user) .run_async(&app) .await; - assert_ok!(conn); - let response_task: Task = conn.response_json(); + assert_ok!(resp); + let response_task: Task = resp.response_json(); assert_eq!(response_task.name, new_name); let task_reload = task.reload(app.db()).await?.unwrap(); @@ -681,14 +681,14 @@ mod update { let (user, account, ..) = fixtures::member(&app).await; let task = fixtures::task(&app, &account).await; - let conn = patch(format!("/api/tasks/{}", task.id)) + let resp = patch(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_request_json(json!({ "name": "" })) .with_state(user) .run_async(&app) .await; - assert_response!(conn, StatusCode::BAD_REQUEST); - let errors: Value = conn.response_json(); + assert_response!(resp, StatusCode::BAD_REQUEST); + let errors: Value = resp.response_json(); assert!(errors.get("name").is_some()); assert_eq!( @@ -706,14 +706,14 @@ mod update { // Set the expiration. let now = OffsetDateTime::now_utc(); - let conn = patch(format!("/api/tasks/{}", task.id)) + let resp = patch(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_request_json(json!({ "expiration": now.format(&Rfc3339).unwrap() })) .with_state(user.clone()) .run_async(&app) .await; - assert_ok!(conn); - let response_task: Task = conn.response_json(); + assert_ok!(resp); + let response_task: Task = resp.response_json(); assert_eq!(response_task.expiration, Some(now)); let task_reload = task.reload(app.db()).await?.unwrap(); @@ -727,14 +727,14 @@ mod update { ); // Unset the expiration. - let conn = patch(format!("/api/tasks/{}", task.id)) + let resp = patch(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_request_json(json!({ "expiration": null })) .with_state(user.clone()) .run_async(&app) .await; - assert_ok!(conn); - let response_task: Task = conn.response_json(); + assert_ok!(resp); + let response_task: Task = resp.response_json(); assert_eq!(response_task.expiration, None); let task_reload = task.reload(app.db()).await?.unwrap(); @@ -750,7 +750,7 @@ mod update { // Set both name and expiration. let now = OffsetDateTime::now_utc(); let new_name = format!("new name {}", fixtures::random_name()); - let conn = patch(format!("/api/tasks/{}", task.id)) + let resp = patch(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_request_json( json!({ "expiration": now.format(&Rfc3339).unwrap(), "name": new_name}), @@ -758,8 +758,8 @@ mod update { .with_state(user) .run_async(&app) .await; - assert_ok!(conn); - let response_task: Task = conn.response_json(); + assert_ok!(resp); + let response_task: Task = resp.response_json(); assert_eq!(response_task.expiration, Some(now)); assert_eq!(response_task.name, new_name); let task_reload = task.reload(app.db()).await?.unwrap(); @@ -774,13 +774,13 @@ mod update { let (user, account, ..) = fixtures::member(&app).await; let task = fixtures::task(&app, &account).await; - let conn = patch(format!("/api/tasks/{}", task.id)) + let resp = patch(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_request_json(json!({ "expiration": "1969-01-01T00:00:00Z" })) .with_state(user) .run_async(&app) .await; - assert_response!(conn, StatusCode::BAD_REQUEST); + assert_response!(resp, StatusCode::BAD_REQUEST); // Task should be unchanged. let task_reload = task.reload(app.db()).await?.unwrap(); @@ -795,14 +795,14 @@ mod update { let account = fixtures::account(&app).await; let task = fixtures::task(&app, &account).await; - let conn = patch(format!("/api/tasks/{}", task.id)) + let resp = patch(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_request_json(json!({ "name": "irrelevant" })) .with_state(user) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); assert_eq!( task.reload(app.db()).await?.unwrap().name, task.name // unchanged @@ -817,14 +817,14 @@ mod update { let task = fixtures::task(&app, &account).await; let new_name = format!("new name {}", fixtures::random_name()); - let conn = patch(format!("/api/tasks/{}", task.id)) + let resp = patch(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_request_json(json!({ "name": &new_name })) .with_state(admin) .run_async(&app) .await; - assert_ok!(conn); - let response_task: Task = conn.response_json(); + assert_ok!(resp); + let response_task: Task = resp.response_json(); assert_eq!(response_task.name, new_name); assert_eq!(task.reload(app.db()).await?.unwrap().name, new_name); Ok(()) @@ -833,13 +833,13 @@ mod update { #[test(harness = set_up)] async fn nonexistant_task(app: DivviupApi) -> TestResult { let user = fixtures::user(); - let conn = patch("/api/tasks/not-a-task-id") + let resp = patch("/api/tasks/not-a-task-id") .with_api_headers() .with_request_json(json!({ "name": "irrelevant" })) .with_state(user) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } @@ -850,14 +850,14 @@ mod update { let task = fixtures::task(&app, &account).await; let new_name = format!("new name {}", fixtures::random_name()); - let conn = patch(format!("/api/tasks/{}", task.id)) + let resp = patch(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_request_json(json!({ "name": &new_name })) .with_auth_header(token) .run_async(&app) .await; - assert_ok!(conn); - let response_task: Task = conn.response_json(); + assert_ok!(resp); + let response_task: Task = resp.response_json(); assert_eq!(response_task.name, new_name); assert_eq!(task.reload(app.db()).await?.unwrap().name, new_name); Ok(()) @@ -870,14 +870,14 @@ mod update { let task = fixtures::task(&app, &account).await; let new_name = format!("new name {}", fixtures::random_name()); - let conn = patch(format!("/api/tasks/{}", task.id)) + let resp = patch(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_request_json(json!({ "name": &new_name })) .with_auth_header(token) .run_async(&app) .await; - assert_ok!(conn); - let response_task: Task = conn.response_json(); + assert_ok!(resp); + let response_task: Task = resp.response_json(); assert_eq!(response_task.name, new_name); assert_eq!(task.reload(app.db()).await?.unwrap().name, new_name); Ok(()) @@ -891,7 +891,7 @@ mod update { let task = fixtures::task(&app, &account).await; let name_before = task.name.clone(); let new_name = format!("new name {}", fixtures::random_name()); - let conn = patch(format!("/api/tasks/{}", task.id)) + let resp = patch(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_request_json(json!({ "name": &new_name })) .with_auth_header(token) @@ -899,7 +899,7 @@ mod update { .await; assert_eq!(task.reload(app.db()).await?.unwrap().name, name_before); - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } } @@ -937,12 +937,12 @@ mod delete { let (user, account, ..) = fixtures::member(&app).await; let task = fixtures::task(&app, &account).await; - let conn = delete(format!("/api/tasks/{}", task.id)) + let resp = delete(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_state(user.clone()) .run_async(&app) .await; - assert_status!(conn, StatusCode::NO_CONTENT); + assert_status!(resp, StatusCode::NO_CONTENT); let task_reload = task.reload(app.db()).await?.unwrap(); assert!(task_reload.expiration.is_some()); assert!(task_reload.expiration <= Some(OffsetDateTime::now_utc())); @@ -952,12 +952,12 @@ mod delete { // Ensure we don't do redundant work when deleting again. let client_logs_len = client_logs.len(); - let conn = delete(format!("/api/tasks/{}", task.id)) + let resp = delete(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_status!(conn, StatusCode::NO_CONTENT); + assert_status!(resp, StatusCode::NO_CONTENT); let task_reload_2 = task.reload(app.db()).await?.unwrap(); assert_eq!(task_reload.deleted_at, task_reload_2.deleted_at); assert_eq!(client_logs_len, client_logs.len()); @@ -973,12 +973,12 @@ mod delete { let (user, account, ..) = fixtures::member(&app).await; let task = fixtures::task(&app, &account).await; - let conn = delete(format!("/api/tasks/{}?force=true", task.id)) + let resp = delete(format!("/api/tasks/{}?force=true", task.id)) .with_api_headers() .with_state(user.clone()) .run_async(&app) .await; - assert_status!(conn, StatusCode::NO_CONTENT); + assert_status!(resp, StatusCode::NO_CONTENT); let task_reload = task.reload(app.db()).await?.unwrap(); assert!(task_reload.expiration.is_some()); assert!(task_reload.expiration <= Some(OffsetDateTime::now_utc())); @@ -1003,12 +1003,12 @@ mod delete { am.expiration = ActiveValue::Set(None); let task = am.update(app.db()).await.unwrap(); - let conn = delete(format!("/api/tasks/{}", task.id)) + let resp = delete(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_state(user.clone()) .run_async(&app) .await; - assert_status!(conn, StatusCode::NO_CONTENT); + assert_status!(resp, StatusCode::NO_CONTENT); let task_reload = task.reload(app.db()).await?.unwrap(); assert!(task_reload.expiration.is_some()); assert!(task_reload.expiration <= Some(OffsetDateTime::now_utc())); @@ -1026,12 +1026,12 @@ mod delete { am.expiration = ActiveValue::Set(Some(OffsetDateTime::from_unix_timestamp(0).unwrap())); let task = am.update(app.db()).await.unwrap(); - let conn = delete(format!("/api/tasks/{}", task.id)) + let resp = delete(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_status!(conn, StatusCode::NO_CONTENT); + assert_status!(resp, StatusCode::NO_CONTENT); let task_reload = task.reload(app.db()).await?.unwrap(); // Shouldn't re-expire an already expired task. assert_eq!(task_reload.expiration, task.expiration); @@ -1047,12 +1047,12 @@ mod delete { let account = fixtures::account(&app).await; let task = fixtures::task(&app, &account).await; - let conn = delete(format!("/api/tasks/{}", task.id)) + let resp = delete(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); let task_reload = task.reload(app.db()).await?.unwrap(); assert_eq!(task_reload.expiration, task.expiration); assert_eq!(task_reload.deleted_at, None); @@ -1065,12 +1065,12 @@ mod delete { let account = fixtures::account(&app).await; let task = fixtures::task(&app, &account).await; - let conn = delete(format!("/api/tasks/{}", task.id)) + let resp = delete(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_state(admin) .run_async(&app) .await; - assert_status!(conn, StatusCode::NO_CONTENT); + assert_status!(resp, StatusCode::NO_CONTENT); let task_reload = task.reload(app.db()).await?.unwrap(); assert!(task_reload.expiration.is_some()); assert!(task_reload.expiration <= Some(OffsetDateTime::now_utc())); @@ -1082,12 +1082,12 @@ mod delete { #[test(harness = set_up)] async fn nonexistant_task(app: DivviupApi) -> TestResult { let user = fixtures::user(); - let conn = delete("/api/tasks/not-a-task-id") + let resp = delete("/api/tasks/not-a-task-id") .with_api_headers() .with_state(user) .run_async(&app) .await; - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } @@ -1097,12 +1097,12 @@ mod delete { let account = fixtures::account(&app).await; let task = fixtures::task(&app, &account).await; - let conn = delete(format!("/api/tasks/{}", task.id)) + let resp = delete(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_status!(conn, StatusCode::NO_CONTENT); + assert_status!(resp, StatusCode::NO_CONTENT); let task_reload = task.reload(app.db()).await?.unwrap(); assert!(task_reload.expiration.is_some()); assert!(task_reload.expiration <= Some(OffsetDateTime::now_utc())); @@ -1117,12 +1117,12 @@ mod delete { let (_, token) = fixtures::api_token(&app, &account).await; let task = fixtures::task(&app, &account).await; - let conn = delete(format!("/api/tasks/{}", task.id)) + let resp = delete(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) .await; - assert_status!(conn, StatusCode::NO_CONTENT); + assert_status!(resp, StatusCode::NO_CONTENT); let task_reload = task.reload(app.db()).await?.unwrap(); assert!(task_reload.expiration.is_some()); assert!(task_reload.expiration <= Some(OffsetDateTime::now_utc())); @@ -1137,7 +1137,7 @@ mod delete { let (_, token) = fixtures::api_token(&app, &other_account).await; let account = fixtures::account(&app).await; let task = fixtures::task(&app, &account).await; - let conn = delete(format!("/api/tasks/{}", task.id)) + let resp = delete(format!("/api/tasks/{}", task.id)) .with_api_headers() .with_auth_header(token) .run_async(&app) @@ -1146,7 +1146,7 @@ mod delete { let task_reload = task.reload(app.db()).await?.unwrap(); assert_eq!(task_reload.expiration, task.expiration); assert_eq!(task_reload.deleted_at, None); - assert_response!(conn, 403); + assert_response!(resp, 403); Ok(()) } } diff --git a/tests/integration/users.rs b/tests/integration/users.rs index 63a53a92d..7df00f3a0 100644 --- a/tests/integration/users.rs +++ b/tests/integration/users.rs @@ -6,13 +6,13 @@ mod get_users_me { #[test(harness = set_up)] async fn as_a_logged_in_user(app: DivviupApi) -> TestResult { let user = fixtures::user(); - let conn = get("/api/users/me") + let resp = get("/api/users/me") .with_api_headers() .with_state(user.clone()) .run_async(&app) .await; - let mut response_user: User = conn.response_json(); + let mut response_user: User = resp.response_json(); assert!(!response_user.is_admin()); response_user.admin = None; // for equality comparison @@ -23,13 +23,13 @@ mod get_users_me { #[test(harness = set_up)] async fn as_an_admin(app: DivviupApi) -> TestResult { let (admin, ..) = fixtures::admin(&app).await; - let conn = get("/api/users/me") + let resp = get("/api/users/me") .with_api_headers() .with_state(admin.clone()) .run_async(&app) .await; - let mut response_user: User = conn.response_json(); + let mut response_user: User = resp.response_json(); assert!(response_user.is_admin()); response_user.admin = None; // for equality comparison @@ -40,13 +40,13 @@ mod get_users_me { #[test(harness = set_up)] async fn as_integration_testing_user(app: DivviupApi) -> TestResult { let user = User::for_integration_testing(); - let conn = get("/api/users/me") + let resp = get("/api/users/me") .with_api_headers() .with_state(user.clone()) .run_async(&app) .await; - let response_user: User = conn.response_json(); + let response_user: User = resp.response_json(); assert!(response_user.is_admin()); assert_eq!(user, response_user); From 92c35ce6a314be79f242cc7450e68296de3249e3 Mon Sep 17 00:00:00 2001 From: "J.C. Jones" Date: Wed, 20 May 2026 09:15:45 -0700 Subject: [PATCH 5/7] Add method back --- client/tests/integration/harness.rs | 2 +- src/clients.rs | 14 +++++++++++--- src/clients/aggregator_client.rs | 10 +++++----- src/clients/auth0_client.rs | 8 ++++---- src/clients/postmark_client.rs | 4 ++-- src/queue/job.rs | 4 +++- 6 files changed, 26 insertions(+), 16 deletions(-) diff --git a/client/tests/integration/harness.rs b/client/tests/integration/harness.rs index e6efe1499..556d567be 100644 --- a/client/tests/integration/harness.rs +++ b/client/tests/integration/harness.rs @@ -35,7 +35,7 @@ where Out: Termination, { with_client_logs(move |app, _api_logs| async move { - install_test_trace_subscriber(); + tracing::install_test_trace_subscriber(); let client_logs = ClientLogs::default(); let router = app .router() diff --git a/src/clients.rs b/src/clients.rs index c17bee1a7..ff2224bce 100644 --- a/src/clients.rs +++ b/src/clients.rs @@ -143,6 +143,7 @@ impl HttpClient { #[derive(Debug)] pub struct HttpStatusNotSuccess { + pub method: Method, pub url: Url, pub status: Option, pub body: String, @@ -150,7 +151,7 @@ pub struct HttpStatusNotSuccess { #[derive(thiserror::Error, Debug)] pub enum ClientError { - #[error("unexpected http status {} {:?}: {}", .0.url, .0.status, .0.body)] + #[error("unexpected http status {} {} {:?}: {}", .0.method, .0.url, .0.status, .0.body)] HttpStatusNotSuccess(Box), #[error(transparent)] @@ -167,12 +168,18 @@ pub enum ClientError { /// and convert non-success into [`ClientError`]. #[async_trait::async_trait] pub trait ResponseExt: Sized { - async fn success_or_client_error(self) -> Result; + async fn success_or_client_error( + self, + method: Method, + ) -> Result; } #[async_trait::async_trait] impl ResponseExt for reqwest::Response { - async fn success_or_client_error(self) -> Result { + async fn success_or_client_error( + self, + method: Method, + ) -> Result { let status = self.status(); if status.is_success() { Ok(self) @@ -181,6 +188,7 @@ impl ResponseExt for reqwest::Response { let body = self.text().await.unwrap_or_default(); Err(ClientError::HttpStatusNotSuccess(Box::new( HttpStatusNotSuccess { + method, url, status: Some(status), body, diff --git a/src/clients/aggregator_client.rs b/src/clients/aggregator_client.rs index 5740ac319..2717bfa03 100644 --- a/src/clients/aggregator_client.rs +++ b/src/clients/aggregator_client.rs @@ -4,7 +4,7 @@ use crate::{ handler::Error, }; use api_types::TaskAggregationJobMetrics; -use axum::http::header; +use axum::http::{header, Method}; use janus_messages::Time as JanusTime; use serde::{de::DeserializeOwned, Serialize}; use url::Url; @@ -42,7 +42,7 @@ impl AggregatorClient { .header(header::ACCEPT, CONTENT_TYPE) .send() .await? - .success_or_client_error() + .success_or_client_error(Method::GET) .await? .json() .await @@ -113,7 +113,7 @@ impl AggregatorClient { .get(path) .send() .await? - .success_or_client_error() + .success_or_client_error(Method::GET) .await? .json() .await @@ -131,7 +131,7 @@ impl AggregatorClient { .json(body) .send() .await? - .success_or_client_error() + .success_or_client_error(Method::POST) .await? .json() .await @@ -149,7 +149,7 @@ impl AggregatorClient { .json(body) .send() .await? - .success_or_client_error() + .success_or_client_error(Method::PATCH) .await? .json() .await diff --git a/src/clients/auth0_client.rs b/src/clients/auth0_client.rs index 7d447b056..889400117 100644 --- a/src/clients/auth0_client.rs +++ b/src/clients/auth0_client.rs @@ -1,5 +1,5 @@ use async_lock::RwLock; -use axum::http::{header, StatusCode}; +use axum::http::{header, Method, StatusCode}; use educe::Educe; use rand::distributions::{Alphanumeric, DistString}; use serde::{de::DeserializeOwned, Serialize}; @@ -154,7 +154,7 @@ impl Auth0Client { })) .send() .await? - .success_or_client_error() + .success_or_client_error(Method::POST) .await? .json() .await?; @@ -184,7 +184,7 @@ impl Auth0Client { .json(json) .send() .await? - .success_or_client_error() + .success_or_client_error(Method::POST) .await? .json() .await @@ -201,7 +201,7 @@ impl Auth0Client { .header(header::AUTHORIZATION, format!("Bearer {token}")) .send() .await? - .success_or_client_error() + .success_or_client_error(Method::GET) .await? .json() .await diff --git a/src/clients/postmark_client.rs b/src/clients/postmark_client.rs index 6f8cde49c..1525066c3 100644 --- a/src/clients/postmark_client.rs +++ b/src/clients/postmark_client.rs @@ -2,7 +2,7 @@ use crate::{ clients::{ClientError, HttpClient, ResponseExt}, Config, }; -use axum::http::{header, HeaderName}; +use axum::http::{header, HeaderName, Method}; use email_address::EmailAddress; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde_json::{json, Value}; @@ -77,7 +77,7 @@ impl PostmarkClient { .json(json) .send() .await? - .success_or_client_error() + .success_or_client_error(Method::POST) .await? .json() .await diff --git a/src/queue/job.rs b/src/queue/job.rs index 5e218605f..cb432c0cd 100644 --- a/src/queue/job.rs +++ b/src/queue/job.rs @@ -29,8 +29,9 @@ pub enum JobError { #[error("{0}")] ClientOther(String), - #[error("unexpected http status {url} {status:?}: {body}")] + #[error("unexpected http status {method} {url} {status:?}: {body}")] HttpStatusNotSuccess { + method: String, url: Url, status: Option, body: String, @@ -56,6 +57,7 @@ impl From for JobError { fn from(value: ClientError) -> Self { match value { ClientError::HttpStatusNotSuccess(e) => Self::HttpStatusNotSuccess { + method: e.method.to_string(), url: e.url, status: e.status.map(|s| s.as_u16()), body: e.body, From 96e2763cc79ffe001061ca6a8241ef13094addc3 Mon Sep 17 00:00:00 2001 From: "J.C. Jones" Date: Thu, 21 May 2026 10:24:51 -0700 Subject: [PATCH 6/7] Review updates --- client/tests/integration/harness.rs | 3 +-- test-support/src/client_logs.rs | 4 ++-- test-support/src/fixtures.rs | 2 -- test-support/src/lib.rs | 8 ++++---- tests/integration/tls_smoke_test.rs | 7 +++++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/client/tests/integration/harness.rs b/client/tests/integration/harness.rs index 556d567be..42cc612e3 100644 --- a/client/tests/integration/harness.rs +++ b/client/tests/integration/harness.rs @@ -34,9 +34,8 @@ where Fut: Future + Send + 'static, Out: Termination, { - with_client_logs(move |app, _api_logs| async move { + with_client_logs(move |app, client_logs| async move { tracing::install_test_trace_subscriber(); - let client_logs = ClientLogs::default(); let router = app .router() .clone() diff --git a/test-support/src/client_logs.rs b/test-support/src/client_logs.rs index af404b701..96f00b943 100644 --- a/test-support/src/client_logs.rs +++ b/test-support/src/client_logs.rs @@ -115,7 +115,7 @@ pub async fn client_logs_middleware( let request_body = if body_bytes.is_empty() { None } else { - String::from_utf8(body_bytes.to_vec()).ok() + Some(String::from_utf8(body_bytes.to_vec()).expect("could not decode body as UTF-8")) }; let request = Request::from_parts(parts, Body::from(body_bytes)); @@ -130,7 +130,7 @@ pub async fn client_logs_middleware( let response_body = if response_bytes.is_empty() { None } else { - String::from_utf8(response_bytes.to_vec()).ok() + Some(String::from_utf8(response_bytes.to_vec()).expect("could not decode body as UTF-8")) }; logs.logged_conns.write().unwrap().push(LoggedConn { diff --git a/test-support/src/fixtures.rs b/test-support/src/fixtures.rs index 062259aa8..81fef90f3 100644 --- a/test-support/src/fixtures.rs +++ b/test-support/src/fixtures.rs @@ -9,8 +9,6 @@ use divviup_api::{ }; use rand::random; use uuid::Uuid; -use HeaderValue; - pub fn user() -> User { User { email: random_email(), diff --git a/test-support/src/lib.rs b/test-support/src/lib.rs index df47c4017..f6c359461 100644 --- a/test-support/src/lib.rs +++ b/test-support/src/lib.rs @@ -435,7 +435,7 @@ impl TestResponse { if self.body.is_empty() { None } else { - String::from_utf8(self.body.to_vec()).ok() + Some(String::from_utf8(self.body.to_vec()).expect("could not decode body as UTF-8")) } } @@ -453,12 +453,12 @@ impl TestResponse { macro_rules! assert_ok { ($resp:expr) => {{ let ref __resp = $resp; - assert_eq!(__resp.status(), $crate::StatusCode::OK); + assert_eq!(__resp.status(), ::axum::http::StatusCode::OK); }}; ($resp:expr, $body:expr $(, $header_name:expr => $header_val:expr)*) => {{ let ref __resp = $resp; - assert_eq!(__resp.status(), $crate::StatusCode::OK); + assert_eq!(__resp.status(), ::axum::http::StatusCode::OK); assert_eq!( __resp.response_body_string().unwrap_or_default(), $body @@ -477,7 +477,7 @@ macro_rules! assert_ok { macro_rules! assert_not_found { ($resp:expr) => {{ let ref __resp = $resp; - assert_eq!(__resp.status(), $crate::StatusCode::NOT_FOUND); + assert_eq!(__resp.status(), ::axum::http::StatusCode::NOT_FOUND); assert_eq!(__resp.response_body_string().unwrap_or_default(), ""); }}; } diff --git a/tests/integration/tls_smoke_test.rs b/tests/integration/tls_smoke_test.rs index a403c7266..cf0fe5ea2 100644 --- a/tests/integration/tls_smoke_test.rs +++ b/tests/integration/tls_smoke_test.rs @@ -28,8 +28,11 @@ async fn https_connection() { server_config.alpn_protocols = vec![b"http/1.1".to_vec()]; let tls_acceptor = TlsAcceptor::from(Arc::new(server_config)); - // Bind to "localhost" (not Ipv6Addr::LOCALHOST) so the address matches the - // self-signed certificate's SAN and the reqwest client's hostname validation. + // Bind to "localhost" so the resolved address matches the loopback IP that + // reqwest and rcgen use for hostname verification. The hostname in the + // listener doesn't need to match the cert's SAN — only the hostname passed + // to reqwest and rcgen matters — but "localhost" ensures we listen on + // whichever loopback address the OS resolves it to. let listener = TcpListener::bind("localhost:0").await.unwrap(); let port = listener.local_addr().unwrap().port(); From 29b686e3ee0e01c480e3558dd91fc7a9c8425487 Mon Sep 17 00:00:00 2001 From: "J.C. Jones" Date: Thu, 21 May 2026 15:04:35 -0700 Subject: [PATCH 7/7] more blank --- test-support/src/fixtures.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/test-support/src/fixtures.rs b/test-support/src/fixtures.rs index 81fef90f3..ae5604c8b 100644 --- a/test-support/src/fixtures.rs +++ b/test-support/src/fixtures.rs @@ -9,6 +9,7 @@ use divviup_api::{ }; use rand::random; use uuid::Uuid; + pub fn user() -> User { User { email: random_email(),