131 lines
4.7 KiB
Rust
131 lines
4.7 KiB
Rust
use storage::{DatabaseConfig, DatabaseConnection};
|
|
|
|
#[test]
|
|
fn postgres_configuration_debug_redacts_credentials() {
|
|
let config = DatabaseConfig::Postgres("postgres://user:secret@localhost/db".into());
|
|
assert!(!format!("{config:?}").contains("secret"));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn explicit_invalid_backends_fail_without_fallback() {
|
|
for config in [
|
|
DatabaseConfig::Postgres(String::new()),
|
|
DatabaseConfig::Postgres("mysql://localhost/db".into()),
|
|
DatabaseConfig::SqliteFile("".into()),
|
|
DatabaseConfig::SqliteFile(":memory:".into()),
|
|
] {
|
|
assert!(DatabaseConnection::connect(&config).await.is_err());
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn memory_connections_start_without_tables_and_clones_share_the_pool() {
|
|
let database = DatabaseConnection::connect(&DatabaseConfig::SqliteMemory)
|
|
.await
|
|
.unwrap();
|
|
let other = DatabaseConnection::connect(&DatabaseConfig::SqliteMemory)
|
|
.await
|
|
.unwrap();
|
|
assert!(database.postgres_pool().is_none());
|
|
let pool = database.sqlite_pool().unwrap();
|
|
let tables: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM sqlite_master WHERE type = 'table'")
|
|
.fetch_one(pool)
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(tables, 0, "connecting must not create application tables");
|
|
// This schema belongs to the test's simulated database-backed object.
|
|
sqlx::query("CREATE TABLE class_rows (id INTEGER PRIMARY KEY, value TEXT NOT NULL)")
|
|
.execute(pool)
|
|
.await
|
|
.unwrap();
|
|
sqlx::query("INSERT INTO class_rows (id, value) VALUES (?, ?)")
|
|
.bind(1_i64)
|
|
.bind("你好 ' ? $1 ;")
|
|
.execute(pool)
|
|
.await
|
|
.unwrap();
|
|
let cloned = database.clone();
|
|
let value: String = sqlx::query_scalar("SELECT value FROM class_rows WHERE id = ?")
|
|
.bind(1_i64)
|
|
.fetch_one(cloned.sqlite_pool().unwrap())
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(value, "你好 ' ? $1 ;");
|
|
let other_tables: i64 =
|
|
sqlx::query_scalar("SELECT COUNT(*) FROM sqlite_master WHERE type = 'table'")
|
|
.fetch_one(other.sqlite_pool().unwrap())
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(other_tables, 0);
|
|
database.close().await;
|
|
assert!(cloned.ping().await.is_err());
|
|
other.close().await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn explicit_sqlite_file_preserves_only_caller_created_data_across_reconnects() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let path = dir.path().join("nested/data/class.db");
|
|
let config = DatabaseConfig::SqliteFile(path.clone());
|
|
let database = DatabaseConnection::connect(&config).await.unwrap();
|
|
let pool = database.sqlite_pool().unwrap();
|
|
let tables: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM sqlite_master WHERE type = 'table'")
|
|
.fetch_one(pool)
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(tables, 0);
|
|
sqlx::query("CREATE TABLE class_rows (id INTEGER PRIMARY KEY, value TEXT NOT NULL)")
|
|
.execute(pool)
|
|
.await
|
|
.unwrap();
|
|
sqlx::query("INSERT INTO class_rows (id, value) VALUES (?, ?)")
|
|
.bind(1_i64)
|
|
.bind("caller-owned data")
|
|
.execute(pool)
|
|
.await
|
|
.unwrap();
|
|
database.close().await;
|
|
assert!(path.is_file());
|
|
let reopened = DatabaseConnection::connect(&config).await.unwrap();
|
|
let value: String = sqlx::query_scalar("SELECT value FROM class_rows WHERE id = ?")
|
|
.bind(1_i64)
|
|
.fetch_one(reopened.sqlite_pool().unwrap())
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(value, "caller-owned data");
|
|
let tables: Vec<String> =
|
|
sqlx::query_scalar("SELECT name FROM sqlite_master WHERE type = 'table'")
|
|
.fetch_all(reopened.sqlite_pool().unwrap())
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(tables, vec!["class_rows"]);
|
|
reopened.close().await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn invalid_sqlite_directory_reports_error() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let file = dir.path().join("file");
|
|
std::fs::write(&file, "occupied").unwrap();
|
|
assert!(
|
|
DatabaseConnection::connect(&DatabaseConfig::SqliteFile(file.join("class.db")))
|
|
.await
|
|
.is_err()
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn postgres_connection_failure_is_an_error() {
|
|
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
|
|
let url = format!(
|
|
"postgres://aster:secret@{}/aster?sslmode=disable",
|
|
listener.local_addr().unwrap()
|
|
);
|
|
let error = match DatabaseConnection::connect(&DatabaseConfig::Postgres(url)).await {
|
|
Ok(_) => panic!("unreachable PostgreSQL must fail"),
|
|
Err(error) => error,
|
|
};
|
|
assert!(!error.to_string().contains("secret"));
|
|
assert!(error.to_string().contains("no fallback"));
|
|
}
|