First working API.

Simple auth by creating sessions and storing in db
This commit is contained in:
Martin Berg Alstad
2024-08-29 16:43:04 +02:00
commit 5d5e6393ac
50 changed files with 6410 additions and 0 deletions

129
src/test.rs Normal file
View File

@ -0,0 +1,129 @@
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,
}
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
}
pub(crate) async fn run_migrations(
conn: &mut AsyncPgConnection,
) -> 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)
}
}