chore(deps): Update to axum 0.7 (#37)

Reviewed-on: #37
Co-authored-by: Felipe Contreras Salinas <felipe@bstr.cl>
Co-committed-by: Felipe Contreras Salinas <felipe@bstr.cl>
This commit is contained in:
Felipe 2024-01-19 18:26:24 -03:00 committed by Felipe
parent 0191a7e2c1
commit 644cdb92b0
Signed by: Ludwig
GPG key ID: 441A26F83D31FAFF
5 changed files with 435 additions and 313 deletions

592
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -5,33 +5,37 @@ edition = "2021"
license = "AGPL-3.0" license = "AGPL-3.0"
[dependencies] [dependencies]
axum = { version = "0.6.20", default-features = false, features = [ anyhow = "1.0.79"
axum = { version = "0.7.4", default-features = false, features = [
"json",
"tracing", "tracing",
"tokio", "tokio",
] } ] }
dotenvy = "0.15.7"
futures = { version = "0.3.30", default-features = false }
serde = { version = "1.0.195", features = ["derive"] }
serde_json = "1.0.111"
sqlx = { version = "0.7.3", default-features = false, features = [
"macros",
"migrate",
"runtime-tokio",
"sqlite",
"tls-rustls",
] }
tokio = { version = "1.35.1", default-features = false, features = [
"macros",
"rt-multi-thread",
"signal",
] }
tower-http = { version = "0.5.1", default-features = false, features = ["fs"] }
tracing = "0.1.40" tracing = "0.1.40"
tracing-subscriber = { version = "0.3.17", default-features = false, features = [ tracing-subscriber = { version = "0.3.18", default-features = false, features = [
"env-filter", "env-filter",
"tracing",
"fmt", "fmt",
"tracing",
"tracing-log", "tracing-log",
] } ] }
tokio = { version = "1.34.0", default-features = false, features = [
"macros", [dev-dependencies]
"signal", # axum-test-helper = "0.3.0"
"rt-multi-thread", axum-test = "14.2.2"
] }
dotenvy = "0.15.7"
tower-http = { version = "0.4.4", default-features = false, features = ["fs"] }
serde = { version = "1.0.192", features = ["derive"] }
serde_json = "1.0.108"
futures = { version = "0.3.29", default-features = false }
axum-test-helper = "0.3.0"
sqlx = { version = "0.7.2", default-features = false, features = [
"runtime-tokio",
"tls-rustls",
"macros",
"migrate",
"sqlite",
] }
anyhow = "1.0.75"

View file

@ -1,5 +1,5 @@
##### Builder #### ##### Builder ####
FROM rust:1.73-alpine as builder FROM rust:1.75-alpine3.19 as builder
# Install dependencies # Install dependencies
RUN apk add --no-cache sqlite npm musl-dev fd minify && npm install -g typescript RUN apk add --no-cache sqlite npm musl-dev fd minify && npm install -g typescript
@ -36,7 +36,7 @@ WORKDIR /usr/src/huellas/ts-client/
# Install dependencies # Install dependencies
RUN npm ci RUN npm ci
# Transpile and delete the first line of jvascript ts-client # Transpile and delete the first line of javascript ts-client
RUN tsc && sed -i '1d' build/client.js RUN tsc && sed -i '1d' build/client.js
# Minify static files # Minify static files
@ -46,7 +46,7 @@ RUN fd -e html . '/usr/src/huellas/static/' -x minify -r -o {} {} \
################ ################
##### Runtime ##### Runtime
FROM alpine:3.18 AS Runtime FROM alpine:3.19 AS Runtime
RUN apk add --no-cache sqlite RUN apk add --no-cache sqlite

View file

@ -37,8 +37,8 @@ async fn main() -> Result<()> {
let port = str::parse(&port).unwrap_or(3000); let port = str::parse(&port).unwrap_or(3000);
let address = SocketAddr::from(([0, 0, 0, 0], port)); let address = SocketAddr::from(([0, 0, 0, 0], port));
tracing::debug!("listening on {}", address); tracing::debug!("listening on {}", address);
axum::Server::bind(&address) let listener = tokio::net::TcpListener::bind(address).await?;
.serve(app.into_make_service()) axum::serve(listener, app.into_make_service())
.with_graceful_shutdown(shutdown_signal()) .with_graceful_shutdown(shutdown_signal())
.await?; .await?;

View file

@ -126,17 +126,19 @@ mod tests {
#![cfg(test)] #![cfg(test)]
use super::places_routes; use super::places_routes;
use crate::place::Place; use crate::place::Place;
use anyhow::{Context, Result};
use axum::http::StatusCode; use axum::http::StatusCode;
use axum::Router; use axum::Router;
use axum_test_helper::TestClient; use axum_test::TestServer;
use sqlx::sqlite::SqlitePool; use sqlx::sqlite::SqlitePool;
fn client(pool: &SqlitePool) -> TestClient { fn server(pool: &SqlitePool) -> Result<TestServer> {
let router = Router::new().nest("/places", places_routes(pool.clone())); let router = Router::new().nest("/places", places_routes(pool.clone()));
TestClient::new(router) TestServer::new(router)
} }
async fn get_from_db(pool: &SqlitePool, id: i64) -> Place { async fn get_from_db(pool: &SqlitePool, id: i64) -> Result<Place> {
sqlx::query_as!( sqlx::query_as!(
Place, Place,
r#"SELECT id, name, address, open_hours, icon, description, url, r#"SELECT id, name, address, open_hours, icon, description, url,
@ -147,12 +149,12 @@ mod tests {
) )
.fetch_one(pool) .fetch_one(pool)
.await .await
.expect("Couldn't get from DB") .context("Couldn't get from DB")
} }
#[sqlx::test] #[sqlx::test]
async fn test_add_place(pool: SqlitePool) { async fn test_add_place(pool: SqlitePool) -> Result<()> {
let client = client(&pool); let server = server(&pool)?;
let mut place = Place { let mut place = Place {
id: None, id: None,
name: "Sherlock Holmes".to_owned(), name: "Sherlock Holmes".to_owned(),
@ -165,25 +167,25 @@ mod tests {
url: Some("https://www.sherlock-holmes.co.uk/".to_owned()), url: Some("https://www.sherlock-holmes.co.uk/".to_owned()),
}; };
// Insert the place // Insert the place
let res = client.put("/places").json(&place).send().await; let res = server.put("/places").json(&place).await;
// We should get a success on the request // We should get a success on the request
assert_eq!(res.status(), StatusCode::OK); assert_eq!(res.status_code(), StatusCode::OK);
let res_place: Place = serde_json::from_value(res.json().await).unwrap(); let res_place: Place = res.json();
// The inserted place should have an ID // The inserted place should have an ID
assert!(res_place.id.is_some()); assert!(res_place.id.is_some());
// Add the returned ID to the original place // Add the returned ID to the original place
place.id = res_place.id; place.id = res_place.id;
// And now they should be equal // And now they should be equal
assert_eq!(place, res_place); assert_eq!(place, res_place);
// TODO: actually query the DB to check the place was inserted // Check against the place stored in the DB
// confirm that the places stored in the DB are also the same let db_place = get_from_db(&pool, place.id.unwrap()).await?;
let db_place = get_from_db(&pool, place.id.unwrap()).await;
assert_eq!(place, db_place); assert_eq!(place, db_place);
Ok(())
} }
#[sqlx::test] #[sqlx::test]
async fn test_get_places(pool: SqlitePool) { async fn test_get_places(pool: SqlitePool) -> Result<()> {
let client = client(&pool); let server = server(&pool)?;
let mut places = vec![ let mut places = vec![
Place { Place {
id: None, id: None,
@ -210,29 +212,30 @@ mod tests {
]; ];
// insert the places // insert the places
for p in &mut places { for p in &mut places {
let res = client.put("/places").json(&p).send().await; let res = server.put("/places").json(&p).await;
// We should get a success on the request // We should get a success on the request
assert_eq!(res.status(), StatusCode::OK); assert_eq!(res.status_code(), StatusCode::OK);
let res_place: Place = serde_json::from_value(res.json().await).unwrap(); let res_place: Place = res.json();
// The inserted place should have an ID // The inserted place should have an ID
assert!(res_place.id.is_some()); assert!(res_place.id.is_some());
// Add the returned ID to the original place // Add the returned ID to the original place
p.id = res_place.id; p.id = res_place.id;
} }
// and fetch them // and fetch them
let res = client.get("/places").send().await; let res = server.get("/places").await;
// We should get a success on the request // We should get a success on the request
assert_eq!(res.status(), StatusCode::OK); assert_eq!(res.status_code(), StatusCode::OK);
let mut res_places: Vec<Place> = serde_json::from_value(res.json().await).unwrap(); let mut res_places: Vec<Place> = res.json();
// and they should be equal // and they should be equal
places.sort_by(|a, b| a.id.cmp(&b.id)); places.sort_by(|a, b| a.id.cmp(&b.id));
res_places.sort_by(|a, b| a.id.cmp(&b.id)); res_places.sort_by(|a, b| a.id.cmp(&b.id));
assert_eq!(places, res_places); assert_eq!(places, res_places);
Ok(())
} }
#[sqlx::test] #[sqlx::test]
async fn test_delete(pool: SqlitePool) { async fn test_delete(pool: SqlitePool) -> Result<()> {
let client = client(&pool); let server = server(&pool)?;
let mut places = vec![ let mut places = vec![
Place { Place {
id: None, id: None,
@ -259,35 +262,45 @@ mod tests {
]; ];
// insert the places // insert the places
for p in &mut places { for p in &mut places {
let res = client.put("/places").json(&p).send().await; let res = server.put("/places").json(&p).await;
// We should get a success on the request // We should get a success on the request
assert_eq!(res.status(), StatusCode::OK); assert_eq!(res.status_code(), StatusCode::OK);
let res_place: Place = serde_json::from_value(res.json().await).unwrap(); let res_place: Place = res.json();
// The inserted place should have an ID // The inserted place should have an ID
assert!(res_place.id.is_some()); assert!(res_place.id.is_some());
// Add the returned ID to the original place // Add the returned ID to the original place
p.id = res_place.id; p.id = res_place.id;
} }
// delete the first one // delete the first one
let res = client let res = server
.delete(&format!("/places/{}", places[0].id.unwrap())) .delete(&format!("/places/{}", places[0].id.unwrap()))
.send()
.await; .await;
// We should get a success on the request // We should get a success on the request
assert_eq!(res.status(), StatusCode::OK); assert_eq!(res.status_code(), StatusCode::OK);
// fetch the remaining places // fetch the remaining places
let res = client.get("/places").send().await; let res = server.get("/places").await;
// We should get a success on the request // We should get a success on the request
assert_eq!(res.status(), StatusCode::OK); assert_eq!(res.status_code(), StatusCode::OK);
let res_places: Vec<Place> = serde_json::from_value(res.json().await).unwrap(); let res_places: Vec<Place> = res.json();
// we should only get the second place // we should only get the second place
assert_eq!(&places[1..], res_places.as_slice()); assert_eq!(&places[1..], res_places.as_slice());
Ok(())
} }
#[sqlx::test] #[sqlx::test]
async fn test_update(pool: SqlitePool) { async fn test_delete_not_existing(pool: SqlitePool) -> Result<()> {
let client = client(&pool); let server = server(&pool)?;
// Try to delete a non-existing place
let res = server.delete("/places/33").await;
// We should get the corresponding status code
assert_eq!(res.status_code(), StatusCode::NOT_FOUND);
Ok(())
}
#[sqlx::test]
async fn test_update(pool: SqlitePool) -> Result<()> {
let server = server(&pool)?;
let mut places = vec![ let mut places = vec![
Place { Place {
id: None, id: None,
@ -313,25 +326,26 @@ mod tests {
}, },
]; ];
// insert original place // insert original place
let res = client.put("/places").json(&places[0]).send().await; let res = server.put("/places").json(&places[0]).await;
// We should get a success on the request // We should get a success on the request
assert_eq!(res.status(), StatusCode::OK); assert_eq!(res.status_code(), StatusCode::OK);
let res_place: Place = serde_json::from_value(res.json().await).unwrap(); let res_place: Place = res.json();
// The inserted place should have an ID // The inserted place should have an ID
assert!(res_place.id.is_some()); assert!(res_place.id.is_some());
// Add the returned ID to the new place so we can do the update // Add the returned ID to the new place so we can do the update
places[1].id = res_place.id; places[1].id = res_place.id;
// update the place // update the place
let res = client.put("/places").json(&places[1]).send().await; let res = server.put("/places").json(&places[1]).await;
// We should get a success on the request // We should get a success on the request
assert_eq!(res.status(), StatusCode::OK); assert_eq!(res.status_code(), StatusCode::OK);
// fetch the places // fetch the places
let res = client.get("/places").send().await; let res = server.get("/places").await;
// We should get a success on the request // We should get a success on the request
assert_eq!(res.status(), StatusCode::OK); assert_eq!(res.status_code(), StatusCode::OK);
let res_places: Vec<Place> = serde_json::from_value(res.json().await).unwrap(); let res_places: Vec<Place> = res.json();
// we should get the updated place // we should get the updated place
assert_eq!(&places[1..], res_places.as_slice()); assert_eq!(&places[1..], res_places.as_slice());
Ok(())
} }
} }