Initial Dever 0.0.1
This commit is contained in:
commit
81233d098c
1479 files changed
+163782
No files matched your search
@@ -0,0 +1,116 @@
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn database_migrations_preserve_data_and_reject_loss_or_changed_history() {
|
||||
let directory = Directory::new();
|
||||
defaults::fixture_defaults(&directory.0);
|
||||
let source = directory.0.join("module/demo/account");
|
||||
let original_model = std::fs::read_to_string(source.join("model.dever"))
|
||||
.unwrap()
|
||||
.replace(
|
||||
"type Account {\n",
|
||||
"type Account {\nlegacy: Text default \"old\"\n",
|
||||
);
|
||||
std::fs::write(source.join("model.dever"), &original_model).unwrap();
|
||||
let original = compile(&directory.0, "original");
|
||||
settings(&directory.0, "data/account.db");
|
||||
let run = |binary: &Path, name: &str, input: &str| {
|
||||
let result = execute(binary, &format!("demo.account.{name}"), input);
|
||||
assert!(
|
||||
result.status.success(),
|
||||
"{name}: {}",
|
||||
String::from_utf8_lossy(&result.stderr)
|
||||
);
|
||||
serde_json::from_slice::<serde_json::Value>(&result.stdout).unwrap()["data"].clone()
|
||||
};
|
||||
let first = run(&original, "create", "{}");
|
||||
assert_eq!(first["name"], "O'Reilly");
|
||||
let target = original_model
|
||||
.replace("legacy: Text default \"old\"\n", "")
|
||||
.replace("name: Text(1, 20)", "display_name: Text(1, 8)")
|
||||
.replace(
|
||||
"default \"O'Reilly\"",
|
||||
"default \"O'Reilly\" from name unique",
|
||||
)
|
||||
.replace("Decimal(5, 2)", "Decimal(7, 3)")
|
||||
+ UPGRADE;
|
||||
std::fs::write(source.join("model.dever"), &target).unwrap();
|
||||
let app = std::fs::read_to_string(source.join("app.dever"))
|
||||
.unwrap()
|
||||
.replace("row.name", "row.display_name");
|
||||
std::fs::write(source.join("app.dever"), app).unwrap();
|
||||
let upgraded = compile(&directory.0, "upgraded");
|
||||
let next = run(&upgraded, "get", r#"{"key":1}"#);
|
||||
assert_eq!(next["name"], "Final");
|
||||
assert_eq!(next["uid"], first["uid"]);
|
||||
assert_eq!(next["amount"], first["amount"]);
|
||||
assert_eq!(run(&upgraded, "count", "{}"), 2);
|
||||
assert_eq!(run(&upgraded, "get", r#"{"key":2}"#)["name"], "Seed");
|
||||
assert_eq!(run(&upgraded, "count", "{}"), 2);
|
||||
assert!(
|
||||
!execute(&original, "demo.account.count", "{}")
|
||||
.status
|
||||
.success()
|
||||
);
|
||||
std::fs::write(
|
||||
source.join("model.dever"),
|
||||
target.replace("[\"Final\",\"Editor\"]", "[\"Changed\",\"Editor\"]"),
|
||||
)
|
||||
.unwrap();
|
||||
let changed = compile(&directory.0, "changed");
|
||||
let rejected = execute(&changed, "demo.account.count", "{}");
|
||||
assert!(!rejected.status.success());
|
||||
assert!(
|
||||
String::from_utf8_lossy(&rejected.stderr)
|
||||
.contains("applied migration content or order changed")
|
||||
);
|
||||
assert_eq!(run(&upgraded, "get", r#"{"key":1}"#), next);
|
||||
let narrow = target
|
||||
.replace("Text(1, 8)", "Text(1, 4)")
|
||||
.replace("default \"O'Reilly\"", "default \"Ok\"");
|
||||
std::fs::write(source.join("model.dever"), narrow).unwrap();
|
||||
let narrowed = compile(&directory.0, "narrowed");
|
||||
assert!(
|
||||
!execute(&narrowed, "demo.account.count", "{}")
|
||||
.status
|
||||
.success()
|
||||
);
|
||||
assert_eq!(run(&upgraded, "get", r#"{"key":1}"#), next);
|
||||
assert_eq!(run(&upgraded, "count", "{}"), 2);
|
||||
settings(&directory.0, "data/fresh.db");
|
||||
// 空库的最终表没有历史 name/legacy;执行旧数据 before 会失败,因此验证确实跳过。
|
||||
assert_eq!(run(&upgraded, "count", "{}"), 1);
|
||||
assert_eq!(run(&upgraded, "get", r#"{"key":1}"#)["name"], "Seed");
|
||||
}
|
||||
|
||||
fn compile(root: &Path, name: &str) -> std::path::PathBuf {
|
||||
let project = Project::load(root).unwrap();
|
||||
let program = project.check().unwrap();
|
||||
build::build(&program, &project.sources, "main", &root.join(name)).unwrap()
|
||||
}
|
||||
|
||||
fn settings(root: &Path, path: &str) {
|
||||
std::fs::create_dir_all(root.join("config")).unwrap();
|
||||
std::fs::write(root.join("config/setting.json"),format!(r#"{{"database":{{"default":{{"type":"sqlite","path":"{path}","max_connections":1,"max_page_size":20}}}}}}"#)).unwrap();
|
||||
}
|
||||
|
||||
const UPGRADE: &str = r#"index(display_name, created_at)
|
||||
|
||||
migrate normalize {
|
||||
drop legacy
|
||||
before {
|
||||
sqlite = "UPDATE demo_account_model SET name = ?1 WHERE name = ?2"
|
||||
postgres = "UPDATE demo_account_model SET name = $1 WHERE name = $2"
|
||||
parameters = ["Editor","O'Reilly"]
|
||||
}
|
||||
after {
|
||||
sqlite = "UPDATE demo_account_model SET display_name = ?1 WHERE display_name = ?2"
|
||||
postgres = "UPDATE demo_account_model SET display_name = $1 WHERE display_name = $2"
|
||||
parameters = ["Final","Editor"]
|
||||
}
|
||||
}
|
||||
|
||||
seed {
|
||||
{ display_name = "Seed", amount = 1 }
|
||||
}
|
||||
"#;
|
||||
Reference in new issue
Block a user