Initial Dever 0.0.1
This commit is contained in:
commit
81233d098c
1479 files changed
+163782
No files matched your search
@@ -0,0 +1,171 @@
|
||||
use super::{APP, ERROR};
|
||||
use dever::{
|
||||
compiler::{check, model::Program},
|
||||
syntax::{diagnostic::Diagnostic, source::Source},
|
||||
};
|
||||
|
||||
const API_ERROR: &str = "public type Error { error Invalid\nerror Unauthorized\nerror Forbidden\nerror NotFound\nerror Conflict\nerror TooManyRequests }";
|
||||
|
||||
fn checked(app: &str, api: &str) -> Result<Program, Diagnostic> {
|
||||
let app = format!("{APP}\n{app}");
|
||||
let api = format!("{api}\npublic get health = app.health");
|
||||
let inputs = [
|
||||
("dever.database", "database.dever", ERROR),
|
||||
("dever.api", "api.dever", API_ERROR),
|
||||
(
|
||||
"dever.auth",
|
||||
"values.dever",
|
||||
include_str!("../../../../lib/dever/auth/values.dever"),
|
||||
),
|
||||
(
|
||||
"dever.auth",
|
||||
"capability.dever",
|
||||
include_str!("../../../../lib/dever/auth/capability.dever"),
|
||||
),
|
||||
(
|
||||
"demo.account.model",
|
||||
"model.dever",
|
||||
"global type Account { subject: Text\nsession: Text }",
|
||||
),
|
||||
("demo.account.app", "app.dever", app.as_str()),
|
||||
("demo.account.api", "api.dever", api.as_str()),
|
||||
];
|
||||
let sources = inputs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, (_, path, text))| Source::new(index, *path, (*text).into()))
|
||||
.collect::<Vec<_>>();
|
||||
let modules = sources
|
||||
.iter()
|
||||
.map(dever::syntax::parse)
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
check::check(
|
||||
modules,
|
||||
&inputs
|
||||
.iter()
|
||||
.map(|(name, _, _)| (*name).into())
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn capability_operations_keep_original_types_and_write_effects() {
|
||||
let program = checked("", "").unwrap();
|
||||
for function in program.functions.iter().filter(|function| {
|
||||
matches!(
|
||||
function.signature.name.as_str(),
|
||||
"register_capability" | "remove_capability"
|
||||
)
|
||||
}) {
|
||||
let effects = function.signature.effects;
|
||||
assert!(effects.identity && effects.authorization_write && effects.database_write);
|
||||
}
|
||||
let error = checked(
|
||||
"register(id: Text) (key: Text) { key = dever.auth.registerCapability(id) }",
|
||||
"post register = app.register",
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(error.code, "C005");
|
||||
let error = checked(
|
||||
"register(id: Uuid) (value: Bool) { value = dever.auth.registerCapability(id) }",
|
||||
"post register = app.register",
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(error.code, "C005");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn capability_writes_require_protected_http_and_keep_transaction_rules() {
|
||||
for api in [
|
||||
"public post register = app.register",
|
||||
"authenticated post register = app.register",
|
||||
"get register = app.register",
|
||||
"cmd register = app.register",
|
||||
"long post register = app.register",
|
||||
] {
|
||||
let error = checked(
|
||||
"register(id: Uuid) (key: Text) { key = dever.auth.registerCapability(id) }",
|
||||
api,
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(error.code, "C014", "{api}: {error:?}");
|
||||
}
|
||||
let program = checked(
|
||||
"transaction register(id: Uuid) (key: Text) { key = dever.auth.registerCapability(id) }",
|
||||
"long post register = app.register",
|
||||
)
|
||||
.unwrap();
|
||||
assert_tenant_requirement(&program, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn protected_role_query_is_authenticated_read_only_and_not_a_tenant_selector() {
|
||||
let app =
|
||||
"protected(user_id: Int) (value: Bool) { value = dever.auth.hasProtectedRoles(user_id) }";
|
||||
for api in [
|
||||
"get protected = app.protected",
|
||||
"authenticated get protected = app.protected",
|
||||
] {
|
||||
let program = checked(app, api).unwrap();
|
||||
assert_tenant_requirement(&program, true);
|
||||
}
|
||||
for api in [
|
||||
"public get protected = app.protected",
|
||||
"cmd protected = app.protected",
|
||||
] {
|
||||
assert_eq!(checked(app, api).unwrap_err().code, "C014");
|
||||
}
|
||||
assert_eq!(checked(
|
||||
"protected(user_id: Int) (value: Bool) { value = dever.auth.hasProtectedRoles(user_id, 42) }",
|
||||
"get protected = app.protected",
|
||||
).unwrap_err().code, "C005");
|
||||
}
|
||||
|
||||
fn assert_tenant_requirement(program: &Program, required: bool) {
|
||||
let entry = program
|
||||
.http
|
||||
.iter()
|
||||
.find(|entry| entry.access != dever::compiler::model::auth::Access::Public)
|
||||
.unwrap();
|
||||
assert_eq!(program.tenant_access(entry.function).required, required);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn protected_write_entries_cannot_replace_the_required_tenant() {
|
||||
for (app, api) in [
|
||||
(
|
||||
"register(id: Uuid) (key: Text) { key = dever.auth.registerCapability(id) }",
|
||||
"post register = app.register",
|
||||
),
|
||||
(
|
||||
"remove(id: Uuid) () { dever.auth.removeCapability(id) }",
|
||||
"delete remove = app.remove",
|
||||
),
|
||||
] {
|
||||
assert_tenant_requirement(&checked(app, api).unwrap(), true);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tenant_requirement_follows_reachable_calls_and_static_handlers() {
|
||||
for (app, required) in [
|
||||
(
|
||||
"register(id: Uuid) (key: Text) { key = helper(id) }\nhelper(id: Uuid) (key: Text) { key = dever.auth.registerCapability(id) }",
|
||||
true,
|
||||
),
|
||||
(
|
||||
"register(id: Uuid) (key: Text) { key = apply(dever.auth.registerCapability, id) }\napply(action: handler(id: Uuid) (key: Text), id: Uuid) (key: Text) { key = action(id) }",
|
||||
true,
|
||||
),
|
||||
] {
|
||||
assert_tenant_requirement(
|
||||
&checked(app, "post register = app.register").unwrap(),
|
||||
required,
|
||||
);
|
||||
}
|
||||
let error = checked(
|
||||
"register(id: Uuid) (key: Text) { key = \"unused\" }\nunused(id: Uuid) (key: Text) { key = dever.auth.registerCapability(id) }",
|
||||
"post register = app.register",
|
||||
).unwrap_err();
|
||||
assert_eq!(error.code, "C016");
|
||||
}
|
||||
Reference in new issue
Block a user