From f7036a18a0d1767e2d732bffe79ced752090665a Mon Sep 17 00:00:00 2001
From: Martin Berg Alstad <git@martials.no>
Date: Tue, 17 Dec 2024 17:05:43 +0000
Subject: [PATCH] Tests

---
 .idea/dataSources.xml               |   2 +-
 Cargo.lock                          |  19 +++--
 Cargo.toml                          |   1 +
 src/database.rs                     |  35 ++------
 src/error.rs                        |  22 ++++-
 src/main.rs                         |  11 ++-
 src/routes/auth.rs                  |  68 ++++++++++-----
 src/services/reservation_service.rs |  93 ++++++++++-----------
 src/services/room_service.rs        |   5 +-
 src/test.rs                         | 124 ++--------------------------
 10 files changed, 145 insertions(+), 235 deletions(-)

diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml
index cf1fc59..75f69bb 100644
--- a/.idea/dataSources.xml
+++ b/.idea/dataSources.xml
@@ -5,7 +5,7 @@
       <driver-ref>postgresql</driver-ref>
       <synchronize>true</synchronize>
       <jdbc-driver>org.postgresql.Driver</jdbc-driver>
-      <jdbc-url>jdbc:postgresql://localhost:32784/postgres</jdbc-url>
+      <jdbc-url>jdbc:postgresql://localhost:32769/postgres</jdbc-url>
       <working-dir>$ProjectFileDir$</working-dir>
     </data-source>
   </component>
