276 lines
7.6 KiB
Rust
276 lines
7.6 KiB
Rust
mod common;
|
|
|
|
use anyhow::Result;
|
|
use axum::http::StatusCode;
|
|
use common::{acquire_db_lock, body_to_vec, TestApp};
|
|
use serde::Deserialize;
|
|
use serde_json::Value;
|
|
use uuid::Uuid;
|
|
|
|
#[derive(Deserialize)]
|
|
struct DocumentDetail {
|
|
document: DocumentInfo,
|
|
current_version: DocumentVersion,
|
|
assets: Vec<DocumentAssetInfo>,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct DocumentInfo {
|
|
id: Uuid,
|
|
original_name: String,
|
|
current_version: i32,
|
|
deleted_at: Option<String>,
|
|
tags: Vec<Value>,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct DocumentVersion {
|
|
id: Uuid,
|
|
s3_key: String,
|
|
size_bytes: i64,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct DocumentAssetInfo {
|
|
id: Uuid,
|
|
asset_type: String,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct DocumentListItem {
|
|
id: Uuid,
|
|
current_version: i32,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct DocumentDownload {
|
|
url: String,
|
|
filename: String,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct BulkReanalyze {
|
|
queued: usize,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct AnalyzeJobPayload {
|
|
document_id: Uuid,
|
|
document_version_id: Uuid,
|
|
#[serde(default)]
|
|
force: bool,
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn upload_and_list_document() -> Result<()> {
|
|
let _lock = acquire_db_lock().await;
|
|
let app = TestApp::new().await?;
|
|
|
|
let password = "passw0rd";
|
|
app.insert_user("dana", password, "admin").await?;
|
|
let token = app.login_token("dana", password).await?;
|
|
|
|
let file_bytes = b"example document body".to_vec();
|
|
let upload = app
|
|
.upload_document(
|
|
"/api/documents",
|
|
"doc.txt",
|
|
"text/plain",
|
|
&file_bytes,
|
|
None,
|
|
&token,
|
|
)
|
|
.await?;
|
|
assert_eq!(upload.status(), StatusCode::CREATED);
|
|
let body = body_to_vec(upload.into_body()).await?;
|
|
let detail: DocumentDetail = serde_json::from_slice(&body)?;
|
|
|
|
assert_eq!(detail.document.original_name, "doc.txt");
|
|
assert_eq!(detail.document.current_version, 1);
|
|
assert_eq!(detail.document.deleted_at, None);
|
|
assert!(detail.document.tags.is_empty());
|
|
assert_eq!(detail.current_version.size_bytes, file_bytes.len() as i64);
|
|
assert!(detail.assets.is_empty());
|
|
|
|
let stored = app
|
|
.storage()
|
|
.get(&detail.current_version.s3_key)
|
|
.await
|
|
.expect("object stored");
|
|
assert_eq!(stored.bytes, file_bytes);
|
|
assert_eq!(app.storage().object_count().await, 1);
|
|
|
|
let response = app.get("/api/documents", Some(&token)).await?;
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
let body = body_to_vec(response.into_body()).await?;
|
|
let mut list: Vec<DocumentListItem> = serde_json::from_slice(&body)?;
|
|
assert_eq!(list.len(), 1);
|
|
let item = list.pop().unwrap();
|
|
assert_eq!(item.id, detail.document.id);
|
|
assert_eq!(item.current_version, 1);
|
|
|
|
let download = app
|
|
.get(
|
|
&format!("/api/documents/{}/download", detail.document.id),
|
|
Some(&token),
|
|
)
|
|
.await?;
|
|
assert_eq!(download.status(), StatusCode::OK);
|
|
let body = body_to_vec(download.into_body()).await?;
|
|
let download_info: DocumentDownload = serde_json::from_slice(&body)?;
|
|
assert!(download_info.url.contains(&detail.current_version.s3_key));
|
|
assert_eq!(download_info.filename, "doc.txt");
|
|
|
|
app.cleanup().await?;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn duplicate_and_restore_document() -> Result<()> {
|
|
let _lock = acquire_db_lock().await;
|
|
let app = TestApp::new().await?;
|
|
|
|
let password = "pass1234";
|
|
app.insert_user("sam", password, "admin").await?;
|
|
let token = app.login_token("sam", password).await?;
|
|
|
|
let payload = b"same bytes".to_vec();
|
|
let first = app
|
|
.upload_document(
|
|
"/api/documents",
|
|
"dup.bin",
|
|
"application/octet-stream",
|
|
&payload,
|
|
None,
|
|
&token,
|
|
)
|
|
.await?;
|
|
assert_eq!(first.status(), StatusCode::CREATED);
|
|
let first_body = body_to_vec(first.into_body()).await?;
|
|
let first_detail: DocumentDetail = serde_json::from_slice(&first_body)?;
|
|
|
|
let second = app
|
|
.upload_document(
|
|
"/api/documents",
|
|
"dup.bin",
|
|
"application/octet-stream",
|
|
&payload,
|
|
None,
|
|
&token,
|
|
)
|
|
.await?;
|
|
assert_eq!(second.status(), StatusCode::OK);
|
|
let second_body = body_to_vec(second.into_body()).await?;
|
|
let second_detail: DocumentDetail = serde_json::from_slice(&second_body)?;
|
|
|
|
assert_eq!(first_detail.document.id, second_detail.document.id);
|
|
assert_eq!(second_detail.document.deleted_at, None);
|
|
assert!(second_detail.assets.is_empty());
|
|
assert_eq!(app.storage().object_count().await, 1);
|
|
|
|
let delete = app
|
|
.delete(
|
|
&format!("/api/documents/{}", first_detail.document.id),
|
|
Some(&token),
|
|
)
|
|
.await?;
|
|
assert_eq!(delete.status(), StatusCode::NO_CONTENT);
|
|
|
|
let third = app
|
|
.upload_document(
|
|
"/api/documents",
|
|
"dup.bin",
|
|
"application/octet-stream",
|
|
&payload,
|
|
None,
|
|
&token,
|
|
)
|
|
.await?;
|
|
assert_eq!(third.status(), StatusCode::OK);
|
|
let third_body = body_to_vec(third.into_body()).await?;
|
|
let third_detail: DocumentDetail = serde_json::from_slice(&third_body)?;
|
|
|
|
assert_eq!(third_detail.document.id, first_detail.document.id);
|
|
assert_eq!(third_detail.document.deleted_at, None);
|
|
assert_eq!(app.storage().object_count().await, 1);
|
|
|
|
app.cleanup().await?;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn bulk_reanalyze_documents() -> Result<()> {
|
|
let _lock = acquire_db_lock().await;
|
|
let app = TestApp::new().await?;
|
|
|
|
let password = "bulkpass";
|
|
app.insert_user("alex", password, "admin").await?;
|
|
let token = app.login_token("alex", password).await?;
|
|
|
|
app.clear_jobs().await?;
|
|
|
|
let first_bytes = b"first doc";
|
|
let first = app
|
|
.upload_document(
|
|
"/api/documents",
|
|
"first.txt",
|
|
"text/plain",
|
|
first_bytes,
|
|
None,
|
|
&token,
|
|
)
|
|
.await?;
|
|
assert_eq!(first.status(), StatusCode::CREATED);
|
|
let first_body = body_to_vec(first.into_body()).await?;
|
|
let first_detail: DocumentDetail = serde_json::from_slice(&first_body)?;
|
|
|
|
let second_bytes = b"second doc";
|
|
let second = app
|
|
.upload_document(
|
|
"/api/documents",
|
|
"second.txt",
|
|
"text/plain",
|
|
second_bytes,
|
|
None,
|
|
&token,
|
|
)
|
|
.await?;
|
|
assert_eq!(second.status(), StatusCode::CREATED);
|
|
let second_body = body_to_vec(second.into_body()).await?;
|
|
let second_detail: DocumentDetail = serde_json::from_slice(&second_body)?;
|
|
|
|
app.clear_jobs().await?;
|
|
|
|
let response = app
|
|
.post_json(
|
|
"/api/documents/reanalyze",
|
|
&serde_json::json!({}),
|
|
Some(&token),
|
|
)
|
|
.await?;
|
|
assert_eq!(response.status(), StatusCode::ACCEPTED);
|
|
let body = body_to_vec(response.into_body()).await?;
|
|
let bulk: BulkReanalyze = serde_json::from_slice(&body)?;
|
|
assert_eq!(bulk.queued, 2);
|
|
|
|
let jobs = app.jobs_by_type("analyze-document").await?;
|
|
assert_eq!(jobs.len(), 2);
|
|
let mut payload_docs = Vec::new();
|
|
for job in jobs {
|
|
let payload: AnalyzeJobPayload = serde_json::from_value(job.payload)?;
|
|
assert!(payload.force);
|
|
payload_docs.push((payload.document_id, payload.document_version_id));
|
|
}
|
|
|
|
let mut expected = vec![
|
|
(first_detail.document.id, first_detail.current_version.id),
|
|
(second_detail.document.id, second_detail.current_version.id),
|
|
];
|
|
payload_docs.sort();
|
|
expected.sort();
|
|
assert_eq!(payload_docs, expected);
|
|
|
|
app.cleanup().await?;
|
|
Ok(())
|
|
}
|