mod common; use anyhow::Result; use axum::http::StatusCode; use common::{acquire_db_lock, body_to_vec, TestApp}; use serde::Deserialize; use serde::Serialize; use uuid::Uuid; #[derive(Deserialize)] struct FolderResponse { folder: FolderInfo, } #[derive(Deserialize)] struct FolderInfo { id: Uuid, } #[derive(Deserialize)] struct FolderContents { documents: Vec, } #[derive(Deserialize)] struct DocSummary { id: Uuid, } #[derive(Serialize)] struct CreateFolder<'a> { name: &'a str, parent_id: Option, } #[derive(Serialize)] struct MoveDocumentRequest { folder_id: Option, } #[derive(Deserialize)] struct DocumentDetail { document: DocSummary, } #[tokio::test] async fn folder_move_and_delete_flow() -> Result<()> { let _lock = acquire_db_lock().await; let Some(app) = TestApp::new().await? else { eprintln!("skipping test: TEST_DATABASE_URL not set"); return Ok(()); }; let password = "folderpass"; app.insert_user("folder-admin", password, "admin").await?; let token = app.login_token("folder-admin", password).await?; let folder_resp = app .post_json( "/api/folders", &CreateFolder { name: "Projects", parent_id: None, }, Some(&token), ) .await?; assert_eq!(folder_resp.status(), StatusCode::OK); let folder_body = body_to_vec(folder_resp.into_body()).await?; let folder: FolderResponse = serde_json::from_slice(&folder_body)?; let upload = app .upload_document( "/api/documents", "plan.pdf", "application/pdf", b"dummy", None, &token, ) .await?; let upload_body = body_to_vec(upload.into_body()).await?; let detail: DocumentDetail = serde_json::from_slice(&upload_body)?; let move_resp = app .patch_json( &format!("/api/documents/{}/folder", detail.document.id), &MoveDocumentRequest { folder_id: Some(folder.folder.id), }, Some(&token), ) .await?; assert_eq!(move_resp.status(), StatusCode::NO_CONTENT); let contents = app .get( &format!("/api/folders/{}/contents", folder.folder.id), Some(&token), ) .await?; assert_eq!(contents.status(), StatusCode::OK); let contents_body = body_to_vec(contents.into_body()).await?; let contents: FolderContents = serde_json::from_slice(&contents_body)?; assert_eq!(contents.documents.len(), 1); assert_eq!(contents.documents[0].id, detail.document.id); let root_contents = app.get("/api/folders/root/contents", Some(&token)).await?; let root_body = body_to_vec(root_contents.into_body()).await?; let root: FolderContents = serde_json::from_slice(&root_body)?; assert!(root .documents .iter() .all(|doc| doc.id != detail.document.id)); let delete_attempt = app .delete(&format!("/api/folders/{}", folder.folder.id), Some(&token)) .await?; assert_eq!(delete_attempt.status(), StatusCode::BAD_REQUEST); let move_back = app .patch_json( &format!("/api/documents/{}/folder", detail.document.id), &MoveDocumentRequest { folder_id: None }, Some(&token), ) .await?; assert_eq!(move_back.status(), StatusCode::NO_CONTENT); let delete = app .delete(&format!("/api/folders/{}", folder.folder.id), Some(&token)) .await?; assert_eq!(delete.status(), StatusCode::NO_CONTENT); app.cleanup().await?; Ok(()) }