fix(e2e): serialize all init_auth tests behind AUTH_LOCK to prevent race

Every test that calls init_auth() now holds AUTH_LOCK for its full
duration, preventing the global AUTH_CONTEXT from being overwritten
by concurrent tests. The e2e_auth_failure_wrong_token test additionally
resets auth back to "devtoken" after its assertion. Tests now pass
reliably with default parallelism (no --test-threads 1 required).
This commit is contained in:
2026-03-04 20:20:03 +01:00
parent fd1accc6dd
commit a90020fe89

View File

@@ -25,8 +25,9 @@ use quicproquo_client::{
};
use quicproquo_core::{GroupMember, HybridKeypair, IdentityKeypair, ReceivedMessage};
/// Serialises all tests that call `init_auth` with a non-devtoken session to prevent
/// the global `AUTH_CONTEXT` from being overwritten by concurrent tests.
/// Serialises ALL tests that call `init_auth` to prevent the global `AUTH_CONTEXT`
/// from being overwritten by concurrent tests. Every test that mutates auth state
/// must hold this lock for its entire duration.
static AUTH_LOCK: Mutex<()> = Mutex::new(());
fn hex_encode(bytes: &[u8]) -> String {
@@ -97,6 +98,7 @@ fn spawn_server(base: &std::path::Path, extra_args: &[&str]) -> (String, PathBuf
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn e2e_happy_path_register_invite_join_send_recv() -> anyhow::Result<()> {
ensure_rustls_provider();
let _auth = AUTH_LOCK.lock().unwrap();
let temp = TempDir::new()?;
let base = temp.path();
@@ -186,6 +188,7 @@ async fn e2e_happy_path_register_invite_join_send_recv() -> anyhow::Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn e2e_three_party_group_invite_join_send_recv() -> anyhow::Result<()> {
ensure_rustls_provider();
let _auth = AUTH_LOCK.lock().unwrap();
let temp = TempDir::new()?;
let base = temp.path();
@@ -449,6 +452,7 @@ async fn e2e_login_rejects_mismatched_identity() -> anyhow::Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn e2e_sealed_sender_enqueue_then_fetch() -> anyhow::Result<()> {
ensure_rustls_provider();
let _auth = AUTH_LOCK.lock().unwrap();
let temp = TempDir::new()?;
let base = temp.path();
@@ -1171,6 +1175,7 @@ async fn e2e_key_rotation_update_path() -> anyhow::Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn e2e_hook_rejects_oversized_payload() -> anyhow::Result<()> {
ensure_rustls_provider();
let _auth = AUTH_LOCK.lock().unwrap();
let temp = TempDir::new()?;
let base = temp.path();
@@ -1216,6 +1221,7 @@ async fn e2e_hook_rejects_oversized_payload() -> anyhow::Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn e2e_multi_party_group() -> anyhow::Result<()> {
ensure_rustls_provider();
let _auth = AUTH_LOCK.lock().unwrap();
let temp = TempDir::new()?;
let base = temp.path();
@@ -1353,6 +1359,7 @@ async fn e2e_multi_party_group() -> anyhow::Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn e2e_file_upload_download() -> anyhow::Result<()> {
ensure_rustls_provider();
let _auth = AUTH_LOCK.lock().unwrap();
let temp = TempDir::new()?;
let base = temp.path();
@@ -1484,6 +1491,7 @@ async fn e2e_file_upload_download() -> anyhow::Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn e2e_blob_hash_mismatch() -> anyhow::Result<()> {
ensure_rustls_provider();
let _auth = AUTH_LOCK.lock().unwrap();
let temp = TempDir::new()?;
let base = temp.path();
@@ -1573,6 +1581,7 @@ fn spawn_server_custom(base: &std::path::Path, args: &[&str]) -> (String, PathBu
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn e2e_auth_failure_wrong_token() -> anyhow::Result<()> {
ensure_rustls_provider();
let _auth = AUTH_LOCK.lock().unwrap();
let temp = TempDir::new()?;
let base = temp.path();
@@ -1615,6 +1624,9 @@ async fn e2e_auth_failure_wrong_token() -> anyhow::Result<()> {
}
}
// Reset auth back to devtoken so other tests aren't poisoned.
init_auth(ClientAuth::from_parts("devtoken".to_string(), None));
Ok(())
}
@@ -1622,6 +1634,7 @@ async fn e2e_auth_failure_wrong_token() -> anyhow::Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn e2e_message_ordering_preserved() -> anyhow::Result<()> {
ensure_rustls_provider();
let _auth = AUTH_LOCK.lock().unwrap();
let temp = TempDir::new()?;
let base = temp.path();
@@ -1837,6 +1850,7 @@ async fn e2e_opaque_login_wrong_password() -> anyhow::Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn e2e_keypackage_exhaustion_graceful() -> anyhow::Result<()> {
ensure_rustls_provider();
let _auth = AUTH_LOCK.lock().unwrap();
let temp = TempDir::new()?;
let base = temp.path();
@@ -1891,6 +1905,7 @@ async fn e2e_keypackage_exhaustion_graceful() -> anyhow::Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn e2e_rate_limiting_rejects_excess() -> anyhow::Result<()> {
ensure_rustls_provider();
let _auth = AUTH_LOCK.lock().unwrap();
let temp = TempDir::new()?;
let base = temp.path();