Initial Dever 0.0.1
This commit is contained in:
commit
81233d098c
1479 files changed
+163782
No files matched your search
@@ -0,0 +1,177 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user