瀏覽代碼

Updated database versions

Efren Yevale Varela 3 周之前
父節點
當前提交
35d12e87ee

+ 2 - 2
README.md

@@ -27,8 +27,8 @@ state is in Redis, so replicas are interchangeable.
 ## Prerequisites
 
 - Rust stable, as pinned by `rust-toolchain.toml`.
-- PostgreSQL 14 or newer with the `citext` extension available.
-- Redis 7.2 or newer, with persistence enabled and eviction off.
+- PostgreSQL 18 or newer with the `citext` extension available.
+- Redis 8 or newer, with persistence enabled and eviction off.
 - A TLS certificate and key for the public listener.
 - Docker, for the container image, for the shared test Postgres and Redis in
   `compose.test.yaml`, and for the tests that still use testcontainers (the

+ 2 - 2
compose.test.yaml

@@ -20,7 +20,7 @@ name: d-oauth-test
 
 services:
   test-db:
-    image: postgres:16-alpine
+    image: postgres:18-alpine
     environment:
       POSTGRES_PASSWORD: oauth-test-only
     ports:
@@ -32,7 +32,7 @@ services:
       retries: 20
 
   test-redis:
-    image: redis:7-alpine
+    image: redis:8-alpine
     # Mirrors the server's durability contract: AOF on, eviction off.
     command: ["redis-server", "--appendonly", "yes", "--maxmemory-policy", "noeviction"]
     ports:

+ 2 - 2
compose.yaml

@@ -34,7 +34,7 @@ name: d-oauth
 
 services:
   db:
-    image: postgres:16
+    image: postgres:18-alpine
     environment:
       POSTGRES_USER: oauth
       POSTGRES_PASSWORD: oauth-dev-only
@@ -48,7 +48,7 @@ services:
       retries: 20
 
   redis:
-    image: redis:7
+    image: redis:8-alpine
     # The server asserts this contract at startup: noeviction + AOF.
     command: ["redis-server", "--appendonly", "yes", "--maxmemory-policy", "noeviction"]
     volumes:

+ 2 - 2
src/cli/test_support.rs

@@ -172,7 +172,7 @@ async fn build() -> Result<Harness, TestError> {
 }
 
 pub(crate) async fn postgres_container() -> Result<(ContainerGuard, String), TestError> {
-  let container = GenericImage::new("postgres", "16-alpine")
+  let container = GenericImage::new("postgres", "18-alpine")
     .with_exposed_port(5432.tcp())
     .with_wait_for(WaitFor::message_on_stderr(
       "database system is ready to accept connections",
@@ -190,7 +190,7 @@ pub(crate) async fn postgres_container() -> Result<(ContainerGuard, String), Tes
 }
 
 async fn redis_container() -> Result<(ContainerGuard, String), TestError> {
-  let container = GenericImage::new("redis", "7.2-alpine")
+  let container = GenericImage::new("redis", "8-alpine")
     .with_exposed_port(6379.tcp())
     .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections"))
     .with_cmd(vec!["redis-server", "--appendonly", "yes"])

+ 1 - 1
src/cli/tests.rs

@@ -1088,7 +1088,7 @@ async fn admin_bootstrap_generated_password_is_256_bit_and_unlogged() -> Result<
 #[tokio::test]
 async fn admin_bootstrap_first_login_restricted_until_rotation() -> Result<(), TestError> {
   let (_db, pool, config) = bootstrap_setup("redis://127.0.0.1:9").await?;
-  let container = GenericImage::new("redis", "7.2-alpine")
+  let container = GenericImage::new("redis", "8-alpine")
     .with_exposed_port(6379.tcp())
     .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections"))
     .with_cmd(vec!["redis-server", "--appendonly", "yes"])

+ 2 - 2
src/db/mod.rs

@@ -15,7 +15,7 @@ pub use redirect_repo::{NewRedirectUri, RedirectKind, RedirectUri, UpsertOutcome
 
 /// Test-only scaffolding shared by the repository suites.
 ///
-/// One `postgres:16-alpine` container is started lazily and reused by every test
+/// One `postgres:18-alpine` container is started lazily and reused by every test
 /// in the lib test binary; each test gets a freshly created database with all
 /// embedded migrations applied, so parallel tests stay isolated at one
 /// container cost. Integration tests under `tests/` cannot reach this
@@ -54,7 +54,7 @@ pub(crate) mod test_support {
   /// dropping the last lease removes the shared container deterministically.
   async fn shared_db() -> Result<Lease<Db>, TestError> {
     DB.lease::<TestError>(async {
-      let container = GenericImage::new("postgres", "16-alpine")
+      let container = GenericImage::new("postgres", "18-alpine")
         .with_exposed_port(5432.tcp())
         .with_wait_for(WaitFor::message_on_stderr(
           "database system is ready to accept connections",

+ 1 - 1
src/db/user_repo/lockout.rs

@@ -335,7 +335,7 @@ mod tests {
 
   async fn harness() -> Result<(ContainerGuard, RedisConn, Store), TestError> {
     let n = NONCE.fetch_add(1, Ordering::Relaxed);
-    let container = GenericImage::new("redis", "7.2-alpine")
+    let container = GenericImage::new("redis", "8-alpine")
       .with_exposed_port(6379.tcp())
       .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections"))
       .with_cmd(["redis-server", "--appendonly", "yes"])

+ 2 - 2
src/jose/dpop/support.rs

@@ -65,7 +65,7 @@ pub(super) const TEST_HTM: &str = "POST";
 /// URI every test proof is bound to.
 pub(super) const TEST_HTU: &str = "https://as.example.test/token";
 
-/// One shared Redis 7.2 container for the whole `dpop` suite.
+/// One shared Redis 8 container for the whole `dpop` suite.
 ///
 /// The slot holds a [`ContainerGuard`], so the last test releasing it removes
 /// the container synchronously instead of orphaning it at process exit.
@@ -81,7 +81,7 @@ static REDIS: Shared<ContainerGuard> = Shared::new();
 pub(super) async fn test_redis() -> Result<(Lease<ContainerGuard>, RedisConn, Store), TestError> {
   let container = REDIS
     .lease::<TestError>(async {
-      let started = GenericImage::new("redis", "7.2-alpine")
+      let started = GenericImage::new("redis", "8-alpine")
         .with_exposed_port(6379.tcp())
         .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections"))
         .with_cmd(["redis-server", "--appendonly", "yes"])

+ 1 - 1
src/middleware/ratelimit/test_support.rs

@@ -50,7 +50,7 @@ impl Service<Request<Body>> for Passthrough {
 ///
 /// The guard removes the container synchronously when the test drops it.
 pub(super) async fn redis_container() -> Result<(ContainerGuard, String), Box<dyn Error>> {
-  let container = GenericImage::new("redis", "7.2-alpine")
+  let container = GenericImage::new("redis", "8-alpine")
     .with_exposed_port(6379.tcp())
     .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections"))
     .with_cmd(["redis-server", "--appendonly", "yes"])

+ 1 - 1
src/middleware/ratelimit/tests.rs

@@ -1,6 +1,6 @@
 //! `test(ratelimit)` suite: fixed-window enforcement against real Redis.
 //!
-//! Each test owns a throwaway `redis:7.2-alpine` container (AOF on), so
+//! Each test owns a throwaway `redis:8-alpine` container (AOF on), so
 //! counter keys never leak across tests. Fixtures live in [`super::test_support`].
 
 use std::net::{IpAddr, Ipv6Addr, SocketAddr};

+ 1 - 1
src/protocol/client_auth/tests_containers.rs

@@ -82,7 +82,7 @@ async fn harness() -> Result<(ContainerGuard, Harness), TestError> {
   let (_db, pool) = crate::db::test_support::migrated_pool()
     .await
     .map_err(|error| TestError::Unexpected(format!("{error}")))?;
-  let container = GenericImage::new("redis", "7.2-alpine")
+  let container = GenericImage::new("redis", "8-alpine")
     .with_exposed_port(6379.tcp())
     .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections"))
     .with_cmd(["redis-server", "--appendonly", "yes"])

+ 2 - 2
src/protocol/code/tests.rs

@@ -65,9 +65,9 @@ fn test_ring() -> Result<KeyRing, TestError> {
   )?)
 }
 
-/// Start a single-node Redis 7.2 with AOF on.
+/// Start a single-node Redis 8 with AOF on.
 async fn redis_container() -> Result<(ContainerGuard, String), TestError> {
-  let container = GenericImage::new("redis", "7.2-alpine")
+  let container = GenericImage::new("redis", "8-alpine")
     .with_exposed_port(6379.tcp())
     .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections"))
     .with_cmd(vec!["redis-server", "--appendonly", "yes"])

+ 1 - 1
src/protocol/dcr/manage/support.rs

@@ -85,7 +85,7 @@ pub(super) async fn harness(policy: &str) -> Result<(ContainerGuard, Harness), T
   let (_db, pool) = crate::db::test_support::migrated_pool()
     .await
     .map_err(|error| TestError::Unexpected(format!("{error}")))?;
-  let container = GenericImage::new("redis", "7.2-alpine")
+  let container = GenericImage::new("redis", "8-alpine")
     .with_exposed_port(6379.tcp())
     .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections"))
     .with_cmd(["redis-server", "--appendonly", "yes"])

+ 1 - 1
src/protocol/dcr/tests.rs

@@ -76,7 +76,7 @@ async fn harness(open: bool, policy: &str) -> Result<(ContainerGuard, Harness),
   let (_db, pool) = crate::db::test_support::migrated_pool()
     .await
     .map_err(|error| TestError::Unexpected(format!("{error}")))?;
-  let container = GenericImage::new("redis", "7.2-alpine")
+  let container = GenericImage::new("redis", "8-alpine")
     .with_exposed_port(6379.tcp())
     .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections"))
     .start()

+ 1 - 1
src/protocol/device/tests.rs

@@ -93,7 +93,7 @@ async fn harness() -> Result<(ContainerGuard, Harness), TestError> {
   let (db, pool) = crate::db::test_support::migrated_pool()
     .await
     .map_err(|error| TestError::Unexpected(format!("pg: {error}")))?;
-  let container = GenericImage::new("redis", "7.2-alpine")
+  let container = GenericImage::new("redis", "8-alpine")
     .with_exposed_port(6379.tcp())
     .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections"))
     .with_cmd(["redis-server", "--appendonly", "yes"])

+ 1 - 1
src/protocol/introspect/tests.rs

@@ -79,7 +79,7 @@ struct Harness {
 
 async fn harness() -> TestResult<(ContainerGuard, Harness)> {
   let (db, pool) = migrated_pool().await?;
-  let container = GenericImage::new("redis", "7.2-alpine")
+  let container = GenericImage::new("redis", "8-alpine")
     .with_exposed_port(6379.tcp())
     .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections"))
     .with_cmd(["redis-server", "--appendonly", "yes"])

+ 1 - 1
src/protocol/par/tests.rs

@@ -67,7 +67,7 @@ async fn harness() -> Result<(ContainerGuard, Harness), TestError> {
   let (_db, pool) = crate::db::test_support::migrated_pool()
     .await
     .map_err(|error| TestError::Unexpected(format!("{error}")))?;
-  let container = GenericImage::new("redis", "7.2-alpine")
+  let container = GenericImage::new("redis", "8-alpine")
     .with_exposed_port(6379.tcp())
     .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections"))
     .start()

+ 1 - 1
src/protocol/purge.rs

@@ -338,7 +338,7 @@ mod tests {
 
   async fn harness() -> Result<(ContainerGuard, RedisConn, Store), TestError> {
     let n = NONCE.fetch_add(1, Ordering::Relaxed);
-    let container = GenericImage::new("redis", "7.2-alpine")
+    let container = GenericImage::new("redis", "8-alpine")
       .with_exposed_port(6379.tcp())
       .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections"))
       .with_cmd(["redis-server", "--appendonly", "yes"])

+ 3 - 3
src/protocol/revoke/tests.rs

@@ -1,6 +1,6 @@
 //! `test(revoke)` suite: ownership-scoped revocation and grant cascade.
 //!
-//! A shared Redis 7.2 container (AOF on) backs the token/grant stores; unique
+//! A shared Redis 8 container (AOF on) backs the token/grant stores; unique
 //! grant/client labels keep the keyspace collision-free. Authentication-scoped
 //! behaviour that needs no database is exercised against a lazy Postgres pool
 //! that is never dialed.
@@ -74,14 +74,14 @@ enum TestError {
   Unexpected(String),
 }
 
-/// One shared Redis 7.2 container for the whole suite.
+/// One shared Redis 8 container for the whole suite.
 static REDIS: Shared<ContainerGuard> = Shared::new();
 
 /// Fresh connection plus store handle against the shared container.
 async fn test_redis() -> Result<(Lease<ContainerGuard>, RedisConn, Store), TestError> {
   let container = REDIS
     .lease::<TestError>(async {
-      let started = GenericImage::new("redis", "7.2-alpine")
+      let started = GenericImage::new("redis", "8-alpine")
         .with_exposed_port(6379.tcp())
         .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections"))
         .with_cmd(["redis-server", "--appendonly", "yes"])

+ 2 - 2
src/protocol/token/grants/authorization_code.rs

@@ -424,7 +424,7 @@ async fn authenticate_confidential(
 mod tests {
   //! `test(grant_code)` suite: happy paths, single-use, and binding checks.
   //!
-  //! Live Postgres (client registry) plus a per-test Redis 7.2 container.
+  //! Live Postgres (client registry) plus a per-test Redis 8 container.
   //! Codes are issued through [`crate::protocol::code::CodeIssuer`] so the
   //! keyed-hash path is exercised end to end; every negative test issues a
   //! fresh code because redemption consumes exactly once.
@@ -539,7 +539,7 @@ mod tests {
     let (db, pool) = crate::db::test_support::migrated_pool()
       .await
       .map_err(|error| TestError::Unexpected(format!("pg: {error}")))?;
-    let container = GenericImage::new("redis", "7.2-alpine")
+    let container = GenericImage::new("redis", "8-alpine")
       .with_exposed_port(6379.tcp())
       .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections"))
       .with_cmd(["redis-server", "--appendonly", "yes"])

+ 3 - 3
src/protocol/token/grants/client_credentials/tests.rs

@@ -1,6 +1,6 @@
 //! `test(grant_client_credentials)` suite: confidential-only, scope, bindings.
 //!
-//! Shared Redis 7.2 container plus per-test connections; unique client
+//! Shared Redis 8 container plus per-test connections; unique client
 //! labels keep the shared keyspace collision-free.
 
 mod binding;
@@ -61,14 +61,14 @@ enum TestError {
   Unexpected(String),
 }
 
-/// One shared Redis 7.2 container for the whole suite.
+/// One shared Redis 8 container for the whole suite.
 static REDIS: Shared<ContainerGuard> = Shared::new();
 
 /// Fresh connection plus store handle against the shared container.
 async fn test_redis() -> Result<(Lease<ContainerGuard>, RedisConn, Store), TestError> {
   let container = REDIS
     .lease::<TestError>(async {
-      let started = GenericImage::new("redis", "7.2-alpine")
+      let started = GenericImage::new("redis", "8-alpine")
         .with_exposed_port(6379.tcp())
         .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections"))
         .with_cmd(["redis-server", "--appendonly", "yes"])

+ 1 - 1
src/protocol/token/grants/device_code/tests.rs

@@ -89,7 +89,7 @@ pub(super) async fn harness() -> Result<(ContainerGuard, Harness), TestError> {
   let (db, pool) = crate::db::test_support::migrated_pool()
     .await
     .map_err(|error| TestError::Unexpected(format!("pg: {error}")))?;
-  let container = GenericImage::new("redis", "7.2-alpine")
+  let container = GenericImage::new("redis", "8-alpine")
     .with_exposed_port(6379.tcp())
     .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections"))
     .with_cmd(["redis-server", "--appendonly", "yes"])

+ 2 - 2
src/protocol/token/grants/refresh_token/tests.rs

@@ -69,7 +69,7 @@ enum TestError {
   Unexpected(String),
 }
 
-/// One shared Redis 7.2 container for the whole suite.
+/// One shared Redis 8 container for the whole suite.
 static REDIS: Shared<ContainerGuard> = Shared::new();
 
 /// Fresh connection plus store handle against the shared container, with the
@@ -77,7 +77,7 @@ static REDIS: Shared<ContainerGuard> = Shared::new();
 async fn test_redis() -> Result<(Lease<ContainerGuard>, RedisConn, Store, String), TestError> {
   let container = REDIS
     .lease::<TestError>(async {
-      let started = GenericImage::new("redis", "7.2-alpine")
+      let started = GenericImage::new("redis", "8-alpine")
         .with_exposed_port(6379.tcp())
         .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections"))
         .with_cmd([

+ 1 - 1
src/protocol/token/grants/refresh_token/tests/failover.rs

@@ -22,7 +22,7 @@ use crate::{
 /// and the current pair stays live.
 #[tokio::test]
 async fn grant_refresh_failover_no_double_rotation() -> TestResult {
-  let container = GenericImage::new("redis", "7.2-alpine")
+  let container = GenericImage::new("redis", "8-alpine")
     .with_exposed_port(6379.tcp())
     .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections"))
     .with_cmd([

+ 3 - 3
src/protocol/token/grants/token_exchange/tests.rs

@@ -1,6 +1,6 @@
 //! `test(grant_token_exchange)` suite: policy, proofs, scope pinning.
 //!
-//! Shared Redis 7.2 container plus per-test connections; unique client
+//! Shared Redis 8 container plus per-test connections; unique client
 //! labels keep the shared keyspace collision-free.
 
 mod exchange_ok;
@@ -62,14 +62,14 @@ enum TestError {
   Unexpected(String),
 }
 
-/// One shared Redis 7.2 container for the whole suite.
+/// One shared Redis 8 container for the whole suite.
 static REDIS: Shared<ContainerGuard> = Shared::new();
 
 /// Fresh connection plus store handle against the shared container.
 async fn test_redis() -> Result<(Lease<ContainerGuard>, RedisConn, Store), TestError> {
   let container = REDIS
     .lease::<TestError>(async {
-      let started = GenericImage::new("redis", "7.2-alpine")
+      let started = GenericImage::new("redis", "8-alpine")
         .with_exposed_port(6379.tcp())
         .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections"))
         .with_cmd(["redis-server", "--appendonly", "yes"])

+ 3 - 3
src/protocol/token/tests.rs

@@ -1,6 +1,6 @@
 //! `test(token_service)` suite: mint, hash-store, sender-constraint matrix.
 //!
-//! Shared Redis 7.2 container (AOF on) plus per-test connections; unique
+//! Shared Redis 8 container (AOF on) plus per-test connections; unique
 //! grant/client labels keep the shared keyspace collision-free.
 
 mod matrix;
@@ -56,7 +56,7 @@ enum TestError {
   Unexpected(String),
 }
 
-/// One shared Redis 7.2 container for the whole suite.
+/// One shared Redis 8 container for the whole suite.
 static REDIS: Shared<ContainerGuard> = Shared::new();
 
 /// Fresh connection plus store handle against the shared container.
@@ -65,7 +65,7 @@ static REDIS: Shared<ContainerGuard> = Shared::new();
 async fn test_redis() -> Result<(Lease<ContainerGuard>, RedisConn, Store), TestError> {
   let container = REDIS
     .lease::<TestError>(async {
-      let started = GenericImage::new("redis", "7.2-alpine")
+      let started = GenericImage::new("redis", "8-alpine")
         .with_exposed_port(6379.tcp())
         .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections"))
         .with_cmd(["redis-server", "--appendonly", "yes"])

+ 4 - 4
src/state/test_support.rs

@@ -1,7 +1,7 @@
 //! Shared testcontainers scaffolding for the `test(health)` suite.
 //!
-//! Postgres and Redis run from locally cached images (`postgres:16-alpine`,
-//! `redis:7.2-alpine`); no pulls, no `redis-server` binary needed.
+//! Postgres and Redis run from locally cached images (`postgres:18-alpine`,
+//! `redis:8-alpine`); no pulls, no `redis-server` binary needed.
 //! Sentinel and Cluster topologies talk over container bridge IPs, which are
 //! directly routable from the Linux host, so Sentinel-advertised and
 //! cluster-gossiped addresses stay valid without port-mapping tricks.
@@ -32,7 +32,7 @@ pub type TestResult = Result<(), TestError>;
 ///
 /// The guard removes the container synchronously when the test drops it.
 pub async fn postgres_container() -> Result<(ContainerGuard, String), TestError> {
-  let container = GenericImage::new("postgres", "16-alpine")
+  let container = GenericImage::new("postgres", "18-alpine")
     .with_exposed_port(5432.tcp())
     .with_wait_for(WaitFor::message_on_stderr(
       "database system is ready to accept connections",
@@ -65,7 +65,7 @@ pub async fn redis_single_container(
     ],
     None => vec!["redis-server", "--appendonly", "yes"],
   };
-  let container = GenericImage::new("redis", "7.2-alpine")
+  let container = GenericImage::new("redis", "8-alpine")
     .with_exposed_port(6379.tcp())
     .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections"))
     .with_cmd(args)

+ 3 - 3
src/state/tests_topology.rs

@@ -157,7 +157,7 @@ async fn health_redis_single_password_userinfo_url_connects() -> TestResult {
 /// Returns the guards plus the sentinel bridge address.
 async fn sentinel_pair() -> Result<(ContainerGuard, ContainerGuard, String), TestError> {
   let master = ContainerGuard::new(
-    GenericImage::new("redis", "7.2-alpine")
+    GenericImage::new("redis", "8-alpine")
       .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections"))
       .with_cmd(["redis-server", "--port", "6379", "--appendonly", "yes"])
       .start()
@@ -173,7 +173,7 @@ async fn sentinel_pair() -> Result<(ContainerGuard, ContainerGuard, String), Tes
     "printf '%s' '{sentinel_conf}' > /tmp/sentinel.conf && exec redis-sentinel /tmp/sentinel.conf"
   );
   let sentinel = ContainerGuard::new(
-    GenericImage::new("redis", "7.2-alpine")
+    GenericImage::new("redis", "8-alpine")
       .with_wait_for(WaitFor::message_on_stdout("+monitor master"))
       .with_cmd(vec!["sh".to_owned(), "-c".to_owned(), script])
       .start()
@@ -214,7 +214,7 @@ async fn health_ready_sentinel_reports_ready() -> TestResult {
 /// gossips an empty address), assign all slots, meet self.
 async fn clustered_container() -> Result<(ContainerGuard, String), TestError> {
   let node = ContainerGuard::new(
-    GenericImage::new("redis", "7.2-alpine")
+    GenericImage::new("redis", "8-alpine")
       .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections"))
       .with_cmd([
         "redis-server",

+ 2 - 2
src/store/pending.rs

@@ -310,9 +310,9 @@ mod tests {
     }
   }
 
-  /// Start a single-node Redis 7.2 with AOF on.
+  /// Start a single-node Redis 8 with AOF on.
   async fn redis_container() -> Result<(ContainerGuard, String), TestError> {
-    let container = GenericImage::new("redis", "7.2-alpine")
+    let container = GenericImage::new("redis", "8-alpine")
       .with_exposed_port(6379.tcp())
       .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections"))
       .with_cmd(vec!["redis-server", "--appendonly", "yes"])

+ 2 - 2
src/store/tests_store.rs

@@ -1,6 +1,6 @@
 //! `test(store)` suite: atomic Lua, tombstones, grant index, durability.
 //!
-//! Every test here spins its own single-node Redis 7.2 container with AOF
+//! Every test here spins its own single-node Redis 8 container with AOF
 //! enabled (mirroring `state::test_support`), so persistence, barriers, and
 //! concurrent compare-and-set run against a real server.
 
@@ -65,7 +65,7 @@ fn unique(label: &str) -> String {
 async fn redis_container(extra: Vec<&str>) -> Result<(ContainerGuard, String), TestError> {
   let mut args = vec!["redis-server", "--appendonly", "yes"];
   args.extend(extra);
-  let container = GenericImage::new("redis", "7.2-alpine")
+  let container = GenericImage::new("redis", "8-alpine")
     .with_exposed_port(6379.tcp())
     .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections"))
     .with_cmd(args)

+ 1 - 1
src/store/tokens.rs

@@ -713,7 +713,7 @@ mod put_member_tests {
   /// shortens the set TTL.
   #[tokio::test]
   async fn put_member_writes_record_and_index_atomically() -> TestResult {
-    let container = GenericImage::new("redis", "7.2-alpine")
+    let container = GenericImage::new("redis", "8-alpine")
       .with_exposed_port(6379.tcp())
       .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections"))
       .with_cmd(vec!["redis-server", "--appendonly", "yes"])

+ 1 - 1
src/ui/consent/tests.rs

@@ -73,7 +73,7 @@ struct Harness {
 /// Start Postgres (fresh DB) + Redis for one test.
 async fn harness() -> Result<Harness, TestError> {
   let (db, pool) = crate::db::test_support::migrated_pool().await?;
-  let container = GenericImage::new("redis", "7.2-alpine")
+  let container = GenericImage::new("redis", "8-alpine")
     .with_exposed_port(6379.tcp())
     .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections"))
     .with_cmd(["redis-server", "--appendonly", "yes"])

+ 1 - 1
src/ui/login/tests.rs

@@ -100,7 +100,7 @@ pub(super) async fn harness() -> Result<Harness, TestError> {
   let (db, pool) = crate::db::test_support::migrated_pool()
     .await
     .map_err(|error| TestError::Unexpected(format!("pg: {error}")))?;
-  let container = GenericImage::new("redis", "7.2-alpine")
+  let container = GenericImage::new("redis", "8-alpine")
     .with_exposed_port(6379.tcp())
     .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections"))
     .with_cmd(["redis-server", "--appendonly", "yes"])

+ 2 - 2
tests/resilience.rs

@@ -75,7 +75,7 @@ fn pg_url_for(port: u16) -> String {
 
 /// Start Postgres and return the guard plus a plaintext URL.
 async fn postgres_container() -> Result<(ContainerGuard, String), TestError> {
-  let container = GenericImage::new("postgres", "16-alpine")
+  let container = GenericImage::new("postgres", "18-alpine")
     .with_exposed_port(5432.tcp())
     .with_wait_for(WaitFor::message_on_stderr(
       "database system is ready to accept connections",
@@ -98,7 +98,7 @@ async fn postgres_container() -> Result<(ContainerGuard, String), TestError> {
 /// its reply, so the `WAITAOF` barrier (and the SIGKILL test below) exercises
 /// real fsync durability instead of the `everysec` background window.
 async fn redis_container() -> Result<(ContainerGuard, String), TestError> {
-  let container = GenericImage::new("redis", "7.2-alpine")
+  let container = GenericImage::new("redis", "8-alpine")
     .with_exposed_port(6379.tcp())
     .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections"))
     .with_cmd(vec![