sqlx_database_tester/
lib.rs

1#![doc = include_str!("../README.md")]
2use std::{env, str::FromStr};
3
4pub use dotenv;
5pub use sqlx_database_tester_macros::test;
6
7/// Environmental variable containing database URL
8const DATABASE_ENV_VAR: &str = "DATABASE_URL";
9#[doc(hidden)]
10/// Extract optional prefix from the database specified in the connection string
11pub fn derive_db_prefix(uri: &str) -> Result<Option<String>, sqlx::Error> {
12	Ok(sqlx::postgres::PgConnectOptions::from_str(uri)?.get_database().map(str::to_owned))
13}
14
15#[doc(hidden)]
16/// Create a UUID based database name with optional prefix from the database
17/// specified in the connection string
18pub fn derive_db_name(uri: &str) -> Result<String, sqlx::Error> {
19	let random_part = uuid::Uuid::new_v4().simple().to_string();
20
21	Ok(if let Some(prefix) = derive_db_prefix(uri)? {
22		format!("{}_{}", prefix, random_part)
23	} else {
24		random_part
25	})
26}
27
28/// Creates a `PgConnectOptions`
29#[must_use]
30pub fn connect_options(
31	database_name: &str,
32	#[allow(unused_variables)] level: &str,
33) -> sqlx::postgres::PgConnectOptions {
34	#[allow(clippy::expect_used)]
35	let mut options = sqlx::postgres::PgConnectOptions::from_str(&get_database_uri())
36		.expect("Failed to parse database URI");
37	options = options.database(database_name);
38	#[cfg(feature = "sqlx-log")]
39	if let Ok(filter) = log::LevelFilter::from_str(level) {
40		use sqlx::ConnectOptions;
41		options.log_statements(filter);
42	}
43	options
44}
45
46#[doc(hidden)]
47#[must_use]
48/// Retrieve database_uri from the env variable, panics if it's not available
49pub fn get_database_uri() -> String {
50	env::var(DATABASE_ENV_VAR)
51		.unwrap_or_else(|_| panic!("The env variable {} needs to be set", DATABASE_ENV_VAR))
52}
53
54#[cfg(test)]
55#[allow(clippy::unwrap_used)]
56mod tests {
57	use std::env;
58
59	use crate::{connect_options, derive_db_name, derive_db_prefix, get_database_uri};
60
61	#[test]
62	fn test_db_prefix() {
63		assert_eq!(derive_db_prefix("postgresql:///").unwrap(), None);
64		assert_eq!(derive_db_prefix("postgres://").unwrap(), None);
65		assert_eq!(derive_db_prefix("postgresql://localhost:5433").unwrap(), None);
66		assert_eq!(
67			derive_db_prefix("postgresql:///mydb?host=localhost&port=5433").unwrap(),
68			Some("mydb".to_owned())
69		);
70		assert_eq!(derive_db_prefix("postgresql://workflow-engine:password@%2Fopt%2Fpostgresql%2Fsockets/workflow-engine").unwrap(), Some("workflow-engine".to_owned()));
71		assert_eq!(
72			derive_db_prefix(
73				"postgresql://other@localhost/otherdb?connect_timeout=10&application_name=myapp"
74			)
75			.unwrap(),
76			Some("otherdb".to_owned())
77		);
78	}
79
80	#[test]
81	fn test_derive_db_name() {
82		assert!(derive_db_name("postgresql:///mydb?host=localhost&port=5433")
83			.unwrap()
84			.starts_with("mydb_"));
85		assert_eq!(
86			derive_db_name("postgresql:///mydb?host=localhost&port=5433").unwrap().len(),
87			37
88		);
89
90		assert_eq!(derive_db_name("postgresql:///").unwrap().len(), 32);
91	}
92
93	#[test]
94	fn test_connect_options() {
95		env::set_var("DATABASE_URL", "postgresql:///");
96		assert_eq!(
97			connect_options("test_database", "info").get_database().unwrap(),
98			"test_database"
99		);
100
101		env::set_var(
102			"DATABASE_URL",
103			"postgresql://workflow-engine:password@%2Fopt%2Fpostgresql%2Fsockets/workflow-engine",
104		);
105		assert_eq!(
106			connect_options("test_database", "info").get_database().unwrap(),
107			"test_database"
108		);
109
110		env::remove_var("DATABASE_URL");
111	}
112
113	#[test]
114	fn test_get_database_uri() {
115		env::set_var(
116			"DATABASE_URL",
117			"postgresql://workflow-engine:password@%2Fopt%2Fpostgresql%2Fsockets/workflow-engine",
118		);
119		assert_eq!(
120			get_database_uri(),
121			"postgresql://workflow-engine:password@%2Fopt%2Fpostgresql%2Fsockets/workflow-engine"
122		);
123
124		env::remove_var("DATABASE_URL");
125	}
126
127	#[test]
128	#[should_panic(expected = "The env variable DATABASE_URL needs to be set")]
129	fn test_get_database_uri_panic() {
130		env::remove_var("DATABASE_URL");
131		let _ = get_database_uri();
132	}
133}