Initial Dever 0.0.1
This commit is contained in:
commit
81233d098c
1479 files changed
+163782
No files matched your search
@@ -0,0 +1,208 @@
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn generated_uuid_is_available_when_entries_only_return_counts() {
|
||||
let directory = Directory::new();
|
||||
fixture_defaults(&directory.0);
|
||||
let source = directory.0.join("module/demo/account");
|
||||
std::fs::write(
|
||||
source.join("app.dever"),
|
||||
r#"seed() (total: Int) {
|
||||
total = model.createMany([{}, {}])
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
std::fs::write(
|
||||
source.join("api.dever"),
|
||||
r#"cmd seed = app.seed
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
let project = Project::load(&directory.0).unwrap();
|
||||
let program = project.check().unwrap();
|
||||
let binary = build::build(
|
||||
&program,
|
||||
&project.sources,
|
||||
"main",
|
||||
&directory.0.join("counts"),
|
||||
)
|
||||
.unwrap();
|
||||
std::fs::create_dir(directory.0.join("config")).unwrap();
|
||||
std::fs::write(
|
||||
directory.0.join("config/setting.json"),
|
||||
r#"{"database":{"default":{"type":"sqlite","path":"counts.db"}}}"#,
|
||||
)
|
||||
.unwrap();
|
||||
let output = execute(&binary, "demo.account.seed", "{}");
|
||||
assert!(
|
||||
output.status.success(),
|
||||
"{}",
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::from_slice::<serde_json::Value>(&output.stdout).unwrap()["data"],
|
||||
2
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn database_defaults_choice_and_generated_uuid_survive_native_roundtrips() {
|
||||
let directory = Directory::new();
|
||||
verify_defaults(
|
||||
&directory,
|
||||
serde_json::json!({"type":"sqlite","path":"data/defaults.db","max_connections":1,"max_page_size":20}),
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "postgres")]
|
||||
#[test]
|
||||
#[ignore = "requires explicit isolated config/setting.json database.postgres_test"]
|
||||
fn postgres_dever_defaults_choice_and_generated_uuid_survive_native_roundtrips() {
|
||||
let directory = Directory::new();
|
||||
let schema = super::postgres::Schema::new(
|
||||
&directory.0,
|
||||
"dever_native_defaults",
|
||||
"CREATE SCHEMA dever_native_defaults",
|
||||
"DROP SCHEMA dever_native_defaults CASCADE",
|
||||
);
|
||||
verify_defaults(&directory, schema.connection.clone());
|
||||
}
|
||||
|
||||
fn verify_defaults(directory: &Directory, connection: serde_json::Value) {
|
||||
fixture_defaults(&directory.0);
|
||||
let project = Project::load(&directory.0).unwrap();
|
||||
let program = project.check().unwrap();
|
||||
let binary = build::build(
|
||||
&program,
|
||||
&project.sources,
|
||||
"main",
|
||||
&directory.0.join("defaults"),
|
||||
)
|
||||
.unwrap();
|
||||
std::fs::create_dir(directory.0.join("config")).unwrap();
|
||||
std::fs::write(
|
||||
directory.0.join("config/setting.json"),
|
||||
serde_json::to_vec(&serde_json::json!({"database":{"default":connection}})).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
let run = |name: &str, input: &str| {
|
||||
let output = execute(&binary, &format!("demo.account.{name}"), input);
|
||||
assert!(
|
||||
output.status.success(),
|
||||
"{name}: {}",
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
serde_json::from_slice::<serde_json::Value>(&output.stdout).unwrap()["data"].clone()
|
||||
};
|
||||
let first = run("create", "{}");
|
||||
assert_eq!(first["name"], "O'Reilly");
|
||||
assert_eq!(
|
||||
dever_runtime::value::decimal::Decimal::parse(first["amount"].as_str().unwrap()).unwrap(),
|
||||
dever_runtime::value::decimal::Decimal::parse("12.30").unwrap()
|
||||
);
|
||||
assert_eq!(first["active"], true);
|
||||
assert_eq!(first["enabled"], true);
|
||||
assert_eq!(first["note"], "default note");
|
||||
assert_eq!(first["day"], "2000-02-29");
|
||||
assert_eq!(first["time"], "23:59:59.999");
|
||||
assert_eq!(first["elapsed"], -1);
|
||||
assert_eq!(first["document"], serde_json::json!({"enabled":true}));
|
||||
assert_eq!(first["empty"], serde_json::Value::Null);
|
||||
let uuid = first["uid"].as_str().unwrap();
|
||||
assert_eq!(&uuid[14..15], "7");
|
||||
assert!(matches!(&uuid[19..20], "8" | "9" | "a" | "b"));
|
||||
assert_eq!(run("get", r#"{"key":1}"#), first);
|
||||
let second = run("override", r#"{"value":null}"#);
|
||||
assert!(second["note"].is_null());
|
||||
assert_eq!(second["active"], false);
|
||||
assert_ne!(first["uid"], second["uid"]);
|
||||
assert_eq!(run("get", r#"{"key":2}"#), second);
|
||||
let failed = execute(
|
||||
&binary,
|
||||
"demo.account.explode",
|
||||
r#"{"value":9223372036854775807}"#,
|
||||
);
|
||||
assert!(!failed.status.success());
|
||||
assert!(String::from_utf8_lossy(&failed.stderr).contains("integer arithmetic fault"));
|
||||
assert_eq!(run("count", "{}"), 2);
|
||||
assert_eq!(run("option", "{}"), "启用");
|
||||
}
|
||||
|
||||
pub(super) fn fixture_defaults(root: &Path) {
|
||||
let root = root.join("module/demo/account");
|
||||
std::fs::create_dir_all(&root).unwrap();
|
||||
let fields = [
|
||||
("uid", "Uuid", "generated unique"),
|
||||
("name", "Text(1, 20)", "default \"O'Reilly\""),
|
||||
("amount", "Decimal(5, 2)", "default 12.30"),
|
||||
("status", "Status", "default Status.Active"),
|
||||
("enabled", "Bool", "default true"),
|
||||
("note", "Text?", "default \"default note\""),
|
||||
("day", "Date", "default \"2000-02-29\""),
|
||||
("time", "Time", "default \"23:59:59.999\""),
|
||||
("elapsed", "Duration", "default -1"),
|
||||
("document", "Json", "default \"{\\\"enabled\\\":true}\""),
|
||||
("empty", "Int?", "default null"),
|
||||
];
|
||||
let fields = fields
|
||||
.iter()
|
||||
.map(|(name, kind, metadata)| format!("{name}: {kind} {metadata}\n"))
|
||||
.collect::<String>();
|
||||
std::fs::write(root.join("model.dever"), format!("type Status {{ Active = \"启用\"\nDisabled = \"禁用\" }}\n\ntype Account {{\n{fields}}}\n")).unwrap();
|
||||
let outputs = "id: model.id, uid: Uuid, name: Text, amount: Decimal, active: Bool, enabled: Bool, note: Text?, day: Date, time: Time, elapsed: Duration, document: Json, empty: Int?";
|
||||
let view = outputs
|
||||
.split(", ")
|
||||
.map(|output| {
|
||||
let field = output.split_once(':').unwrap().0;
|
||||
if field == "active" {
|
||||
"active = row.status == model.Status.Active\n".into()
|
||||
} else {
|
||||
format!("{field} = row.{field}\n")
|
||||
}
|
||||
})
|
||||
.collect::<String>();
|
||||
let methods = [
|
||||
(
|
||||
"create",
|
||||
"",
|
||||
outputs,
|
||||
format!("row = model.create({{}})\n{view}"),
|
||||
),
|
||||
(
|
||||
"get",
|
||||
"key: model.id",
|
||||
outputs,
|
||||
format!("row = model.get(key)\n{view}"),
|
||||
),
|
||||
(
|
||||
"override",
|
||||
"value: Text?",
|
||||
outputs,
|
||||
format!(
|
||||
"row = model.create({{ note = value, status = model.Status.Disabled }})\n{view}"
|
||||
),
|
||||
),
|
||||
(
|
||||
"explode",
|
||||
"value: Int",
|
||||
"id: model.id, result: Int",
|
||||
"row = model.create({})\nid = row.id\nresult = value + 1".into(),
|
||||
),
|
||||
("count", "", "total: Int", "total = model.count()".into()),
|
||||
(
|
||||
"option",
|
||||
"",
|
||||
"label: Text?",
|
||||
"choices = model.Status.options\nlabel = get(choices, model.Status.Active)".into(),
|
||||
),
|
||||
];
|
||||
let mut app = String::new();
|
||||
let mut api = String::new();
|
||||
for (name, inputs, outputs, body) in methods {
|
||||
app.push_str(&format!("{name}({inputs}) ({outputs}) {{\n{body}\n}}\n"));
|
||||
api.push_str(&format!("cmd {name} = app.{name}\n"));
|
||||
}
|
||||
std::fs::write(root.join("app.dever"), app).unwrap();
|
||||
std::fs::write(root.join("api.dever"), api).unwrap();
|
||||
}
|
||||
Reference in new issue
Block a user