diesel-factories 2.0.0

Test factories for Diesel
Documentation
#![allow(proc_macro_derive_resolution_fallback)]

#[macro_use]
extern crate diesel;

use diesel::{pg::PgConnection, prelude::*};
use diesel_factories::{Association, Factory};
use std::env;

mod schema {
    table! {
        users (id) {
            id -> Integer,
            name -> Text,
            age -> Integer,
            country_id -> Nullable<Integer>,
            home_city_id -> Nullable<Integer>,
            current_city_id -> Nullable<Integer>,
        }
    }

    table! {
        countries (identity) {
            identity -> Integer,
            name -> Text,
        }
    }

    table! {
        cities (id) {
            id -> Integer,
            name -> Text,
            team_association -> Text,
            association_label -> Text,
            country_id -> Integer,
        }
    }
}

#[derive(Queryable, Clone)]
struct User {
    pub id: i32,
    pub name: String,
    pub age: i32,
    pub country_id: Option<i32>,
    pub home_city_id: Option<i32>,
    pub current_city_id: Option<i32>,
}

#[derive(Queryable, Clone)]
struct Country {
    pub identity: i32,
    pub name: String,
}

#[derive(Queryable, Clone)]
struct City {
    pub id: i32,
    pub name: String,
    pub team_association: String,
    pub association_label: String,
    pub country_id: i32,
}

#[derive(Clone, Factory)]
#[factory(
    model = User,
    table = crate::schema::users,
    connection = diesel::pg::PgConnection
)]
struct UserFactory<'a> {
    pub name: &'a str,
    pub age: i32,
    pub country: std::option::Option<diesel_factories::Association<'a, Country, CountryFactory>>,
    pub home_city: Option<diesel_factories::Association<'a, City, CityFactory<'a>>>,
    pub current_city: Option<Association<'a, City, CityFactory<'a>>>,
}

impl<'a> Default for UserFactory<'a> {
    fn default() -> Self {
        Self {
            name: "Bob",
            age: 30,
            country: None,
            home_city: None,
            current_city: None,
        }
    }
}

#[derive(Clone, Factory)]
#[factory(
    model = Country,
    table = crate::schema::countries,
    id_name = identity
)]
struct CountryFactory {
    pub name: String,
}

impl Default for CountryFactory {
    fn default() -> Self {
        Self {
            name: "Denmark".into(),
        }
    }
}

#[derive(Clone, Factory)]
#[factory(model = City, table = crate::schema::cities)]
struct CityFactory<'b> {
    pub name: String,
    pub team_association: String,
    pub association_label: String,
    pub country: Association<'b, Country, CountryFactory>,
}

impl<'b> Default for CityFactory<'b> {
    fn default() -> Self {
        Self {
            name: "Copenhagen".into(),
            team_association: "teamfive".into(),
            association_label: "thebest".into(),
            country: Association::default(),
        }
    }
}

#[test]
fn insert_one_user() {
    let con = setup();

    let user = UserFactory::default().name("Alice").insert(&con);

    assert_eq!(user.name, "Alice");
    assert_eq!(user.age, 30);
    assert_eq!(1, count_users(&con));
    assert_eq!(0, count_countries(&con));
}

#[test]
fn overriding_country() {
    let con = setup();

    let bob = UserFactory::default()
        .country(Some(CountryFactory::default().name("USA")))
        .insert(&con);

    let country = find_country_by_id(bob.country_id.unwrap(), &con);

    assert_eq!("USA", country.name);
    assert_eq!(1, count_users(&con));
    assert_eq!(1, count_countries(&con));
}

#[test]
fn insert_two_users_sharing_country() {
    let con = setup();

    let country = CountryFactory::default().insert(&con);
    let bob = UserFactory::default().country(Some(&country)).insert(&con);
    let alice = UserFactory::default().country(Some(&country)).insert(&con);

    assert_eq!(bob.country_id, alice.country_id);
    assert_eq!(2, count_users(&con));
    assert_eq!(1, count_countries(&con));
}

fn setup() -> PgConnection {
    let pg_host = env::var("POSTGRES_HOST").unwrap_or_else(|_| "localhost".to_string());
    let pg_port = env::var("POSTGRES_PORT").unwrap_or_else(|_| "5432".to_string());
    let pg_password = env::var("POSTGRES_PASSWORD").ok();

    let auth = if let Some(pg_password) = pg_password {
        format!("postgres:{}@", pg_password)
    } else {
        String::new()
    };

    let database_url = format!(
        "postgres://{auth}{host}:{port}/diesel_factories_test",
        auth = auth,
        host = pg_host,
        port = pg_port
    );
    let con = PgConnection::establish(&database_url).unwrap();
    con.begin_test_transaction().unwrap();
    con
}

fn count_users(con: &PgConnection) -> i64 {
    use crate::schema::users;
    use diesel::dsl::count_star;
    users::table.select(count_star()).first(con).unwrap()
}

fn count_countries(con: &PgConnection) -> i64 {
    use crate::schema::countries;
    use diesel::dsl::count_star;
    countries::table.select(count_star()).first(con).unwrap()
}

fn find_country_by_id(input: i32, con: &PgConnection) -> Country {
    use crate::schema::countries::dsl::*;
    countries
        .filter(identity.eq(&input))
        .first::<Country>(con)
        .unwrap()
}