chore(deps): Update to axum 0.7 #37
5 changed files with 435 additions and 313 deletions
592
Cargo.lock
generated
592
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
48
Cargo.toml
48
Cargo.toml
|
|
@ -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"
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue