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

104 lines
4.3 KiB
Rust

use super::{APP, ERROR, packages};
const CLOCK: &str = "public type ClockResult { Read(value: Int)\nerror Failed(message: Text) }\npublic unixMillis() (value: Int) { value = read(dever.system.timeUnixMillis()) }\nread(result: ClockResult.Read(number)) (value: Int) { value = number }\nread(result: ClockResult.Failed(message)) (value: Int) { fail(ClockResult.Failed(message)) }\npublic type SleepResult { Done\nerror Failed(message: Text) }\npublic sleep(millis: Int) () { slept(dever.system.timeSleep(millis)) }\nslept(result: SleepResult.Done) () {}\nslept(result: SleepResult.Failed(message)) () { fail(SleepResult.Failed(message)) }";
const TASK: &str = "public sleep(millis: Int) () { slept(dever.system.taskSleep(millis)) }\nslept(result: dever.time.SleepResult.Done) () {}\nslept(result: dever.time.SleepResult.Failed(message)) () { fail(dever.time.SleepResult.Failed(message)) }";
#[test]
fn provider_expiration_reads_clock_but_cannot_wait() {
for (operation, error) in [
("current = dever.time.unixMillis()", None),
("dever.time.sleep(1)\ncurrent = 0", Some("C013")),
("dever.task.sleep(1)\ncurrent = 0", Some("C014")),
] {
let app = APP
.replace(
"account = model.first",
&format!("{operation}\naccount = model.first"),
)
.replace(
"session == claims.session",
"session == claims.session and expires_at > current",
);
let result = packages(&[
("dever.database", ERROR),
("dever.api", "public type Error { error Unauthorized }"),
(
"dever.auth",
"public type Claims { subject: Text\nsession: Text\ntenant: Text?\nsite: Text }",
),
("dever.time", CLOCK),
("dever.task", TASK),
(
"demo.account.model",
"global type Account { subject: Text\nsession: Text\nexpires_at: Int }",
),
("demo.account.app", &app),
("demo.account.api", "public get health = app.health"),
]);
match error {
Some(code) => assert_eq!(result.unwrap_err().code, code, "{operation}"),
None => {
let program = result.unwrap();
let effects = program.functions[program.verifiers[0].function]
.signature
.effects;
assert!(effects.clock);
assert!(!effects.timer);
}
}
}
}
#[test]
fn shipped_session_expiration_contract_checks_without_dropping_the_deadline() {
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../skills/assets/examples/http-rest");
let project = dever::compiler::project::Project::load(&root).unwrap();
let program = project.check().unwrap();
assert!(
program
.verifiers
.iter()
.any(|verifier| { program.functions[verifier.function].signature.effects.clock })
);
}
#[test]
fn provider_checks_every_model_loaded_by_a_relation() {
let model =
"global type Account { subject: Text\nsession: Text\nprofile_id: demo.profile.model.id }";
let app = APP.replace(
"session == claims.session })",
"session == claims.session, with = [profile] })",
);
for global in [true, false] {
let profile = format!(
"{}type Profile {{ name: Text }}",
if global { "global " } else { "" }
);
let result = packages(&[
("dever.database", ERROR),
("dever.api", "public type Error { error Unauthorized }"),
(
"dever.auth",
"public type Claims { subject: Text\nsession: Text\ntenant: Text?\nsite: Text }",
),
("demo.account.model", model),
("demo.profile.model", &profile),
("demo.account.app", &app),
("demo.account.api", "public get health = app.health"),
]);
if global {
result.unwrap();
} else {
let error = result.unwrap_err();
assert_eq!(error.code, "C014");
assert!(
error
.message
.contains("Provider verify cannot read a tenant Model")
);
}
}
}