Initial Dever 0.0.1
This commit is contained in:
commit
81233d098c
1479 files changed
+163782
No files matched your search
@@ -0,0 +1,117 @@
|
||||
use super::{Directory, fixture};
|
||||
use dever::compiler::{build, project::Project};
|
||||
use std::process::Command;
|
||||
|
||||
mod settings;
|
||||
|
||||
#[test]
|
||||
fn native_ports_execute_file_io_and_preserve_capture_and_faults() {
|
||||
let directory = Directory::new();
|
||||
let project = Project::load(&fixture().with_file_name("ports")).unwrap();
|
||||
let program = project.check().unwrap();
|
||||
let connect = program
|
||||
.functions
|
||||
.iter()
|
||||
.find(|function| function.signature.name == "connect")
|
||||
.unwrap();
|
||||
assert!(connect.signature.effects.network && connect.signature.effects.suspend);
|
||||
assert!(!connect.signature.effects.blocking && !connect.signature.effects.unmediated_io);
|
||||
let binary =
|
||||
build::build(&program, &project.sources, "main", &directory.0.join("app")).unwrap();
|
||||
let file = directory.0.join("input.txt");
|
||||
std::fs::write(&file, "owned Port test file").unwrap();
|
||||
let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
|
||||
let address = listener.local_addr().unwrap();
|
||||
for (name, input, expected) in [
|
||||
("open", serde_json::json!({"path": file}).to_string(), "1"),
|
||||
("adjust", "{\"value\":41}".into(), "42"),
|
||||
("captured", "{\"value\":41}".into(), "true"),
|
||||
("captured", "{\"value\":-1}".into(), "false"),
|
||||
(
|
||||
"connect",
|
||||
serde_json::json!({"host":"127.0.0.1", "port":address.port()}).to_string(),
|
||||
"true",
|
||||
),
|
||||
] {
|
||||
let result = Command::new(&binary)
|
||||
.args([format!("demo.files.{name}"), input])
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(
|
||||
result.status.success(),
|
||||
"{name}: {}",
|
||||
String::from_utf8_lossy(&result.stderr)
|
||||
);
|
||||
assert_eq!(
|
||||
String::from_utf8(result.stdout).unwrap(),
|
||||
format!("{{\"code\":0,\"message\":\"ok\",\"data\":{expected}}}\n")
|
||||
);
|
||||
}
|
||||
// TCP 握手可先进入监听队列;非阻塞 accept 确认应用确实建立了连接。
|
||||
listener.set_nonblocking(true).unwrap();
|
||||
let (peer, _) = listener.accept().unwrap();
|
||||
drop(peer);
|
||||
for (name, input, expected, source) in [
|
||||
(
|
||||
"open",
|
||||
serde_json::json!({"path": directory.0.join("missing")}).to_string(),
|
||||
"OpenResult.Failed",
|
||||
"lib/dever/io.dever:",
|
||||
),
|
||||
(
|
||||
"adjust",
|
||||
"{\"value\":-1}".into(),
|
||||
"Problem.Missing",
|
||||
"adapter.dever:",
|
||||
),
|
||||
(
|
||||
"captured",
|
||||
"{\"value\":9223372036854775807}".into(),
|
||||
"integer arithmetic fault",
|
||||
"adapter.dever:",
|
||||
),
|
||||
] {
|
||||
let result = Command::new(&binary)
|
||||
.args([format!("demo.files.{name}"), input])
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(!result.status.success());
|
||||
assert!(result.stdout.is_empty());
|
||||
let error = String::from_utf8(result.stderr).unwrap();
|
||||
assert!(
|
||||
error.contains(expected) && error.contains(source),
|
||||
"{error}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn adapter_selection_remains_runtime_configuration() {
|
||||
let directory = Directory::new();
|
||||
let module = directory.0.join("module/demo/files");
|
||||
std::fs::create_dir_all(&module).unwrap();
|
||||
std::fs::write(module.join("app.dever"), "not parsed before settings").unwrap();
|
||||
let config = directory.0.join("config");
|
||||
std::fs::create_dir(&config).unwrap();
|
||||
for text in [
|
||||
r#"{"adapter":{"demo.files":{"use":"default"}}}"#,
|
||||
r#"{"adapter":null}"#,
|
||||
] {
|
||||
std::fs::write(config.join("setting.json"), text).unwrap();
|
||||
assert!(Project::load(&directory.0).is_ok(), "{text}");
|
||||
}
|
||||
std::fs::write(
|
||||
config.join("setting.json"),
|
||||
r#"{"adapter":{},"adapter":{}}"#,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(Project::load(&directory.0).is_err());
|
||||
std::fs::write(
|
||||
config.join("setting.json"),
|
||||
r#"{"package":{"registry":"https://packages.test","use":[],"fixture-only-secret":true}}"#,
|
||||
)
|
||||
.unwrap();
|
||||
let error = Project::load(&directory.0).err().unwrap();
|
||||
assert!(error.contains("invalid project configuration JSON"));
|
||||
assert!(!error.contains("fixture-only-secret"));
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
use super::{Directory, fixture};
|
||||
use dever::compiler::{build, project::Project};
|
||||
use std::{path::Path, process::Command};
|
||||
|
||||
fn execute(binary: &Path, name: &str, input: serde_json::Value) -> std::process::Output {
|
||||
Command::new(binary)
|
||||
.args([format!("demo.value.{name}"), input.to_string()])
|
||||
.output()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_adapter_selection_settings_and_tasks_share_one_binary() {
|
||||
let directory = Directory::new();
|
||||
let source = fixture().with_file_name("adapters");
|
||||
let project = Project::load(&source).unwrap();
|
||||
let program = project.check().unwrap();
|
||||
let binary =
|
||||
build::build(&program, &project.sources, "main", &directory.0.join("app")).unwrap();
|
||||
let original = std::fs::read(&binary).unwrap();
|
||||
let config = directory.0.join("config");
|
||||
std::fs::create_dir(&config).unwrap();
|
||||
let path = config.join("setting.json");
|
||||
let local = serde_json::json!({"adapter":{"demo.value":{"use":"local","setting":{"offset":1,"prefix":"local:","token":"key"}}}});
|
||||
let remote = serde_json::json!({"adapter":{"demo.value":{"use":"remote"}}});
|
||||
for (setting, number, prefix, sign) in
|
||||
[(&local, 42, "local:", 247), (&remote, 141, "remote:", 82)]
|
||||
{
|
||||
std::fs::write(&path, setting.to_string()).unwrap();
|
||||
for name in [
|
||||
"calculate",
|
||||
"worker",
|
||||
"nested",
|
||||
"mapped",
|
||||
"parallel",
|
||||
"scheduled",
|
||||
"cancelled",
|
||||
] {
|
||||
let result = execute(&binary, name, serde_json::json!({"value":41}));
|
||||
assert!(
|
||||
result.status.success(),
|
||||
"{name}: {}",
|
||||
String::from_utf8_lossy(&result.stderr)
|
||||
);
|
||||
let actual: serde_json::Value = serde_json::from_slice(&result.stdout).unwrap();
|
||||
assert_eq!(
|
||||
actual["data"],
|
||||
if name == "mapped" { number * 2 } else { number }
|
||||
);
|
||||
}
|
||||
for (name, value, expected) in [
|
||||
("echo", "雪", serde_json::json!(format!("{prefix}雪"))),
|
||||
(
|
||||
"sign",
|
||||
"The quick brown fox jumps over the lazy dog",
|
||||
serde_json::json!(sign),
|
||||
),
|
||||
] {
|
||||
let result = execute(&binary, name, serde_json::json!({"value":value}));
|
||||
assert!(
|
||||
result.status.success(),
|
||||
"{name}: {}",
|
||||
String::from_utf8_lossy(&result.stderr)
|
||||
);
|
||||
let actual: serde_json::Value = serde_json::from_slice(&result.stdout).unwrap();
|
||||
assert_eq!(actual["data"], expected);
|
||||
}
|
||||
}
|
||||
for name in ["parallel", "scheduled"] {
|
||||
let result = execute(&binary, name, serde_json::json!({"value":-1}));
|
||||
assert!(!result.status.success());
|
||||
assert!(result.stdout.is_empty());
|
||||
let error = String::from_utf8_lossy(&result.stderr);
|
||||
assert!(
|
||||
error.contains("Problem.Missing") && error.contains("remote.dever:"),
|
||||
"{error}"
|
||||
);
|
||||
}
|
||||
for setting in [
|
||||
"{}",
|
||||
"null",
|
||||
"[]",
|
||||
"{",
|
||||
r#"{"adapter":{"demo.value":{"use":"absent"}}}"#,
|
||||
r#"{"adapter":{"demo.value":{"use":"local"}}}"#,
|
||||
r#"{"adapter":{"demo.value":{"use":"local","setting":{"offset":"fixture-only-secret","prefix":"x","token":"x"}}}}"#,
|
||||
r#"{"adapter":{"demo.value":{"use":"local","setting":[1,"x","fixture-only-secret"]}}}"#,
|
||||
r#"{"adapter":{"demo.value":{"use":"remote","setting":{"token":"fixture-only-secret"}}}}"#,
|
||||
r#"{"adapter":{"demo.value":{"use":"local","setting":{"offset":1,"prefix":"x","token":"x","token":"fixture-only-secret"}}}}"#,
|
||||
] {
|
||||
std::fs::write(&path, setting).unwrap();
|
||||
let result = execute(&binary, "calculate", serde_json::json!({"value":41}));
|
||||
assert!(!result.status.success(), "{setting}");
|
||||
assert!(result.stdout.is_empty());
|
||||
let error = String::from_utf8_lossy(&result.stderr);
|
||||
assert!(
|
||||
error.contains("Adapter") && !error.contains("fixture-only-secret"),
|
||||
"{error}"
|
||||
);
|
||||
}
|
||||
// 同一产物迁移到新目录;配置路径属于部署,不进入编译缓存或二进制。
|
||||
let relocated = directory.0.join("relocated");
|
||||
std::fs::create_dir(&relocated).unwrap();
|
||||
let copy = relocated.join("app");
|
||||
std::fs::copy(&binary, ©).unwrap();
|
||||
std::fs::write(&path, local.to_string()).unwrap();
|
||||
let result = Command::new(©)
|
||||
.arg("--config")
|
||||
.arg(&path)
|
||||
.args(["demo.value.calculate", r#"{"value":41}"#])
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(
|
||||
result.status.success(),
|
||||
"{}",
|
||||
String::from_utf8_lossy(&result.stderr)
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::from_slice::<serde_json::Value>(&result.stdout).unwrap()["data"],
|
||||
42
|
||||
);
|
||||
assert_eq!(std::fs::read(&binary).unwrap(), original);
|
||||
|
||||
// run 使用项目配置;只复制此测试源码到自有临时目录。
|
||||
let project_root = directory.0.join("project");
|
||||
let module = project_root.join("module/demo/value");
|
||||
std::fs::create_dir_all(module.join("adapter")).unwrap();
|
||||
for relative in [
|
||||
"app.dever",
|
||||
"port.dever",
|
||||
"api.dever",
|
||||
"adapter/local.dever",
|
||||
"adapter/remote.dever",
|
||||
] {
|
||||
std::fs::copy(
|
||||
source.join("module/demo/value").join(relative),
|
||||
module.join(relative),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
std::fs::create_dir(project_root.join("config")).unwrap();
|
||||
std::fs::write(project_root.join("config/setting.json"), local.to_string()).unwrap();
|
||||
let result = Command::new(env!("CARGO_BIN_EXE_dever"))
|
||||
.arg("run")
|
||||
.arg(&project_root)
|
||||
.args(["--", "demo.value.calculate", r#"{"value":41}"#])
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(
|
||||
result.status.success(),
|
||||
"{}",
|
||||
String::from_utf8_lossy(&result.stderr)
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::from_slice::<serde_json::Value>(&result.stdout).unwrap()["data"],
|
||||
42
|
||||
);
|
||||
// 单实现省略 use,只保留字段输入;无需多实现选择分支。
|
||||
let local_source = std::fs::read_to_string(module.join("adapter/local.dever")).unwrap();
|
||||
std::fs::write(
|
||||
module.join("adapter.dever"),
|
||||
local_source.replace("demo.value.adapter.local", "demo.value.adapter"),
|
||||
)
|
||||
.unwrap();
|
||||
std::fs::remove_dir_all(module.join("adapter")).unwrap();
|
||||
std::fs::write(project_root.join("config/setting.json"), serde_json::json!({"adapter":{"demo.value":{"setting":{"offset":2,"prefix":"one:","token":"key"}}}}).to_string()).unwrap();
|
||||
let result = Command::new(env!("CARGO_BIN_EXE_dever"))
|
||||
.arg("run")
|
||||
.arg(&project_root)
|
||||
.args(["--", "demo.value.worker", r#"{"value":41}"#])
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(
|
||||
result.status.success(),
|
||||
"{}",
|
||||
String::from_utf8_lossy(&result.stderr)
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::from_slice::<serde_json::Value>(&result.stdout).unwrap()["data"],
|
||||
43
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user