use std::collections::HashSet;
use pretty_assertions::assert_eq;
use toql::mock_db::MockDb;
use toql::prelude::{
fields, paths, query, Cache, ContextBuilder, SqlBuilderError, Toql, ToqlApi, ToqlError,
};
use toql::row;
use tracing_test::traced_test;
#[derive(Debug, Default, Toql)]
#[toql(
auto_key,
roles(
load = "load1_role",
insert = "ins1_role",
update = "upd1_role",
delete = "del1_role"
)
)]
pub struct Level1 {
#[toql(key)]
id: u64,
text: Option<String>,
#[toql(merge)]
level2: Option<Vec<Level2>>,
}
#[derive(Debug, Default, Toql)]
#[toql(
auto_key,
roles(
load = "load2_role",
insert = "ins2_role",
update = "upd2_role",
delete = "del2_role"
)
)]
pub struct Level2 {
#[toql(key)]
id: u64,
#[toql(foreign_key)]
level1_id: u64,
text: Option<String>, }
fn populated_level() -> Level1 {
let l2 = Level2 {
id: 0, level1_id: 1,
text: Some("level2".to_string()),
};
Level1 {
id: 1,
text: Some("level1".to_string()),
level2: Some(vec![l2]),
}
}
fn toql_for_roles<'a>(role_names: &[&str], cache: &'a Cache) -> MockDb<'a> {
let mut roles = HashSet::new();
role_names.into_iter().for_each(|r| {
roles.insert(r.to_string());
});
let context = ContextBuilder::new().with_roles(roles).build();
MockDb::with_context(cache, context)
}
#[tokio::test]
#[traced_test("info")]
async fn load() {
let cache = Cache::new();
let mut toql = MockDb::from(&cache);
let q = query!(Level1, "*");
let err = toql.load_many(q).await.err().unwrap();
assert_eq!(
err.to_string(),
ToqlError::SqlBuilderError(SqlBuilderError::RoleRequired(
"load1_role".to_string(),
"mapper `Level1`".to_string()
))
.to_string()
);
let mut toql = toql_for_roles(&["load1_role"], &cache);
let q = query!(Level1, "*");
assert!(toql.load_many(q).await.is_ok());
assert_eq!(
toql.take_unsafe_sql(),
"SELECT level1.id, level1.text FROM Level1 level1"
);
let mut toql = toql_for_roles(&["load1_role"], &cache);
toql.mock_rows("SELECT level1.id FROM Level1 level1", vec![row!(1u64)]);
let q = query!(Level1, "level2_*");
let err = toql.load_many(q).await.err().unwrap();
assert_eq!(
err.to_string(),
ToqlError::SqlBuilderError(SqlBuilderError::RoleRequired(
"load2_role".to_string(),
"path `level2`".to_string()
))
.to_string()
);
let mut toql = toql_for_roles(&["load1_role", "load2_role"], &cache);
toql.mock_rows("SELECT level1.id FROM Level1 level1", vec![row!(1u64)]);
toql.mock_rows(
"SELECT level1.id, level2.id, level2.level1_id, level2.text \
FROM Level2 level2 \
JOIN Level1 level1 ON (level1.id = level2.level1_id AND level1.id = 1)",
vec![row!(1u64, 2u64, 1u64, "level2")],
);
let q = query!(Level1, "level2_*");
assert!(toql.load_many(q).await.is_ok());
assert_eq!(
toql.take_unsafe_sqls(),
[
"SELECT level1.id FROM Level1 level1",
"SELECT level1.id, level2.id, level2.level1_id, level2.text \
FROM Level2 level2 \
JOIN Level1 level1 ON (level1.id = level2.level1_id AND level1.id = 1)"
]
);
}
#[tokio::test]
#[traced_test("info")]
async fn update() {
let cache = Cache::new();
let mut toql = MockDb::from(&cache);
let mut l = populated_level();
let err = toql
.update_one(&mut l, fields!(Level1, "*"))
.await
.err()
.unwrap();
assert_eq!(
err.to_string(),
ToqlError::SqlBuilderError(SqlBuilderError::RoleRequired(
"upd1_role".to_string(),
"mapper `Level1`".to_string()
))
.to_string()
);
let mut toql = toql_for_roles(&["upd1_role"], &cache);
assert!(toql.update_one(&mut l, fields!(Level1, "*")).await.is_ok());
assert_eq!(
toql.take_unsafe_sql(),
"UPDATE Level1 SET text = 'level1' WHERE id = 1"
);
let mut toql = toql_for_roles(&["upd2_role"], &cache);
assert!(toql
.update_one(&mut l, fields!(Level1, "level2_*"))
.await
.is_ok());
assert!(toql.sqls_empty());
}
#[tokio::test]
#[traced_test("info")]
async fn insert() {
let cache = Cache::new();
let mut toql = MockDb::from(&cache);
let mut l = populated_level();
let err = toql
.insert_one(&mut l, paths!(Level1, ""))
.await
.err()
.unwrap();
assert_eq!(
err.to_string(),
ToqlError::SqlBuilderError(SqlBuilderError::RoleRequired(
"ins1_role".to_string(),
"mapper `Level1`".to_string()
))
.to_string()
);
let mut toql = toql_for_roles(&["ins1_role"], &cache);
assert!(toql.insert_one(&mut l, paths!(Level1, "")).await.is_ok());
assert_eq!(
toql.take_unsafe_sql(),
"INSERT INTO Level1 (text) VALUES ('level1')"
);
let mut toql = toql_for_roles(&["ins2_role"], &cache);
let err = toql
.insert_one(&mut l, paths!(Level1, "level2"))
.await
.err()
.unwrap();
assert_eq!(
err.to_string(),
ToqlError::SqlBuilderError(SqlBuilderError::RoleRequired(
"ins1_role".to_string(),
"mapper `Level1`".to_string()
))
.to_string()
);
let mut toql = toql_for_roles(&["ins1_role", "ins2_role"], &cache);
assert!(toql
.insert_one(&mut l, paths!(Level1, "level2"))
.await
.is_ok());
assert_eq!(
toql.take_unsafe_sqls(),
[
"INSERT INTO Level1 (text) VALUES (\'level1\')",
"INSERT INTO Level2 (level1_id, text) VALUES (100, \'level2\')"
]
);
}
#[tokio::test]
#[traced_test("info")]
async fn delete() {
let cache = Cache::new();
let mut toql = MockDb::from(&cache);
let q = query!(Level1, "id eq 1");
let err = toql.delete_many(q).await.err().unwrap();
assert_eq!(
err.to_string(),
ToqlError::SqlBuilderError(SqlBuilderError::RoleRequired(
"del1_role".to_string(),
"mapper `Level1`".to_string()
))
.to_string()
);
let mut toql = toql_for_roles(&["del1_role"], &cache);
let q = query!(Level1, "id eq 1");
assert!(toql.delete_many(q).await.is_ok());
assert_eq!(
toql.take_unsafe_sql(),
"DELETE level1 FROM Level1 level1 WHERE level1.id = 1"
);
let q = query!(Level1, "level2_id eq 2");
assert!(toql.delete_many(q).await.is_ok());
assert!(toql.sqls_empty());
}