use diesel::prelude::*;
use dotenvy::dotenv;
cfg_if::cfg_if! {
if #[cfg(feature = "postgres")] {
#[allow(dead_code)]
type DB = diesel::pg::Pg;
type DbConnection = PgConnection;
fn database_url_for_env() -> String {
database_url_from_env("PG_DATABASE_URL")
}
fn connection_no_transaction() -> PgConnection {
let connection_url = database_url_for_env();
PgConnection::establish(&connection_url).unwrap()
}
fn setup_database(connection: &mut PgConnection) {
connection.begin_test_transaction().unwrap();
clean_tables(connection);
create_tables_with_data(connection);
}
fn clean_tables(connection: &mut PgConnection) {
diesel::sql_query("DROP TABLE IF EXISTS users CASCADE").execute(connection).unwrap();
diesel::sql_query("DROP TABLE IF EXISTS animals CASCADE").execute(connection).unwrap();
diesel::sql_query("DROP TABLE IF EXISTS posts CASCADE").execute(connection).unwrap();
diesel::sql_query("DROP TABLE IF EXISTS comments CASCADE").execute(connection).unwrap();
diesel::sql_query("DROP TABLE IF EXISTS brands CASCADE").execute(connection).unwrap();
}
fn connection_no_data() -> PgConnection {
let mut connection = connection_no_transaction();
connection.begin_test_transaction().unwrap();
clean_tables(&mut connection);
connection
}
fn create_tables_with_data(connection: &mut PgConnection) {
diesel::sql_query("CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR NOT NULL
)")
.execute(connection)
.unwrap();
diesel::sql_query("INSERT INTO users (name) VALUES ('Sean'), ('Tess')")
.execute(connection)
.unwrap();
diesel::sql_query("CREATE TABLE animals (
id SERIAL PRIMARY KEY,
species VARCHAR NOT NULL,
legs INTEGER NOT NULL,
name VARCHAR
)")
.execute(connection)
.unwrap();
diesel::sql_query("INSERT INTO animals (species, legs, name) VALUES
('dog', 4, 'Jack'),
('spider', 8, null)"
).execute(connection).unwrap();
diesel::sql_query("CREATE TABLE posts (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL,
title VARCHAR NOT NULL
)").execute(connection).unwrap();
diesel::sql_query("INSERT INTO posts (user_id, title) VALUES
(1, 'My first post'),
(1, 'About Rust'),
(2, 'My first post too')").execute(connection).unwrap();
diesel::sql_query("CREATE TABLE comments (
id SERIAL PRIMARY KEY,
post_id INTEGER NOT NULL,
body VARCHAR NOT NULL
)").execute(connection).unwrap();
diesel::sql_query("INSERT INTO comments (post_id, body) VALUES
(1, 'Great post'),
(2, 'Yay! I am learning Rust'),
(3, 'I enjoyed your post')").execute(connection).unwrap();
diesel::sql_query("CREATE TABLE brands (
id SERIAL PRIMARY KEY,
color VARCHAR NOT NULL DEFAULT 'Green',
accent VARCHAR DEFAULT 'Blue'
)").execute(connection).unwrap();
}
#[allow(dead_code)]
fn establish_connection() -> PgConnection {
let mut connection = connection_no_data();
create_tables_with_data(&mut connection);
connection
}
} else if #[cfg(feature = "sqlite")] {
#[allow(dead_code)]
type DB = diesel::sqlite::Sqlite;
type DbConnection = SqliteConnection;
fn database_url_for_env() -> String {
String::from(":memory:")
}
fn connection_no_data() -> SqliteConnection {
SqliteConnection::establish(":memory:").unwrap()
}
fn setup_database(connection: &mut SqliteConnection) {
create_tables_with_data(connection);
}
fn create_tables_with_data(connection: &mut SqliteConnection) {
diesel::sql_query("CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR NOT NULL
)").execute(connection).unwrap();
diesel::sql_query("INSERT INTO users (name) VALUES ('Sean'), ('Tess')")
.execute(connection).unwrap();
diesel::sql_query("CREATE TABLE animals (
id INTEGER PRIMARY KEY AUTOINCREMENT,
species VARCHAR NOT NULL,
legs INTEGER NOT NULL,
name VARCHAR
)").execute(connection).unwrap();
diesel::sql_query("INSERT INTO animals (species, legs, name) VALUES
('dog', 4, 'Jack'),
('spider', 8, null)"
).execute(connection).unwrap();
diesel::sql_query("CREATE TABLE posts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
title VARCHAR NOT NULL
)").execute(connection).unwrap();
diesel::sql_query("INSERT INTO posts (user_id, title) VALUES
(1, 'My first post'),
(1, 'About Rust'),
(2, 'My first post too')").execute(connection).unwrap();
diesel::sql_query("CREATE TABLE comments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
post_id INTEGER NOT NULL,
body VARCHAR NOT NULL
)").execute(connection).unwrap();
diesel::sql_query("INSERT INTO comments (post_id, body) VALUES
(1, 'Great post'),
(2, 'Yay! I am learning Rust'),
(3, 'I enjoyed your post')").execute(connection).unwrap();
}
#[allow(dead_code)]
fn establish_connection() -> SqliteConnection {
let mut connection = connection_no_data();
create_tables_with_data(&mut connection);
connection
}
#[allow(dead_code)]
fn sqlite_version(conn: &mut SqliteConnection) -> (u32, u32, u32) {
let version = diesel::select(diesel::dsl::sql::<diesel::sql_types::Text>("sqlite_version();"))
.get_result::<String>(conn)
.unwrap();
let mut version_components = version.split('.');
let major: u32 = version_components.next().unwrap().parse().unwrap();
let minor: u32 = version_components.next().unwrap().parse().unwrap();
let patch: u32 = version_components.next().unwrap().parse().unwrap();
(major, minor, patch)
}
macro_rules! assert_version {
($conn: ident, $major: literal, $minor: literal, $patch: literal) => {
let version = sqlite_version($conn);
if version.0 < $major ||
(version.0 == $major && version.1 < $minor) ||
(version.0 == $major && version.1 == $minor && version.2 < $patch)
{
println!("SQLite version is too old, skipping the test.");
return Ok(());
}
}
}
} else if #[cfg(feature = "mysql")] {
#[allow(dead_code)]
type DB = diesel::mysql::Mysql;
type DbConnection = MysqlConnection;
fn database_url_for_env() -> String {
database_url_from_env("MYSQL_UNIT_TEST_DATABASE_URL")
}
fn setup_database(connection: &mut MysqlConnection) {
clean_tables(connection);
create_tables_with_data(connection);
}
fn clean_tables(_connection: &mut MysqlConnection) {}
fn connection_no_data() -> MysqlConnection {
let connection_url = database_url_for_env();
let mut connection = MysqlConnection::establish(&connection_url).unwrap();
clean_tables(&mut connection);
connection
}
fn create_tables_with_data(connection: &mut MysqlConnection) {
diesel::sql_query("CREATE TEMPORARY TABLE users (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
name TEXT NOT NULL
) CHARACTER SET utf8mb4").execute(connection).unwrap();
diesel::sql_query("INSERT INTO users (name) VALUES ('Sean'), ('Tess')")
.execute(connection).unwrap();
diesel::sql_query("CREATE TEMPORARY TABLE animals (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
species TEXT NOT NULL,
legs INTEGER NOT NULL,
name TEXT
) CHARACTER SET utf8mb4").execute(connection).unwrap();
diesel::sql_query("INSERT INTO animals (species, legs, name) VALUES
('dog', 4, 'Jack'),
('spider', 8, null)").execute(connection).unwrap();
diesel::sql_query("CREATE TEMPORARY TABLE posts (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
user_id INTEGER NOT NULL,
title TEXT NOT NULL
) CHARACTER SET utf8mb4").execute(connection).unwrap();
diesel::sql_query("INSERT INTO posts (user_id, title) VALUES
(1, 'My first post'),
(1, 'About Rust'),
(2, 'My first post too')").execute(connection).unwrap();
diesel::sql_query("CREATE TEMPORARY TABLE comments (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
post_id INTEGER NOT NULL,
body TEXT NOT NULL
) CHARACTER SET utf8mb4").execute(connection).unwrap();
diesel::sql_query("INSERT INTO comments (post_id, body) VALUES
(1, 'Great post'),
(2, 'Yay! I am learning Rust'),
(3, 'I enjoyed your post')").execute(connection).unwrap();
diesel::sql_query("CREATE TEMPORARY TABLE brands (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
color VARCHAR(255) NOT NULL DEFAULT 'Green',
accent VARCHAR(255) DEFAULT 'Blue'
)").execute(connection).unwrap();
}
#[allow(dead_code)]
fn establish_connection() -> MysqlConnection {
let mut connection = connection_no_data();
create_tables_with_data(&mut connection);
connection.begin_test_transaction().unwrap();
connection
}
} else {
compile_error!(
"At least one backend must be used to test this crate.\n \
Pass argument `--features \"<backend>\"` with one or more of the following backends, \
'mysql', 'postgres', or 'sqlite'. \n\n \
ex. cargo test --features \"mysql postgres sqlite\"\n"
);
}
}
fn database_url_from_env(backend_specific_env_var: &str) -> String {
use std::env;
dotenv().ok();
env::var(backend_specific_env_var)
.or_else(|_| env::var("DATABASE_URL"))
.expect("DATABASE_URL must be set in order to run tests")
}
mod schema {
use diesel::prelude::*;
table! {
animals {
id -> Integer,
species -> VarChar,
legs -> Integer,
name -> Nullable<VarChar>,
}
}
table! {
comments {
id -> Integer,
post_id -> Integer,
body -> VarChar,
}
}
table! {
posts {
id -> Integer,
user_id -> Integer,
title -> VarChar,
}
}
table! {
users {
id -> Integer,
name -> VarChar,
}
}
#[cfg(not(feature = "sqlite"))]
table! {
brands {
id -> Integer,
color -> VarChar,
accent -> Nullable<VarChar>,
}
}
joinable!(posts -> users (user_id));
allow_tables_to_appear_in_same_query!(animals, comments, posts, users);
}