feat: harden community production foundation through story 1.5
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
use std::process::{Command, Output};
|
||||
|
||||
fn command(arguments: &[&str], database_url: Option<&str>) -> Output {
|
||||
let mut command = Command::new(env!("CARGO_BIN_EXE_crank-migrate"));
|
||||
command.args(arguments);
|
||||
command.current_dir(std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../.."));
|
||||
for (name, _) in std::env::vars() {
|
||||
if name.starts_with("CRANK_") || name.starts_with("POSTGRES_") || name.starts_with("OTEL_")
|
||||
{
|
||||
command.env_remove(name);
|
||||
}
|
||||
}
|
||||
if let Some(database_url) = database_url {
|
||||
command.env("CRANK_DATABASE_URL", database_url);
|
||||
}
|
||||
command.output().expect("migration command must run")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plan_is_deterministic_and_committed_contract_is_current() {
|
||||
let first = command(&["plan"], None);
|
||||
let second = command(&["plan"], None);
|
||||
assert!(
|
||||
first.status.success(),
|
||||
"{}",
|
||||
String::from_utf8_lossy(&first.stderr)
|
||||
);
|
||||
assert_eq!(first.stdout, second.stdout);
|
||||
let plan: serde_json::Value = serde_json::from_slice(&first.stdout).unwrap();
|
||||
assert_eq!(plan["sequence"].as_array().unwrap().len(), 3);
|
||||
|
||||
let checked = command(&["plan", "--check"], None);
|
||||
assert!(
|
||||
checked.status.success(),
|
||||
"{}",
|
||||
String::from_utf8_lossy(&checked.stderr)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_command_is_bounded_and_does_not_echo_arguments() {
|
||||
let canary = "secret-command-canary";
|
||||
let output = command(&[canary], None);
|
||||
assert!(!output.status.success());
|
||||
let stderr = String::from_utf8(output.stderr).unwrap();
|
||||
assert!(stderr.len() < 1_024);
|
||||
assert!(!stderr.contains(canary));
|
||||
let diagnostic: serde_json::Value = serde_json::from_str(stderr.trim()).unwrap();
|
||||
assert_eq!(diagnostic["code"], "invalid_command");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn database_only_config_can_apply_and_preflight_a_fresh_schema() {
|
||||
let database_url = crank_test_support::postgres_schema_url("test_migration_command").await;
|
||||
let applied = command(&["apply"], Some(&database_url));
|
||||
assert!(
|
||||
applied.status.success(),
|
||||
"{}",
|
||||
String::from_utf8_lossy(&applied.stderr)
|
||||
);
|
||||
let result: serde_json::Value = serde_json::from_slice(&applied.stdout).unwrap();
|
||||
assert_eq!(result["status"], "applied");
|
||||
|
||||
let preflight = command(&["preflight"], Some(&database_url));
|
||||
assert!(
|
||||
preflight.status.success(),
|
||||
"{}",
|
||||
String::from_utf8_lossy(&preflight.stderr)
|
||||
);
|
||||
let result: serde_json::Value = serde_json::from_slice(&preflight.stdout).unwrap();
|
||||
assert_eq!(result["status"], "current");
|
||||
assert_eq!(result["version"], 3);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn migration_error_json_preserves_affected_version() {
|
||||
let database_url = crank_test_support::postgres_schema_url("test_migration_cli_version").await;
|
||||
assert!(command(&["apply"], Some(&database_url)).status.success());
|
||||
let pool = sqlx::PgPool::connect(&database_url).await.unwrap();
|
||||
sqlx::query("update __crank_migrations set checksum = 'tampered' where version = 2")
|
||||
.execute(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let output = command(&["preflight"], Some(&database_url));
|
||||
assert!(!output.status.success());
|
||||
let diagnostic: serde_json::Value = serde_json::from_slice(&output.stderr).unwrap();
|
||||
assert_eq!(diagnostic["code"], "checksum_mismatch");
|
||||
assert_eq!(diagnostic["version"], 2);
|
||||
}
|
||||
Reference in New Issue
Block a user