247 lines
7.7 KiB
Rust
247 lines
7.7 KiB
Rust
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));
|
|
}
|
|
}
|