brewlog/tests/server/webauthn_api.rs
Jon Seager 590868e51f
test: add integration tests for webauthn, passkeys, and input validation
Add webauthn API tests (register, auth, passkey add flows), passkey
endpoint auth tests, bag validation (zero/negative amount, invalid date),
brew validation (zero weight, negative grind, zero water, temp > 100),
and fix flaky date assertion in bag close test.
2026-02-13 13:06:27 +00:00

158 lines
4.6 KiB
Rust

use reqwest::{Client, StatusCode};
use serde_json::json;
use crate::helpers::spawn_app_with_auth;
#[tokio::test]
async fn register_start_with_missing_token_returns_400() {
let app = spawn_app_with_auth().await;
let client = Client::new();
// POST without a token field — Axum returns 422 for missing fields
let response = client
.post(app.webauthn_url("/register/start"))
.json(&json!({ "display_name": "Alice" }))
.send()
.await
.expect("Failed to send request");
assert_eq!(response.status(), StatusCode::UNPROCESSABLE_ENTITY);
}
#[tokio::test]
async fn register_start_with_invalid_token_returns_401_or_gone() {
let app = spawn_app_with_auth().await;
let client = Client::new();
let response = client
.post(app.webauthn_url("/register/start"))
.json(&json!({
"token": "totally-invalid-token",
"display_name": "Alice"
}))
.send()
.await
.expect("Failed to send request");
// Expired/missing token → 401 (token lookup fails) or 410 (token expired)
let status = response.status().as_u16();
assert!(
status == 401 || status == 410,
"expected 401 or 410, got {status}"
);
}
#[tokio::test]
async fn register_finish_with_invalid_challenge_returns_400() {
let app = spawn_app_with_auth().await;
let client = Client::new();
// `credential: {}` is invalid for RegisterPublicKeyCredential, so Axum returns 422
let response = client
.post(app.webauthn_url("/register/finish"))
.json(&json!({
"challenge_id": "nonexistent-challenge-id",
"passkey_name": "my-key",
"credential": {}
}))
.send()
.await
.expect("Failed to send request");
assert_eq!(response.status(), StatusCode::UNPROCESSABLE_ENTITY);
}
#[tokio::test]
async fn auth_start_with_no_passkeys_returns_404() {
let app = spawn_app_with_auth().await;
let client = Client::new();
// No passkeys have been registered, so auth start should fail.
// auth/start is a GET endpoint.
let response = client
.get(app.webauthn_url("/auth/start"))
.send()
.await
.expect("Failed to send request");
assert_eq!(response.status(), StatusCode::NOT_FOUND);
}
#[tokio::test]
async fn auth_finish_with_invalid_challenge_returns_400() {
let app = spawn_app_with_auth().await;
let client = Client::new();
// `credential: {}` is invalid for PublicKeyCredential, so Axum returns 422
let response = client
.post(app.webauthn_url("/auth/finish"))
.json(&json!({
"challenge_id": "nonexistent-challenge-id",
"credential": {}
}))
.send()
.await
.expect("Failed to send request");
assert_eq!(response.status(), StatusCode::UNPROCESSABLE_ENTITY);
}
#[tokio::test]
async fn passkey_add_start_requires_auth() {
let app = spawn_app_with_auth().await;
let client = Client::new();
// No auth header → 401
let response = client
.post(app.webauthn_url("/passkey/start"))
.json(&json!({ "name": "my-passkey" }))
.send()
.await
.expect("Failed to send request");
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
}
#[tokio::test]
async fn passkey_add_finish_requires_auth() {
let app = spawn_app_with_auth().await;
let client = Client::new();
// No auth header → 401
let response = client
.post(app.webauthn_url("/passkey/finish"))
.json(&json!({
"challenge_id": "nonexistent-challenge-id",
"name": "my-passkey",
"credential": {}
}))
.send()
.await
.expect("Failed to send request");
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
}
#[tokio::test]
async fn auth_start_with_non_localhost_cli_callback_returns_400_or_404() {
let app = spawn_app_with_auth().await;
let client = Client::new();
// auth/start is GET. Without passkeys, we get 404 before the callback validation.
// With passkeys, a non-localhost callback would return 400.
// We verify the endpoint handles these params without crashing.
let response = client
.get(&format!(
"{}/auth/start?cli_callback=http://evil.com/steal&state=abc&token_name=test",
app.webauthn_url("")
))
.send()
.await
.expect("Failed to send request");
let status = response.status().as_u16();
assert!(
status == 400 || status == 404,
"expected 400 or 404, got {status}"
);
}