use axum::Router; use axum::extract::{Path, State}; use axum::http::StatusCode; use axum::routing::{delete, get, put}; use axum_msgpack::MsgPack; use super::models::{Place, PlaceUpsert}; use super::repository::{PlacesError, PlacesRepository}; type Result = std::result::Result; fn internal_error(err: PlacesError) -> (StatusCode, String) { match err { PlacesError::FailToGet(_) | PlacesError::FailToUpsert(_) | PlacesError::FailToDelete(_) => { (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()) } PlacesError::NotFound(_) => (StatusCode::NOT_FOUND, err.to_string()), } } async fn get_places( State(repository): State, ) -> Result>> { let places = repository.get_places().await.map_err(internal_error)?; Ok(MsgPack(places)) } async fn upsert_place( State(repository): State, MsgPack(place): MsgPack, ) -> Result> { let place = match place.into() { (place, Some(id)) => repository.update_place((place, id).into()).await, (place, None) => repository.insert_place(place).await, } .map_err(internal_error)?; Ok(MsgPack(place)) } async fn delete_place( State(repository): State, Path(id): Path, ) -> Result<()> { repository.delete_place(id).await.map_err(internal_error)?; Ok(()) } pub fn places_routes(repository: PR) -> Router { Router::new() .route("/", get(get_places::)) .route("/", put(upsert_place::)) .route("/{id}", delete(delete_place::)) .with_state(repository) } mod tests { #![cfg(test)] use super::places_routes; use crate::places::models::{Place, PlaceUpsert}; use crate::places::repository::MockPlacesRepository; use anyhow::Result; use axum::Router; use axum::http::StatusCode; use axum_test::TestServer; fn setup_server() -> Result<(TestServer, MockPlacesRepository)> { let places_repository = MockPlacesRepository::new(); let router = Router::new().nest("/places", places_routes(places_repository.clone())); Ok((TestServer::new(router)?, places_repository)) } #[tokio::test] async fn test_add_place() -> Result<()> { let (server, mock_repository) = setup_server()?; let place = PlaceUpsert { id: None, name: "Sherlock Holmes".to_owned(), address: "221 B Baker Street, London".to_owned(), description: "Museum and Gift Shop".to_owned(), icon: "museum".to_owned(), latitude: 51.5237669, longitude: -0.1627829, open_hours: "Tu-Su 09:30-18:00".to_owned(), url: Some("https://www.sherlock-holmes.co.uk/".to_owned()), }; // Insert the place let res = server.put("/places").msgpack(&place).await; // We should get a success on the request assert_eq!(res.status_code(), StatusCode::OK); let _res_place: Place = res.msgpack(); // The correct function should be called assert_eq!(mock_repository.insert_place_count().await, 1); Ok(()) } #[tokio::test] async fn test_get_places() -> Result<()> { let (server, mock_repository) = setup_server()?; // Get the places let res = server.get("/places").await; // We should get a success on the request assert_eq!(res.status_code(), StatusCode::OK); let _res_places: Vec = res.msgpack(); // and the correct function should be called assert_eq!(mock_repository.get_places_count().await, 1); Ok(()) } #[tokio::test] async fn test_delete() -> Result<()> { let (server, mock_repository) = setup_server()?; // Call delete let res = server.delete("/places/0").await; // We should get a success on the request assert_eq!(res.status_code(), StatusCode::OK); // The correct function should be called assert_eq!(mock_repository.delete_place_count().await, 1); Ok(()) } #[tokio::test] async fn test_update() -> Result<()> { let (server, mock_repository) = setup_server()?; let places = PlaceUpsert { id: Some(1), name: "Sherlock Holmes".to_owned(), address: "221 B Baker Street, London".to_owned(), description: "Museum and Gift Shop".to_owned(), icon: "museum".to_owned(), latitude: 51.5237669, longitude: -0.1627829, open_hours: "Tu-Su 09:30-18:00".to_owned(), url: Some("https://www.sherlock-holmes.co.uk/".to_owned()), }; // upsert the place let res = server.put("/places").msgpack(&places).await; // We should get a success on the request assert_eq!(res.status_code(), StatusCode::OK); let _res_place: Place = res.msgpack(); // The correct function should be called assert_eq!(mock_repository.update_place_count().await, 1); Ok(()) } }