Skip to content

Instantly share code, notes, and snippets.

@SnoozeTime
Created August 23, 2019 00:36
Show Gist options
  • Save SnoozeTime/b6c671f609df10a5f2441227a73a95e8 to your computer and use it in GitHub Desktop.
Save SnoozeTime/b6c671f609df10a5f2441227a73a95e8 to your computer and use it in GitHub Desktop.
use diesel::pg::PgConnection;
use diesel::prelude::*;
use diesel_migrations::embed_migrations;
use std::default::Default;
use actix_web::dev::Server;
use mycrate::app;
use mycrate::app::AppConfig;
use std::sync::mpsc;
use std::thread;
embed_migrations!();
pub struct TestContext {
db_name: String,
base_url: String,
}
impl Drop for TestContext {
/// Drop will delete the database for the current test.
fn drop(&mut self) {
let postgres_url = format!("{}/postgres", self.base_url);
let conn =
PgConnection::establish(&postgres_url).expect("Cannot connect to postgres database.");
let revoke = format!("REVOKE CONNECT ON DATABASE {} FROM public;", self.db_name);
diesel::sql_query(revoke.as_str()).execute(&conn).unwrap();
let disconnect_users = format!(
"SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE datname = '{}';",
self.db_name
);
diesel::sql_query(disconnect_users.as_str())
.execute(&conn)
.unwrap();
let query = diesel::sql_query(format!("DROP DATABASE {}", self.db_name).as_str());
query
.execute(&conn)
.expect(&format!("Couldn't drop database {}", self.db_name));
}
}
impl TestContext {
/// Creates new test context for my local db :)
pub fn local(db_name: &str) -> Self {
TestContext::new("postgres://postgres:example@127.0.0.1", db_name)
}
/// Will set up a database for a test.
/// This will return a TestContext that will tear down the
/// database once the test is over.
pub fn new(base_url: &str, db_name: &str) -> Self {
// First, connect to postgres db to be able to create our test
// database.
let postgres_url = format!("{}/postgres", base_url);
let conn =
PgConnection::establish(&postgres_url).expect("Cannot connect to postgres database.");
// Create a new database for the test
let query = diesel::sql_query(format!("CREATE DATABASE {}", db_name).as_str());
query
.execute(&conn)
.expect(format!("Could not create database {}", db_name).as_str());
// Now we can connect to the database and run the migrations
let conn = PgConnection::establish(&format!("{}/{}", base_url, db_name))
.expect(&format!("Cannot connect to {} database", db_name));
embedded_migrations::run(&conn);
let ctx = TestContext {
db_name: db_name.to_string(),
base_url: base_url.to_string(),
};
ctx
}
}
pub fn get_conn(ctx: &TestContext) -> PgConnection {
let database_url = format!("{}/{}", ctx.base_url, ctx.db_name);
let conn =
PgConnection::establish(&database_url).expect("Cannot establish connection with database");
conn
}
use std::net::{Ipv4Addr, SocketAddrV4, TcpListener};
/// Utility method to find a port available for testing.
///
/// Would it work with multiple threads for testing?
/// or race condition?
fn find_free_port() -> u16 {
let loopback = Ipv4Addr::new(127, 0, 0, 1);
// 0 to ask the OS for a free port.
let socket = SocketAddrV4::new(loopback, 0);
let listener = TcpListener::bind(socket).unwrap();
let port = listener.local_addr().unwrap().port();
port
}
pub fn start_server(db_name: &str) -> (Server, u16) {
let port = find_free_port();
let mut app_config = AppConfig::default();
app_config.port = port;
let (tx, rx) = mpsc::channel();
let database_url = format!("postgres://postgres:example@127.0.0.1/{}", db_name);
app_config.database_url = database_url;
app_config.secure = false;
app_config.enable_csrf = false;
// First, start the server
thread::spawn(move || {
let sys = actix_rt::System::new("");
let server: Server = app::create_server(app_config);
// send the address so that we can stop!
tx.send(server.clone()).unwrap();
sys.run();
println!("Finished running");
});
(rx.recv().unwrap(), port)
}
pub fn get_url(port: u16, endpoint: &str) -> String {
format!("http://localhost:{}/{}", port, endpoint)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment