Initial Dever 0.0.1

This commit is contained in:
Codex committed 2026-11-03 03:32:07 +00:00
commit 81233d098c
1479 files changed
+163782

No files matched your search

+103
View File
@@ -0,0 +1,103 @@
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")
);
}
}
}
+67
View File
@@ -0,0 +1,67 @@
use super::{database::ERROR, package::packages};
use dever::{compiler::model::Program, syntax::diagnostic::Diagnostic};
#[path = "../authorization/mod.rs"]
mod capability;
mod effects;
mod request;
mod rest;
const APP: &str = "type Identity { id: Text\nuser_id: model.id?\ntenant_id: model.id? }\nverify(claims: dever.auth.Claims) (identity: Identity) { account = model.first({ where = subject == claims.subject and session == claims.session })\nidentity = identity(account) }\nidentity(account: model.Account) (identity: Identity) { identity = Identity { id = account.subject, user_id = account.id, tenant_id = null } }\nidentity(account: null) (identity: Identity) { fail(dever.api.Error.Unauthorized) }\nhealth() (value: Bool) { value = true }";
fn checked(model: &str, app: &str) -> Result<Program, Diagnostic> {
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.account.app", app),
("demo.account.api", "public get health = app.health"),
])
}
#[test]
fn provider_identity_is_a_compiled_app_entry_with_global_reads() {
let program = checked("global type Account { subject: Text\nsession: Text }", APP).unwrap();
let verifier = &program.verifiers[0];
assert_eq!(verifier.name, "demo.account.verify");
assert!(program.models[0].global);
assert!(program.entries().any(|entry| entry == verifier.function));
assert!(program.api_snapshot().contains("global=true"));
assert_eq!(
checked("type Account { subject: Text\nsession: Text }", APP)
.unwrap_err()
.code,
"C014"
);
assert_eq!(
packages(&[("main", "global type Value { number: Int }")])
.unwrap_err()
.code,
"C006"
);
}
#[test]
fn provider_cannot_write_or_replace_nominal_identity_with_permissions() {
let model = "global type Account { subject: Text\nsession: Text }";
let writing = APP.replace(
"model.first({ where = subject == claims.subject and session == claims.session })",
"model.create({ subject = claims.subject, session = claims.session })",
);
assert_eq!(checked(model, &writing).unwrap_err().code, "C014");
let invalid = APP
.replace("user_id: model.id?", "user_id: Int?")
.replace("user_id = account.id", "user_id = 1");
assert_eq!(checked(model, &invalid).unwrap_err().code, "C005");
let invalid = APP
.replace(
"tenant_id: model.id?",
"tenant_id: model.id?\npermissions: List<Text>",
)
.replace("tenant_id = null", "tenant_id = null, permissions = []");
assert_eq!(checked(model, &invalid).unwrap_err().code, "C005");
}
+61
View File
@@ -0,0 +1,61 @@
use super::packages;
const ERROR: &str = "public type Error { error Invalid\nerror Unauthorized\nerror Forbidden }";
const AUTH: &str = "public id() (value: Text) { value = dever.system.authId() }";
#[test]
fn identity_cannot_escape_to_public_commands_domains_or_tasks() {
for api in ["public get me = app.me", "cmd me = app.me"] {
let error = packages(&[
("dever.api", ERROR),
("dever.auth", AUTH),
(
"demo.user.app",
"me() (value: Text) { value = dever.auth.id() }",
),
("demo.user.api", api),
])
.unwrap_err();
assert_eq!(error.code, "C014", "{api}: {error:?}");
}
for (package, source, code) in [
(
"demo.user.domain",
"me() (value: Text) { value = dever.auth.id() }",
"C014",
),
(
"main",
"main() (value: Text) { task = run(dever.auth.id())\nvalue = wait(task) }",
"C013",
),
(
"main",
"main() (value: Text) { value = dever.system.authId() }",
"C006",
),
] {
assert_eq!(
packages(&[
("dever.api", ERROR),
("dever.auth", AUTH),
(package, source)
])
.unwrap_err()
.code,
code
);
}
}
#[test]
fn provider_cannot_reenter_request_identity() {
let error = packages(&[
("dever.api", ERROR),
("dever.auth", "public type Claims { subject: Text\nsession: Text\ntenant: Text?\nsite: Text }\npublic id() (value: Text) { value = dever.system.authId() }"),
("demo.user.model", "global type User { name: Text }"),
("demo.user.app", "type Identity { id: Text\nuser_id: model.id?\ntenant_id: model.id? }\nverify(claims: dever.auth.Claims) (value: Identity) { value = Identity { id = dever.auth.id(), user_id = null, tenant_id = null } }\nhealth() (value: Bool) { value = true }"),
("demo.user.api", "public get health = app.health"),
]).unwrap_err();
assert_eq!(error.code, "C014");
}
+202
View File
@@ -0,0 +1,202 @@
use super::*;
fn rest(model: &str, domain: &str) -> Result<Program, Diagnostic> {
packages(&[
("dever.database", ERROR),
("dever.api", "public type Error { error Unauthorized }"),
(
"dever.auth",
"public type Claims { subject: Text\nsession: Text\ntenant: Text?\nsite: Text }",
),
(
"text",
"public trim(input: Text) (value: Text) pure { value = input }",
),
(
"demo.account.model",
"global type Account { subject: Text\nsession: Text }",
),
("demo.account.model.profile", model),
("demo.account.domain", domain),
("demo.account.app", APP),
(
"demo.account.api.admin.session",
"public get health = app.health",
),
("demo.account.api.admin.profile", "rest model.profile"),
])
}
#[test]
fn rest_wire_reuses_schema_proof_without_skipping_json_validation() {
for (kind, decoder) in [
("Text", "decode_fixed"),
("Int?", "decode_fixed"),
("Json", "decode"),
("Json?", "decode"),
] {
let program = rest(&format!("type Profile {{ value: {kind} }}"), "").unwrap();
let generated = dever::compiler::emit::application::generate(&program);
assert!(!generated.contains("wire::decode_json("));
for operation in ["Create", "Replace"] {
assert!(
generated.contains(&format!(
"let args: Rest{operation}0 = input.decode(dever_runtime::wire::{decoder})"
)),
"{kind}/{operation}"
);
}
if kind == "Json" {
assert!(
generated.contains("field0: Box<dever_runtime::wire::serde_json::value::RawValue>")
);
assert!(generated.contains("Json::from_validated_raw(decoded.field0)"));
} else if kind == "Json?" {
assert!(
generated.contains(
"field0: Option<Box<dever_runtime::wire::serde_json::value::RawValue>>"
)
);
assert!(generated.contains(
"decoded.field0.map(dever_runtime::wire::json::Json::from_validated_raw)"
));
} else {
assert!(!generated.contains("Json::deserialize_validated"));
assert!(!generated.contains("Json::deserialize_optional_validated"));
}
}
}
#[test]
fn rest_json_output_projects_only_public_fields() {
let program = rest(
"type Profile { value: Json\nprivate secret: Text create = \"hidden\"\nextra: Json? }",
"",
)
.unwrap();
let generated = dever::compiler::emit::application::generate(&program);
let view = generated
.split_once("impl RestView0<'_> {")
.unwrap()
.1
.split_once("struct RestItems0")
.unwrap()
.0;
assert!(view.contains("field2"));
assert!(!view.contains("field3"));
assert!(view.contains("field4"));
assert!(!view.contains("secret"));
assert!(!view.contains("serialize_field"));
}
#[test]
fn rest_json_defaults_keep_their_typed_decoder() {
for (kind, value, decoder) in [
("Json", r#""{}""#, "deserialize_validated"),
("Json?", "null", "deserialize_optional_validated"),
] {
let program = rest(
&format!("type Profile {{ value: {kind} default {value} }}"),
"",
)
.unwrap();
let generated = dever::compiler::emit::application::generate(&program);
assert!(generated.contains(&format!("Json::{decoder}")));
assert!(generated.contains("#[serde(default=\"rest_create_0_default0\")]"));
}
}
#[test]
fn rest_bindings_use_checked_functions_and_preserve_owner_identity() {
let program = rest("type Profile { private owner user_id: model.id = dever.auth.userId()\nname: Text unique create = domain.normalize(input) replace = text.trim(input) search = input\nprivate source: Text create = \"source\" replace = \"replaced\" }","normalize(input: Text) (value: Text) pure { value = input }").unwrap();
let entry = &program.rest[0];
assert_eq!(entry.owner.as_ref().unwrap().position, 2);
assert!(!entry.read.contains(&2));
assert_eq!(
entry.create.iter().filter(|binding| binding.input).count(),
1
);
assert_eq!(entry.search.as_ref().unwrap().position, 3);
assert!(
entry
.functions()
.all(|function| program.entries().any(|entry| entry == function))
);
}
#[test]
fn rest_rejects_unsafe_field_sources_and_ambiguous_search() {
for model in [
"type Profile { private name: Text create = input }",
"type Profile { owner id_value: Int? = dever.auth.userId()\nname: Text }",
"type Profile { owner first_id: Int = dever.auth.userId()\nowner second_id: Int = dever.auth.tenantId()\nname: Text }",
"type Profile { name: Text search = input }",
"type Profile { name: Text index search = \"fixed\" }",
"type Profile { name: Text index search = input\nother_name: Text index search = input }",
"type Profile { private required: Text\nname: Text }",
"type Profile { name: Text create = domain.normalize(input) }",
] {
let error = rest(
model,
if model.contains("domain.normalize") {
"normalize(input: Text) (value: Text) { value = input }"
} else {
""
},
)
.unwrap_err();
assert_eq!(error.code, "C014", "{model}: {error:?}");
}
}
#[test]
fn rest_search_reserves_pagination_names_without_banning_storage_fields() {
for name in ["page", "size"] {
let model = format!("type Profile {{ {name}: Int index search = input }}");
let error = rest(&model, "").unwrap_err();
assert_eq!(error.code, "C014");
assert_eq!(
error.message,
"REST search field cannot use reserved pagination names"
);
let model = format!("type Profile {{ {name}: Int index }}");
assert!(
rest(&model, "").is_ok(),
"{name} remains a valid storage field"
);
}
}
#[test]
fn rest_preserves_required_private_fields_with_only_a_create_source() {
let program = rest(
"type Profile { private created_by: Text create = \"creator\"\nname: Text }",
"",
)
.unwrap();
let entry = &program.rest[0];
let source = entry
.create
.iter()
.find(|binding| binding.position == 2)
.unwrap();
assert!(source.function.is_some());
assert!(!source.input);
assert!(!entry.read.contains(&2));
assert!(entry.replace.iter().all(|binding| binding.position != 2));
let error = rest(
"type Profile { private created_by: Text replace = \"replacement\"\nname: Text }",
"",
)
.unwrap_err();
assert_eq!(error.code, "C014");
assert_eq!(
error.message,
"REST field is private and required without a source"
);
}
#[test]
fn rest_field_metadata_does_not_escape_into_handler_contracts() {
assert!(packages(&[("main","apply(callback: handler(value: Int create = input) (answer: Int)) (answer: Int) { answer = callback(1) }")]).is_err());
}