176 lines
5.2 KiB
Rust
176 lines
5.2 KiB
Rust
#[path = "support/orm_fixture.rs"]
|
|
mod orm_fixture;
|
|
mod support;
|
|
|
|
use std::fs;
|
|
|
|
use dever_runtime::config::RuntimeProfile;
|
|
use dever_runtime::sqlite::Database;
|
|
use orm_fixture::DatabaseState;
|
|
|
|
#[test]
|
|
fn shared_orm_fixture_remains_valid_without_opening_a_database() {
|
|
let sources = orm_fixture::sources();
|
|
let program = dever_core::check(&sources).unwrap_or_else(|errors| {
|
|
panic!(
|
|
"{}",
|
|
errors
|
|
.iter()
|
|
.map(|error| error.render(&sources))
|
|
.collect::<String>()
|
|
)
|
|
});
|
|
assert!(
|
|
program
|
|
.model_snapshot()
|
|
.contains("model app.user.model name=User")
|
|
);
|
|
}
|
|
|
|
async fn database_state(database: &Database) -> DatabaseState {
|
|
let columns = database
|
|
.query(
|
|
"SELECT name FROM pragma_table_info('user') ORDER BY cid",
|
|
vec![],
|
|
)
|
|
.await
|
|
.unwrap();
|
|
let managed_index_count = database
|
|
.query(
|
|
"SELECT COUNT(*) FROM sqlite_schema WHERE type = 'index' AND tbl_name = 'user' AND name LIKE '_dever_idx_%'",
|
|
vec![],
|
|
)
|
|
.await
|
|
.unwrap();
|
|
let history = database
|
|
.query(
|
|
"SELECT kind, revision FROM _dever_history WHERE model = 'app.user.model' AND kind IN ('schema', 'seed') ORDER BY kind",
|
|
vec![],
|
|
)
|
|
.await
|
|
.unwrap();
|
|
let users = database
|
|
.query(
|
|
"SELECT email, display_name, status, age, alias FROM user ORDER BY email",
|
|
vec![],
|
|
)
|
|
.await
|
|
.unwrap();
|
|
orm_fixture::database_state(columns, managed_index_count, history, users)
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn sqlite_executes_the_shared_orm_fixture() {
|
|
let profile = RuntimeProfile {
|
|
sqlite: true,
|
|
postgres: false,
|
|
};
|
|
let main = orm_fixture::compile("app.user.main", profile);
|
|
let rollback = orm_fixture::compile("app.user.rollback", profile);
|
|
assert_eq!(main.revisions, rollback.revisions);
|
|
|
|
let root = main.native.executable().parent().unwrap();
|
|
let setting = r#"{
|
|
"database": {
|
|
"default": {
|
|
"type": "sqlite",
|
|
"path": "data/db/app.db",
|
|
"max_connections": 1,
|
|
"max_page_size": 10
|
|
}
|
|
}
|
|
}"#;
|
|
orm_fixture::configure(main.native.executable(), setting);
|
|
orm_fixture::assert_main_output(orm_fixture::run(main.native.executable()));
|
|
|
|
let database = Database::open(root.join("data/db/app.db"), 1, 10).unwrap();
|
|
let committed = database_state(&database).await;
|
|
orm_fixture::assert_committed_state(&committed, &main.revisions);
|
|
drop(database);
|
|
|
|
let rollback_executable = root.join("rollback");
|
|
fs::copy(rollback.native.executable(), &rollback_executable).unwrap();
|
|
let output = orm_fixture::run(&rollback_executable);
|
|
assert!(!output.status.success());
|
|
let stderr = String::from_utf8(output.stderr).unwrap();
|
|
assert!(stderr.contains("UNIQUE constraint failed"), "{stderr}");
|
|
|
|
let database = Database::open(root.join("data/db/app.db"), 1, 10).unwrap();
|
|
assert_eq!(database_state(&database).await, committed);
|
|
}
|
|
|
|
#[test]
|
|
fn database_failures_are_captured_after_transaction_rollback_and_task_wait() {
|
|
let mut sources = dever_core::source::SourceMap::default();
|
|
sources.add(
|
|
"app/item/model.dever",
|
|
r#"type Item { name: Text unique }
|
|
"#,
|
|
);
|
|
sources.add(
|
|
"app/item/app.dever",
|
|
r#"type ReadResult {
|
|
Found(value: app.item.model.Item)
|
|
error Failed(error: dever.database.Error)
|
|
}
|
|
transaction missing() (value: app.item.model.Item) {
|
|
saved = model.create({ name = "temporary" })
|
|
removed = model.delete(saved.id)
|
|
value = model.get(saved.id)
|
|
}
|
|
transaction create_duplicate() (value: app.item.model.Item) {
|
|
saved = model.create({ name = "duplicate" })
|
|
value = model.create({ name = "duplicate" })
|
|
}
|
|
task_missing() (value: app.item.model.Item) {
|
|
task = run(missing())
|
|
value = wait(task)
|
|
}
|
|
main() (missing: ReadResult, duplicate: ReadResult, count: Int) {
|
|
missing = result(task_missing())
|
|
duplicate = result(create_duplicate())
|
|
count = model.count()
|
|
}
|
|
"#,
|
|
);
|
|
let program = support::checked(&sources);
|
|
let snapshot = program.api_snapshot();
|
|
assert!(
|
|
snapshot.contains("dever.database.Error.NotFound"),
|
|
"{snapshot}"
|
|
);
|
|
let native = dever_core::native::compile_application(
|
|
&program,
|
|
&sources,
|
|
"app.item.main",
|
|
&std::ffi::OsString::from("rustc"),
|
|
RuntimeProfile {
|
|
sqlite: true,
|
|
postgres: false,
|
|
},
|
|
)
|
|
.unwrap();
|
|
orm_fixture::configure(
|
|
native.executable(),
|
|
r#"{
|
|
"database": { "default": { "type": "sqlite", "path": "data/db/errors.db", "max_connections": 1 } }
|
|
}"#,
|
|
);
|
|
let result = orm_fixture::run(native.executable());
|
|
assert!(
|
|
result.status.success(),
|
|
"{}",
|
|
String::from_utf8_lossy(&result.stderr)
|
|
);
|
|
let output = String::from_utf8(result.stdout).unwrap();
|
|
assert!(
|
|
output.contains("app.item.ReadResult.Failed(dever.database.Error.NotFound("),
|
|
"{output}"
|
|
);
|
|
assert!(
|
|
output.contains("app.item.ReadResult.Failed(dever.database.Error.Constraint("),
|
|
"{output}"
|
|
);
|
|
assert!(output.ends_with("count = 0\n"), "{output}");
|
|
}
|