Initial Dever 0.0.1
This commit is contained in:
commit
81233d098c
1479 files changed
+163782
No files matched your search
@@ -0,0 +1,246 @@
|
||||
use super::*;
|
||||
mod defaults;
|
||||
mod migration;
|
||||
#[cfg(feature = "postgres")]
|
||||
pub(super) mod postgres;
|
||||
mod query;
|
||||
mod read;
|
||||
mod relation;
|
||||
mod scalar;
|
||||
mod text;
|
||||
|
||||
#[test]
|
||||
fn database_source_compiles_and_persists_with_entry_rollback() {
|
||||
let directory = Directory::new();
|
||||
let project = Project::load(&fixture().with_file_name("database")).unwrap();
|
||||
let program = project.check().unwrap();
|
||||
let binary = build::build(
|
||||
&program,
|
||||
&project.sources,
|
||||
"main",
|
||||
&directory.0.join("database"),
|
||||
)
|
||||
.unwrap();
|
||||
std::fs::create_dir(directory.0.join("config")).unwrap();
|
||||
std::fs::write(directory.0.join("config/setting.json"), r#"{"database":{"default":{"type":"sqlite","path":"data/account.db","max_connections":1,"max_page_size":20}}}"#).unwrap();
|
||||
let run = |name: &str, input: &str| {
|
||||
let result = execute(&binary, &format!("demo.account.{name}"), input);
|
||||
assert!(
|
||||
result.status.success(),
|
||||
"{name}: {}",
|
||||
String::from_utf8_lossy(&result.stderr)
|
||||
);
|
||||
serde_json::from_slice::<serde_json::Value>(&result.stdout).unwrap()["data"].clone()
|
||||
};
|
||||
let created = run("create", r#"{"name":"Dever 中文"}"#);
|
||||
assert_eq!(created["id"], 1);
|
||||
assert_eq!(created["name"], "Dever 中文");
|
||||
assert!(created["note"].is_null());
|
||||
assert!(created["created_at"].as_str().unwrap().ends_with('Z'));
|
||||
assert_eq!(run("get", r#"{"id":1}"#), created);
|
||||
for input in [
|
||||
r#"{"name":"Dever 中文"}"#.to_owned(),
|
||||
r#"{"name":""}"#.to_owned(),
|
||||
serde_json::json!({"name": "雪".repeat(65)}).to_string(),
|
||||
] {
|
||||
assert!(
|
||||
!execute(&binary, "demo.account.create", &input)
|
||||
.status
|
||||
.success()
|
||||
);
|
||||
}
|
||||
assert_eq!(
|
||||
run("rename", r#"{"id":1,"name":"updated"}"#)["name"],
|
||||
"updated"
|
||||
);
|
||||
let page = run("search", r#"{"name":"updated","page":1,"size":2}"#);
|
||||
assert_eq!(page["items"][0]["id"], 1);
|
||||
assert_eq!(page["total"], 1);
|
||||
assert_eq!(page["pages"], 1);
|
||||
assert_eq!(
|
||||
run("search", r#"{"name":"updated","page":2,"size":2}"#)["items"],
|
||||
serde_json::json!([])
|
||||
);
|
||||
assert_eq!(run("find", "{}")["id"], 1);
|
||||
assert!(run("find", r#"{"note":"missing"}"#).is_null());
|
||||
assert!(
|
||||
!execute(
|
||||
&binary,
|
||||
"demo.account.search",
|
||||
r#"{"name":"updated","page":1,"size":21}"#
|
||||
)
|
||||
.status
|
||||
.success()
|
||||
);
|
||||
let failed = execute(
|
||||
&binary,
|
||||
"demo.account.explode",
|
||||
r#"{"name":"rollback","value":9223372036854775807}"#,
|
||||
);
|
||||
assert!(!failed.status.success());
|
||||
assert!(String::from_utf8_lossy(&failed.stderr).contains("integer arithmetic fault"));
|
||||
assert_eq!(run("count", "{}"), 1);
|
||||
assert_eq!(run("remove", r#"{"id":1}"#), 1);
|
||||
assert_eq!(run("count", "{}"), 0);
|
||||
let embedded_nul = format!("{}\0", "雪".repeat(63));
|
||||
let nul_record = run(
|
||||
"create",
|
||||
&serde_json::json!({"name": embedded_nul}).to_string(),
|
||||
);
|
||||
assert_eq!(nul_record["name"], embedded_nul);
|
||||
assert!(
|
||||
!execute(
|
||||
&binary,
|
||||
"demo.account.create",
|
||||
&serde_json::json!({"name": format!("{}\0", "雪".repeat(64))}).to_string()
|
||||
)
|
||||
.status
|
||||
.success()
|
||||
);
|
||||
assert_eq!(
|
||||
run(
|
||||
"remove",
|
||||
&serde_json::json!({"id": nul_record["id"]}).to_string()
|
||||
),
|
||||
1
|
||||
);
|
||||
let missing = execute(&binary, "demo.account.get", r#"{"id":1}"#);
|
||||
assert!(!missing.status.success());
|
||||
assert!(String::from_utf8_lossy(&missing.stderr).contains("database operation failed"));
|
||||
assert!(
|
||||
!execute(
|
||||
&binary,
|
||||
"demo.account.stage",
|
||||
r#"{"name":"committed stage","value":9223372036854775807}"#
|
||||
)
|
||||
.status
|
||||
.success()
|
||||
);
|
||||
assert_eq!(run("count", "{}"), 1);
|
||||
let stage = run("find", "{}");
|
||||
assert_eq!(stage["name"], "committed stage");
|
||||
run(
|
||||
"remove",
|
||||
&serde_json::json!({"id": stage["id"]}).to_string(),
|
||||
);
|
||||
http_round_trip(&binary, &directory.0);
|
||||
}
|
||||
|
||||
fn http_round_trip(binary: &Path, directory: &Path) {
|
||||
use super::http::{Server, request};
|
||||
use std::{
|
||||
net::{TcpListener, TcpStream},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
|
||||
let port = listener.local_addr().unwrap().port();
|
||||
drop(listener);
|
||||
let config = directory.join("setting-http.json");
|
||||
std::fs::write(&config, format!(r#"{{"http":{{"host":"127.0.0.1","port":{port}}},"sites":{{"public":{{"path":"","hosts":["fixture.test"]}}}},"database":{{"default":{{"type":"sqlite","path":"data/account.db","max_connections":1,"max_page_size":20}}}}}}"#)).unwrap();
|
||||
let mut server = Server(
|
||||
Command::new(binary)
|
||||
.args(["--config", config.to_str().unwrap()])
|
||||
.stdout(std::process::Stdio::null())
|
||||
.spawn()
|
||||
.unwrap(),
|
||||
);
|
||||
let deadline = Instant::now() + Duration::from_secs(5);
|
||||
while TcpStream::connect(("127.0.0.1", port)).is_err() {
|
||||
assert!(server.0.try_wait().unwrap().is_none());
|
||||
assert!(Instant::now() < deadline);
|
||||
std::thread::sleep(Duration::from_millis(10));
|
||||
}
|
||||
let (status, body) = request(
|
||||
port,
|
||||
"POST",
|
||||
"/demo/account/create",
|
||||
"fixture.test",
|
||||
"Content-Type: application/json\r\n",
|
||||
r#"{"name":"HTTP 中文"}"#,
|
||||
);
|
||||
assert_eq!(status, 200, "{body}");
|
||||
let created: serde_json::Value = serde_json::from_str(&body).unwrap();
|
||||
let id = created["data"]["id"].as_i64().unwrap();
|
||||
let (status, body) = request(
|
||||
port,
|
||||
"GET",
|
||||
&format!("/demo/account/get?id={id}"),
|
||||
"fixture.test",
|
||||
"",
|
||||
"",
|
||||
);
|
||||
assert_eq!(status, 200, "{body}");
|
||||
assert_eq!(
|
||||
serde_json::from_str::<serde_json::Value>(&body).unwrap(),
|
||||
created
|
||||
);
|
||||
assert_eq!(
|
||||
request(
|
||||
port,
|
||||
"POST",
|
||||
"/demo/account/explode",
|
||||
"fixture.test",
|
||||
"Content-Type: application/json\r\n",
|
||||
r#"{"name":"rollback","value":9223372036854775807}"#
|
||||
)
|
||||
.0,
|
||||
500
|
||||
);
|
||||
assert_eq!(
|
||||
request(
|
||||
port,
|
||||
"GET",
|
||||
"/demo/account/get?id=99999",
|
||||
"fixture.test",
|
||||
"",
|
||||
""
|
||||
)
|
||||
.0,
|
||||
500
|
||||
);
|
||||
let count = execute(binary, "demo.account.count", "{}");
|
||||
assert!(count.status.success());
|
||||
assert_eq!(
|
||||
serde_json::from_slice::<serde_json::Value>(&count.stdout).unwrap()["data"],
|
||||
1
|
||||
);
|
||||
assert_eq!(
|
||||
request(
|
||||
port,
|
||||
"POST",
|
||||
"/demo/account/stage",
|
||||
"fixture.test",
|
||||
"Content-Type: application/json\r\n",
|
||||
r#"{"name":"HTTP committed stage","value":9223372036854775807}"#
|
||||
)
|
||||
.0,
|
||||
500
|
||||
);
|
||||
let count = execute(binary, "demo.account.count", "{}");
|
||||
assert!(count.status.success());
|
||||
assert_eq!(
|
||||
serde_json::from_slice::<serde_json::Value>(&count.stdout).unwrap()["data"],
|
||||
2
|
||||
);
|
||||
assert!(
|
||||
Command::new("sh")
|
||||
.args([
|
||||
"-c",
|
||||
"kill -TERM \"$1\"",
|
||||
"dever-test",
|
||||
&server.0.id().to_string()
|
||||
])
|
||||
.status()
|
||||
.unwrap()
|
||||
.success()
|
||||
);
|
||||
let deadline = Instant::now() + Duration::from_secs(5);
|
||||
loop {
|
||||
if let Some(status) = server.0.try_wait().unwrap() {
|
||||
assert!(status.success());
|
||||
break;
|
||||
}
|
||||
assert!(Instant::now() < deadline);
|
||||
std::thread::sleep(Duration::from_millis(10));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user