/// Generates mechanical CRUD tests that are identical across entities. /// Entity-specific tests remain hand-written in each file. macro_rules! define_crud_tests { ( entity: $entity:ident, path: $path:expr, list_type: $list_type:ty $(, malformed_json: $malformed:expr)? $(, missing_fields: $missing:expr)? ) => { paste::paste! { #[tokio::test] async fn []() { let app = crate::helpers::spawn_app_with_auth().await; let client = reqwest::Client::new(); let response = client .get(app.api_url(&format!("{}/999999", $path))) .send() .await .expect("Failed to execute request"); assert_eq!(response.status(), 404); } #[tokio::test] async fn []() { let app = crate::helpers::spawn_app_with_auth().await; let client = reqwest::Client::new(); let response = client .delete(app.api_url(&format!("{}/999999", $path))) .bearer_auth(app.auth_token.as_ref().unwrap()) .send() .await .expect("Failed to execute request"); assert_eq!(response.status(), 404); } #[tokio::test] async fn []() { let app = crate::helpers::spawn_app_with_auth().await; let client = reqwest::Client::new(); let response = client .get(app.api_url($path)) .send() .await .expect("Failed to execute request"); assert_eq!(response.status(), 200); let items: Vec<$list_type> = response.json().await.expect("Failed to parse response"); assert_eq!(items.len(), 0); } $( #[tokio::test] async fn []() { let app = crate::helpers::spawn_app_with_auth().await; let client = reqwest::Client::new(); let response = client .post(app.api_url($path)) .bearer_auth(app.auth_token.as_ref().unwrap()) .header("content-type", "application/json") .body($malformed) .send() .await .expect("Failed to execute request"); assert_eq!(response.status(), 400); } )? $( #[tokio::test] async fn []() { let app = crate::helpers::spawn_app_with_auth().await; let client = reqwest::Client::new(); let response = client .post(app.api_url($path)) .bearer_auth(app.auth_token.as_ref().unwrap()) .header("content-type", "application/json") .body($missing) .send() .await .expect("Failed to execute request"); assert_eq!(response.status(), 400); } )? } }; } pub(crate) use define_crud_tests; /// Generates list (with/without datastar header) and delete (with datastar header) /// tests for a given entity. The setup function creates an entity and returns its /// ID as a String (used for the delete test; ignored for list tests). macro_rules! define_datastar_entity_tests { ( entity: $entity:ident, type_param: $type_param:expr, api_path: $api_path:expr, list_element: $list_element:expr, selector: $selector:expr, setup: $setup:expr ) => { paste::paste! { #[tokio::test] async fn [<$entity _list_with_datastar_header_returns_fragment>]() { let app = crate::helpers::spawn_app_with_auth().await; $setup(&app).await; let client = reqwest::Client::new(); let response = client .get(format!("{}/data?type={}", app.address, $type_param)) .header("datastar-request", "true") .send() .await .expect(concat!("failed to fetch ", stringify!($entity))); assert_eq!(response.status(), 200); crate::helpers::assert_datastar_headers_with_mode( &response, "#data-content", "inner", ); let body = response.text().await.expect("failed to read body"); crate::helpers::assert_html_fragment(&body); assert!( body.contains($list_element), "Fragment should contain the selector element" ); } #[tokio::test] async fn [<$entity _list_without_datastar_header_returns_full_page>]() { let app = crate::helpers::spawn_app_with_auth().await; $setup(&app).await; let client = reqwest::Client::new(); let response = client .get(format!("{}/data?type={}", app.address, $type_param)) .send() .await .expect(concat!("failed to fetch ", stringify!($entity))); assert_eq!(response.status(), 200); assert!(response.headers().get("datastar-selector").is_none()); let body = response.text().await.expect("failed to read body"); crate::helpers::assert_full_page(&body); } #[tokio::test] async fn [<$entity _delete_with_datastar_header_returns_fragment>]() { let app = crate::helpers::spawn_app_with_auth().await; let entity_id = $setup(&app).await; let client = reqwest::Client::new(); let response = client .delete(app.api_url(&format!("{}/{}", $api_path, entity_id))) .bearer_auth(app.auth_token.as_ref().unwrap()) .header("datastar-request", "true") .send() .await .expect(concat!("failed to delete ", stringify!($entity))); assert_eq!(response.status(), 200); crate::helpers::assert_datastar_headers(&response, $selector); let body = response.text().await.expect("failed to read body"); crate::helpers::assert_html_fragment(&body); } } }; } pub(crate) use define_datastar_entity_tests;