mod support;
use cratestack::sqlx::query;
use cratestack::{Migration, apply_pending};
use cratestack_migrate::diff;
use cratestack_migrate::emit::postgres;
use cratestack_parser::parse_schema;
use support::pg;
const SCHEMA: &str = r#"
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model DbgeneratedProbe {
id String @id @default(dbgenerated())
title String
}
"#;
async fn reset(pool: &cratestack::sqlx::PgPool) {
query("DROP TABLE IF EXISTS cratestack_migrations, dbgenerated_probes")
.execute(pool)
.await
.expect("drop");
}
#[tokio::test]
async fn dbgenerated_probe_migration_and_insert_behavior() {
let _guard = pg::serial_guard().await;
let Some(test_pg) = pg::connect_or_skip().await else {
return;
};
let pool = &test_pg.pool;
reset(pool).await;
let empty = parse_schema("").expect("empty schema should parse");
let next = parse_schema(SCHEMA).expect("probe schema should parse");
let migration = postgres::emit(&diff(&empty, &next));
assert!(
!migration.up.contains("DEFAULT dbgenerated()"),
"emitted DDL must never contain the literal `DEFAULT dbgenerated()`: {}",
migration.up
);
assert_eq!(
migration.unverified_dbgenerated,
vec![("dbgenerated_probes".to_owned(), "id".to_owned())],
);
apply_pending(
pool,
&[Migration {
id: "20260722000000_dbgenerated_probe".to_owned(),
description: "dbgenerated probe".to_owned(),
up: migration.up.clone(),
down: None,
}],
)
.await
.expect("emitted DDL must apply cleanly against real Postgres");
let insert_without_default = query("INSERT INTO dbgenerated_probes (title) VALUES ('first')")
.execute(pool)
.await;
let error = insert_without_default.expect_err(
"inserting without a value for a dbgenerated() column with no real default \
must fail",
);
assert!(
error.to_string().contains("null value")
&& error.to_string().contains("not-null constraint"),
"expected a NOT NULL violation, got: {error}",
);
query("ALTER TABLE dbgenerated_probes ALTER COLUMN id SET DEFAULT (md5(random()::text))")
.execute(pool)
.await
.expect("set a real default by hand");
query("INSERT INTO dbgenerated_probes (title) VALUES ('second')")
.execute(pool)
.await
.expect("insert should succeed once a real default exists");
let count: (i64,) =
cratestack::sqlx::query_as("SELECT COUNT(*)::BIGINT FROM dbgenerated_probes")
.fetch_one(pool)
.await
.expect("count rows");
assert_eq!(count.0, 1, "only the successful insert should have landed");
}