This commit is contained in:
2025-10-31 01:52:51 +01:00
parent 722c2f9a5c
commit 17db827e3a
11 changed files with 220 additions and 334 deletions
+3 -8
View File
@@ -3,7 +3,7 @@ mod common;
use anyhow::{anyhow, Context, Result};
use axum::http::{header::SET_COOKIE, StatusCode};
use chrono::{Duration as ChronoDuration, Utc};
use common::{acquire_db_lock, body_to_vec, TestApp};
use common::{acquire_db_lock, body_to_vec, ApiErrorResponse, TestApp};
use diesel::prelude::*;
use papercrate::auth::passkeys::{
PasskeyLoginFinishPayload, PasskeyLoginStartPayload, PasskeyRegistrationFinishPayload,
@@ -28,11 +28,6 @@ struct AuthenticatedUser {
username: String,
}
#[derive(Deserialize)]
struct ErrorResponse {
error: String,
}
#[derive(Deserialize)]
struct LoginTenant {
id: Uuid,
@@ -100,7 +95,7 @@ async fn login_rejects_unknown_user() -> Result<()> {
let response = app.post_json("/api/auth/login", &payload, None).await?;
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
let body = body_to_vec(response.into_body()).await?;
let err: ErrorResponse = serde_json::from_slice(&body)?;
let err: ApiErrorResponse = serde_json::from_slice(&body)?;
assert_eq!(err.error, "password authentication is no longer supported");
app.cleanup().await?;
@@ -411,7 +406,7 @@ async fn login_rejects_invalid_password() -> Result<()> {
let response = app.post_json("/api/auth/login", &payload, None).await?;
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
let body = body_to_vec(response.into_body()).await?;
let err: ErrorResponse = serde_json::from_slice(&body)?;
let err: ApiErrorResponse = serde_json::from_slice(&body)?;
assert_eq!(err.error, "password authentication is no longer supported");
app.cleanup().await?;
+16 -2
View File
@@ -29,7 +29,7 @@ use papercrate::state::AppState;
use papercrate::storage::ObjectStorage;
use rand::rngs::OsRng;
use rand::RngCore;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use serde_json::{self, json};
use sha2::{Digest, Sha256};
use tokio::sync::Mutex;
@@ -122,6 +122,13 @@ pub struct TestApp {
storage: Arc<FakeStorage>,
}
#[derive(Debug, Deserialize)]
pub struct ApiErrorResponse {
pub error: String,
#[serde(default)]
pub code: Option<String>,
}
impl TestApp {
pub async fn new() -> Result<Self> {
let database_url = env::var("TEST_DATABASE_URL")
@@ -538,6 +545,7 @@ impl TestApp {
correspondents_json: None,
issued_at: None,
skip_existing: false,
document_type_id: None,
};
self.upload_document_with_extras(
path,
@@ -666,6 +674,7 @@ pub struct UploadExtras<'a> {
pub correspondents_json: Option<&'a str>,
pub issued_at: Option<&'a str>,
pub skip_existing: bool,
pub document_type_id: Option<Uuid>,
}
impl<'a> UploadExtras<'a> {
@@ -677,6 +686,7 @@ impl<'a> UploadExtras<'a> {
correspondents_json: None,
issued_at: None,
skip_existing: false,
document_type_id: None,
}
}
}
@@ -709,7 +719,11 @@ mod helper_tests {
let (access, refresh, refresh_id) = app.create_session(username).await?;
assert!(!access.is_empty(), "access token should not be empty");
assert!(!refresh.is_empty(), "refresh token should not be empty");
assert_ne!(refresh_id, Uuid::nil(), "refresh token id should be assigned");
assert_ne!(
refresh_id,
Uuid::nil(),
"refresh token id should be assigned"
);
let bearer = app.login_token(username, password).await?;
assert!(!bearer.is_empty(), "login_token must yield bearer");
+15 -16
View File
@@ -2,7 +2,7 @@ mod common;
use anyhow::Result;
use axum::http::StatusCode;
use common::{acquire_db_lock, body_to_vec, TestApp, UploadExtras};
use common::{acquire_db_lock, body_to_vec, ApiErrorResponse, TestApp, UploadExtras};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use uuid::Uuid;
@@ -97,13 +97,6 @@ struct AnalyzeJobPayload {
force: bool,
}
#[derive(Deserialize)]
struct ErrorResponse {
error: String,
#[serde(default)]
code: Option<String>,
}
#[derive(Deserialize)]
struct FolderResponse {
folder: FolderInfo,
@@ -1062,7 +1055,7 @@ async fn patch_document_updates_title_and_handles_conflict() -> Result<()> {
.await?;
assert_eq!(conflict.status(), StatusCode::CONFLICT);
let conflict_body = body_to_vec(conflict.into_body()).await?;
let conflict_json: ErrorResponse = serde_json::from_slice(&conflict_body)?;
let conflict_json: ApiErrorResponse = serde_json::from_slice(&conflict_body)?;
assert_eq!(conflict_json.code.as_deref(), Some("duplicate_filename"));
app.cleanup().await?;
@@ -1244,7 +1237,7 @@ async fn patch_document_validation_errors() -> Result<()> {
.await?;
assert_eq!(empty_title.status(), StatusCode::BAD_REQUEST);
let title_body = body_to_vec(empty_title.into_body()).await?;
let title_error: ErrorResponse = serde_json::from_slice(&title_body)?;
let title_error: ApiErrorResponse = serde_json::from_slice(&title_body)?;
assert_eq!(title_error.error, "title must not be empty");
let empty_issued = app
@@ -1256,7 +1249,7 @@ async fn patch_document_validation_errors() -> Result<()> {
.await?;
assert_eq!(empty_issued.status(), StatusCode::BAD_REQUEST);
let issued_body = body_to_vec(empty_issued.into_body()).await?;
let issued_error: ErrorResponse = serde_json::from_slice(&issued_body)?;
let issued_error: ApiErrorResponse = serde_json::from_slice(&issued_body)?;
assert_eq!(issued_error.error, "issued_at must not be empty");
let invalid_merge = app
@@ -1272,7 +1265,7 @@ async fn patch_document_validation_errors() -> Result<()> {
.await?;
assert_eq!(invalid_merge.status(), StatusCode::BAD_REQUEST);
let merge_body = body_to_vec(invalid_merge.into_body()).await?;
let merge_error: ErrorResponse = serde_json::from_slice(&merge_body)?;
let merge_error: ApiErrorResponse = serde_json::from_slice(&merge_body)?;
assert_eq!(
merge_error.error,
"metadata value must be a JSON object when replace is false"
@@ -1308,7 +1301,7 @@ async fn patch_document_validation_errors() -> Result<()> {
.await?;
assert_eq!(merge_after_scalar.status(), StatusCode::BAD_REQUEST);
let merge_after_body = body_to_vec(merge_after_scalar.into_body()).await?;
let merge_after_error: ErrorResponse = serde_json::from_slice(&merge_after_body)?;
let merge_after_error: ApiErrorResponse = serde_json::from_slice(&merge_after_body)?;
assert_eq!(
merge_after_error.error,
"existing metadata is not an object; set replace=true to overwrite"
@@ -1323,7 +1316,7 @@ async fn patch_document_validation_errors() -> Result<()> {
.await?;
assert_eq!(malformed_timestamp.status(), StatusCode::BAD_REQUEST);
let malformed_body = body_to_vec(malformed_timestamp.into_body()).await?;
let malformed_error: ErrorResponse = serde_json::from_slice(&malformed_body)?;
let malformed_error: ApiErrorResponse = serde_json::from_slice(&malformed_body)?;
assert!(
malformed_error
.error
@@ -1591,7 +1584,10 @@ async fn list_documents_filtered_by_document_type() -> Result<()> {
assert!(refreshed_a.status().is_success());
let refreshed_a_body = body_to_vec(refreshed_a.into_body()).await?;
let refreshed_a_detail: DocumentDetail = serde_json::from_slice(&refreshed_a_body)?;
assert_eq!(refreshed_a_detail.document.document_type_id, Some(invoices.id));
assert_eq!(
refreshed_a_detail.document.document_type_id,
Some(invoices.id)
);
let refreshed_b = app
.get(
@@ -1602,7 +1598,10 @@ async fn list_documents_filtered_by_document_type() -> Result<()> {
assert!(refreshed_b.status().is_success());
let refreshed_b_body = body_to_vec(refreshed_b.into_body()).await?;
let refreshed_b_detail: DocumentDetail = serde_json::from_slice(&refreshed_b_body)?;
assert_eq!(refreshed_b_detail.document.document_type_id, Some(receipts.id));
assert_eq!(
refreshed_b_detail.document.document_type_id,
Some(receipts.id)
);
app.cleanup().await?;
Ok(())