Makefile.toml
TestContainers and diesel test database
This commit is contained in:
parent
ce770e9c6f
commit
8fb89e0459
1
.idea/runConfigurations/All_Tests.xml
generated
1
.idea/runConfigurations/All_Tests.xml
generated
@ -1,5 +1,6 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="All Tests" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
<configuration default="false" name="All Tests" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||||
|
<option name="buildProfile" value="Test" />
|
||||||
<option name="command" value="test --workspace" />
|
<option name="command" value="test --workspace" />
|
||||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
||||||
<envs />
|
<envs />
|
||||||
|
737
Cargo.lock
generated
737
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
21
Cargo.toml
21
Cargo.toml
@ -26,13 +26,16 @@ tower = { version = "0.5", optional = true }
|
|||||||
tower-http = { version = "0.5", optional = true, features = ["trace", "cors", "normalize-path"] }
|
tower-http = { version = "0.5", optional = true, features = ["trace", "cors", "normalize-path"] }
|
||||||
mime = { version = "0.3", optional = true }
|
mime = { version = "0.3", optional = true }
|
||||||
# Async
|
# Async
|
||||||
tokio = { version = "1.39", optional = true, features = ["fs"] }
|
tokio = { workspace = true, optional = true, features = ["fs", "rt-multi-thread"] }
|
||||||
tokio-util = { version = "0.7", optional = true, features = ["io"] }
|
tokio-util = { version = "0.7", optional = true, features = ["io"] }
|
||||||
# Database
|
# Database
|
||||||
|
diesel = { workspace = true, optional = true, features = ["postgres"] }
|
||||||
|
diesel-async = { workspace = true, optional = true, features = ["postgres", "deadpool"] }
|
||||||
diesel-crud-derive = { path = "crates/diesel_crud_derive", optional = true }
|
diesel-crud-derive = { path = "crates/diesel_crud_derive", optional = true }
|
||||||
diesel-crud-trait = { path = "crates/diesel_crud_trait", optional = true }
|
diesel-crud-trait = { path = "crates/diesel_crud_trait", optional = true }
|
||||||
|
deadpool-diesel = { workspace = true, optional = true, features = ["postgres"] }
|
||||||
# Error handling
|
# Error handling
|
||||||
thiserror = { version = "1.0", optional = true }
|
thiserror = { workspace = true, optional = true }
|
||||||
# Logging
|
# Logging
|
||||||
tracing = { version = "0.1", optional = true }
|
tracing = { version = "0.1", optional = true }
|
||||||
tracing-subscriber = { version = "0.3", optional = true }
|
tracing-subscriber = { version = "0.3", optional = true }
|
||||||
@ -49,17 +52,25 @@ chrono = { version = "0.4", optional = true, features = ["serde"] }
|
|||||||
derive_more = { workspace = true, features = ["from", "constructor"] }
|
derive_more = { workspace = true, features = ["from", "constructor"] }
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
|
# Async
|
||||||
|
tokio = "1.40"
|
||||||
|
# Database
|
||||||
|
diesel = "2.2"
|
||||||
|
diesel-async = "0.5"
|
||||||
|
deadpool-diesel = "0.6"
|
||||||
|
# Error handling
|
||||||
|
thiserror = "1.0"
|
||||||
|
# Procedural macros
|
||||||
syn = "2.0"
|
syn = "2.0"
|
||||||
quote = "1.0"
|
quote = "1.0"
|
||||||
deluxe = "0.5"
|
deluxe = "0.5"
|
||||||
proc-macro2 = "1.0"
|
proc-macro2 = "1.0"
|
||||||
diesel = "2.2"
|
# Utils
|
||||||
diesel-async = "0.5"
|
|
||||||
derive_more = "1.0"
|
derive_more = "1.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
axum = ["dep:axum", "dep:tower", "dep:tower-http", "dep:thiserror", "dep:tracing", "dep:tracing-subscriber", "dep:tokio", "dep:mime"]
|
axum = ["dep:axum", "dep:tower", "dep:tower-http", "dep:thiserror", "dep:tracing", "dep:tracing-subscriber", "dep:tokio", "dep:mime"]
|
||||||
diesel = ["dep:diesel-crud-trait"]
|
diesel = ["dep:diesel-crud-trait", "dep:diesel", "dep:diesel-async", "dep:deadpool-diesel"]
|
||||||
io = ["dep:tokio", "dep:tokio-util"]
|
io = ["dep:tokio", "dep:tokio-util"]
|
||||||
iter = []
|
iter = []
|
||||||
nom = ["dep:nom"]
|
nom = ["dep:nom"]
|
||||||
|
3
Makefile
3
Makefile
@ -1,3 +0,0 @@
|
|||||||
fmt:
|
|
||||||
cargo clippy --all-targets --all-features -- -D warnings
|
|
||||||
cargo fmt
|
|
11
Makefile.toml
Normal file
11
Makefile.toml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[tasks.clippy]
|
||||||
|
command = "cargo"
|
||||||
|
args = ["clippy", "--all-targets", "--all-features", "--", "-D", "warnings"]
|
||||||
|
|
||||||
|
[tasks.fmt]
|
||||||
|
command = "cargo"
|
||||||
|
args = ["fmt", "--all"]
|
||||||
|
|
||||||
|
[tasks.test]
|
||||||
|
command = "cargo"
|
||||||
|
args = ["test", "--all-features"]
|
@ -10,7 +10,11 @@ homepage.workspace = true
|
|||||||
diesel = { workspace = true }
|
diesel = { workspace = true }
|
||||||
diesel-async = { workspace = true }
|
diesel-async = { workspace = true }
|
||||||
lib = { path = "../../../lib", features = ["diesel", "derive"] }
|
lib = { path = "../../../lib", features = ["diesel", "derive"] }
|
||||||
|
derive_more = { workspace = true, features = ["constructor", "from"] }
|
||||||
|
thiserror = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tokio = { version = "1.39", features = ["macros"] }
|
tokio = { workspace = true, features = ["macros"] }
|
||||||
dotenvy_macro = "0.15"
|
dotenvy_macro = "0.15"
|
||||||
|
testcontainers-modules = { version = "0.9", features = ["postgres"] }
|
||||||
|
diesel_async_migrations = "0.14"
|
||||||
|
9
crates/tests/diesel.toml
Normal file
9
crates/tests/diesel.toml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# For documentation on how to configure this file,
|
||||||
|
# see https://diesel.rs/guides/configuring-diesel-cli
|
||||||
|
|
||||||
|
[print_schema]
|
||||||
|
file = "src/schema.rs"
|
||||||
|
custom_type_derives = ["diesel::query_builder::QueryId", "Clone"]
|
||||||
|
|
||||||
|
[migrations_directory]
|
||||||
|
dir = "/home/martin/git/rust/lib/crates/tests/migrations"
|
0
crates/tests/migrations/.keep
Normal file
0
crates/tests/migrations/.keep
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
-- This file was automatically created by Diesel to setup helper functions
|
||||||
|
-- and other internal bookkeeping. This file is safe to edit, any future
|
||||||
|
-- changes will be added to existing projects as new migrations.
|
||||||
|
|
||||||
|
DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass);
|
||||||
|
DROP FUNCTION IF EXISTS diesel_set_updated_at();
|
@ -0,0 +1,36 @@
|
|||||||
|
-- This file was automatically created by Diesel to setup helper functions
|
||||||
|
-- and other internal bookkeeping. This file is safe to edit, any future
|
||||||
|
-- changes will be added to existing projects as new migrations.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- Sets up a trigger for the given table to automatically set a column called
|
||||||
|
-- `updated_at` whenever the row is modified (unless `updated_at` was included
|
||||||
|
-- in the modified columns)
|
||||||
|
--
|
||||||
|
-- # Example
|
||||||
|
--
|
||||||
|
-- ```sql
|
||||||
|
-- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW());
|
||||||
|
--
|
||||||
|
-- SELECT diesel_manage_updated_at('users');
|
||||||
|
-- ```
|
||||||
|
CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$
|
||||||
|
BEGIN
|
||||||
|
EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s
|
||||||
|
FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl);
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$
|
||||||
|
BEGIN
|
||||||
|
IF (
|
||||||
|
NEW IS DISTINCT FROM OLD AND
|
||||||
|
NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at
|
||||||
|
) THEN
|
||||||
|
NEW.updated_at := current_timestamp;
|
||||||
|
END IF;
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
@ -0,0 +1 @@
|
|||||||
|
DROP TABLE IF EXISTS "user" CASCADE;
|
@ -0,0 +1,4 @@
|
|||||||
|
CREATE TABLE "user"
|
||||||
|
(
|
||||||
|
email VARCHAR(255) PRIMARY KEY
|
||||||
|
);
|
@ -1,10 +1,12 @@
|
|||||||
use diesel::{AsChangeset, Insertable, Queryable, Selectable};
|
use diesel::{AsChangeset, Insertable, Queryable, Selectable};
|
||||||
use diesel_async::{AsyncConnection, AsyncPgConnection};
|
|
||||||
use dotenvy_macro::dotenv;
|
|
||||||
use lib::diesel_crud_derive::{
|
use lib::diesel_crud_derive::{
|
||||||
DieselCrudCreate, DieselCrudDelete, DieselCrudList, DieselCrudRead, DieselCrudUpdate,
|
DieselCrudCreate, DieselCrudDelete, DieselCrudList, DieselCrudRead, DieselCrudUpdate,
|
||||||
};
|
};
|
||||||
use lib::diesel_crud_trait::DieselCrudCreate;
|
use lib::diesel_crud_trait::DieselCrudCreate;
|
||||||
|
use test_containers::create_test_containers_pool;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod test_containers;
|
||||||
|
|
||||||
diesel::table! {
|
diesel::table! {
|
||||||
user (email) {
|
user (email) {
|
||||||
@ -14,6 +16,8 @@ diesel::table! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
Queryable,
|
Queryable,
|
||||||
Selectable,
|
Selectable,
|
||||||
Insertable,
|
Insertable,
|
||||||
@ -39,14 +43,19 @@ struct InsertUser {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_insert_user() {
|
async fn test_insert_user() {
|
||||||
let database_url = dotenv!("DATABASE_URL");
|
let container = create_test_containers_pool().await.unwrap();
|
||||||
let mut conn = AsyncPgConnection::establish(database_url).await.unwrap();
|
let mut conn = container.pool.get().await.unwrap();
|
||||||
conn.begin_test_transaction().await.unwrap();
|
let user = User::insert(
|
||||||
let _user = User::insert(
|
|
||||||
InsertUser {
|
InsertUser {
|
||||||
email: "test".to_string(),
|
email: "test".to_string(),
|
||||||
},
|
},
|
||||||
&mut conn,
|
&mut conn,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
assert_eq!(
|
||||||
|
user,
|
||||||
|
Ok(User {
|
||||||
|
email: "test".to_string()
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
48
crates/tests/tests/test_containers.rs
Normal file
48
crates/tests/tests/test_containers.rs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
use derive_more::{Constructor, From};
|
||||||
|
use diesel_async::pooled_connection::deadpool::{BuildError, PoolError};
|
||||||
|
use diesel_async::AsyncPgConnection;
|
||||||
|
use diesel_async_migrations::EmbeddedMigrations;
|
||||||
|
use lib::diesel::pool::{create_pool_from_url, PgPool};
|
||||||
|
use testcontainers_modules::postgres::Postgres;
|
||||||
|
use testcontainers_modules::testcontainers::runners::AsyncRunner;
|
||||||
|
use testcontainers_modules::testcontainers::{ContainerAsync, TestcontainersError};
|
||||||
|
|
||||||
|
/// 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 struct TestContainer {
|
||||||
|
pub container: ContainerAsync<Postgres>,
|
||||||
|
pub pool: PgPool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, From)]
|
||||||
|
pub enum ContainerError {
|
||||||
|
TestContainers(TestcontainersError),
|
||||||
|
BuildError(BuildError),
|
||||||
|
PoolError(PoolError),
|
||||||
|
DieselError(diesel::result::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_test_containers_pool<'a>() -> Result<TestContainer, ContainerError> {
|
||||||
|
let container = create_postgres_container().await?;
|
||||||
|
let connection_string = format!(
|
||||||
|
"postgres://postgres:postgres@localhost:{}/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 run_migrations(
|
||||||
|
conn: &mut AsyncPgConnection,
|
||||||
|
) -> Result<(), diesel::result::Error> {
|
||||||
|
static EMBEDDED_MIGRATIONS: EmbeddedMigrations =
|
||||||
|
diesel_async_migrations::embed_migrations!("./migrations");
|
||||||
|
EMBEDDED_MIGRATIONS.run_pending_migrations(conn).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_postgres_container() -> Result<ContainerAsync<Postgres>, TestcontainersError> {
|
||||||
|
Postgres::default().start().await
|
||||||
|
}
|
4
examples/multipart_file/Cargo.lock
generated
4
examples/multipart_file/Cargo.lock
generated
@ -640,9 +640,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.39.3"
|
version = "1.40.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5"
|
checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -6,4 +6,4 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
lib = { path = "../..", features = ["axum"] }
|
lib = { path = "../..", features = ["axum"] }
|
||||||
axum = "0.7.5"
|
axum = "0.7.5"
|
||||||
tokio = { version = "1.39", features = ["rt-multi-thread", "macros"] }
|
tokio = { version = "1.40", features = ["rt-multi-thread", "macros"] }
|
||||||
|
1
src/diesel/mod.rs
Normal file
1
src/diesel/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod pool;
|
24
src/diesel/pool.rs
Normal file
24
src/diesel/pool.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
use deadpool_diesel::postgres::BuildError;
|
||||||
|
use diesel_async::pooled_connection::deadpool::Pool;
|
||||||
|
use diesel_async::pooled_connection::AsyncDieselConnectionManager;
|
||||||
|
use diesel_async::AsyncPgConnection;
|
||||||
|
|
||||||
|
/// A type alias for the asynchronous PostgreSQL connection pool.
|
||||||
|
pub type PgPool = Pool<AsyncPgConnection>;
|
||||||
|
|
||||||
|
/// Create a deadpool connection pool from the given URL.
|
||||||
|
/// Using the default pool size and other settings.
|
||||||
|
pub fn create_pool_from_url(url: impl Into<String>) -> Result<PgPool, BuildError> {
|
||||||
|
let config = AsyncDieselConnectionManager::<AsyncPgConnection>::new(url);
|
||||||
|
Pool::builder(config).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a deadpool connection pool from the given URL.
|
||||||
|
/// Using the given pool size and other default settings.
|
||||||
|
pub fn create_pool_from_url_with_size(
|
||||||
|
url: impl Into<String>,
|
||||||
|
size: usize,
|
||||||
|
) -> Result<PgPool, BuildError> {
|
||||||
|
let config = AsyncDieselConnectionManager::<AsyncPgConnection>::new(url);
|
||||||
|
Pool::builder(config).max_size(size).build()
|
||||||
|
}
|
@ -1,6 +1,4 @@
|
|||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
extern crate self as lib;
|
|
||||||
|
|
||||||
#[cfg(all(feature = "derive", feature = "diesel"))]
|
#[cfg(all(feature = "derive", feature = "diesel"))]
|
||||||
pub extern crate diesel_crud_derive;
|
pub extern crate diesel_crud_derive;
|
||||||
#[cfg(feature = "diesel")]
|
#[cfg(feature = "diesel")]
|
||||||
@ -9,9 +7,12 @@ pub extern crate diesel_crud_trait;
|
|||||||
pub extern crate into_response_derive;
|
pub extern crate into_response_derive;
|
||||||
#[cfg(feature = "read-files")]
|
#[cfg(feature = "read-files")]
|
||||||
pub extern crate read_files;
|
pub extern crate read_files;
|
||||||
|
extern crate self as lib;
|
||||||
|
|
||||||
#[cfg(feature = "axum")]
|
#[cfg(feature = "axum")]
|
||||||
pub mod axum;
|
pub mod axum;
|
||||||
|
#[cfg(feature = "diesel")]
|
||||||
|
pub mod diesel;
|
||||||
#[cfg(feature = "io")]
|
#[cfg(feature = "io")]
|
||||||
pub mod io;
|
pub mod io;
|
||||||
#[cfg(feature = "nom")]
|
#[cfg(feature = "nom")]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user