178 lines
6.2 KiB
Rust
178 lines
6.2 KiB
Rust
use super::*;
|
|
use dever_runtime::{
|
|
database::{Database, Options, Sql},
|
|
task::{Limits, Runtime},
|
|
};
|
|
|
|
// 验收只读取显式测试连接,并独占一个新 schema;失败也清理本次创建的对象。
|
|
pub(in crate::application) struct Schema {
|
|
database: Database,
|
|
runtime: Runtime,
|
|
drop_sql: &'static str,
|
|
pub connection: serde_json::Value,
|
|
}
|
|
|
|
impl Drop for Schema {
|
|
fn drop(&mut self) {
|
|
self.runtime
|
|
.run(async |scope| {
|
|
self.database
|
|
.executor()
|
|
.await
|
|
.unwrap()
|
|
.initialize(scope, Sql::same(self.drop_sql))
|
|
.await
|
|
.unwrap();
|
|
})
|
|
.unwrap();
|
|
self.database.close();
|
|
}
|
|
}
|
|
|
|
impl Schema {
|
|
pub fn new(
|
|
directory: &Path,
|
|
name: &'static str,
|
|
create_sql: &'static str,
|
|
drop_sql: &'static str,
|
|
) -> Self {
|
|
let root = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap();
|
|
let settings: serde_json::Value =
|
|
serde_json::from_slice(&std::fs::read(root.join("config/setting.json")).unwrap())
|
|
.unwrap();
|
|
let mut connection = settings["database"]["postgres_test"].clone();
|
|
let options: Options = serde_json::from_value(connection.clone()).unwrap();
|
|
assert_eq!(options.driver, "postgres");
|
|
let runtime = Runtime::new(Limits::default()).unwrap();
|
|
let database = runtime
|
|
.run(async |scope| {
|
|
let database = Database::open(directory, options, scope).await.unwrap();
|
|
database
|
|
.executor()
|
|
.await
|
|
.unwrap()
|
|
.initialize(scope, Sql::same(create_sql))
|
|
.await
|
|
.unwrap();
|
|
database
|
|
})
|
|
.unwrap();
|
|
let mut schema = Schema {
|
|
database,
|
|
runtime,
|
|
drop_sql,
|
|
connection: serde_json::Value::Null,
|
|
};
|
|
let mut url = url::Url::parse(connection["url"].as_str().unwrap()).unwrap();
|
|
assert!(
|
|
!url.query_pairs().any(|(key, _)| key == "options"),
|
|
"test connection must not override search_path"
|
|
);
|
|
url.query_pairs_mut()
|
|
.append_pair("options", &format!("-csearch_path={name}"));
|
|
connection["url"] = serde_json::Value::String(url.into());
|
|
schema.connection = connection;
|
|
schema
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
#[ignore = "requires explicit isolated config/setting.json database.postgres_test"]
|
|
fn postgres_dever_queries_roundtrip_and_rollback() {
|
|
let directory = Directory::new();
|
|
let schema = Schema::new(
|
|
&directory.0,
|
|
"dever_native_queries",
|
|
"CREATE SCHEMA dever_native_queries",
|
|
"DROP SCHEMA dever_native_queries CASCADE",
|
|
);
|
|
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("postgres"),
|
|
)
|
|
.unwrap();
|
|
std::fs::create_dir(directory.0.join("config")).unwrap();
|
|
std::fs::write(
|
|
directory.0.join("config/setting.json"),
|
|
serde_json::to_vec(&serde_json::json!({"database":{"default":schema.connection}})).unwrap(),
|
|
)
|
|
.unwrap();
|
|
let run = |name: &str, input: serde_json::Value| {
|
|
let output = execute(&binary, &format!("demo.account.{name}"), &input.to_string());
|
|
assert!(
|
|
output.status.success(),
|
|
"{name}: {}",
|
|
String::from_utf8_lossy(&output.stderr)
|
|
);
|
|
serde_json::from_slice::<serde_json::Value>(&output.stdout).unwrap()["data"].clone()
|
|
};
|
|
let first = run("create", serde_json::json!({"name":"雪_%' OR 1=1 --"}));
|
|
assert_eq!(&first["uid"].as_str().unwrap()[14..15], "7");
|
|
assert_eq!(run("get", serde_json::json!({"id":first["id"]})), first);
|
|
assert_eq!(
|
|
run("probe", serde_json::json!({"needle":"_%'"}))["contained"],
|
|
1
|
|
);
|
|
assert_eq!(
|
|
run("probe", serde_json::json!({"needle":""}))["prefixed"],
|
|
1
|
|
);
|
|
assert_eq!(
|
|
run("bulk", serde_json::json!({"first":"one","second":"two"})),
|
|
2
|
|
);
|
|
let recent = run("recent", serde_json::json!({}));
|
|
let items = recent["items"].as_array().unwrap();
|
|
assert_eq!(items.len(), 3);
|
|
assert_eq!(items[0]["rank"], 9);
|
|
assert_eq!(items[1]["rank"], 7);
|
|
let unique: std::collections::HashSet<_> = items
|
|
.iter()
|
|
.map(|row| row["uid"].as_str().unwrap())
|
|
.collect();
|
|
assert_eq!(unique.len(), 3);
|
|
let created = run(
|
|
"save",
|
|
serde_json::json!({"name":"upsert","initial":"first","replacement":"ignored"}),
|
|
);
|
|
let updated = run(
|
|
"save",
|
|
serde_json::json!({"name":"upsert","initial":"ignored","replacement":"changed"}),
|
|
);
|
|
assert_eq!(updated["id"], created["id"]);
|
|
assert_eq!(updated["uid"], created["uid"]);
|
|
assert_eq!(updated["created_at"], created["created_at"]);
|
|
assert_eq!(updated["note"], "changed");
|
|
assert_eq!(run("retain", serde_json::json!({"name":"upsert"})), updated);
|
|
let page = run("scan", serde_json::json!({"after":null,"size":2}));
|
|
assert_eq!(page["has_more"], true);
|
|
let next = run("scan", serde_json::json!({"after":page["next"],"size":2}));
|
|
assert_eq!(next["has_more"], false);
|
|
assert_eq!(next["items"].as_array().unwrap().len(), 2);
|
|
assert_ne!(page["items"][0]["id"], next["items"][0]["id"]);
|
|
let count = run("count", serde_json::json!({}));
|
|
for (command, input) in [
|
|
(
|
|
"bulk",
|
|
serde_json::json!({"first":"rollback","second":"one"}),
|
|
),
|
|
(
|
|
"failed_bulk",
|
|
serde_json::json!({"first":"rollback1","second":"rollback2","value":i64::MAX}),
|
|
),
|
|
("create", serde_json::json!({"name":"embedded\0nul"})),
|
|
] {
|
|
let failed = execute(
|
|
&binary,
|
|
&format!("demo.account.{command}"),
|
|
&input.to_string(),
|
|
);
|
|
assert!(!failed.status.success(), "{command}");
|
|
assert_eq!(run("count", serde_json::json!({})), count);
|
|
}
|
|
}
|