Initial Dever 0.0.1
This commit is contained in:
commit
81233d098c
1479 files changed
+163782
No files matched your search
@@ -0,0 +1,259 @@
|
||||
use super::{Directory, fixture};
|
||||
mod auth;
|
||||
mod database;
|
||||
mod http;
|
||||
mod job;
|
||||
mod run;
|
||||
mod tenant;
|
||||
mod transport;
|
||||
mod upload;
|
||||
use dever::compiler::{build, project::Project};
|
||||
use std::{
|
||||
path::Path,
|
||||
process::{Command, Output},
|
||||
};
|
||||
|
||||
fn execute(binary: &Path, name: &str, input: &str) -> Output {
|
||||
Command::new(binary).args([name, input]).output().unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn application_commands_execute_with_strict_typed_json_and_original_faults() {
|
||||
let directory = Directory::new();
|
||||
let project = Project::load(&fixture().with_file_name("application")).unwrap();
|
||||
let program = project.check().unwrap();
|
||||
assert_eq!(program.commands.len(), 9);
|
||||
let binary =
|
||||
build::build(&program, &project.sources, "main", &directory.0.join("app")).unwrap();
|
||||
for (name, input, data) in [
|
||||
("greet", r#"{"name":"雪"}"#, r#""[Hello, 雪]""#),
|
||||
("double", r#"{"value":21}"#, "42"),
|
||||
("pair", r#"{"value":41}"#, r#"{"first":41,"second":42}"#),
|
||||
("empty", "{}", "null"),
|
||||
("positive", r#"{"value":7}"#, "7"),
|
||||
("divide", r#"{"value":6,"divisor":2}"#, "3.0"),
|
||||
("maybe", "{}", "null"),
|
||||
("maybe", r#"{"value":null}"#, "null"),
|
||||
("maybe", r#"{"value":7}"#, "7"),
|
||||
(
|
||||
"maybe",
|
||||
r#"{"value":-9223372036854775808}"#,
|
||||
"-9223372036854775808",
|
||||
),
|
||||
(
|
||||
"maybe",
|
||||
r#"{"value":9223372036854775807}"#,
|
||||
"9223372036854775807",
|
||||
),
|
||||
(
|
||||
"echo",
|
||||
r#"{"value":{"id":"a","name":"雪","scores":[1,2]}}"#,
|
||||
r#"{"id":"a","name":"雪","scores":[1,2],"ratio":null}"#,
|
||||
),
|
||||
(
|
||||
"many",
|
||||
r#"{"rows":[{"id":"a","name":"雪","scores":[],"ratio":0.5}]}"#,
|
||||
r#"[{"id":"a","name":"雪","scores":[],"ratio":0.5}]"#,
|
||||
),
|
||||
] {
|
||||
let result = execute(&binary, &format!("demo.greeting.{name}"), input);
|
||||
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\":{data}}}\n")
|
||||
);
|
||||
}
|
||||
let name = "雪".repeat(24000);
|
||||
let input = format!(r#"{{"rows":[{{"id":"a","name":"{name}","scores":[1,2]}}]}}"#);
|
||||
let result = execute(&binary, "demo.greeting.many", &input);
|
||||
assert!(
|
||||
result.status.success(),
|
||||
"{}",
|
||||
String::from_utf8_lossy(&result.stderr)
|
||||
);
|
||||
assert_eq!(
|
||||
String::from_utf8(result.stdout).unwrap(),
|
||||
format!(
|
||||
"{{\"code\":0,\"message\":\"ok\",\"data\":[{{\"id\":\"a\",\"name\":\"{name}\",\"scores\":[1,2],\"ratio\":null}}]}}\n"
|
||||
)
|
||||
);
|
||||
let input =
|
||||
format!(r#"{{"rows":[{{"id":"a","name":"{name}","na\u006de":"duplicate","scores":[]}}]}}"#);
|
||||
let result = execute(&binary, "demo.greeting.many", &input);
|
||||
assert!(!result.status.success());
|
||||
assert!(result.stdout.is_empty());
|
||||
|
||||
for (name, input) in [
|
||||
("double", "{}"),
|
||||
("double", r#"{"value":1,"value":2}"#),
|
||||
("double", r#"{"value":1,"extra":2}"#),
|
||||
("double", "[1]"),
|
||||
("double", r#"{"value":1.5}"#),
|
||||
("double", r#"{"value":9223372036854775808}"#),
|
||||
("double", r#"{"value":"1"}"#),
|
||||
("double", r#"{"value":null}"#),
|
||||
("maybe", r#"{"value":"7"}"#),
|
||||
("maybe", r#"{"value":null,"value":7}"#),
|
||||
("double", r#"{"value":1} {}"#),
|
||||
("echo", r#"{"value":["a","b",[],null]}"#),
|
||||
("echo", r#"{"value":{"id":1,"name":"a","scores":[]}}"#),
|
||||
(
|
||||
"echo",
|
||||
r#"{"value":{"id":"a","name":"b","name":"c","scores":[]}}"#,
|
||||
),
|
||||
(
|
||||
"echo",
|
||||
r#"{"value":{"id":"a","name":"b","scores":[],"extra":0}}"#,
|
||||
),
|
||||
("many", r#"{"rows":[["a","b",[],null]]}"#),
|
||||
(
|
||||
"many",
|
||||
r#"{"rows":[{"id":"a","name":"b","scores":[],"na\u006de":"c"}]}"#,
|
||||
),
|
||||
(
|
||||
"many",
|
||||
r#"{"rows":[{"id":"a","name":"b","scores":[],"ratio":null,"ratio":0.5}]}"#,
|
||||
),
|
||||
(
|
||||
"many",
|
||||
r#"{"rows":[{"id":"a","name":"b","scores":[],"extra":{}}]}"#,
|
||||
),
|
||||
("empty", "[]"),
|
||||
("divide", r#"{"value":1e400,"divisor":2}"#),
|
||||
] {
|
||||
let result = execute(&binary, &format!("demo.greeting.{name}"), input);
|
||||
assert!(!result.status.success(), "accepted {name}: {input}");
|
||||
assert!(result.stdout.is_empty());
|
||||
assert!(
|
||||
String::from_utf8_lossy(&result.stderr).contains("invalid command input"),
|
||||
"{name} {input}: {}",
|
||||
String::from_utf8_lossy(&result.stderr)
|
||||
);
|
||||
}
|
||||
for (name, input, message) in [
|
||||
(
|
||||
"double",
|
||||
r#"{"value":9223372036854775807}"#,
|
||||
"integer arithmetic fault",
|
||||
),
|
||||
("positive", r#"{"value":-1}"#, "Problem.Rejected"),
|
||||
] {
|
||||
let result = execute(&binary, &format!("demo.greeting.{name}"), input);
|
||||
assert!(!result.status.success());
|
||||
assert!(result.stdout.is_empty());
|
||||
let error = String::from_utf8(result.stderr).unwrap();
|
||||
assert!(
|
||||
error.contains("domain.dever:") && error.contains(message),
|
||||
"{error}"
|
||||
);
|
||||
}
|
||||
let result = execute(
|
||||
&binary,
|
||||
"demo.greeting.divide",
|
||||
r#"{"value":1,"divisor":0}"#,
|
||||
);
|
||||
assert!(!result.status.success());
|
||||
assert!(result.stdout.is_empty());
|
||||
assert!(String::from_utf8_lossy(&result.stderr).contains("non-finite Float"));
|
||||
for arguments in [
|
||||
vec![],
|
||||
vec!["demo.greeting.empty"],
|
||||
vec!["missing", "{}"],
|
||||
vec!["demo.greeting.empty", "{}", "extra"],
|
||||
] {
|
||||
let result = Command::new(&binary).args(arguments).output().unwrap();
|
||||
assert!(!result.status.success());
|
||||
assert!(result.stdout.is_empty());
|
||||
}
|
||||
let rejected = directory.0.join("bypass");
|
||||
assert!(
|
||||
build::build(
|
||||
&program,
|
||||
&project.sources,
|
||||
"demo.utility.decorate",
|
||||
&rejected
|
||||
)
|
||||
.is_err()
|
||||
);
|
||||
assert!(!rejected.exists());
|
||||
}
|
||||
|
||||
fn source(root: &Path, relative: &str) {
|
||||
let path = root.join(relative);
|
||||
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
|
||||
std::fs::write(path, "layout validation precedes parsing").unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn application_layout_checks_roles_topics_and_project_root() {
|
||||
for (files, needle) in [
|
||||
(
|
||||
vec!["module/shop/account/main.dever"],
|
||||
"expected component/domain/role",
|
||||
),
|
||||
(
|
||||
vec!["module/shop/account/test.dever"],
|
||||
"role 'test' is not implemented yet",
|
||||
),
|
||||
(
|
||||
vec![
|
||||
"module/shop/account/app.dever",
|
||||
"module/shop/account/app/read.dever",
|
||||
],
|
||||
"not both",
|
||||
),
|
||||
(
|
||||
vec!["module/shop/account/domain/rules/read.dever"],
|
||||
"must be flat",
|
||||
),
|
||||
(
|
||||
vec!["module/shop/account/app/utils.dever"],
|
||||
"generic application",
|
||||
),
|
||||
] {
|
||||
let directory = Directory::new();
|
||||
for path in files {
|
||||
source(&directory.0, path);
|
||||
}
|
||||
let error = Project::load(&directory.0)
|
||||
.err()
|
||||
.expect("invalid application layout accepted");
|
||||
assert!(error.contains(needle), "{error}");
|
||||
}
|
||||
let directory = Directory::new();
|
||||
for path in [
|
||||
"module/shop/account/app/read.dever",
|
||||
"module/shop/account/app/write.dever",
|
||||
"module/shop/account/api.dever",
|
||||
"module/shop/account/job.dever",
|
||||
"module/shop/account/api/admin/read.dever",
|
||||
"module/shop/account/model.dever",
|
||||
"module/shop/account/model/session.dever",
|
||||
] {
|
||||
source(&directory.0, path);
|
||||
}
|
||||
assert!(Project::load(&directory.0).is_ok());
|
||||
for relative in [
|
||||
"module",
|
||||
"module/shop",
|
||||
"module/shop/account/app",
|
||||
"module/shop/account/app/read.dever",
|
||||
] {
|
||||
let error = Project::load(&directory.0.join(relative))
|
||||
.err()
|
||||
.expect("project root bypass accepted");
|
||||
assert!(error.contains("project root"), "{error}");
|
||||
}
|
||||
let linked = Directory::new();
|
||||
std::os::unix::fs::symlink(directory.0.join("module"), linked.0.join("module")).unwrap();
|
||||
assert!(
|
||||
Project::load(&linked.0)
|
||||
.err()
|
||||
.unwrap()
|
||||
.contains("not a link")
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user