//! Content insertion workflow tests: presign, confirm, rename, delete. //! //! These tests use the in-memory storage backend. use crate::harness::TestHarness; use serde_json::Value; /// Setup: creator user logged in with small_files tier. Returns user_id. async fn setup_creator(h: &mut TestHarness) -> String { let user_id = h.create_creator("insuser").await; h.grant_tier(user_id, "small_files").await; user_id.to_string() } #[tokio::test] async fn presign_requires_auth() { let mut h = TestHarness::with_storage().await; let resp = h .client .post_json( "/api/users/me/insertions/presign", r#"{"file_name": "test.mp3", "content_type": "audio/mpeg"}"#, ) .await; assert!( resp.status.is_client_error() || resp.status.is_redirection(), "Unauthenticated presign should be rejected: {} {}", resp.status, resp.text ); } #[tokio::test] async fn presign_invalid_content_type_rejected() { let mut h = TestHarness::with_storage().await; let _user_id = setup_creator(&mut h).await; let resp = h .client .post_json( "/api/users/me/insertions/presign", r#"{"file_name": "evil.exe", "content_type": "application/octet-stream"}"#, ) .await; assert!( resp.status.is_client_error(), "Invalid content type should be rejected: {} {}", resp.status, resp.text ); } #[tokio::test] async fn presign_valid_audio_succeeds() { let mut h = TestHarness::with_storage().await; let _user_id = setup_creator(&mut h).await; let resp = h .client .post_json( "/api/users/me/insertions/presign", r#"{"file_name": "intro.mp3", "content_type": "audio/mpeg"}"#, ) .await; assert!( resp.status.is_success(), "Valid presign should succeed: {} {}", resp.status, resp.text ); let body: Value = resp.json(); assert!(body["upload_url"].is_string(), "Should return upload_url"); assert!(body["s3_key"].is_string(), "Should return s3_key"); } #[tokio::test] async fn confirm_nonexistent_object_rejected() { let mut h = TestHarness::with_storage().await; let _user_id = setup_creator(&mut h).await; let resp = h .client .post_json( "/api/users/me/insertions/confirm", r#"{"s3_key": "nonexistent/key.mp3", "title": "Test", "duration_ms": 5000, "file_size": 1024, "mime_type": "audio/mpeg"}"#, ) .await; assert!( resp.status.is_client_error() || resp.status.is_server_error(), "Confirming nonexistent object should fail: {} {}", resp.status, resp.text ); } #[tokio::test] async fn confirm_with_object_succeeds() { let mut h = TestHarness::with_storage().await; let user_id = setup_creator(&mut h).await; // Pre-populate storage with a fake object (key must match user_id prefix) let s3_key = format!("{}/insertions/intro.mp3", user_id); h.storage.as_ref().unwrap().put(&s3_key, vec![0u8; 1024]); let resp = h .client .post_json( "/api/users/me/insertions/confirm", &format!( r#"{{"s3_key": "{}", "title": "Intro Music", "duration_ms": 5000, "file_size": 1024, "mime_type": "audio/mpeg"}}"#, s3_key ), ) .await; assert!( resp.status.is_success(), "Confirm with existing object should succeed: {} {}", resp.status, resp.text ); let body: Value = resp.json(); assert_eq!(body["title"].as_str().unwrap(), "Intro Music"); } #[tokio::test] async fn confirm_empty_title_rejected() { let mut h = TestHarness::with_storage().await; let user_id = setup_creator(&mut h).await; let s3_key = format!("{}/insertions/empty.mp3", user_id); h.storage.as_ref().unwrap().put(&s3_key, vec![0u8; 1024]); let resp = h .client .post_json( "/api/users/me/insertions/confirm", &format!( r#"{{"s3_key": "{}", "title": "", "duration_ms": 5000, "file_size": 1024, "mime_type": "audio/mpeg"}}"#, s3_key ), ) .await; assert!( resp.status.is_client_error(), "Empty title should be rejected: {} {}", resp.status, resp.text ); } #[tokio::test] async fn delete_nonexistent_insertion_returns_404() { let mut h = TestHarness::with_storage().await; let _user_id = setup_creator(&mut h).await; let fake_id = uuid::Uuid::new_v4(); let resp = h .client .delete(&format!("/api/insertions/{}", fake_id)) .await; assert_eq!( resp.status, 404, "Deleting nonexistent insertion should return 404: {} {}", resp.status, resp.text ); } #[tokio::test] async fn rename_nonexistent_insertion_returns_404() { let mut h = TestHarness::with_storage().await; let _user_id = setup_creator(&mut h).await; let fake_id = uuid::Uuid::new_v4(); let resp = h .client .put_json( &format!("/api/insertions/{}", fake_id), r#"{"title": "New Name"}"#, ) .await; assert_eq!( resp.status, 404, "Renaming nonexistent insertion should return 404: {} {}", resp.status, resp.text ); }