use super::{APP, JOB, checked}; fn app(body: &str) -> String { format!( "{}\ntransaction send(key: Text) () {{ id = submit(key) }}\nlaunch() () {{ {body} }}", APP ) } #[test] fn tasks_can_inherit_enqueue_identity_without_sharing_a_transaction() { for body in [ "pending = run(send(\"one\"))\nwait(pending)\nsend(\"after\")", "pool = group(2)\nrun(pool, send(\"one\"))\nrun(pool, send(\"two\"))\nwait(pool)\nsend(\"after\")", "parallelEach(send, [\"one\", \"two\"], 2)\nsend(\"after\")", ] { let source = app(body); let program = checked(&source, JOB, "long cmd launch = app.launch").unwrap(); let signature = &program.functions[program.commands[0].function].signature; assert!(signature.effects.enqueue); assert!(!signature.effects.request); assert!(!signature.effects.unscoped_write); assert!(signature.effects.concurrent || signature.effects.parallel); // 普通入口不能把自动事务变成多个独立提交;必须显式声明 long。 let error = checked(&source, JOB, "cmd launch = app.launch").unwrap_err(); assert_eq!(error.code, "C014", "{body}: {error:?}"); } } #[test] fn long_jobs_can_launch_children_only_through_explicit_transactions() { let source = app("parallelEach(send, [\"one\", \"two\"], 2)"); let jobs = format!("{JOB}\nlong job fanout() () retry(1) timeout(1000) {{ app.launch() }}"); assert!(checked(&source, &jobs, "").is_ok()); assert_eq!( checked(&source, &jobs.replace("long job", "job"), "") .unwrap_err() .code, "C014" ); } #[test] fn enqueue_does_not_escape_transaction_or_synchronous_worker_restrictions() { for body in [ "pending = run(send(\"one\"))\nwait(pending)", "pool = group(1)\nrun(pool, send(\"one\"))\nwait(pool)", "parallelEach(send, [\"one\"], 1)", ] { let source = app(body).replace("launch() ()", "transaction launch() ()"); assert_eq!( checked(&source, JOB, "long cmd launch = app.launch") .unwrap_err() .code, "C014", "{body}" ); } for boundary in ["blocking", "parallel"] { let source = app(&format!("{boundary}(send(\"one\"))")); assert_eq!( checked(&source, JOB, "long cmd launch = app.launch") .unwrap_err() .code, "C013" ); } let unscoped = app("pending = run(send(\"one\"))\nwait(pending)").replace("transaction send", "send"); assert_eq!( checked(&unscoped, JOB, "long cmd launch = app.launch") .unwrap_err() .code, "C014" ); } #[test] fn public_enqueue_remains_forbidden_through_tasks() { let source = app("pending = run(send(\"one\"))\nwait(pending)"); assert_eq!( checked(&source, JOB, "public long post launch = app.launch") .unwrap_err() .code, "C014" ); } #[test] fn enqueue_capability_does_not_make_request_identity_transferable() { for body in [ "pending = run(send(\"one\"))\nwait(pending)", "pool = group(1)\nrun(pool, send(\"one\"))\nwait(pool)", "parallelEach(send, [\"one\"], 1)", ] { let source = app(body).replace( "id = submit(key)", "identity = dever.auth.id()\nid = submit(identity + key)", ); let error = super::packages(&[ ("dever.database", super::ERROR), ( "dever.api", "public type Error { error Invalid\nerror Unauthorized\nerror Forbidden }", ), ( "dever.auth", "public id() (value: Text) { value = dever.system.authId() }", ), ("demo.queue.app", &source), ("demo.queue.job", JOB), ("demo.queue.api", "long cmd launch = app.launch"), ]) .unwrap_err(); assert_eq!(error.code, "C013", "{body}: {error:?}"); } }