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.
158 lines
4.6 KiB
Rust
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}"
|
|
);
|
|
}
|