use super::package::packages; #[test] fn fixed_wire_budget_proof_retains_dynamic_checks_at_schema_limits() { for (levels, fields, input_fixed, output_fixed) in [ (63, 1, true, true), (64, 1, false, false), (14, 2, true, true), (15, 2, true, false), (16, 2, false, false), ] { let mut app = String::new(); let mut kind = "Int".to_owned(); for level in 0..levels { let members = if fields == 1 { format!("value: {kind}") } else { format!("first: {kind}\nsecond: {kind}") }; app.push_str(&format!("type Layer{level} {{ {members} }}\n")); kind = format!("Layer{level}"); } app.push_str(&format!( "run(value: {kind}) (answer: {kind}) {{ answer = value }}" )); let program = packages(&[ ("shop.account.app", &app), ( "shop.account.api", "cmd run = app.run\npublic post run = app.run", ), ]) .unwrap(); let emitted = dever::compiler::emit::application::generate(&program); assert_eq!( emitted.contains("wire::decode_fixed(input)"), input_fixed, "{levels}/{fields}" ); assert_eq!( emitted.contains("input.decode(dever_runtime::wire::decode_fixed)"), input_fixed, "HTTP {levels}/{fields}" ); assert_eq!( emitted.matches("wire::response_fixed(&result)").count(), usize::from(output_fixed), "{levels}/{fields}" ); assert_eq!( emitted.contains("wire::response_fixed_bytes(&result)"), output_fixed, "HTTP {levels}/{fields}" ); } for (kind, decoder, encoder) in [ ("List", "decode_shaped", "response"), ("Json", "decode_json", "response_with"), ("List", "decode", "response_with"), ("List?", "decode_shaped", "response"), ] { let app = format!("run(value: {kind}) (answer: {kind}) {{ answer = value }}"); let program = packages(&[ ("shop.account.app", &app), ( "shop.account.api", "cmd run = app.run\npublic post run = app.run", ), ]) .unwrap(); let emitted = dever::compiler::emit::application::generate(&program); if decoder == "decode_json" { assert!(emitted.contains("let args: Args0 = decode_args0(input)?;")); assert!(emitted.contains("input.decode(decode_httpargs0)")); assert_eq!(emitted.matches("wire::decode_json(input,").count(), 2); assert_eq!( emitted.contains("wire::decode(input)"), kind == "List" ); } else { assert!(!emitted.contains("wire::decode_json(")); assert!( emitted.contains(&format!("wire::{decoder}(input)")), "{kind}" ); assert!( emitted.contains(&format!("input.decode(dever_runtime::wire::{decoder})")), "{kind}" ); } assert!(emitted.contains(&format!("wire::{encoder}(")), "{kind}"); } } #[test] fn json_input_budget_includes_fixed_records_and_optional_json() { let program = packages(&[ ( "shop.account.app", "type Row { payload: Json?\nnote: Text? }\nrun(row: Row?, other: Json, label: Text) (answer: Row?, extra: Json, name: Text) { answer = row\nextra = other\nname = label }", ), ( "shop.account.api", "cmd run = app.run\npublic post run = app.run", ), ]) .unwrap(); let generated = dever::compiler::emit::application::generate(&program); assert_eq!(generated.matches("wire::decode_json(input,").count(), 2); assert!(generated.contains("budget.open()?; budget.value(3)?;")); assert!(generated.contains("budget.open()?; budget.value(2)?;")); assert!(generated.contains("if let Some(item) = (&(args).field0).as_ref()")); assert!(generated.contains("budget.json(item)?;")); assert!(generated.contains("budget.json(&(args).field1)?;")); assert!(!generated.contains("Json::from_validated_raw")); assert!(!generated.contains("Json::deserialize_validated")); } #[test] fn json_input_with_any_typed_list_keeps_whole_input_validation() { for kind in ["List", "List", "List?"] { for nested in [false, true] { let (definition, kind) = if nested { (format!("type Row {{ values: {kind} }}\n"), "Row?") } else { (String::new(), kind) }; let app = format!( "{definition}run(payload: Json, other: {kind}) (answer: Json, extra: {kind}) {{ answer = payload\nextra = other }}" ); let program = packages(&[ ("shop.account.app", &app), ( "shop.account.api", "cmd run = app.run\npublic post run = app.run", ), ]) .unwrap(); let generated = dever::compiler::emit::application::generate(&program); assert!(generated.contains("let args: Args0 = dever_runtime::wire::decode(input)?;")); assert!(generated.contains("input.decode(dever_runtime::wire::decode)")); assert!(!generated.contains("wire::decode_json(")); assert!(!generated.contains("budget.")); } } } #[test] fn variable_wire_depth_is_proved_before_skipping_validation() { for (levels, decoder) in [(63, "decode_shaped"), (64, "decode")] { let kind = format!("{}Int{}", "List<".repeat(levels), ">".repeat(levels)); let app = format!("run(value: {kind}) (answer: Int) {{ answer = length(value) }}"); let program = packages(&[ ("shop.account.app", &app), ( "shop.account.api", "cmd run = app.run\npublic post run = app.run", ), ]) .unwrap(); let emitted = dever::compiler::emit::application::generate(&program); assert!( emitted.contains(&format!("wire::{decoder}(input)")), "{levels}" ); assert!( emitted.contains(&format!("input.decode(dever_runtime::wire::{decoder})")), "{levels}" ); } } #[test] fn http_entries_bind_typed_apps_and_keep_protected_entries_closed() { assert!(packages(&[("main", "get(value: Int) (answer: Int) { answer = value }")]).is_ok()); let app = "echo(value: Int) (answer: Int) { answer = value }"; let program = packages(&[ ("shop.account.app", app), ( "shop.account.api.admin.session", "public get status = app.echo", ), ]) .unwrap(); assert_eq!(program.http[0].path, "/shop/account/admin/session/status"); assert_eq!(program.http[0].topic, "admin/session"); assert_eq!(program.http[0].method, "GET"); for (declaration, code) in [ ("get status = app.echo", "C014"), ("authenticated get status = app.echo", "C014"), ( "public get status = app.echo\npublic get status = app.echo", "C002", ), ("public get status = other.echo", "C006"), ] { assert_eq!( packages(&[("shop.account.app", app), ("shop.account.api", declaration)]) .unwrap_err() .code, code ); } assert_eq!( packages(&[ ( "shop.account.app", "echo(value: List) (answer: List) { answer = value }" ), ("shop.account.api", "public get status = app.echo") ]) .unwrap_err() .code, "C005" ); } #[test] fn command_wire_rejects_nested_constrained_records() { for kind in ["Port", "List", "Envelope"] { let app = format!( "type Port {{ value: Int >= 1 }}\ntype Envelope {{ ports: List }}\nrun(value: {kind}) (answer: {kind}) {{ answer = value }}" ); assert_eq!( packages(&[ ("shop.account.app", &app), ("shop.account.api", "cmd run = app.run") ]) .unwrap_err() .code, "C011" ); } } #[test] fn application_topics_share_app_contracts_and_domain_dependencies() { let program = packages(&[ ("shop.account.app.read", "type User { name: Text }\nread(name: Text) (answer: User) { answer = User { name = domain.label(name) } }"), ("shop.account.app.write", "echo(user: User) (answer: User) { answer = user }"), ("shop.account.domain", "label(name: Text) (answer: Text) { answer = name + \"!\" }"), ("shop.account.api", "cmd read = app.read\ncmd echo = app.echo"), ]).unwrap(); assert_eq!(program.commands.len(), 2); assert_eq!(program.commands[0].name, "shop.account.read"); assert!( program .functions .iter() .any(|function| function.signature.name == "label") ); } #[test] fn private_app_fields_share_topic_ownership_and_reject_other_domains() { let declaration = "type Session { private key: Text\nlabel: Text }"; assert!(packages(&[ ("shop.account.app.types", declaration), ("shop.account.app.write", "make(key: Text) (answer: Session) { answer = Session { key = key, label = \"name\" } }"), ("shop.account.app.read", "read(value: Session) (answer: Text) { value.key = value.key + \"!\"\nanswer = value.key }"), ]).is_ok()); for operation in [ "answer = value.key", "value.key = \"changed\"\nanswer = value.label", "value = shop.account.Session { key = \"hidden\", label = \"name\" }\nanswer = value.label", ] { let caller = format!("read(value: shop.account.Session) (answer: Text) {{ {operation} }}"); assert_eq!( packages(&[ ("shop.account.app", declaration), ("shop.other.app", &caller) ]) .unwrap_err() .code, "C006" ); } } #[test] fn command_wire_rejects_nested_private_records_in_both_directions() { for kind in ["Session", "List", "Envelope"] { for function in [ format!("run(value: {kind}) () {{}}"), format!("run(value: {kind}) (answer: {kind}) {{ answer = value }}"), ] { let app = format!( "type Session {{ private key: Text }}\ntype Envelope {{ sessions: List }}\n{function}" ); let error = packages(&[ ("shop.account.app", &app), ("shop.account.api", "cmd run = app.run"), ]) .unwrap_err(); assert_eq!(error.code, "C006", "{error:?}"); assert!(error.message.contains("wire boundary"), "{error:?}"); } } let app = "type Session { private key: Text }\nrun() (answer: Session) { answer = Session { key = \"hidden\" } }"; assert_eq!( packages(&[ ("shop.account.app", app), ("shop.account.api", "cmd run = app.run") ]) .unwrap_err() .code, "C006" ); } #[test] fn application_role_access_cannot_be_bypassed_with_qualified_names() { for (source, target) in [ ("shop.first.domain", "shop.second.run"), ("shop.first.domain", "app.run"), ("shop.first.app", "shop.second.domain.hidden"), ] { let caller = format!("entry() (answer: Int) {{ answer = {target}() }}"); let result = packages(&[ (source, &caller), ("shop.first.app.extra", "run() (answer: Int) { answer = 1 }"), ("shop.second.app", "run() (answer: Int) { answer = 2 }"), ( "shop.second.domain", "hidden() (answer: Int) { answer = 3 }", ), ]); assert_eq!(result.unwrap_err().code, "C006", "{source}: {target}"); } assert_eq!( packages(&[ ("shop.account.app", "type Visible { secret: domain.Hidden }"), ("shop.account.domain", "type Hidden { value: Int }"), ]) .unwrap_err() .code, "C006" ); assert_eq!( packages(&[("shop.account.app", "public run() () {}")]) .unwrap_err() .code, "C006" ); assert_eq!( packages(&[("shop.account.api", "run() () {}")]) .unwrap_err() .code, "C006" ); assert_eq!( packages(&[("main", "cmd run = app.run")]).unwrap_err().code, "C006" ); } #[test] fn command_secret_inputs_use_the_existing_input_codec_without_output_access() { for kind in ["Secret", "Secret?", "List"] { let app = format!("run(value: {kind}) (answer: Bool) {{ answer = true }}"); assert!( packages(&[ ("shop.account.app", &app), ("shop.account.api", "cmd run = app.run") ]) .is_ok() ); let leaking = format!("run(value: {kind}) (answer: {kind}) {{ answer = value }}"); assert!( packages(&[ ("shop.account.app", &leaking), ("shop.account.api", "cmd run = app.run") ]) .is_err() ); } } #[test] fn commands_reject_ambiguous_targets_and_invalid_wire_contracts() { for app in [ "run() () {}\nrun(value: Int) () {}", "run(action: handler() ()) () { action() }", "type State { Ready(value: Int) }\nrun(value: State) (answer: State) { answer = value }", "type State { Ready\n error Failed }\nrun(value: State) (answer: State) { answer = value }", ] { assert_eq!( packages(&[ ("shop.account.app", app), ("shop.account.api", "cmd run = app.run") ]) .unwrap_err() .code, "C005" ); } assert_eq!( packages(&[ ("shop.account.app", "run() () {}"), ("shop.account.api", "cmd run = app.run\ncmd run = app.run"), ]) .unwrap_err() .code, "C002" ); assert_eq!( packages(&[ ("shop.account.app", "run() () {}"), ("shop.account.api", "cmd run = shop.account.run"), ]) .unwrap_err() .code, "C006" ); } #[test] fn application_entry_reachability_and_domain_cycles_are_checked() { assert_eq!( packages(&[ ("shop.account.app", "run() () {}\nunused() () {}"), ("shop.account.api", "cmd run = app.run"), ]) .unwrap_err() .code, "C016" ); assert_eq!(packages(&[ ("shop.first.app", "run() (answer: Int) { answer = second.read() }\nread() (answer: Int) { answer = 1 }"), ("shop.second.app", "read() (answer: Int) { answer = first.read() }"), ("shop.first.api", "cmd run = app.run"), ]).unwrap_err().code, "C007"); assert_eq!( packages(&[ ("shop.account.app.read", "run() () {}"), ("shop.account.app.write", "run() () {}"), ]) .unwrap_err() .code, "C013" ); }