#![allow(clippy::doc_markdown, clippy::print_stdout, clippy::print_stderr)] #![allow(dead_code)]
use std::sync::Arc;
use tokio::sync::OnceCell;
const SCHEMA_SQL: &str = include_str!("../fixtures/schema.sql");
const SEED_SQL: &str = include_str!("../fixtures/seed_data.sql");
static CONTAINER: OnceCell<Arc<TestContainer>> = OnceCell::const_new();
pub struct TestContainer {
#[allow(dead_code)]
service: fraiseql_test_support::Service,
url: String,
pub host: String,
pub port: u16,
pub user: String,
pub password: String,
pub database: String,
}
impl TestContainer {
#[allow(dead_code)] pub fn connection_string(&self) -> String {
self.url.clone()
}
}
pub async fn get_test_container() -> Arc<TestContainer> {
CONTAINER
.get_or_init(|| async { Arc::new(provision_database().await) })
.await
.clone()
}
async fn provision_database() -> TestContainer {
let service = fraiseql_test_support::postgres()
.await
.expect("DATABASE_URL must be set (or enable fraiseql-test-support/local-testcontainers)");
let url = service.url().to_string();
let parts = PgParts::parse(&url);
let (client, connection) = tokio_postgres::connect(&url, tokio_postgres::NoTls)
.await
.expect("Failed to connect to harness Postgres for setup");
tokio::spawn(async move {
if let Err(e) = connection.await {
eprintln!("Connection error during setup: {}", e);
}
});
client
.batch_execute(SCHEMA_SQL)
.await
.expect("Failed to create schema");
let row = client
.query_one("SELECT COUNT(*) FROM test.tb_project", &[])
.await
.expect("Failed to count seed rows");
let count: i64 = row.get(0);
if count == 0 {
client
.batch_execute(SEED_SQL)
.await
.expect("Failed to seed data");
}
TestContainer {
service,
url,
host: parts.host,
port: parts.port,
user: parts.user,
password: parts.password,
database: parts.database,
}
}
struct PgParts {
host: String,
port: u16,
user: String,
password: String,
database: String,
}
impl PgParts {
fn parse(url: &str) -> Self {
let rest = url
.strip_prefix("postgresql://")
.or_else(|| url.strip_prefix("postgres://"))
.expect("harness url must start with postgres://");
let (userinfo, hostpart) = rest.split_once('@').expect("harness url must contain '@'");
let (user, password) = userinfo.split_once(':').unwrap_or((userinfo, ""));
let (hostport, dbpart) = hostpart.split_once('/').unwrap_or((hostpart, ""));
let database = dbpart.split('?').next().unwrap_or("");
let (host, port) = hostport.split_once(':').unwrap_or((hostport, "5432"));
Self {
host: host.to_string(),
port: port.parse().unwrap_or(5432),
user: user.to_string(),
password: password.to_string(),
database: database.to_string(),
}
}
}
#[allow(dead_code)] pub async fn connect_test_client() -> fraiseql_wire::error::Result<fraiseql_wire::FraiseClient> {
let container = get_test_container().await;
fraiseql_wire::FraiseClient::connect(&container.connection_string()).await
}