huellas/src/places/routes.rs

146 lines
5.1 KiB
Rust
Raw Normal View History

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<T, E = (StatusCode, String)> = std::result::Result<T, E>;
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<PR: PlacesRepository>(
State(repository): State<PR>,
) -> Result<MsgPack<Vec<Place>>> {
let places = repository.get_places().await.map_err(internal_error)?;
Ok(MsgPack(places))
}
async fn upsert_place<PR: PlacesRepository>(
State(repository): State<PR>,
MsgPack(place): MsgPack<PlaceUpsert>,
) -> Result<MsgPack<Place>> {
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<PR: PlacesRepository>(
State(repository): State<PR>,
Path(id): Path<i64>,
) -> Result<()> {
repository.delete_place(id).await.map_err(internal_error)?;
Ok(())
}
pub fn places_routes<PR: PlacesRepository>(repository: PR) -> Router {
Router::new()
.route("/", get(get_places::<PR>))
.route("/", put(upsert_place::<PR>))
.route("/{id}", delete(delete_place::<PR>))
.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<Place> = 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(())
}
}