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

110 lines
4.1 KiB
Rust

use super::{checked, package::packages};
#[test]
fn api_snapshot_uses_declared_contracts_not_private_bodies_or_inferred_constants() {
let snapshot = |body: &str| {
checked(&format!("public value(input: Int) (answer: Int) {{ answer = helper(input) }}\nhelper(input: Int) (answer: Int) {{ {body} }}")).unwrap().api_snapshot()
};
let first = snapshot("answer = 1");
assert_eq!(
first,
"dever-api 5\nfunction main.value(Int) (answer: Int []) pure=false recover=None effects=[] errors=[] recoveries={}\n"
);
assert_eq!(first, snapshot("answer = input + 2"));
let constrained =
checked("public value(input: Int) (answer: Int >= 0) { answer = 1 }").unwrap();
assert!(
constrained
.api_snapshot()
.contains("answer: Int [0..9223372036854775807]")
);
let difference = constrained.check_api(&first).unwrap_err();
assert!(
difference.contains("- function main.value")
&& difference.contains("+ function main.value")
);
}
#[test]
fn api_snapshot_tracks_public_shape_construction_failure_and_transitive_recovery() {
let snapshot = |key: &str| {
checked(&format!(
"public type Session {{ private key: {key}\nlabel: Text }}"
))
.unwrap()
.api_snapshot()
};
let private = snapshot("Int");
assert_eq!(private, snapshot("Text"));
assert_eq!(
private,
"dever-api 5\ntype main.Session record construct=private {label: Text []}\n"
);
let exposed = checked("public type Session { key: Int\nlabel: Text }").unwrap();
assert!(
exposed
.check_api(&private)
.unwrap_err()
.contains("construct=public")
);
let failing = checked("public type Problem { error Bad(message: Text) }\npublic value() () { fail(Problem.Bad(\"bad\")) }").unwrap();
let baseline = failing.api_snapshot();
assert!(baseline.contains("errors=[main.Problem.Bad]"));
assert!(baseline.contains("type main.Problem error Bad(message: Text [])"));
let snapshot = |reason: &str| {
checked(&format!(
"public value() () {{ helper() }}\nhelper() () recover(\"{reason}\") {{}}"
))
.unwrap()
.api_snapshot()
};
assert_ne!(snapshot("use zero"), snapshot("use one"));
assert!(snapshot("use zero").contains("recoveries={\"use zero\"}"));
}
#[test]
fn api_snapshot_is_stable_across_app_topics_and_binds_command_inputs() {
let snapshot = |topic: &str, input: &str| {
packages(&[
(
topic,
&format!("value({input}: Int) (answer: Int) {{ answer = {input} }}"),
),
("shop.account.api", "cmd value = app.value"),
])
.unwrap()
.api_snapshot()
};
assert_eq!(
snapshot("shop.account.app", "input"),
snapshot("shop.account.app.read", "input")
);
assert_ne!(
snapshot("shop.account.app", "input"),
snapshot("shop.account.app", "renamed")
);
assert!(
snapshot("shop.account.app", "input")
.contains("cmd shop.account.value = shop.account.value(input: Int [])")
);
let program = checked("public apply(action: handler(input: Int) (answer: Int), input: Int) (answer: Int) { answer = action(input) }").unwrap();
assert!(
program
.api_snapshot()
.contains("function main.apply(handler(Int [])(answer: Int []), Int)")
);
}
#[test]
fn public_handler_snapshot_keeps_constraints_and_ignores_specializations() {
let source = "public apply(action: handler(input: Int >= 0) (answer: Int >= 0)) (answer: Int >= 0) { answer = action(1) }";
let original = checked(source).unwrap().api_snapshot();
assert!(
original.contains(
"handler(Int [0..9223372036854775807])(answer: Int [0..9223372036854775807])"
)
);
let called = checked(&format!("{source}\nnonnegative(input: Int) (answer: Int >= 0) {{ answer = 0 }}\nmain() (answer: Int) {{ answer = apply(nonnegative) }}")).unwrap();
assert_eq!(original, called.api_snapshot());
}