Files
core/test/rust/native/dependency/worker.rs
T
2026-11-03 03:32:07 +00:00

173 lines
5.8 KiB
Rust

//! 从合法 Dever Port 到实际嵌入 ELF;无 fails 的声明必须在检查期拒绝。
use super::{Fixture, app, command, write};
use dever::compiler::{
build::{self, BuildTarget, Resource},
project::Project,
};
use std::{fs, os::unix::fs::PermissionsExt, process::Command};
mod arguments;
mod command;
mod jobs;
mod roots;
#[test]
fn external_builds_and_executes_after_source_removal() {
let fixture = Fixture::new(&[]);
let root = &fixture.directory.0;
write(
root,
"module/demo/value/app.dever",
app("port.native(input) + input"),
);
write(root, "module/demo/value/api.dever", command());
write(
root,
"module/demo/value/port.dever",
"native(input: Int) (value: Int)\n",
);
write(
root,
"module/demo/value/adapter.dever",
"external exec \"worker\" {}\n",
);
let port = root.join("module/demo/value/port.dever");
assert!(
Project::load(root)
.unwrap()
.check()
.unwrap_err()
.contains("fails")
);
let source = fs::read_to_string(&port).unwrap().replace(
"native(input: Int) (value: Int)",
"native(input: Int) (value: Int) fails dever.api.Error",
);
fs::write(&port, source).unwrap();
roots::prepare(root);
jobs::prepare(root);
let project = Project::load(root).unwrap();
let program = project.check().unwrap();
let contract = program.external_workers().next().unwrap();
let manifest: serde_json::Value = serde_json::from_str(&contract.contract).unwrap();
let identity = "dever-component-v1;setting=none;operation=native;input=input:wire-v1:Output:Int;;output=value:wire-v1:Output:Int;;dever.api.Error.Invalid;dever.api.Error.Unauthorized;dever.api.Error.Forbidden;dever.api.Error.NotFound;dever.api.Error.Conflict;dever.api.Error.TooManyRequests;";
let checksum = identity.bytes().fold(0xcbf29ce484222325u64, |hash, byte| {
(hash ^ u64::from(byte)).wrapping_mul(0x100000001b3)
});
assert_eq!(manifest["schema"], format!("{checksum:016x}"));
let worker = root.join("fixture");
let source = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../test/rust/runtime/worker/fixture.rs");
let result = Command::new("rustup")
.args(["run", "1.95.0", "rustc", "--edition=2024", "-O"])
.arg(source)
.arg("-o")
.arg(&worker)
.output()
.unwrap();
assert!(
result.status.success(),
"{}",
String::from_utf8_lossy(&result.stderr)
);
let launch = serde_json::to_vec(&serde_json::json!({"format":"dever-worker-launch-v1","ecosystem":"exec","entry":contract.entry,"executable":contract.entry,"arguments":[],"working_directory":"module/demo/value"})).unwrap();
let resources = [
(contract.entry.clone(), fs::read(&worker).unwrap(), true),
(
format!("{}.dever-worker.json", contract.entry),
launch,
false,
),
]
.into_iter()
.map(|(path, bytes, executable)| Resource {
path,
bytes,
executable,
})
.collect::<Vec<_>>();
let binary = build::build_with_resources(
&program,
&project.sources,
"main",
&root.join("app"),
BuildTarget::host().unwrap(),
&resources,
)
.unwrap();
fs::remove_dir_all(root.join("module")).unwrap();
fs::remove_file(worker).unwrap();
fs::create_dir(root.join("data")).unwrap();
fs::set_permissions(root.join("data"), fs::Permissions::from_mode(0o755)).unwrap();
roots::commands(&binary);
roots::http(&binary, root);
jobs::run(&binary, root);
}
fn command_cases(binary: &std::path::Path, entry: &str) {
for _ in 0..2 {
let result = Command::new(binary)
.env_clear()
.args([entry, "{\"input\":8}"])
.output()
.unwrap();
assert!(
result.status.success(),
"{}",
String::from_utf8_lossy(&result.stderr)
);
assert_eq!(
result.stdout,
b"{\"code\":0,\"message\":\"ok\",\"data\":50}\n"
);
}
for (input, expected) in [
(-1, "Worker output does not match checked Port"),
(-2, "Worker returned an undeclared business error"),
(-3, "Invalid"),
] {
let output = Command::new(binary)
.env_clear()
.args([entry, &format!("{{\"input\":{input}}}")])
.output()
.unwrap();
assert!(!output.status.success());
let error = String::from_utf8_lossy(&output.stderr);
assert!(
error.contains(expected),
"input {input}, expected {expected}: {error}"
);
if input != -3 {
assert!(error.contains("adapter.dever:"), "{error}");
}
}
// Worker 已交付合法终态;根入口仍须等 shutdown,不能提前发布成功结果。
for input in [-4, -5] {
let output = Command::new(binary)
.env_clear()
.args([entry, &format!("{{\"input\":{input}}}")])
.output()
.unwrap();
assert!(
!output.status.success(),
"input {input}: shutdown failure was discarded"
);
assert!(
output.stdout.is_empty(),
"input {input}: CMD published output before cleanup"
);
let error = String::from_utf8_lossy(&output.stderr);
assert!(
error.contains("invalid Worker shutdown acknowledgement"),
"input {input}: {error}"
);
if input == -5 {
assert!(
error.contains("dever.api.Error.Invalid") && error.contains("cleanup:"),
"business error and cleanup must both survive: {error}"
);
}
}
}