diff --git a/Cargo.lock b/Cargo.lock
index 46554eb..ea4671d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -441,18 +441,19 @@ dependencies = [
 
 [[package]]
 name = "bon"
-version = "2.0.0"
+version = "2.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea8256e3cff531086cc3faf94c1649930ff64bceb2d0e8cc84fc0356d7ee9806"
+checksum = "811d7882589e047896e5974d039dd8823a67973a63d559e6ad1e87ff5c42ed4f"
 dependencies = [
  "bon-macros",
+ "rustversion",
 ]
 
 [[package]]
 name = "bon-macros"
-version = "2.0.0"
+version = "2.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b99838f77c5073bc7846ecce92b64e7e5a5bd152a8ec392facf90ee4d90b4b35"
+checksum = "d8e745a763e579a5ce70130e66f9dd35abf77cfeb9f418f305aeab8d1ae54c43"
 dependencies = [
  "darling",
  "ident_case",
@@ -1509,7 +1510,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
 name = "lib"
 version = "1.4.3"
 dependencies = [
+ "async-trait",
  "axum",
+ "bon",
  "chrono",
  "deadpool-diesel",
  "derive_more",
@@ -1517,9 +1520,12 @@ dependencies = [
  "diesel-async",
  "diesel-crud-derive",
  "diesel-crud-trait",
+ "diesel_async_migrations",
  "into-response-derive",
  "mime",
  "serde",
+ "serde_json",
+ "testcontainers-modules",
  "thiserror",
  "tokio",
  "tower 0.5.0",
@@ -2841,15 +2847,14 @@ dependencies = [
 
 [[package]]
 name = "tower-http"
-version = "0.5.2"
+version = "0.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5"
+checksum = "8437150ab6bbc8c5f0f519e3d5ed4aa883a83dd4cdd3d1b21f9482936046cb97"
 dependencies = [
  "bitflags 2.6.0",
  "bytes",
  "http",
  "http-body",
- "http-body-util",
  "pin-project-lite",
  "tower-layer",
  "tower-service",
diff --git a/Cargo.toml b/Cargo.toml
index 8996b1f..a51d48f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -45,6 +45,7 @@ lib = { path = "../lib", features = ["axum", "serde", "derive", "diesel", "time"
 #lib = { git = "https://github.com/emberal/rust-lib", tag = "1.4.3", features = ["axum", "serde", "derive"] }
 
 [dev-dependencies]
+lib = { path = "../lib", features = ["test"] }
 rstest = "0.22.0"
 testcontainers-modules = { version = "0.10.0", features = ["postgres"] }
 async-std = { version = "1.12.0", features = ["attributes"] }
diff --git a/src/database.rs b/src/database.rs
index b559a62..f48ef8e 100644
--- a/src/database.rs
+++ b/src/database.rs
@@ -1,35 +1,10 @@
 use crate::config;
-use crate::error::AppError;
-use axum::async_trait;
 use deadpool_diesel::postgres::BuildError;
-use deadpool_diesel::Status;
-use diesel_async::pooled_connection::deadpool::{Object, Pool};
-use diesel_async::pooled_connection::AsyncDieselConnectionManager;
-use diesel_async::AsyncPgConnection;
-
-pub type PgPool = Pool<AsyncPgConnection>;
-
-#[async_trait]
-pub trait GetConnection: Clone + Send + Sync {
-    async fn get(&self) -> Result<Object<AsyncPgConnection>, AppError>;
-    fn status(&self) -> Status;
-}
-
-#[async_trait]
-impl GetConnection for PgPool {
-    async fn get(&self) -> Result<Object<AsyncPgConnection>, AppError> {
-        self.get().await.map_err(Into::into)
-    }
-    fn status(&self) -> Status {
-        self.status()
-    }
-}
+use lib::diesel::pool::PgPool;
 
 pub(crate) fn create_pool() -> Result<PgPool, BuildError> {
-    create_pool_from_url(config::DATABASE_URL)
-}
-
-pub(crate) fn create_pool_from_url(url: impl Into<String>) -> Result<PgPool, BuildError> {
-    let config = AsyncDieselConnectionManager::<AsyncPgConnection>::new(url);
-    Pool::builder(config).max_size(config::POOL_SIZE).build()
+    lib::diesel::pool::create_pool()
+        .url(config::DATABASE_URL)
+        .size(config::POOL_SIZE)
+        .call()
 }
diff --git a/src/error.rs b/src/error.rs
index 75deff2..da1dc2b 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,11 +1,11 @@
+use crate::services::reservation_service::ReservationError;
 use axum::http::StatusCode;
 use axum::response::{IntoResponse, Response};
 use axum_login::AuthnBackend;
 use derive_more::Constructor;
 use diesel::result::DatabaseErrorKind;
 use diesel_async::pooled_connection::deadpool;
-
-use crate::services::reservation_service::ReservationError;
+use lib::diesel::get_connection::GetConnectionError;
 use lib::diesel_crud_trait::CrudError;
 use lib::into_response_derive::IntoResponse;
 use serde::{Deserialize, Serialize};
@@ -96,6 +96,15 @@ impl IntoResponse for ResponseError {
     }
 }
 
+impl From<GetConnectionError> for ResponseError {
+    fn from(value: GetConnectionError) -> Self {
+        match value {
+            GetConnectionError::PoolError(error) => Self::InternalServerError(error.to_string()),
+            GetConnectionError::DieselError(error) => Self::InternalServerError(error.to_string()),
+        }
+    }
+}
+
 impl From<CrudError> for ResponseError {
     fn from(value: CrudError) -> Self {
         match &value {
@@ -156,6 +165,15 @@ impl IntoResponse for AppError {
     }
 }
 
+impl From<GetConnectionError> for AppError {
+    fn from(value: GetConnectionError) -> Self {
+        match value {
+            GetConnectionError::PoolError(error) => Self::PoolError(error),
+            GetConnectionError::DieselError(error) => Self::DatabaseError(error),
+        }
+    }
+}
+
 impl From<base64ct::Error> for AppError {
     fn from(value: base64ct::Error) -> Self {
         Self::Base64Error(value.to_string())
diff --git a/src/main.rs b/src/main.rs
index aadd46b..539deb6 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -2,13 +2,16 @@
 #[macro_use]
 extern crate rstest;
 
-use crate::database::{create_pool, GetConnection};
+use crate::database::create_pool;
 use crate::services::session_service::SessionService;
 use crate::services::user_service::UserService;
+use axum::middleware::{from_fn, Next};
+use axum::response::IntoResponse;
 use axum::Router;
 use axum_login::tower_sessions::SessionManagerLayer;
 use axum_login::AuthManagerLayerBuilder;
 use lib::axum::app::AppBuilder;
+use lib::diesel::get_connection::GetConnection;
 use tower_sessions::cookie::time::Duration;
 use tower_sessions::Expiry;
 
@@ -73,7 +76,7 @@ async fn main() {
 trait LoginRequired {
     fn login_required<Pool>(self) -> Self
     where
-        Pool: GetConnection + Send + Sync + 'static;
+        Pool: GetConnection + 'static;
 }
 
 impl<S> LoginRequired for Router<S>
@@ -84,10 +87,6 @@ where
     where
         Pool: GetConnection + Send + Sync + 'static,
     {
-        use axum_login::axum::{
-            middleware::{from_fn, Next},
-            response::IntoResponse,
-        };
         self.route_layer(from_fn(
             |auth_session: axum_login::AuthSession<UserService<Pool>>, req, next: Next| async move {
                 if auth_session.user.is_some() {
diff --git a/src/routes/auth.rs b/src/routes/auth.rs
index 72cef36..33f69ff 100644
--- a/src/routes/auth.rs
+++ b/src/routes/auth.rs
@@ -1,5 +1,5 @@
 use crate::auth::AuthSession;
-use crate::database::GetConnection;
+use crate::error::ResponseError;
 use crate::models::user::{CreateUser, User, UserCredentials};
 use crate::result::{ResponseResult, Success};
 use crate::services::user_service::UserService;
@@ -7,17 +7,17 @@ use crate::{bad_request, internal_server_error, ok};
 use axum::extract::State;
 use axum::Json;
 use axum_valid::Valid;
+use lib::diesel::get_connection::GetConnection;
 // router!(
 //     "/auth",
 //     routes!(
 //         post "/login" => login::<Pool>,
 //         post "/register" => register::<Pool>
 //     ),
-//     Pool: Clone, Send, Sync, GetConnection -> UserService
+//     Pool: GetConnection -> UserService
 // );
 
-pub fn router<Pool: Clone + Send + Sync + GetConnection + 'static>(
-) -> axum::Router<UserService<Pool>> {
+pub fn router<Pool: GetConnection + 'static>() -> axum::Router<UserService<Pool>> {
     axum::Router::new().nest(
         "/auth",
         axum::Router::new()
@@ -53,7 +53,7 @@ where
         .insert(create_user)
         .await
         .map(Success::Created)
-        .map_err(Into::into)
+        .map_err(ResponseError::from)
 }
 
 #[cfg(test)]
@@ -61,26 +61,51 @@ mod tests {
     use crate::create_app;
     use crate::error::ErrorResponseBody;
     use crate::models::user::{CreateUser, User, UserRole};
-    use crate::test::{create_test_containers_pool, create_test_pool, BuildJson, DeserializeInto};
+    use crate::test::create_test_containers_pool;
     use axum::http::request::Builder;
     use axum::http::{Request, StatusCode};
+    use axum::Router;
+    use futures::executor::block_on;
+    use lib::axum::traits::BuildJson;
+    use lib::serde::traits::DeserializeInto;
     use secrecy::ExposeSecret;
     use serde::ser::SerializeStruct;
     use serde::{Serialize, Serializer};
+    use testcontainers_modules::postgres::Postgres;
+    use testcontainers_modules::testcontainers::ContainerAsync;
     use tower::ServiceExt;
 
     fn register() -> Builder {
         Request::builder().uri("/auth/register").method("POST")
     }
 
-    #[tokio::test]
-    async fn test_register_created() {
-        let pool = create_test_pool().await.unwrap();
-        let app = create_app(pool);
-        let create_user = CreateUser::new("test@email.com", "password");
+    #[fixture]
+    #[once]
+    fn setup() -> Setup {
+        block_on(async {
+            let test_container = create_test_containers_pool().await.unwrap();
+            let app = create_app(test_container.pool);
+            Setup {
+                _container: test_container.container,
+                app,
+            }
+        })
+    }
+
+    struct Setup {
+        _container: ContainerAsync<Postgres>,
+        app: Router,
+    }
+
+    #[rstest]
+    #[tokio::test(flavor = "multi_thread")]
+    async fn test_register_created(setup: &Setup) {
+        let create_user = CreateUser::new("test_register_created@email.com", "password");
         let create_email = create_user.email.clone();
 
-        let response = app
+        let response = setup
+            .app
+            .clone()
             .oneshot(register().json(create_user).unwrap())
             .await
             .unwrap();
@@ -95,15 +120,18 @@ mod tests {
         assert!(!user.salt.is_empty());
     }
 
-    #[tokio::test]
-    async fn test_register_email_already_registered() {
-        let test_container = create_test_containers_pool().await.unwrap();
-        let app = create_app(test_container.pool);
-
-        let create_user = CreateUser::new("test@email.com", "password");
+    #[rstest]
+    #[tokio::test(flavor = "multi_thread")]
+    async fn test_register_email_already_registered(setup: &Setup) {
+        let create_user = CreateUser::new(
+            "test_register_email_already_registered@email.com",
+            "password",
+        );
 
         let call = || async {
-            app.clone()
+            setup
+                .app
+                .clone()
                 .oneshot(register().json(create_user.clone()).unwrap())
                 .await
                 .unwrap()
@@ -129,7 +157,7 @@ mod tests {
         where
             S: Serializer,
         {
-            let mut s = serializer.serialize_struct("CreateUser", 2)?;
+            let mut s = serializer.serialize_struct("CreateUser", 3)?;
             s.serialize_field("email", &self.email)?;
             s.serialize_field("password", &self.password.expose_secret())?;
             s.serialize_field("role", &self.role)?;
diff --git a/src/services/reservation_service.rs b/src/services/reservation_service.rs
index ac5b7b4..b073b38 100644
--- a/src/services/reservation_service.rs
+++ b/src/services/reservation_service.rs
@@ -115,28 +115,27 @@ mod tests {
     use crate::models::room::Room;
     use crate::models::user::{CreateUser, User};
     use crate::schema::{hotel, room, user};
-    use crate::test::setup_test_transaction;
+    use crate::test::create_test_containers_pool;
     use chrono::{Duration, Utc};
     use diesel::dsl::insert_into;
     use diesel_async::AsyncPgConnection;
+    use futures::executor::block_on;
+    use lib::diesel::pool::PgPool;
     use lib::diesel_crud_trait::{DieselCrudCreate, DieselCrudRead};
     use secrecy::SecretString;
+    use testcontainers_modules::postgres::Postgres;
+    use testcontainers_modules::testcontainers::ContainerAsync;
 
     #[rstest]
-    #[tokio::test]
-    async fn test_check_in(#[future] setup: Setup) {
-        let Setup {
-            mut conn,
-            reservation,
-            ..
-        } = setup.await;
-
-        Reservation::check_in(reservation.id, &mut conn)
+    #[tokio::test(flavor = "multi_thread")]
+    async fn test_check_in(setup: &Setup) {
+        let mut conn = setup.pool.get().await.unwrap();
+        Reservation::check_in(setup.reservation.id, &mut conn)
             .await
             .unwrap();
 
         assert!(
-            Reservation::read(reservation.id, &mut conn)
+            Reservation::read(setup.reservation.id, &mut conn)
                 .await
                 .unwrap()
                 .checked_in
@@ -144,20 +143,15 @@ mod tests {
     }
 
     #[rstest]
-    #[tokio::test]
-    async fn test_check_out(#[future] setup: Setup) {
-        let Setup {
-            mut conn,
-            reservation,
-            ..
-        } = setup.await;
-
-        Reservation::check_out(reservation.id, &mut conn)
+    #[tokio::test(flavor = "multi_thread")]
+    async fn test_check_out(setup: &Setup) {
+        let mut conn = setup.pool.get().await.unwrap();
+        Reservation::check_out(setup.reservation.id, &mut conn)
             .await
             .unwrap();
 
         assert!(
-            !Reservation::read(reservation.id, &mut conn)
+            !Reservation::read(setup.reservation.id, &mut conn)
                 .await
                 .unwrap()
                 .checked_in
@@ -190,40 +184,34 @@ mod tests {
         Duration::days(11),
         Err(ReservationError::RoomNotAvailable)
     )]
-    #[tokio::test]
+    #[tokio::test(flavor = "multi_thread")]
     async fn test_room_available(
-        #[future] setup: Setup,
+        setup: &Setup,
         #[case] start: Duration,
         #[case] end: Duration,
         #[case] expected: Result<(), ReservationError>,
     ) {
-        let Setup {
-            mut conn,
-            reservation,
-            ..
-        } = setup.await;
+        let mut conn = setup.pool.get().await.unwrap();
 
         let now = Utc::now().naive_utc();
         let start = now + start;
         let end = now + end;
 
         assert_eq!(
-            Reservation::room_available(reservation.room_id, (start, end).into(), &mut conn).await,
+            Reservation::room_available(setup.reservation.room_id, (start, end).into(), &mut conn)
+                .await,
             expected
         );
     }
 
     #[rstest]
-    #[tokio::test]
-    async fn test_room_available_no_reservations(#[future] setup: Setup) {
-        let Setup {
-            mut conn, hotel, ..
-        } = setup.await;
-
+    #[tokio::test(flavor = "multi_thread")]
+    async fn test_room_available_no_reservations(setup: &Setup) {
+        let mut conn = setup.pool.get().await.unwrap();
         let room = Room::insert(
             Room {
                 id: 2,
-                hotel_id: hotel.id,
+                hotel_id: setup.hotel.id,
                 beds: 1,
                 size: 1,
             },
@@ -243,21 +231,30 @@ mod tests {
     }
 
     #[fixture]
-    async fn setup() -> Setup {
-        let mut conn = setup_test_transaction().await.unwrap();
-        let hotel = insert_hotel(&mut conn).await;
-        let user = insert_user(&mut conn).await;
-        let room = insert_room(&mut conn, hotel.id).await;
-        let reservation = insert_reservation(&mut conn, room.id, user.email).await;
-        Setup {
-            conn,
-            hotel,
-            reservation,
-        }
+    #[once]
+    fn setup() -> Setup {
+        block_on(async {
+            let test_container = create_test_containers_pool().await.unwrap();
+            let pool = test_container.pool;
+            let mut conn = pool.get().await.unwrap();
+
+            let hotel = insert_hotel(&mut conn).await;
+            let user = insert_user(&mut conn).await;
+            let room = insert_room(&mut conn, hotel.id).await;
+            let reservation = insert_reservation(&mut conn, room.id, user.email).await;
+
+            Setup {
+                _container: test_container.container,
+                pool,
+                hotel,
+                reservation,
+            }
+        })
     }
 
     struct Setup {
-        conn: AsyncPgConnection,
+        _container: ContainerAsync<Postgres>,
+        pool: PgPool,
         hotel: Hotel,
         reservation: Reservation,
     }
diff --git a/src/services/room_service.rs b/src/services/room_service.rs
index 5a60c45..450bab7 100644
--- a/src/services/room_service.rs
+++ b/src/services/room_service.rs
@@ -55,13 +55,14 @@ impl Room {
 #[cfg(test)]
 mod tests {
     use super::*;
+    use crate::config;
     use crate::models::hotel::{CreateHotel, Hotel};
     use crate::models::reservation::{CreateReservation, Reservation};
     use crate::models::room::Room;
     use crate::models::user::{CreateUser, User};
-    use crate::test::setup_test_transaction;
     use diesel_async::AsyncPgConnection;
     use lib::diesel_crud_trait::DieselCrudCreate;
+    use lib::test::diesel_pool::setup_test_transaction;
     use serde_json::json;
 
     #[rstest]
@@ -148,7 +149,7 @@ mod tests {
 
     #[fixture]
     async fn setup() -> Setup {
-        let mut conn = setup_test_transaction().await.unwrap();
+        let mut conn = setup_test_transaction(config::DATABASE_URL).await.unwrap();
         let hotels = insert_hotels(&mut conn).await;
         let rooms = insert_rooms(&mut conn, &hotels).await;
         let _reservation = insert_reservation(&mut conn, rooms[0].id).await;
diff --git a/src/test.rs b/src/test.rs
index eda1ac6..4c45958 100644
--- a/src/test.rs
+++ b/src/test.rs
@@ -1,78 +1,11 @@
 use crate::config;
-use crate::database::{create_pool, create_pool_from_url, GetConnection, PgPool};
-use crate::error::AppError;
-use axum::async_trait;
-use axum::body::{to_bytes, Body};
-use axum::http::header::CONTENT_TYPE;
-use axum::http::Request;
-use axum::response::Response;
-use deadpool_diesel::postgres::BuildError;
-use deadpool_diesel::Status;
-use derive_more::Constructor;
-use diesel_async::pooled_connection::deadpool::{Object, PoolError};
-use diesel_async::{AsyncConnection, AsyncPgConnection};
-use mime::APPLICATION_JSON;
-use serde::{Deserialize, Serialize};
-use serde_json::json;
-use testcontainers_modules::postgres::Postgres;
-use testcontainers_modules::testcontainers::runners::AsyncRunner;
-use testcontainers_modules::testcontainers::{ContainerAsync, TestcontainersError};
-use thiserror::Error;
-
-#[derive(Debug, PartialEq, Error)]
-pub enum Error {
-    #[error(transparent)]
-    Connection(#[from] diesel::ConnectionError),
-    #[error(transparent)]
-    Database(#[from] diesel::result::Error),
-}
-
-pub async fn setup_test_transaction() -> Result<AsyncPgConnection, Error> {
-    let mut conn = AsyncPgConnection::establish(config::DATABASE_URL).await?;
-    conn.begin_test_transaction().await?;
-    Ok(conn)
-}
-
-pub(crate) async fn create_test_pool() -> Result<PoolStub, BuildError> {
-    let pool = create_pool()?;
-    Ok(PoolStub(pool))
-}
-
-#[derive(Debug, Error)]
-pub(crate) enum ContainerError {
-    #[error(transparent)]
-    TestContainers(#[from] TestcontainersError),
-    #[error(transparent)]
-    BuildError(#[from] BuildError),
-    #[error(transparent)]
-    PoolError(#[from] PoolError),
-    #[error(transparent)]
-    DieselError(#[from] diesel::result::Error),
-}
-
-/// When the TestContainer is dropped, the container will be removed.
-/// # Panics
-/// If destructed and the container field is dropped, the container will be removed, and using the pool will cause panic.
-#[derive(Constructor)]
-pub(crate) struct TestContainer {
-    pub _container: ContainerAsync<Postgres>,
-    pub pool: PgPool,
-}
+use diesel_async::AsyncPgConnection;
+use lib::test::test_containers::{ContainerError, TestContainer};
 
 pub(crate) async fn create_test_containers_pool<'a>() -> Result<TestContainer, ContainerError> {
-    let container = create_postgres_container().await?;
-    let connection_string = format!(
-        "postgres://postgres:postgres@127.0.0.1:{}/postgres",
-        container.get_host_port_ipv4(5432).await?
-    );
-    let pool = create_pool_from_url(connection_string)?;
-    run_migrations(pool.get().await?.as_mut()).await?;
-    Ok(TestContainer::new(container, pool))
-}
-
-pub(crate) async fn create_postgres_container(
-) -> Result<ContainerAsync<Postgres>, TestcontainersError> {
-    Postgres::default().start().await
+    let test_container = lib::test::test_containers::create_test_containers_pool().await?;
+    run_migrations(test_container.pool.get().await?.as_mut()).await?;
+    Ok(test_container)
 }
 
 pub(crate) async fn run_migrations(
@@ -80,50 +13,3 @@ pub(crate) async fn run_migrations(
 ) -> Result<(), diesel::result::Error> {
     config::MIGRATIONS.run_pending_migrations(conn).await
 }
-
-#[derive(Clone)]
-pub(crate) struct PoolStub(PgPool);
-
-#[async_trait]
-impl GetConnection for PoolStub {
-    async fn get(&self) -> Result<Object<AsyncPgConnection>, AppError> {
-        let mut conn = self.0.get().await?;
-        conn.begin_test_transaction().await?;
-        Ok(conn)
-    }
-    fn status(&self) -> Status {
-        unimplemented!("PoolStub does not support status")
-    }
-}
-
-pub trait BuildJson {
-    fn json<T: Serialize>(self, body: T) -> Result<Request<Body>, axum::http::Error>;
-}
-
-impl BuildJson for axum::http::request::Builder {
-    fn json<T: Serialize>(self, body: T) -> Result<Request<Body>, axum::http::Error> {
-        self.header(CONTENT_TYPE, APPLICATION_JSON.as_ref())
-            .body(Body::new(json!(body).to_string()))
-    }
-}
-
-#[derive(Debug, Error)]
-pub(crate) enum DeserializeError {
-    #[error(transparent)]
-    SerdeError(#[from] serde_json::Error),
-    #[error(transparent)]
-    AxumError(#[from] axum::Error),
-}
-
-#[async_trait]
-pub trait DeserializeInto {
-    async fn deserialize_into<T: for<'de> Deserialize<'de>>(self) -> Result<T, DeserializeError>;
-}
-
-#[async_trait]
-impl DeserializeInto for Response {
-    async fn deserialize_into<T: for<'de> Deserialize<'de>>(self) -> Result<T, DeserializeError> {
-        let body = to_bytes(self.into_body(), usize::MAX).await?;
-        serde_json::from_slice(&body).map_err(Into::into)
-    }
-}