cratefield_testing/dialect.rs
1//! The database engines a [`TestHarness`](crate::TestHarness) can run a
2//! module's tests against (issue #20): SQLite always, Postgres when the
3//! environment names a server.
4
5/// The database engine backing a test harness.
6///
7/// `Dialect::available()` is the parity entry point: a module suite
8/// loops over it so one test definition runs against every dialect the
9/// environment provides — SQLite in-memory always, Postgres 16 when
10/// `FZ_TEST_POSTGRES_URL` names a server (CI's `postgres:16` service
11/// container).
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub enum Dialect {
14 /// A fresh in-memory SQLite database (the default; always
15 /// available).
16 Sqlite,
17 /// A throwaway database on the Postgres server at `url`, created
18 /// when the harness builds and dropped when it drops. Requires
19 /// building `cratefield-testing` with the `postgres` feature.
20 Postgres { url: String },
21}
22
23impl Dialect {
24 /// The lowercase engine name, for test-output tagging (`sqlite`,
25 /// `postgres`).
26 #[must_use]
27 pub fn name(&self) -> &'static str {
28 match self {
29 Dialect::Sqlite => "sqlite",
30 Dialect::Postgres { .. } => "postgres",
31 }
32 }
33
34 /// The Postgres server URL from `FZ_TEST_POSTGRES_URL` (trimmed,
35 /// non-empty), when the parity leg is available.
36 #[must_use]
37 pub fn postgres_url() -> Option<String> {
38 std::env::var("FZ_TEST_POSTGRES_URL")
39 .ok()
40 .map(|url| url.trim().to_owned())
41 .filter(|url| !url.is_empty())
42 }
43
44 /// The dialects available in this environment: always SQLite;
45 /// Postgres when `FZ_TEST_POSTGRES_URL` names a server. Prints one
46 /// notice per process when the Postgres leg is unavailable, so a
47 /// silently-skipped matrix leg is visible in the output.
48 #[must_use]
49 pub fn available() -> Vec<Self> {
50 static NOTICED: std::sync::Once = std::sync::Once::new();
51 let mut dialects = vec![Dialect::Sqlite];
52 match Self::postgres_url() {
53 Some(url) => dialects.push(Dialect::Postgres { url }),
54 None => NOTICED.call_once(|| {
55 eprintln!(
56 "NOTE: FZ_TEST_POSTGRES_URL is not set — the Postgres parity leg is \
57 skipped ({}); CI runs it against a postgres:16 service container",
58 crate::POSTGRES_SKIP_REASON
59 );
60 }),
61 }
62 dialects
63 }
64}