#![cfg(feature = "sqlite")]
use std::path::{Path, PathBuf};
use std::process::Command;
const fn autumn_bin() -> &'static str {
env!("CARGO_BIN_EXE_autumn")
}
fn run_autumn(dir: &Path, args: &[&str], envs: &[(&str, &str)]) -> (String, String, Option<i32>) {
let output = Command::new(autumn_bin())
.args(args)
.current_dir(dir)
.envs(envs.iter().copied())
.output()
.expect("failed to run autumn");
(
String::from_utf8_lossy(&output.stdout).into_owned(),
String::from_utf8_lossy(&output.stderr).into_owned(),
output.status.code(),
)
}
fn run_autumn_ok(dir: &Path, args: &[&str], envs: &[(&str, &str)]) -> (String, String) {
let (stdout, stderr, code) = run_autumn(dir, args, envs);
assert_eq!(
code,
Some(0),
"autumn {args:?} failed (exit={code:?})\nstdout: {stdout}\nstderr: {stderr}",
);
(stdout, stderr)
}
fn fresh_project(name: &str) -> (tempfile::TempDir, PathBuf) {
let tmp = tempfile::tempdir().expect("tempdir");
run_autumn_ok(tmp.path(), &["new", name], &[]);
let project = tmp.path().join(name);
(tmp, project)
}
fn write_post_model(project: &Path) {
let src = project.join("src");
let _ = std::fs::remove_dir_all(src.join("models"));
std::fs::write(
src.join("models.rs"),
"#[autumn_web::model(managed)]\npub struct Post {\n #[id]\n pub id: i64,\n pub title: String,\n}\n",
)
.expect("write models.rs");
}
#[test]
fn schema_migrate_applies_to_a_sqlite_file_and_advances_snapshot_at_generation() {
let (_tmp, project) = fresh_project("sqlite_migrate_app");
let db_path = project.join("app.db");
let url = format!("sqlite://{}", db_path.display());
let envs = [("AUTUMN_DATABASE__URL", url.as_str())];
let snapshot_path = project.join(".autumn/schema-snapshot.json");
write_post_model(&project);
std::fs::write(project.join("empty_models.rs"), "").expect("empty models");
run_autumn_ok(
&project,
&[
"schema",
"snapshot",
"--from",
"empty_models.rs",
"--backend",
"sqlite",
],
&envs,
);
let (diff_out, _) = run_autumn_ok(
&project,
&[
"schema",
"diff",
"--write-migration",
"--name",
"create_posts",
],
&envs,
);
assert!(diff_out.contains("wrote migration"), "diff: {diff_out}");
assert!(
diff_out.contains("advanced schema snapshot"),
"diff --write-migration reports advancing the snapshot: {diff_out}"
);
let snap_after_generation = std::fs::read_to_string(&snapshot_path).expect("snapshot");
assert!(
snap_after_generation.contains("\"backend\": \"Sqlite\""),
"snapshot: {snap_after_generation}"
);
assert!(
snap_after_generation.contains("\"name\": \"posts\""),
"snapshot advanced at generation: {snap_after_generation}"
);
let (mig_out, _) = run_autumn_ok(&project, &["schema", "migrate"], &envs);
assert!(
mig_out.contains("Applied 1 migration(s)."),
"migrate stdout: {mig_out}"
);
assert!(
!mig_out.contains("refreshed schema snapshot"),
"migrate must NOT refresh the snapshot anymore: {mig_out}"
);
assert!(db_path.is_file(), "sqlite db file created");
let snap_after_apply = std::fs::read_to_string(&snapshot_path).expect("snapshot");
assert_eq!(
snap_after_generation, snap_after_apply,
"migrate must leave the snapshot byte-for-byte unchanged"
);
std::fs::write(
project.join("src/models.rs"),
"#[autumn_web::model(managed)]\npub struct Post {\n #[id]\n pub id: i64,\n pub title: String,\n pub body: Option<String>,\n}\n",
)
.expect("edit models.rs with ungenerated drift");
let (rerun_out, _) = run_autumn_ok(&project, &["schema", "migrate"], &envs);
assert!(
rerun_out.contains("Database already up to date."),
"second migrate: {rerun_out}"
);
assert!(
!rerun_out.contains("refreshed schema snapshot"),
"a no-op migrate must NOT refresh the snapshot: {rerun_out}"
);
let snap_after_noop = std::fs::read_to_string(&snapshot_path).expect("snapshot");
assert_eq!(
snap_after_apply, snap_after_noop,
"a no-op migrate must leave the snapshot byte-for-byte unchanged"
);
assert!(
!snap_after_noop.contains("body"),
"the ungenerated `body` edit must NOT be baked into the baseline: {snap_after_noop}"
);
let (diff_after, _) = run_autumn_ok(&project, &["schema", "diff"], &envs);
assert!(
!diff_after.contains("No schema changes"),
"drift must still be detected after the no-op migrate: {diff_after}"
);
assert!(
diff_after.contains("body"),
"the pending `body` column add still diffs: {diff_after}"
);
}