use std::collections::HashSet;
use pretty_assertions::assert_eq;
use toql::mock_db::MockDb;
use toql::prelude::{
fields, paths, query, Cache, ContextBuilder, Join, SqlBuilderError, Toql, ToqlApi, ToqlError,
};
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(join)]
level2: Option<Join<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,
text: Option<String>, }
fn populated_level() -> Level1 {
let l2 = Level2 {
id: 2,
text: Some("level2".to_string()),
};
Level1 {
id: 1,
text: Some("level1".to_string()),
level2: Some(Join::with_entity(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);
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 ``".to_string()
))
.to_string()
);
let mut toql = toql_for_roles(&["load1_role", "load2_role"], &cache);
let q = query!(Level1, "level2_*");
assert!(toql.load_many(q).await.is_ok());
assert_eq!(
toql.take_unsafe_sql(),
"SELECT level1.id, level1_level2.id, level1_level2.text \
FROM Level1 level1 \
JOIN (Level2 level1_level2) ON (level1.level2_id = level1_level2.id)"
);
}
#[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\', level2_id = 2 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_eq!(
toql.take_unsafe_sql(),
"UPDATE Level2 SET text = \'level2\' WHERE id = 2"
);
}
#[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, level2_id) VALUES (\'level1\', 2)"
);
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 Level2 (text) VALUES (\'level2\')",
"INSERT INTO Level1 (text, level2_id) VALUES (\'level1\', 100)",
]
);
}
#[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_eq!(
toql.take_unsafe_sql(),
"DELETE level1 FROM Level1 level1 JOIN (Level2 level1_level2) ON (level1.level2_id = level1_level2.id) WHERE level1_level2.id = 2"
);
}