Initial Dever 0.0.1

This commit is contained in:
Codex committed 2026-11-03 03:32:07 +00:00
commit 81233d098c
1479 files changed
+163782

No files matched your search

+112
View File
@@ -0,0 +1,112 @@
use super::{ERROR, packages};
#[test]
fn text_policy_is_a_single_model_modifier_and_defaults_use_the_normalized_length() {
use dever::compiler::model::persistence::{TextMode, Value};
let program = packages(&[("demo.account.model", "type Account { name: Text(1, 2) lower default \" Ä \"\ncode: Text(2) upper default \" ß \"\nbody: Text raw default \" a \"\nnote: Text? }")]).unwrap();
let columns = &program.models[0].columns;
for (column, mode, expected) in [
(2, TextMode::Lower, "ä"),
(3, TextMode::Upper, "SS"),
(4, TextMode::Raw, " a "),
] {
assert_eq!(columns[column].text, mode);
assert!(matches!(&columns[column].default, Some(Value::Text(value)) if value == expected));
}
assert_eq!(columns[5].text, TextMode::Trim);
for field in [
"name: Text raw lower",
"name: Text lower upper",
"name: Text raw raw",
"name: Int lower",
"name: List<Text> raw",
"name: Text(1, 2) default \" \"",
"name: Text(1) upper default \"ß\"",
"name: Text trim",
] {
assert!(
packages(&[("demo.account.model", &format!("type Account {{ {field} }}"))]).is_err(),
"{field}"
);
}
assert!(packages(&[("demo.account.domain", "type Input { name: Text raw }")]).is_err());
let seeded = packages(&[(
"demo.account.model",
"type Account { name: Text(1, 2) lower unique }\nseed { { name = \" Ä \" } }",
)])
.unwrap();
assert!(matches!(&seeded.models[0].seeds[0].values[0].1, Value::Text(value) if value == "ä"));
}
#[test]
fn model_choice_options_preserve_labels_and_order() {
let program = packages(&[
("demo.account.model", "type Status { Active = \"启用\"\nDisabled = \"禁用\" }\ntype Account { status: Status }"),
("demo.account.app", "options() (values: Map<model.Status, Text>) { values = model.Status.options }"),
]).unwrap();
assert!(!program.functions[0].signature.effects.database);
assert!(packages(&[("main", "type Status { Active }\npublic main() (values: Map<Text, Text>) { values = Status.options }")]).is_err());
}
#[test]
fn checked_defaults_and_generated_columns_keep_static_types() {
let program = packages(&[
("dever.database", ERROR),
("demo.account.model", "type Status { Active = \"启用\"\nDisabled = \"禁用\" }\ntype Account { uid: Uuid generated unique\nstatus: Status default Status.Active\namount: Decimal(5, 2) default -12.30\nnote: Text? default null\nname: Text(1, 8) default \"雪\" }"),
("demo.account.app", "create() (id: model.id) { row = model.create({})\nid = row.id }"),
("demo.account.api", "cmd create = app.create"),
]).unwrap();
let columns = &program.models[0].columns;
assert!(columns[2].generated);
assert_eq!(
columns[3].choice.as_ref().unwrap(),
&[
("Active".into(), "启用".into()),
("Disabled".into(), "禁用".into())
]
);
assert!(
matches!(&columns[4].default, Some(dever::compiler::model::persistence::Value::Text(value)) if value == "098770")
);
}
#[test]
fn invalid_defaults_and_generated_writes_fail_before_database_access() {
for field in [
"value: Decimal(5, 2) default 0.001",
"value: Decimal(5, 2) default 1000",
"value: Text(2) default \"三字符\"",
"value: Int default 1.1",
"value: Int default 9223372036854775808",
"value: Float default 1e999",
"value: Bool default null",
"value: Uuid default \"broken\"",
"value: Date default \"2025-02-29\"",
"value: Time default \"24:00:00\"",
"value: Json default \"{\\\"x\\\":1,\\\"x\\\":2}\"",
"value: Int generated",
"value: Uuid? generated",
"value: Uuid generated default \"00112233-4455-6677-8899-aabbccddeeff\"",
] {
let model = format!("type Account {{ {field} }}");
let error = packages(&[("demo.account.model", &model)]).unwrap_err();
assert_eq!(error.code, "C014", "{field}: {error:?}");
}
for body in [
"row = model.create({ uid = uid })",
"row = model.update(id, { uid = uid })",
"row = model.get(id)\nrow.uid = uid",
] {
let app = format!(
"write(id: model.id, uid: Uuid) (value: model.id) {{ {body}\nvalue = row.id }}"
);
let error = packages(&[
("dever.database", ERROR),
("demo.account.model", "type Account { uid: Uuid generated }"),
("demo.account.app", &app),
])
.unwrap_err();
assert!(matches!(error.code, "C014" | "C005"), "{body}: {error:?}");
assert!(error.message.contains("read-only"), "{error:?}");
}
}