#![allow(unused_imports)]
use std::sync::Arc;
use super::CanonicalStore;
use super::conformance::{
run_admin_audit_store_contract, run_contract, run_migration_audit_store_contract,
run_projection_task_contract, run_saga_store_contract,
};
use super::system_store::{AdminAuditStore, MigrationAuditStore, ProjectionTaskStore, SagaStore};
#[cfg(any(feature = "postgres", feature = "mysql"))]
fn swap_database(dsn: &str, new_db: &str) -> String {
match dsn.rsplit_once('/') {
Some((prefix, _db)) => format!("{prefix}/{new_db}"),
None => format!("{dsn}/{new_db}"),
}
}
#[cfg(any(feature = "postgres", feature = "mysql", feature = "clickhouse"))]
fn fresh_db_name() -> String {
format!("udb_conf_{}", uuid::Uuid::new_v4().simple())
}
#[cfg(feature = "postgres")]
#[tokio::test]
async fn postgres_canonical_store_satisfies_all_contracts_live() {
let Ok(dsn) = std::env::var("UDB_PG_DSN") else {
eprintln!("UDB_PG_DSN unset — skipping live Postgres conformance");
return;
};
use super::postgres::PostgresCanonicalStore;
use sqlx::postgres::PgPoolOptions;
let admin = PgPoolOptions::new()
.max_connections(1)
.connect(&dsn)
.await
.expect("connect to live Postgres (UDB_PG_DSN)");
let db = fresh_db_name();
sqlx::query(&format!("CREATE DATABASE \"{db}\""))
.execute(&admin)
.await
.expect("create throwaway database");
let conf_dsn = swap_database(&dsn, &db);
let pool = PgPoolOptions::new()
.max_connections(4)
.connect(&conf_dsn)
.await
.expect("connect to throwaway database");
let store = Arc::new(PostgresCanonicalStore::new(
pool.clone(),
"conformance-live",
"udb_outbox_events",
));
run_contract(store.clone()).await;
run_projection_task_contract(store.clone()).await;
run_saga_store_contract(store.clone()).await;
run_admin_audit_store_contract(store.clone()).await;
run_migration_audit_store_contract(store.clone()).await;
drop(store);
pool.close().await;
let _ = sqlx::query(&format!("DROP DATABASE IF EXISTS \"{db}\" WITH (FORCE)"))
.execute(&admin)
.await;
}
#[cfg(feature = "postgres")]
#[tokio::test]
async fn postgres_upsert_read_modify_write_round_trips_live() {
let Ok(dsn) = std::env::var("UDB_PG_DSN") else {
eprintln!("UDB_PG_DSN unset — skipping live upsert read-modify-write regression");
return;
};
use crate::generation::ManifestColumn;
use crate::runtime::postgres_helpers::bind_one;
use serde_json::json;
use sqlx::Row;
use sqlx::postgres::PgPoolOptions;
let pool = PgPoolOptions::new()
.max_connections(2)
.connect(&dsn)
.await
.expect("connect to live Postgres (UDB_PG_DSN)");
let schema = format!("udb_rmw_{}", uuid::Uuid::new_v4().simple());
sqlx::query(&format!("CREATE SCHEMA \"{schema}\""))
.execute(&pool)
.await
.expect("create throwaway schema");
sqlx::query(&format!(
"CREATE TABLE \"{schema}\".stored_files (\
id uuid PRIMARY KEY, tenant_id uuid NOT NULL, name text NOT NULL, \
size_bytes bigint, has_text bool NOT NULL, \
created_at timestamptz NOT NULL DEFAULT now(), \
updated_at timestamptz NOT NULL DEFAULT now(), \
deleted_at timestamptz, created_by varchar(120))"
))
.execute(&pool)
.await
.expect("create stored_files");
let column = |name: &str, sql_type: &str| ManifestColumn {
column_name: name.to_string(),
sql_type: sql_type.to_string(),
..ManifestColumn::default()
};
let id = uuid::Uuid::new_v4().to_string();
let id_uuid = id.parse::<uuid::Uuid>().unwrap();
let tenant = uuid::Uuid::new_v4().to_string();
let insert_cols = [
column("id", "UUID"),
column("tenant_id", "UUID"),
column("name", "TEXT"),
column("size_bytes", "BIGINT"),
column("has_text", "BOOL"),
column("created_by", "VARCHAR(120)"),
];
let insert_vals = [
json!(id),
json!(tenant),
json!("demo.json"),
json!(1234),
json!(true),
json!("admin"),
];
let insert_sql = format!(
"INSERT INTO \"{schema}\".stored_files \
(id, tenant_id, name, size_bytes, has_text, created_by) \
VALUES ($1, $2, $3, $4, $5, $6)"
);
let mut q = sqlx::query(&insert_sql);
for (c, v) in insert_cols.iter().zip(insert_vals.iter()) {
q = bind_one(q, Some(c), v).expect("bind fresh-insert value");
}
q.execute(&pool)
.await
.expect("fresh insert (audit timestamps omitted) succeeds");
let select_audit_sql =
format!("SELECT created_at, updated_at FROM \"{schema}\".stored_files WHERE id = $1");
let row = sqlx::query(&select_audit_sql)
.bind(id_uuid)
.fetch_one(&pool)
.await
.expect("select the row back");
let created_at: chrono::DateTime<chrono::Utc> = row.try_get("created_at").unwrap();
let updated_at: chrono::DateTime<chrono::Utc> = row.try_get("updated_at").unwrap();
let upsert_sql = format!(
"INSERT INTO \"{schema}\".stored_files \
(id, tenant_id, name, size_bytes, has_text, created_at, updated_at, deleted_at, created_by) \
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) \
ON CONFLICT (id) DO UPDATE SET \
tenant_id = EXCLUDED.tenant_id, name = EXCLUDED.name, size_bytes = EXCLUDED.size_bytes, \
has_text = EXCLUDED.has_text, created_at = EXCLUDED.created_at, \
updated_at = EXCLUDED.updated_at, deleted_at = EXCLUDED.deleted_at, \
created_by = EXCLUDED.created_by"
);
let upsert_cols = [
column("id", "UUID"),
column("tenant_id", "UUID"),
column("name", "TEXT"),
column("size_bytes", "BIGINT"),
column("has_text", "BOOL"),
column("created_at", "TIMESTAMPTZ"),
column("updated_at", "TIMESTAMPTZ"),
column("deleted_at", "TIMESTAMPTZ"),
column("created_by", "VARCHAR(120)"),
];
let soft_delete_vals = [
json!(id),
json!(tenant),
json!("demo.json"),
json!(1234),
json!(true),
json!(created_at.to_rfc3339()),
json!(updated_at.to_rfc3339()),
json!(updated_at.to_rfc3339()), json!("admin"),
];
let mut q = sqlx::query(&upsert_sql);
for (c, v) in upsert_cols.iter().zip(soft_delete_vals.iter()) {
q = bind_one(q, Some(c), v).expect("bind soft-delete value");
}
let result = q
.execute(&pool)
.await
.expect("read-modify-write upsert must round-trip (pre-fix: SQLSTATE 42804)");
assert_eq!(result.rows_affected(), 1, "conflict-update affects the row");
let select_deleted_sql =
format!("SELECT deleted_at FROM \"{schema}\".stored_files WHERE id = $1");
let deleted: Option<chrono::DateTime<chrono::Utc>> = sqlx::query(&select_deleted_sql)
.bind(id_uuid)
.fetch_one(&pool)
.await
.unwrap()
.try_get("deleted_at")
.unwrap();
assert!(deleted.is_some(), "soft-delete deleted_at must persist");
let edit_vals = [
json!(id),
json!(tenant),
json!("demo-renamed.json"),
json!(4321),
json!(false),
json!(created_at.to_rfc3339()),
json!(updated_at.to_rfc3339()),
json!(null),
json!("admin"),
];
let mut q = sqlx::query(&upsert_sql);
for (c, v) in upsert_cols.iter().zip(edit_vals.iter()) {
q = bind_one(q, Some(c), v).expect("bind edit value");
}
q.execute(&pool)
.await
.expect("read-modify-write upsert with null deleted_at must round-trip");
let deleted: Option<chrono::DateTime<chrono::Utc>> = sqlx::query(&select_deleted_sql)
.bind(id_uuid)
.fetch_one(&pool)
.await
.unwrap()
.try_get("deleted_at")
.unwrap();
assert!(deleted.is_none(), "edit must clear deleted_at back to NULL");
let _ = sqlx::query(&format!("DROP SCHEMA IF EXISTS \"{schema}\" CASCADE"))
.execute(&pool)
.await;
pool.close().await;
}
#[cfg(feature = "postgres")]
#[tokio::test]
async fn postgres_go005_cas_precondition_serializes_racing_writers_live() {
let Ok(dsn) = std::env::var("UDB_PG_DSN") else {
eprintln!("UDB_PG_DSN unset — skipping GO-005 CAS precondition regression");
return;
};
use sqlx::Row;
use sqlx::postgres::PgPoolOptions;
let pool = PgPoolOptions::new()
.max_connections(4)
.connect(&dsn)
.await
.expect("connect to live Postgres (UDB_PG_DSN)");
let schema = format!("udb_cas_{}", uuid::Uuid::new_v4().simple());
sqlx::query(&format!("CREATE SCHEMA \"{schema}\""))
.execute(&pool)
.await
.expect("create throwaway schema");
sqlx::query(&format!(
"CREATE TABLE \"{schema}\".fleet (\
id uuid PRIMARY KEY, version bigint NOT NULL, status text NOT NULL)"
))
.execute(&pool)
.await
.expect("create fleet");
let id = uuid::Uuid::new_v4();
sqlx::query(&format!(
"INSERT INTO \"{schema}\".fleet (id, version, status) VALUES ($1, 1, 'INIT')"
))
.bind(id)
.execute(&pool)
.await
.expect("seed version 1");
let sel = format!("SELECT version FROM \"{schema}\".fleet WHERE id = $1 FOR UPDATE");
let mut tx_a = pool.begin().await.expect("begin A");
let ver_a: i64 = sqlx::query(&sel)
.bind(id)
.fetch_one(&mut *tx_a)
.await
.expect("A select for update")
.try_get("version")
.unwrap();
assert_eq!(
ver_a, 1,
"A observes version 1; its expected=1 precondition holds"
);
let pool_b = pool.clone();
let schema_b = schema.clone();
let handle = tokio::spawn(async move {
let sel_b = format!("SELECT version FROM \"{schema_b}\".fleet WHERE id = $1 FOR UPDATE");
let mut tx_b = pool_b.begin().await.expect("begin B");
let ver_b: i64 = sqlx::query(&sel_b)
.bind(id)
.fetch_one(&mut *tx_b)
.await
.expect("B select for update")
.try_get("version")
.unwrap();
let _ = tx_b.rollback().await;
ver_b
});
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
sqlx::query(&format!(
"UPDATE \"{schema}\".fleet SET version = 2, status = 'A' WHERE id = $1"
))
.bind(id)
.execute(&mut *tx_a)
.await
.expect("A conditional update");
tx_a.commit().await.expect("A commit");
let ver_b = handle.await.expect("B task join");
assert_eq!(
ver_b, 2,
"B unblocks after A commits and observes the bumped version — expected=1 fails"
);
let row = sqlx::query(&format!(
"SELECT version, status FROM \"{schema}\".fleet WHERE id = $1"
))
.bind(id)
.fetch_one(&pool)
.await
.unwrap();
let final_version: i64 = row.try_get("version").unwrap();
let final_status: String = row.try_get("status").unwrap();
assert_eq!(final_version, 2, "exactly one CAS writer committed");
assert_eq!(
final_status, "A",
"the winner committed; the loser wrote nothing"
);
let _ = sqlx::query(&format!("DROP SCHEMA IF EXISTS \"{schema}\" CASCADE"))
.execute(&pool)
.await;
pool.close().await;
}
#[cfg(feature = "mysql")]
#[tokio::test]
async fn mysql_canonical_store_satisfies_all_contracts_live() {
let Ok(dsn) = std::env::var("UDB_MYSQL_DSN") else {
eprintln!("UDB_MYSQL_DSN unset — skipping live MySQL conformance");
return;
};
use super::mysql::MysqlCanonicalStore;
use sqlx::mysql::MySqlPoolOptions;
let admin = MySqlPoolOptions::new()
.max_connections(1)
.connect(&dsn)
.await
.expect("connect to live MySQL (UDB_MYSQL_DSN)");
let db = fresh_db_name();
sqlx::query(&format!("CREATE DATABASE `{db}`"))
.execute(&admin)
.await
.expect("create throwaway database");
let conf_dsn = swap_database(&dsn, &db);
let pool = MySqlPoolOptions::new()
.max_connections(4)
.connect(&conf_dsn)
.await
.expect("connect to throwaway database");
let store = Arc::new(MysqlCanonicalStore::new(
pool.clone(),
"conformance-live",
"udb_outbox_events",
));
run_contract(store.clone()).await;
run_projection_task_contract(store.clone()).await;
run_saga_store_contract(store.clone()).await;
run_admin_audit_store_contract(store.clone()).await;
run_migration_audit_store_contract(store.clone()).await;
drop(store);
pool.close().await;
let _ = sqlx::query(&format!("DROP DATABASE IF EXISTS `{db}`"))
.execute(&admin)
.await;
}
#[cfg(feature = "redis")]
#[tokio::test]
async fn redis_canonical_store_satisfies_all_contracts_live() {
let Ok(dsn) = std::env::var("UDB_REDIS_DSN")
.or_else(|_| std::env::var("REDIS_URL"))
.or_else(|_| std::env::var("UDB_INTEGRATION_REDIS_URL"))
else {
eprintln!(
"UDB_REDIS_DSN / REDIS_URL / UDB_INTEGRATION_REDIS_URL unset — skipping live Redis conformance"
);
return;
};
use super::redis::RedisCanonicalStore;
let instance = format!("conformance-live-{}", uuid::Uuid::new_v4().simple());
let client = redis::Client::open(dsn).expect("create live Redis client");
let store = Arc::new(RedisCanonicalStore::new(client.clone(), instance.clone()));
run_contract(store.clone()).await;
run_projection_task_contract(store.clone()).await;
run_saga_store_contract(store.clone()).await;
run_admin_audit_store_contract(store.clone()).await;
run_migration_audit_store_contract(store.clone()).await;
drop(store);
let prefix = format!("udb:system:{instance}:*");
if let Ok(mut conn) = client.get_multiplexed_async_connection().await {
if let Ok(keys) = redis::cmd("KEYS")
.arg(prefix)
.query_async::<Vec<String>>(&mut conn)
.await
&& !keys.is_empty()
{
let _ = redis::cmd("DEL")
.arg(keys)
.query_async::<i64>(&mut conn)
.await;
}
}
}
#[cfg(feature = "mssql")]
#[tokio::test]
async fn mssql_canonical_store_satisfies_all_contracts_live() {
let Ok(dsn) = std::env::var("UDB_MSSQL_DSN") else {
eprintln!("UDB_MSSQL_DSN unset — skipping live SQL Server conformance");
return;
};
use super::mssql::MssqlCanonicalStore;
use crate::runtime::executors::mssql::MssqlClient;
let suffix = uuid::Uuid::new_v4().simple().to_string();
let outbox_rel = format!("udb_outbox_events_conf_{suffix}");
let projection_rel = format!("udb_projection_tasks_conf_{suffix}");
let saga_rel = format!("udb_sagas_conf_{suffix}");
let audit_rel = format!("udb_admin_audit_log_conf_{suffix}");
let runs_rel = format!("udb_migration_runs_conf_{suffix}");
let ledger_rel = format!("udb_migration_op_ledger_conf_{suffix}");
let client = MssqlClient::new(dsn);
let store = Arc::new(
MssqlCanonicalStore::new(client.clone(), "conformance-live", outbox_rel.clone())
.with_projection_relation(projection_rel.clone())
.with_saga_relation(saga_rel.clone())
.with_admin_audit_relation(audit_rel.clone())
.with_migration_relations(runs_rel.clone(), ledger_rel.clone()),
);
run_contract(store.clone()).await;
run_projection_task_contract(store.clone()).await;
run_saga_store_contract(store.clone()).await;
run_admin_audit_store_contract(store.clone()).await;
run_migration_audit_store_contract(store.clone()).await;
drop(store);
for rel in [
&ledger_rel,
&runs_rel,
&audit_rel,
&saga_rel,
&projection_rel,
&outbox_rel,
] {
let _ = client
.simple_batch(&format!(
"IF OBJECT_ID(N'{rel}', N'U') IS NOT NULL DROP TABLE {rel}"
))
.await;
}
}
#[cfg(feature = "mongodb-native")]
#[tokio::test]
async fn mongodb_canonical_store_satisfies_all_contracts_live() {
let Ok(dsn) = std::env::var("UDB_MONGODB_DSN").or_else(|_| std::env::var("UDB_NOSQL_DSN"))
else {
eprintln!("UDB_MONGODB_DSN / UDB_NOSQL_DSN unset — skipping live MongoDB conformance");
return;
};
use super::mongodb::MongoDbCanonicalStore;
use crate::runtime::executors::mongodb::{MongoDbExecutor, MongoDbNativeConfig};
use mongodb_driver::bson::doc;
let db_name = format!("udb_conf_{}", uuid::Uuid::new_v4().simple());
let executor = MongoDbExecutor::new_native(MongoDbNativeConfig {
dsn: dsn.clone(),
database: db_name.clone(),
timeout_secs: 10,
app_name: Some("udb-mongo-conformance".to_string()),
max_pool_size: Some(4),
direct_connection: None,
retry_writes: None,
})
.await
.expect("construct native MongoDbExecutor from UDB_MONGODB_DSN");
let database = executor
.native_database()
.expect("native executor must expose its Database handle");
let hello = database
.run_command(doc! { "hello": 1 })
.await
.expect("mongodb hello command");
let is_replica = hello.contains_key("setName");
let is_sharded = hello
.get_str("msg")
.map(|m| m == "isdbgrid")
.unwrap_or(false);
assert!(
is_replica || is_sharded,
"mongodb conformance expects a replica set or sharded cluster topology"
);
let store = Arc::new(MongoDbCanonicalStore::new(
database.clone(),
"conformance-live",
"udb_outbox_events",
));
run_contract(store.clone()).await;
run_projection_task_contract(store.clone()).await;
run_saga_store_contract(store.clone()).await;
run_admin_audit_store_contract(store.clone()).await;
run_migration_audit_store_contract(store.clone()).await;
drop(store);
let _ = database.run_command(doc! { "dropDatabase": 1 }).await;
}
#[cfg(feature = "cassandra")]
#[tokio::test]
async fn cassandra_canonical_store_satisfies_all_contracts_live() {
let Ok(dsn) = std::env::var("UDB_CASSANDRA_DSN") else {
eprintln!("UDB_CASSANDRA_DSN unset — skipping live Cassandra conformance");
return;
};
use super::cassandra::CassandraCanonicalStore;
use crate::runtime::executors::cassandra::CassandraClient;
let client = CassandraClient::connect(&dsn)
.await
.expect("connect to live Cassandra (UDB_CASSANDRA_DSN)");
let keyspace = format!("udb_conf_{}", uuid::Uuid::new_v4().simple());
let store = Arc::new(CassandraCanonicalStore::new(
client.clone(),
"conformance-live",
keyspace.clone(),
"udb_outbox_events",
));
run_contract(store.clone()).await;
run_projection_task_contract(store.clone()).await;
run_saga_store_contract(store.clone()).await;
run_admin_audit_store_contract(store.clone()).await;
run_migration_audit_store_contract(store.clone()).await;
drop(store);
let _ = client
.cql_execute(&format!("DROP KEYSPACE IF EXISTS \"{keyspace}\""), ())
.await;
}
#[cfg(feature = "neo4j")]
#[tokio::test]
async fn neo4j_canonical_store_satisfies_all_contracts_live() {
let Ok(http_base) = std::env::var("UDB_NEO4J_DSN")
.or_else(|_| std::env::var("UDB_GRAPH_HTTP_URL"))
.or_else(|_| std::env::var("UDB_GRAPH_DSN"))
else {
eprintln!(
"UDB_NEO4J_DSN / UDB_GRAPH_HTTP_URL / UDB_GRAPH_DSN unset — skipping live Neo4j conformance"
);
return;
};
use super::neo4j::Neo4jCanonicalStore;
use crate::runtime::executors::neo4j::{Neo4jConfig, Neo4jExecutor};
let http_base = if http_base.starts_with("http://") || http_base.starts_with("https://") {
http_base
} else {
Neo4jConfig::http_base_from_dsn(&http_base)
};
let username = std::env::var("UDB_NEO4J_USER")
.or_else(|_| std::env::var("UDB_GRAPH_USER"))
.unwrap_or_else(|_| "neo4j".to_string());
let password = std::env::var("UDB_NEO4J_PASSWORD")
.or_else(|_| std::env::var("UDB_GRAPH_PASSWORD"))
.unwrap_or_default();
let database = std::env::var("UDB_NEO4J_DATABASE")
.or_else(|_| std::env::var("UDB_GRAPH_DATABASE"))
.unwrap_or_else(|_| "neo4j".to_string());
let executor = Neo4jExecutor::new(Neo4jConfig {
http_base,
username,
password,
database,
is_cloud: false,
dev_mode: true,
timeout_secs: 15,
});
let run_tag = uuid::Uuid::new_v4().simple().to_string();
let store = Arc::new(
Neo4jCanonicalStore::new(executor.clone(), "conformance-live")
.with_run_tag(run_tag.clone()),
);
run_contract(store.clone()).await;
run_projection_task_contract(store.clone()).await;
run_saga_store_contract(store.clone()).await;
run_admin_audit_store_contract(store.clone()).await;
run_migration_audit_store_contract(store.clone()).await;
drop(store);
let _ = executor
.cypher_rows(
"MATCH (n) WHERE n.run_tag = $tag DETACH DELETE n",
serde_json::json!({ "tag": run_tag }),
)
.await;
}
#[cfg(feature = "clickhouse")]
#[tokio::test]
async fn clickhouse_canonical_store_satisfies_all_contracts_live() {
let Ok(dsn) = std::env::var("UDB_COLUMN_DSN").or_else(|_| std::env::var("UDB_CLICKHOUSE_DSN"))
else {
eprintln!(
"UDB_COLUMN_DSN / UDB_CLICKHOUSE_DSN unset — skipping live ClickHouse conformance"
);
return;
};
use super::clickhouse::ClickHouseCanonicalStore;
use crate::runtime::executors::clickhouse::{ClickHouseConfig, ClickHouseExecutor};
let http_base = if dsn.starts_with("http://") || dsn.starts_with("https://") {
dsn.split('/').take(3).collect::<Vec<_>>().join("/")
} else {
ClickHouseConfig::http_base_from_dsn(&dsn)
};
let username = std::env::var("UDB_COLUMN_USER").unwrap_or_else(|_| "udb".to_string());
let password = std::env::var("UDB_COLUMN_PASSWORD").unwrap_or_else(|_| "udb".to_string());
let base_db = std::env::var("UDB_COLUMN_DATABASE")
.ok()
.or_else(|| ClickHouseConfig::db_from_dsn(&dsn))
.unwrap_or_else(|| "udb".to_string());
let mk_cfg = |database: String| ClickHouseConfig {
http_base: http_base.clone(),
username: username.clone(),
password: password.clone(),
database,
is_cloud: false,
connect_timeout_secs: 10,
query_timeout_secs: 30,
};
let admin = ClickHouseExecutor::new(mk_cfg(base_db.clone()));
let conf_db = fresh_db_name();
admin
.execute_ddl(&format!("CREATE DATABASE IF NOT EXISTS `{conf_db}`"))
.await
.expect("create throwaway ClickHouse database");
let executor = ClickHouseExecutor::new(mk_cfg(conf_db.clone()));
let store = Arc::new(ClickHouseCanonicalStore::new(
executor,
"conformance-live",
conf_db.clone(),
));
run_contract(store.clone()).await;
run_projection_task_contract(store.clone()).await;
run_saga_store_contract(store.clone()).await;
run_admin_audit_store_contract(store.clone()).await;
run_migration_audit_store_contract(store.clone()).await;
drop(store);
let _ = admin
.execute_ddl(&format!("DROP DATABASE IF EXISTS `{conf_db}`"))
.await;
}
#[cfg(feature = "elasticsearch")]
#[tokio::test]
async fn elasticsearch_vector_canonical_store_satisfies_all_contracts_live() {
let Ok(raw_dsn) = std::env::var("UDB_ELASTIC_DSN") else {
eprintln!("UDB_ELASTIC_DSN unset — skipping live Elasticsearch canonical conformance");
return;
};
use super::vector_system::VectorSystemCanonicalStore;
use crate::runtime::core::setup_data::parse_elasticsearch_dsn;
use crate::runtime::executors::elasticsearch::ElasticsearchHttpClient;
let (base_url, auth) = parse_elasticsearch_dsn(&raw_dsn);
let client = ElasticsearchHttpClient::new(base_url, auth);
let instance = format!("conf_{}", uuid::Uuid::new_v4().simple());
let index = format!("udb-system-{instance}");
let store = Arc::new(VectorSystemCanonicalStore::new_elasticsearch(
client.clone(),
instance,
));
run_contract(store.clone()).await;
run_projection_task_contract(store.clone()).await;
run_saga_store_contract(store.clone()).await;
run_admin_audit_store_contract(store.clone()).await;
run_migration_audit_store_contract(store.clone()).await;
drop(store);
let _ = client
.request_json(
reqwest::Method::DELETE,
&format!("/{index}"),
&serde_json::Value::Null,
)
.await;
}
#[cfg(feature = "qdrant")]
#[tokio::test]
async fn qdrant_canonical_store_fails_closed_until_native_cas_live() {
let Ok(url) = std::env::var("UDB_QDRANT_URL") else {
eprintln!("UDB_QDRANT_URL unset — skipping live Qdrant fail-closed oracle");
return;
};
use super::CanonicalStore;
use super::qdrant::QdrantCanonicalStore;
use crate::runtime::executors::qdrant::QdrantHttpClient;
let client = QdrantHttpClient {
base_url: url.trim_end_matches('/').to_string(),
api_key: std::env::var("UDB_QDRANT_API_KEY").ok(),
http: crate::runtime::executors::http::HttpClientSpec::with_timeout(
std::time::Duration::from_secs(30),
)
.build(),
};
let instance = format!("conf_{}", uuid::Uuid::new_v4().simple());
let store = Arc::new(QdrantCanonicalStore::new(client, instance.clone()));
let err = CanonicalStore::ensure_system_tables(store.as_ref())
.await
.expect_err("Qdrant canonical SystemStores must fail closed until native CAS is wired");
assert!(
err.contains("real multi-process CAS") && err.contains("Qdrant-native conditional write"),
"Qdrant fail-closed error must name the missing CAS primitive; got: {err}"
);
}