feat: release Dever 0.1.5 with direct workers and long transactions
This commit is contained in:
1 parent
89629c2197
commit
a900d7e8db
153 files changed
+3676
-3708
No files matched your search
@@ -40,6 +40,155 @@ fn reject(files: &[(&str, &str)], expected: &str) {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ordinary_and_long_entries_have_explicit_transaction_policies() {
|
||||
for entry in ["public post create = app.create", "cmd create = app.create"] {
|
||||
let files = [
|
||||
(
|
||||
"content/item/model.dever",
|
||||
"global type Item { name: Text }",
|
||||
),
|
||||
(
|
||||
"content/item/app.dever",
|
||||
"create(name: Text) (value: Text) { saved = model.create({ name = name })\nvalue = saved.name }",
|
||||
),
|
||||
("content/item/api.dever", entry),
|
||||
];
|
||||
check(&files);
|
||||
let long_entry = if entry.starts_with("public ") {
|
||||
entry.replace("public ", "public long ")
|
||||
} else {
|
||||
format!("long {entry}")
|
||||
};
|
||||
let mut long = files;
|
||||
long[2].1 = &long_entry;
|
||||
reject(
|
||||
&long,
|
||||
"long entry database writes must be enclosed in an explicit transaction",
|
||||
);
|
||||
let mut transactional = long;
|
||||
let app = format!("transaction {}", files[1].1);
|
||||
transactional[1].1 = &app;
|
||||
check(&transactional);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn entry_orchestration_can_wait_between_explicit_transactions() {
|
||||
let files = [
|
||||
(
|
||||
"content/item/model.dever",
|
||||
"global type Item { name: Text }",
|
||||
),
|
||||
(
|
||||
"content/item/app.dever",
|
||||
"create() (count: Int) { first = store(\"first\")\ndever.task.sleep(1)\nsecond = store(\"second\")\ncount = first + second }\ntransaction store(name: Text) (count: Int) { saved = model.create({ name = name })\ncount = model.count() }",
|
||||
),
|
||||
(
|
||||
"content/item/api.dever",
|
||||
"public long post create = app.create\nlong cmd create = app.create",
|
||||
),
|
||||
];
|
||||
let source_map = sources(&files);
|
||||
let program = dever_core::check(&source_map).unwrap_or_else(|errors| panic!("{errors:?}"));
|
||||
let native = dever_core::native::emit_project(
|
||||
&program,
|
||||
&source_map,
|
||||
dever_runtime::config::RuntimeProfile {
|
||||
sqlite: true,
|
||||
postgres: false,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
assert!(!native.contains("let transaction = transaction_database.begin()"));
|
||||
assert!(native.contains("let transaction_owner = if _database.is_none()"));
|
||||
let (_root, settings) = auth_settings();
|
||||
let mut configured = identity_sources().to_vec();
|
||||
configured.extend([
|
||||
files[0],
|
||||
files[1],
|
||||
("content/item/api/admin/create.dever", files[2].1),
|
||||
]);
|
||||
let configured_sources = sources(&configured);
|
||||
let configured_program = dever_core::check_with_settings(&configured_sources, &settings)
|
||||
.unwrap_or_else(|errors| panic!("{errors:?}"));
|
||||
dever_core::llvm::emit_application(&configured_program, &configured_sources).unwrap();
|
||||
let ordinary_entry = files[2].1.replace("long ", "");
|
||||
let mut ordinary = files;
|
||||
ordinary[2].1 = &ordinary_entry;
|
||||
reject(&ordinary, "automatic entry transactions cannot contain");
|
||||
let mut invalid = files;
|
||||
let app = files[1].1.replace("create()", "transaction create()");
|
||||
invalid[1].1 = &app;
|
||||
reject(
|
||||
&invalid,
|
||||
"transaction functions cannot start concurrent work",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn job_entries_require_long_before_using_staged_transactions() {
|
||||
let model = "global type Item { name: Text }";
|
||||
let ordinary = "apply() () { saved = model.create({ name = \"job\" }) }";
|
||||
let transactional = "transaction apply() () { saved = model.create({ name = \"job\" }) }";
|
||||
for long in [false, true] {
|
||||
let prefix = if long { "long " } else { "" };
|
||||
let job = format!(
|
||||
"database default\n{prefix}job apply() () retry(3) timeout(30000) {{ app.apply() }}"
|
||||
);
|
||||
let mut files = [
|
||||
("content/item/model.dever", model),
|
||||
("content/item/app.dever", ordinary),
|
||||
("content/item/job.dever", job.as_str()),
|
||||
];
|
||||
if long {
|
||||
reject(&files, "long entry database writes must be enclosed");
|
||||
files[1].1 = transactional;
|
||||
}
|
||||
check(&files);
|
||||
}
|
||||
let files = [
|
||||
("content/item/model.dever", model),
|
||||
(
|
||||
"content/item/app.dever",
|
||||
"apply() () { save()\ndever.task.sleep(1) }\ntransaction save() () { saved = model.create({ name = \"job\" }) }",
|
||||
),
|
||||
(
|
||||
"content/item/job.dever",
|
||||
"database default\njob apply() () retry(3) timeout(30000) { app.apply() }",
|
||||
),
|
||||
];
|
||||
reject(&files, "declare the entry 'long'");
|
||||
let mut long = files;
|
||||
long[2].1 = "database default\nlong job apply() () retry(3) timeout(30000) { app.apply() }";
|
||||
check(&long);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn long_is_an_entry_modifier_not_an_app_function_modifier() {
|
||||
for (path, text) in [
|
||||
("content/item/api.dever", "long rest"),
|
||||
(
|
||||
"content/item/api.dever",
|
||||
"long public post create = app.create",
|
||||
),
|
||||
("content/item/app.dever", "long create() () {}"),
|
||||
("content/item/app.dever", "long transaction create() () {}"),
|
||||
(
|
||||
"content/item/api.dever",
|
||||
"public long cmd create = app.create",
|
||||
),
|
||||
] {
|
||||
let mut source = SourceMap::default();
|
||||
let id = source.add(path, text);
|
||||
assert!(dever_core::parse(source.get(id)).is_err());
|
||||
}
|
||||
check(&[(
|
||||
"content/item/app.dever",
|
||||
"long() (value: Int) { value = 1 }",
|
||||
)]);
|
||||
}
|
||||
|
||||
fn auth_settings() -> (temp::TemporaryDirectory, Settings) {
|
||||
let root = temp::TemporaryDirectory::new();
|
||||
fs::create_dir(root.path().join("config")).unwrap();
|
||||
@@ -698,7 +847,7 @@ fn sql_projection_files(sqlite: &str, postgres: &str, method: &str) -> [(&'stati
|
||||
(
|
||||
"news/article/app.dever",
|
||||
"type View { name: Text }\n\
|
||||
read(name: Text, size: Int) (rows: List<View>) {\n\
|
||||
transaction read(name: Text, size: Int) (rows: List<View>) {\n\
|
||||
rows = model.lookup(name, size)\n}\n"
|
||||
.into(),
|
||||
),
|
||||
@@ -767,7 +916,7 @@ fn unproven_sql_keeps_its_write_effect_in_each_dialect() {
|
||||
|
||||
#[test]
|
||||
fn get_rejects_writes_reached_through_cross_domain_app_calls() {
|
||||
let writer = "create(name: Text) (value: Text) { account = model.create({ name = text.trim(name) })\nvalue = account.name }";
|
||||
let writer = "transaction create(name: Text) (value: Text) { account = model.create({ name = text.trim(name) })\nvalue = account.name }";
|
||||
reject(
|
||||
&[
|
||||
(
|
||||
|
||||
Reference in new issue
Block a user