#![allow(unused)] #![allow(clippy::unwrap_used)] use std::sync::OnceLock; use api::app; use axum_test::{TestRequest, TestResponse, TestServer}; use chrono::NaiveDate; use serde::de::DeserializeOwned; use serde_json::json; use services::User; use tracing::Level; use types::{ organization::Organization, tag::{Category, Tag}, todo::Todo, }; use uuid::Uuid; pub struct Client<'a> { uuid: Option, org: Option, server: &'a TestServer, } impl<'a> Client<'a> { pub fn new(server: &'a TestServer) -> Client<'a> { let uuid = Uuid::new_v4(); Client { uuid: Some(uuid), org: None, server, } } pub fn new_no_user(server: &'a TestServer) -> Client<'a> { Client { uuid: None, org: None, server, } } pub fn new_with_org(server: &'a TestServer, org: Uuid) -> Client<'a> { let uuid = Uuid::new_v4(); Client { uuid: Some(uuid), org: Some(org), server, } } pub fn set_org(&mut self, org: Uuid) { self.org = Some(org) } pub fn uuid(&self) -> Option { self.uuid.clone() } } impl<'a> Client<'a> { fn add_header(&self, mut request: TestRequest) -> TestRequest { if let Some(uuid) = self.uuid { request = request.add_header("x-user-id", uuid.to_string()); }; if let Some(uuid) = self.org { request = request.add_header("x-org-id", uuid.to_string()); }; request } async fn get(&self, path: &str) -> TestResponse { self.add_header(self.server.get(&format!("/api/{path}"))) .await } async fn post(&self, path: &str) -> TestResponse { self.add_header(self.server.post(&format!("/api/{path}"))) .await } async fn post_json(&self, path: &str, body: serde_json::Value) -> TestResponse { self.add_header(self.server.post(&format!("/api/{path}")).json(&body)) .await } async fn put_json(&self, path: &str, body: serde_json::Value) -> TestResponse { self.add_header(self.server.put(&format!("/api/{path}")).json(&body)) .await } async fn delete(&self, path: &str) -> TestResponse { self.add_header(self.server.delete(&format!("/api/{path}"))) .await } fn json(response: &TestResponse) -> T { response.assert_status_success(); response.json() } } impl<'a> Client<'a> { pub async fn get_todos(&self) -> TestResponse { self.get("todos").await } pub async fn get_todos_json(&self) -> Vec { Self::json(&self.get_todos().await) } pub async fn get_deleted_todos(&self) -> TestResponse { self.get("todos?deleted=true").await } pub async fn get_deleted_todos_json(&self) -> Vec { Self::json(&self.get_deleted_todos().await) } pub async fn add_todo(&self, todo: serde_json::Value) -> TestResponse { self.post_json("todos", todo).await } pub async fn add_todo_json(&self, todo: serde_json::Value) -> Todo { Self::json(&self.add_todo(todo).await) } pub async fn update_todo(&self, todo: serde_json::Value) -> TestResponse { self.put_json("todos", todo).await } pub async fn update_todo_json(&self, todo: serde_json::Value) -> Todo { Self::json(&self.update_todo(todo).await) } pub async fn reactivate_todo(&self, todo_uuid: Uuid) -> TestResponse { self.post(&format!("todos/reactivate/{todo_uuid}")).await } pub async fn delete_todo(&self, todo_uuid: Uuid) -> TestResponse { self.delete(&format!("todos/{todo_uuid}")).await } pub async fn delete_todo_json(&self, todo_uuid: Uuid) -> Todo { Self::json(&self.delete_todo(todo_uuid).await) } pub async fn check_todo_with_date(&self, todo_uuid: Uuid, date: NaiveDate) -> TestResponse { self.post_json(&format!("todos/check/{todo_uuid}"), json!({ "date": date })) .await } pub async fn check_todo(&self, todo_uuid: Uuid) -> TestResponse { self.post(&format!("todos/check/{todo_uuid}")).await } pub async fn remove_check_todo(&self, todo_uuid: Uuid) -> TestResponse { self.delete(&format!("todos/check/{todo_uuid}")).await } pub async fn add_organization(&self, organization: serde_json::Value) -> TestResponse { self.post_json("organization", organization).await } pub async fn add_organization_json(&self, organization: serde_json::Value) -> Organization { Self::json(&self.add_organization(organization).await) } pub async fn leave_organization(&self, organization: serde_json::Value) -> TestResponse { self.post_json("organization/leave", organization).await } pub async fn join_organization(&self, organization: serde_json::Value) -> TestResponse { self.post_json("organization/join", organization).await } pub async fn get_organizations(&self) -> TestResponse { self.get("organization").await } pub async fn get_organizations_json(&self) -> Vec { Self::json(&self.get_organizations().await) } pub async fn get_tags(&self) -> TestResponse { self.get("tags").await } pub async fn get_tags_json(&self) -> Vec { Self::json(&self.get_tags().await) } pub async fn add_tag(&self, tag: serde_json::Value) -> TestResponse { self.post_json("tags", tag).await } pub async fn add_tag_json(&self, tag: serde_json::Value) -> Tag { Self::json(&self.add_tag(tag).await) } pub async fn update_tag(&self, tag: serde_json::Value) -> TestResponse { self.put_json("tags", tag).await } pub async fn update_tag_json(&self, tag: serde_json::Value) -> Tag { Self::json(&self.update_tag(tag).await) } pub async fn delete_tag(&self, tag_uuid: Uuid) -> TestResponse { self.delete(&format!("tags/{tag_uuid}")).await } pub async fn delete_tag_json(&self, tag_uuid: Uuid) -> Tag { Self::json(&self.delete_tag(tag_uuid).await) } pub async fn get_categories(&self) -> TestResponse { self.get("categories").await } pub async fn get_categories_json(&self) -> Vec { Self::json(&self.get_categories().await) } pub async fn add_category(&self, category: serde_json::Value) -> TestResponse { self.post_json("categories", category).await } pub async fn add_category_json(&self, category: serde_json::Value) -> Category { Self::json(&self.add_category(category).await) } pub async fn update_category(&self, category: serde_json::Value) -> TestResponse { self.put_json("categories", category).await } pub async fn update_category_json(&self, category: serde_json::Value) -> Category { Self::json(&self.update_category(category).await) } pub async fn delete_category(&self, category_uuid: Uuid) -> TestResponse { self.delete(&format!("categories/{category_uuid}")).await } pub async fn delete_category_json(&self, category_uuid: Uuid) -> Category { Self::json(&self.delete_category(category_uuid).await) } pub async fn move_todo(&self, value: serde_json::Value) -> TestResponse { self.post_json("todos/move", value).await } pub async fn add_web_push_subscription(&self, subscription: serde_json::Value) -> TestResponse { self.post_json("subscription", subscription).await } } static TRACING: OnceLock<()> = OnceLock::new(); pub async fn get_server() -> TestServer { TRACING.get_or_init(|| { tracing_subscriber::fmt().with_max_level(Level::WARN).init(); }); let app = app( "sqlite::memory:", true, None, "http://localhost:3000".to_string(), "http://localhost:8080".to_string(), ) .await .unwrap(); let mut test_server = TestServer::builder().http_transport().build(app); test_server }