use super::{
CDC_MEDIATED_ON, CDC_MEDIATED_VAR, CHANGELOG_PORTABLE_INSERT_COLUMNS,
CLOCK_TIMESTAMP_DIRECTIVE, DURATION_CALC_VERSION, STARTED_AT_VAR, build_changelog_insert_sql,
duration_ms_sql,
};
use crate::types::DatabaseType;
#[test]
fn duration_expr_uses_epoch_not_the_milliseconds_trap() {
let sql = duration_ms_sql(STARTED_AT_VAR);
assert!(
sql.contains("EXTRACT(EPOCH"),
"must use EXTRACT(EPOCH FROM interval) for full wall-clock seconds"
);
assert!(
!sql.to_uppercase().contains("MILLISECONDS"),
"must NOT use EXTRACT(MILLISECONDS) — it truncates intervals >= 1 minute"
);
assert!(sql.contains("* 1000"), "seconds are scaled to milliseconds");
assert!(sql.contains("::INTEGER"), "result is an INTEGER duration_ms");
}
#[test]
fn duration_expr_reads_back_the_started_at_var_on_the_db_clock() {
let sql = duration_ms_sql(STARTED_AT_VAR);
assert!(
sql.contains("current_setting('fraiseql.started_at')"),
"reads the start timestamp back from the session var"
);
assert!(
sql.contains("clock_timestamp()"),
"closes the interval against the DB clock (same clock that set started_at)"
);
}
#[test]
fn clock_directive_is_not_a_plausible_real_value() {
assert!(CLOCK_TIMESTAMP_DIRECTIVE.starts_with('\u{1}'));
assert!(!CLOCK_TIMESTAMP_DIRECTIVE.contains(' '));
}
#[test]
fn duration_calc_version_is_the_post_fix_marker() {
assert_eq!(DURATION_CALC_VERSION, 2);
}
#[test]
fn cdc_mediated_marker_is_a_dotted_custom_guc() {
assert_eq!(CDC_MEDIATED_VAR, "fraiseql.cdc_mediated");
assert!(CDC_MEDIATED_VAR.contains('.'), "a custom GUC needs a dotted prefix");
assert!(STARTED_AT_VAR.contains('.'), "sibling GUC is also dotted");
}
#[test]
fn cdc_mediated_on_value_is_an_exact_match_token() {
assert_eq!(CDC_MEDIATED_ON, "on");
assert!(!CDC_MEDIATED_ON.is_empty(), "must be a non-empty truthy token");
}
#[test]
fn portable_insert_uses_dialect_specific_placeholders() {
let table = "core.tb_entity_change_log";
let n = CHANGELOG_PORTABLE_INSERT_COLUMNS.len();
let pg = build_changelog_insert_sql(table, DatabaseType::PostgreSQL);
assert!(
pg.contains("VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)"),
"PG placeholders: {pg}"
);
let mssql = build_changelog_insert_sql(table, DatabaseType::SQLServer);
assert!(
mssql.contains(
"VALUES (@P1, @P2, @P3, @P4, @P5, @P6, @P7, @P8, @P9, @P10, @P11, @P12, @P13)"
),
"MSSQL placeholders: {mssql}"
);
for dialect in [DatabaseType::MySQL, DatabaseType::SQLite] {
let sql = build_changelog_insert_sql(table, dialect);
assert_eq!(sql.matches('?').count(), n, "{dialect} uses one `?` per column: {sql}");
assert!(
sql.contains("VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"),
"{dialect}: {sql}"
);
}
}
#[test]
fn portable_insert_writes_the_identity_and_envelope_subset() {
let sql = build_changelog_insert_sql("t", DatabaseType::MySQL);
assert!(sql.starts_with("INSERT INTO t ("), "names the target table: {sql}");
let cols = CHANGELOG_PORTABLE_INSERT_COLUMNS
.iter()
.map(|c| format!("`{c}`"))
.collect::<Vec<_>>()
.join(", ");
assert!(sql.contains(&cols), "writes the portable contract columns `{cols}`: {sql}");
assert!(!sql.contains("started_at"), "started_at is omitted (PG-only): {sql}");
assert!(!sql.contains("duration_ms"), "duration_ms is omitted (PG-only): {sql}");
assert!(!CHANGELOG_PORTABLE_INSERT_COLUMNS.contains(&"seq"), "seq is not in the INSERT");
assert!(
CHANGELOG_PORTABLE_INSERT_COLUMNS.contains(&"trace_id"),
"trace_id is written: {sql}"
);
assert!(
CHANGELOG_PORTABLE_INSERT_COLUMNS.contains(&"schema_version"),
"schema_version is written: {sql}"
);
assert!(
CHANGELOG_PORTABLE_INSERT_COLUMNS.contains(&"trace_context"),
"trace_context is written: {sql}"
);
}
#[test]
fn portable_insert_quotes_identifiers_per_dialect() {
let mysql = build_changelog_insert_sql("t", DatabaseType::MySQL);
assert!(mysql.contains("`cascade`"), "MySQL backtick-quotes columns: {mysql}");
let mssql = build_changelog_insert_sql("t", DatabaseType::SQLServer);
assert!(mssql.contains("[cascade]"), "SQL Server bracket-quotes columns: {mssql}");
let pg = build_changelog_insert_sql("t", DatabaseType::PostgreSQL);
assert!(pg.contains("\"cascade\""), "PostgreSQL double-quotes columns: {pg}");
}