diff --git a/.github/workflows/maintenance-queue.yml b/.github/workflows/maintenance-queue.yml index 2727599..7211df1 100644 --- a/.github/workflows/maintenance-queue.yml +++ b/.github/workflows/maintenance-queue.yml @@ -135,11 +135,11 @@ jobs: - `cargo upgrade` reports a newer manifest dependency requirement for **${dep}**. + \`cargo upgrade\` reports a newer manifest dependency requirement for **${dep}**. ${details} - Update this dependency everywhere appropriate, including incompatible/major changes. Read the crate's changelog/release notes where useful, adapt code to current APIs and best practices, refresh `Cargo.lock`, and run the full relevant CI/test suite. Do not skip an update merely because it is transitive, low-risk, or already SemVer-compatible. + Update this dependency everywhere appropriate, including incompatible/major changes. Read the crate's changelog/release notes where useful, adapt code to current APIs and best practices, refresh \`Cargo.lock\`, and run the full relevant CI/test suite. Do not skip an update merely because it is transitive, low-risk, or already SemVer-compatible. This issue is generated and refreshed automatically. A ChatGPT maintenance automation is expected to implement it. EOF @@ -179,7 +179,7 @@ jobs: log="$(tail -n 200 /tmp/cargo-upgrade.log)" body="$(cat < - The passive dependency detector failed while running `cargo upgrade`. + The passive dependency detector failed while running \`cargo upgrade\`. \`\`\`text ${log} @@ -233,7 +233,7 @@ jobs: - A plain `cargo update` changes `Cargo.lock`, so there are compatible and/or transitive dependency updates available even if no manifest requirement needs changing. + A plain \`cargo update\` changes \`Cargo.lock\`, so there are compatible and/or transitive dependency updates available even if no manifest requirement needs changing. \`\`\`text ${stat} @@ -252,7 +252,7 @@ jobs: gh issue create --repo "$REPO" --title "deps: refresh Cargo.lock" --label "automation:lockfile" --body "$body" fi elif [[ -n "$existing" ]]; then - gh issue close "$existing" --repo "$REPO" --reason completed --comment "Closed automatically: `cargo update` no longer changes Cargo.lock." + gh issue close "$existing" --repo "$REPO" --reason completed --comment "Closed automatically: \`cargo update\` no longer changes Cargo.lock." fi - name: Report cargo-update detector failure @@ -268,7 +268,7 @@ jobs: log="$(tail -n 200 /tmp/cargo-update.log)" body="$(cat < - The passive dependency detector failed while running `cargo update`. + The passive dependency detector failed while running \`cargo update\`. \`\`\`text ${log} @@ -321,7 +321,7 @@ jobs: - Read the official Rust ${VERSION} announcement and detailed release notes/changelog. - Review relevant language, standard-library, rustc, Cargo, Clippy, rustfmt, target/platform, and tooling changes. - - Review all Rust code, `Cargo.toml`/`Cargo.lock`, CI, Docker/build tooling, `SKILL.md`, README/docs, examples, lints, and project conventions for newly preferable patterns or obsolete workarounds. + - Review all Rust code, \`Cargo.toml\`/\`Cargo.lock\`, CI, Docker/build tooling, \`SKILL.md\`, README/docs, examples, lints, and project conventions for newly preferable patterns or obsolete workarounds. - Adopt useful newly-stable features and current best practices where they improve the project; remove workarounds that the new stable release makes unnecessary. - Update the current-stable CI/toolchain references while preserving the intentional MSRV contract unless there is a separately justified reason to raise it. - Run the full CI-equivalent test matrix and fix fallout. diff --git a/Cargo.lock b/Cargo.lock index 1a091c0..3abea57 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -346,16 +346,6 @@ dependencies = [ "tracing-opentelemetry-instrumentation-sdk", ] -[[package]] -name = "axum-valid" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c0cba26dfbdeff786711f747a51efd368c3864ab9574981ae532514dd492dd7" -dependencies = [ - "axum", - "validator", -] - [[package]] name = "base16" version = "0.2.1" @@ -424,9 +414,9 @@ dependencies = [ [[package]] name = "bollard" -version = "0.20.2" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee04c4c84f1f811b017f2fbb7dd8815c976e7ca98593de9c1e2afad0f636bff4" +checksum = "dbe8358268799ebb3e4df23cb9d47f4c72bbc4f5247e2fa6a1bf7b6c0baea220" dependencies = [ "async-stream", "base64 0.22.1", @@ -448,7 +438,7 @@ dependencies = [ "log", "num", "pin-project-lite", - "rand 0.9.5", + "rand 0.10.2", "rustls", "rustls-native-certs", "rustls-pki-types", @@ -469,22 +459,21 @@ dependencies = [ [[package]] name = "bollard-buildkit-proto" -version = "0.7.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a885520bf6249ab931a764ffdb87b0ceef48e6e7d807cfdb21b751e086e1ad" +checksum = "b5c97450e79c7c565302dd92e86b08823b47550fcb4fc5ce910194d1b087a1a3" dependencies = [ "prost", "prost-types", "tonic", "tonic-prost", - "ureq", ] [[package]] name = "bollard-stubs" -version = "1.52.1-rc.29.1.3" +version = "1.53.1-rc.29.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f0a8ca8799131c1837d1282c3f81f31e76ceb0ce426e04a7fe1ccee3287c066" +checksum = "ce412eb6f7096743011dc3cb5c674caeb24ced61d8c498fe07cf7998a4fea889" dependencies = [ "base64 0.22.1", "bollard-buildkit-proto", @@ -780,9 +769,9 @@ dependencies = [ [[package]] name = "console" -version = "0.16.4" +version = "0.16.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fe5f465a4f6fee88fad41b85d990f84c835335e85b5d9e6e63e0d06d28cba7c" +checksum = "e96a4956774c13c126a8b5af4daa79384f4d826534c95a02d76afb39e2ab64e3" dependencies = [ "encode_unicode", "libc", @@ -1419,7 +1408,6 @@ dependencies = [ "serde_json", "sqlx", "testcontainers", - "testcontainers-modules", "tokio", "tokio-util", "tower-http 0.7.1", @@ -1777,7 +1765,6 @@ dependencies = [ "axum-prometheus", "axum-tower-sessions-csrf", "axum-tracing-opentelemetry", - "axum-valid", "base64 0.23.1", "chrono", "console-subscriber", @@ -4203,9 +4190,9 @@ dependencies = [ [[package]] name = "parse-display" -version = "0.9.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914a1c2265c98e2446911282c6ac86d8524f495792c38c5bd884f80499c7538a" +checksum = "e78deb158fb1d73b29efb4b7e9b9860b78059c670de06bd28df8d0b458ded0eb" dependencies = [ "parse-display-derive", "regex", @@ -4214,9 +4201,9 @@ dependencies = [ [[package]] name = "parse-display-derive" -version = "0.9.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ae7800a4c974efd12df917266338e79a7a74415173caf7e70aa0a0707345281" +checksum = "8e95a50d1084dab562913062c4c34bb204b68fc6ec38a1395909ff5aaaf4f10a" dependencies = [ "proc-macro2", "quote", @@ -6073,9 +6060,9 @@ dependencies = [ [[package]] name = "testcontainers" -version = "0.27.3" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd5785b5483672915ed5fe3cddf9f546802779fc1eceff0a6fb7321fac81c1e" +checksum = "6e2bbe381afaaa58ea610c5fc3ffb2184063a32b3e358a179f0b4865dd59934a" dependencies = [ "astral-tokio-tar", "async-trait", @@ -6102,15 +6089,6 @@ dependencies = [ "url", ] -[[package]] -name = "testcontainers-modules" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5985fde5befe4ffa77a052e035e16c2da86e8bae301baa9f9904ad3c494d357" -dependencies = [ - "testcontainers", -] - [[package]] name = "thiserror" version = "1.0.69" @@ -7003,12 +6981,11 @@ dependencies = [ [[package]] name = "validator" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43fb22e1a008ece370ce08a3e9e4447a910e92621bb49b85d6e48a45397e7cfa" +checksum = "c3d68c6633c483df6780cc5277a417c7c2d1bceee2649d06c8ab6b0fd2dd3c81" dependencies = [ "idna", - "once_cell", "regex", "serde", "serde_derive", diff --git a/Cargo.toml b/Cargo.toml index ae2d32f..c2adfcd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,8 +43,7 @@ uuid = { version = "1.26", features = ["serde"] } wasm-bindgen = "0.2" axum-prometheus = "0.10" tower_governor = { version = "0.8", features = ["axum"] } -axum-valid = "0.25" -validator = { version = "0.20", features = ["derive"] } +validator = { version = "0.21", features = ["derive"] } aws-lc-rs = "1" moka = { version = "0.12", features = ["future"] } stylance = "0.8" @@ -68,8 +67,7 @@ console-subscriber = "0.5" criterion = { version = "0.8", features = ["html_reports"] } insta = "1" proptest = "1" -testcontainers = "0.27" -testcontainers-modules = { version = "0.15", features = ["postgres"] } +testcontainers = "0.28" # Layered config: TOML file + RWF_* environment variable overrides. # The `config` crate gives us typed deserialization + per-source priority # without needing unsafe or hand-rolled merge logic. diff --git a/e2e-tests/Cargo.toml b/e2e-tests/Cargo.toml index 54d4dc8..158fb6f 100644 --- a/e2e-tests/Cargo.toml +++ b/e2e-tests/Cargo.toml @@ -77,7 +77,6 @@ opentelemetry_sdk = { workspace = true, optional = true, features = ["testing"] # Test infrastructure testcontainers = { workspace = true } -testcontainers-modules = { workspace = true } # Snapshot testing & property-based testing insta = { workspace = true, features = ["json"] } diff --git a/e2e-tests/src/common/db.rs b/e2e-tests/src/common/db.rs index b2a8113..b4187cf 100644 --- a/e2e-tests/src/common/db.rs +++ b/e2e-tests/src/common/db.rs @@ -6,16 +6,16 @@ use anyhow::{Context, Result}; use sqlx::PgPool; +use testcontainers::core::{IntoContainerPort, WaitFor}; use testcontainers::runners::AsyncRunner; -use testcontainers::{ContainerAsync, ImageExt}; -use testcontainers_modules::postgres::Postgres; +use testcontainers::{ContainerAsync, GenericImage, ImageExt}; /// RAII guard for a test-scoped Postgres database. pub struct TestEnv { pool: PgPool, connection_string: String, #[allow(dead_code, reason = "Kept alive for Drop side-effect on TestEnv")] - container: Box>, + container: Box>, } impl std::fmt::Debug for TestEnv { @@ -40,8 +40,18 @@ impl TestEnv { /// Returns an error if the container cannot start, its host/port cannot be /// resolved, the pool cannot connect, migrations fail, or seeding fails. pub async fn postgres() -> Result { - let container = Postgres::default() - .with_tag("17-alpine") + let container = GenericImage::new("postgres", "17-alpine") + .with_exposed_port(5432.tcp()) + .with_wait_for(WaitFor::message_on_stderr( + "database system is ready to accept connections", + )) + .with_wait_for(WaitFor::message_on_stdout( + "database system is ready to accept connections", + )) + .with_env_var("POSTGRES_DB", "postgres") + .with_env_var("POSTGRES_USER", "postgres") + .with_env_var("POSTGRES_PASSWORD", "postgres") + .with_cmd(["-c", "fsync=off"]) .start() .await .context("Failed to start Postgres testcontainer")?; diff --git a/gateway/Cargo.toml b/gateway/Cargo.toml index a974d31..93929af 100644 --- a/gateway/Cargo.toml +++ b/gateway/Cargo.toml @@ -21,7 +21,6 @@ dev-tools = ["dep:console-subscriber"] anyhow = { workspace = true } axum = { workspace = true } axum-prometheus = { workspace = true } -axum-valid = { workspace = true } chrono = { workspace = true } aws-lc-rs = { workspace = true } base64 = { workspace = true } diff --git a/gateway/src/auth/handlers.rs b/gateway/src/auth/handlers.rs index 258e070..3d72464 100644 --- a/gateway/src/auth/handlers.rs +++ b/gateway/src/auth/handlers.rs @@ -7,7 +7,7 @@ //! * Rate limiting is applied at the router level via `tower_governor`. //! * JWT errors are mapped to distinct error variants. //! * Secrets (passwords, tokens) are never included in log output. -//! * Public auth request payloads are typed and validated with `axum-valid`. +//! * Public auth request payloads are typed and validated with `validator`. use std::str::FromStr; @@ -88,10 +88,12 @@ pub struct ProtectedResponse { pub async fn login_handler( State(state): State, session: tower_sessions::Session, - axum_valid::Valid(axum::Json(LoginRequest { user_id, password })): axum_valid::Valid< - axum::Json, - >, + axum::Json(request): axum::Json, ) -> Result, AppError> { + request + .validate() + .map_err(|_| AppError::BadRequest("invalid request payload".to_string()))?; + let LoginRequest { user_id, password } = request; let s = &state.settings; let parsed_user_id = UserId::from_str(&user_id)?; @@ -219,10 +221,12 @@ async fn remove_unissued_refresh_token(pool: &sqlx::PgPool, refresh_jti: Uuid) { /// access, or JWT creation fails. pub async fn refresh_handler( State(state): State, - axum_valid::Valid(axum::Json(RefreshRequest { refresh_token })): axum_valid::Valid< - axum::Json, - >, + axum::Json(request): axum::Json, ) -> Result, AppError> { + request + .validate() + .map_err(|_| AppError::BadRequest("invalid request payload".to_string()))?; + let RefreshRequest { refresh_token } = request; let pool = state.db_pool.as_ref().ok_or_else(|| { AppError::internal( "refresh-token store unavailable",