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 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) { values = model.Status.options }"), ]).unwrap(); assert!(!program.functions[0].signature.effects.database); assert!(packages(&[("main", "type Status { Active }\npublic main() (values: Map) { 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:?}"); } }