Files
core/test/rust/compiler/application/mod.rs
T
2026-11-03 03:32:07 +00:00

443 lines
15 KiB
Rust

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<Int>", "decode_shaped", "response"),
("Json", "decode_json", "response_with"),
("List<Json>", "decode", "response_with"),
("List<Text>?", "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<Json>"
);
} 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<Int>", "List<Json>", "List<Json?>?"] {
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<Int>) (answer: List<Int>) { 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<Port?>", "Envelope"] {
let app = format!(
"type Port {{ value: Int >= 1 }}\ntype Envelope {{ ports: List<Port?> }}\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<Session?>", "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<Session?> }}\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<Secret>"] {
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"
);
}