mod parse;
use parse::Parse;
mod helpers;
use helpers::*;
use std::collections::HashMap;
use std::time::{Duration, SystemTime};
use surrealdb::dbs::Session;
use surrealdb::err::Error;
use surrealdb::iam::Role;
use surrealdb::kvs::{LockType, TransactionType};
use surrealdb::sql::Idiom;
use surrealdb::sql::{Part, Value};
use test_log::test;
use tracing::info;
#[tokio::test]
async fn define_statement_namespace() -> Result<(), Error> {
let sql = "
DEFINE NAMESPACE test;
INFO FOR ROOT;
";
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 2);
let tmp = res.remove(0).result;
assert!(tmp.is_ok(), "{:?}", tmp);
let tmp = res.remove(0).result?;
let val = Value::parse(
"{
accesses: {},
namespaces: { test: 'DEFINE NAMESPACE test' },
nodes: {},
system: {
available_parallelism: 0,
cpu_usage: 0.0f,
load_average: [
0.0f,
0.0f,
0.0f
],
memory_allocated: 0,
memory_usage: 0,
physical_cores: 0,
threads: 0
},
users: {},
}",
);
assert_eq!(tmp, val);
Ok(())
}
#[tokio::test]
async fn define_statement_database() -> Result<(), Error> {
let sql = "
DEFINE DATABASE test;
INFO FOR NS;
";
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 2);
let tmp = res.remove(0).result;
tmp.unwrap();
let tmp = res.remove(0).result?;
let val = Value::parse(
"{
accesses: {},
databases: { test: 'DEFINE DATABASE test' },
users: {},
}",
);
assert_eq!(tmp, val);
Ok(())
}
#[tokio::test]
async fn define_statement_event_when_event() -> Result<(), Error> {
let sql = "
DEFINE EVENT test ON user WHEN $event = 'CREATE' THEN (
CREATE activity SET user = $this, value = $after.email, action = $event
);
REMOVE EVENT test ON user;
DEFINE EVENT test ON TABLE user WHEN $event = 'CREATE' THEN (
CREATE activity SET user = $this, value = $after.email, action = $event
);
INFO FOR TABLE user;
UPSERT user:test SET email = 'info@surrealdb.com', updated_at = time::now();
UPSERT user:test SET email = 'info@surrealdb.com', updated_at = time::now();
UPSERT user:test SET email = 'test@surrealdb.com', updated_at = time::now();
SELECT count() FROM activity GROUP ALL;
";
let mut t = Test::new(sql).await?;
t.skip_ok(3)?;
t.expect_val(
r#"{
events: { test: "DEFINE EVENT test ON user WHEN $event = 'CREATE' THEN (CREATE activity SET user = $this, `value` = $after.email, action = $event)" },
fields: {},
tables: {},
indexes: {},
lives: {},
}"#,
)?;
t.skip_ok(3)?;
t.expect_val(
"[{
count: 1
}]",
)?;
Ok(())
}
#[tokio::test]
async fn define_statement_event_when_logic() -> Result<(), Error> {
let sql = "
DEFINE EVENT test ON user WHEN $before.email != $after.email THEN (
CREATE activity SET user = $this, value = $after.email, action = $event
);
REMOVE EVENT test ON user;
DEFINE EVENT test ON TABLE user WHEN $before.email != $after.email THEN (
CREATE activity SET user = $this, value = $after.email, action = $event
);
INFO FOR TABLE user;
UPSERT user:test SET email = 'info@surrealdb.com', updated_at = time::now();
UPSERT user:test SET email = 'info@surrealdb.com', updated_at = time::now();
UPSERT user:test SET email = 'test@surrealdb.com', updated_at = time::now();
SELECT count() FROM activity GROUP ALL;
";
let mut t = Test::new(sql).await?;
t.skip_ok(3)?;
t.expect_val(
"{
events: { test: 'DEFINE EVENT test ON user WHEN $before.email != $after.email THEN (CREATE activity SET user = $this, `value` = $after.email, action = $event)' },
fields: {},
tables: {},
indexes: {},
lives: {},
}",
)?;
t.skip_ok(3)?;
t.expect_val(
"[{
count: 2
}]",
)?;
Ok(())
}
async fn define_statement_index_concurrently_building_status(
def_index: &str,
skip_def: usize,
initial_size: usize,
appended_size: usize,
) -> Result<(), Error> {
let session = Session::owner().with_ns("test").with_db("test");
let ds = new_ds().await?;
info!("Populate: {}", initial_size);
for i in 0..initial_size {
let mut responses = ds
.execute(
&format!("CREATE user:{i} SET email = 'test{i}@surrealdb.com';"),
&session,
None,
)
.await?;
skip_ok(&mut responses, 1)?;
}
info!("Indexing starts");
let mut r = ds.execute(def_index, &session, None).await?;
assert_eq!(r.len(), skip_def);
skip_ok(&mut r, skip_def)?;
let now = SystemTime::now();
let mut initial_count = None;
let mut pending_count = None;
let mut updated_count = None;
let mut appended_count = 0;
info!("Loop");
let time_out = Duration::from_secs(300);
loop {
if now.elapsed().map_err(|e| Error::Internal(e.to_string()))?.gt(&time_out) {
panic!("Time-out {time_out:?}");
}
if appended_count < appended_size {
let sql = if appended_count % 2 == 0 {
format!(
"UPDATE user:{appended_count} SET email = 'new{appended_count}@surrealdb.com'"
)
} else {
format!("DELETE user:{appended_count}")
};
let mut responses = ds.execute(&sql, &session, None).await?;
skip_ok(&mut responses, 1)?;
appended_count += 1;
}
let mut r = ds.execute("INFO FOR INDEX test ON user", &session, None).await?;
let tmp = r.remove(0).result?;
if let Value::Object(o) = &tmp {
if let Some(Value::Object(o)) = o.get("building") {
if let Some(Value::Strand(s)) = o.get("status") {
let new_initial = o.get("initial").cloned();
let new_pending = o.get("pending").cloned();
let new_updated = o.get("updated").cloned();
match s.as_str() {
"started" => {
info!("Started");
continue;
}
"indexing" => {
{
if new_initial != initial_count {
assert!(new_initial > initial_count, "{new_initial:?}");
info!("New initial count: {:?}", new_initial);
initial_count = new_initial;
}
}
{
if new_pending != pending_count {
info!("New pending count: {:?}", new_pending);
pending_count = new_pending;
}
}
{
if new_updated != updated_count {
assert!(new_updated > updated_count, "{new_updated:?}");
info!("New updated count: {:?}", new_updated);
updated_count = new_updated;
}
}
continue;
}
"ready" => {
let initial = new_initial.unwrap().coerce_to_i64()? as usize;
let pending = new_pending.unwrap().coerce_to_i64()?;
let updated = new_updated.unwrap().coerce_to_i64()? as usize;
assert!(initial > 0, "{initial} > 0");
assert!(initial <= initial_size, "{initial} <= {initial_size}");
assert_eq!(pending, 0);
assert!(updated > 0, "{updated} > 0");
assert!(updated <= appended_count, "{updated} <= appended_count");
break;
}
_ => {}
}
}
}
}
panic!("Invalid info: {tmp:#}");
}
info!("Appended: {appended_count}");
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn define_statement_index_concurrently_building_status_standard() -> Result<(), Error> {
define_statement_index_concurrently_building_status(
"DEFINE INDEX test ON user FIELDS email CONCURRENTLY",
1,
10000,
100,
)
.await
}
#[tokio::test(flavor = "multi_thread")]
async fn define_statement_index_concurrently_building_status_standard_overwrite(
) -> Result<(), Error> {
define_statement_index_concurrently_building_status(
"DEFINE INDEX OVERWRITE test ON user FIELDS email CONCURRENTLY",
1,
10000,
100,
)
.await
}
#[test(tokio::test)]
async fn define_statement_index_concurrently_building_status_full_text() -> Result<(), Error> {
define_statement_index_concurrently_building_status(
"DEFINE ANALYZER simple TOKENIZERS blank,class;
DEFINE INDEX test ON user FIELDS email SEARCH ANALYZER simple BM25 HIGHLIGHTS CONCURRENTLY;",
2,
200,
10,
)
.await
}
#[test(tokio::test)]
async fn define_statement_index_concurrently_building_status_full_text_overwrite(
) -> Result<(), Error> {
define_statement_index_concurrently_building_status(
"DEFINE ANALYZER simple TOKENIZERS blank,class;
DEFINE INDEX OVERWRITE test ON user FIELDS email SEARCH ANALYZER simple BM25 HIGHLIGHTS CONCURRENTLY;",
2,
200, 10
)
.await
}
#[tokio::test]
async fn define_statement_analyzer() -> Result<(), Error> {
let sql = r#"
DEFINE ANALYZER english TOKENIZERS blank,class FILTERS lowercase,snowball(english);
DEFINE ANALYZER autocomplete FILTERS lowercase,edgengram(2,10);
DEFINE FUNCTION fn::stripHtml($html: string) {
RETURN string::replace($html, /<[^>]*>/, "");
};
DEFINE ANALYZER htmlAnalyzer FUNCTION fn::stripHtml TOKENIZERS blank,class;
DEFINE ANALYZER englishLemmatizer TOKENIZERS blank,class FILTERS mapper('../../tests/data/lemmatization-en.txt');
INFO FOR DB;
"#;
let mut t = Test::new(sql).await?;
t.expect_size(6)?;
t.skip_ok(5)?;
t.expect_val(
r#"{
accesses: {},
analyzers: {
autocomplete: 'DEFINE ANALYZER autocomplete FILTERS LOWERCASE,EDGENGRAM(2,10)',
english: 'DEFINE ANALYZER english TOKENIZERS BLANK,CLASS FILTERS LOWERCASE,SNOWBALL(ENGLISH)',
englishLemmatizer: 'DEFINE ANALYZER englishLemmatizer TOKENIZERS BLANK,CLASS FILTERS MAPPER(../../tests/data/lemmatization-en.txt)',
htmlAnalyzer: 'DEFINE ANALYZER htmlAnalyzer FUNCTION fn::stripHtml TOKENIZERS BLANK,CLASS'
},
apis: {},
configs: {},
functions: {
stripHtml: "DEFINE FUNCTION fn::stripHtml($html: string) { RETURN string::replace($html, /<[^>]*>/, ''); } PERMISSIONS FULL"
},
models: {},
params: {},
tables: {},
users: {},
}"#,
)?;
Ok(())
}
#[tokio::test]
async fn define_statement_search_index() -> Result<(), Error> {
let sql = r#"
CREATE blog:1 SET title = 'Understanding SurrealQL and how it is different from PostgreSQL';
CREATE blog:3 SET title = 'This blog is going to be deleted';
DEFINE ANALYZER simple TOKENIZERS blank,class FILTERS lowercase;
DEFINE INDEX blog_title ON blog FIELDS title SEARCH ANALYZER simple BM25(1.2,0.75) HIGHLIGHTS;
CREATE blog:2 SET title = 'Behind the scenes of the exciting beta 9 release';
DELETE blog:3;
INFO FOR TABLE blog;
ANALYZE INDEX blog_title ON blog;
"#;
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 8);
for i in 0..6 {
let tmp = res.remove(0).result;
assert!(tmp.is_ok(), "{}", i);
}
let tmp = res.remove(0).result?;
let val = Value::parse(
"{
events: {},
fields: {},
tables: {},
indexes: { blog_title: 'DEFINE INDEX blog_title ON blog FIELDS title \
SEARCH ANALYZER simple BM25(1.2,0.75) \
DOC_IDS_ORDER 100 DOC_LENGTHS_ORDER 100 POSTINGS_ORDER 100 TERMS_ORDER 100 \
DOC_IDS_CACHE 100 DOC_LENGTHS_CACHE 100 POSTINGS_CACHE 100 TERMS_CACHE 100 HIGHLIGHTS' },
lives: {},
}",
);
assert_eq!(format!("{:#}", tmp), format!("{:#}", val));
let tmp = res.remove(0).result?;
check_path(&tmp, &["doc_ids", "keys_count"], |v| assert_eq!(v, Value::from(2)));
check_path(&tmp, &["doc_ids", "max_depth"], |v| assert_eq!(v, Value::from(1)));
check_path(&tmp, &["doc_ids", "nodes_count"], |v| assert_eq!(v, Value::from(1)));
check_path(&tmp, &["doc_ids", "total_size"], |v| assert_eq!(v, Value::from(62)));
check_path(&tmp, &["doc_lengths", "keys_count"], |v| assert_eq!(v, Value::from(2)));
check_path(&tmp, &["doc_lengths", "max_depth"], |v| assert_eq!(v, Value::from(1)));
check_path(&tmp, &["doc_lengths", "nodes_count"], |v| assert_eq!(v, Value::from(1)));
check_path(&tmp, &["doc_lengths", "total_size"], |v| assert_eq!(v, Value::from(56)));
check_path(&tmp, &["postings", "keys_count"], |v| assert_eq!(v, Value::from(17)));
check_path(&tmp, &["postings", "max_depth"], |v| assert_eq!(v, Value::from(1)));
check_path(&tmp, &["postings", "nodes_count"], |v| assert_eq!(v, Value::from(1)));
check_path(&tmp, &["postings", "total_size"], |v| assert!(v > Value::from(150)));
check_path(&tmp, &["terms", "keys_count"], |v| assert_eq!(v, Value::from(17)));
check_path(&tmp, &["terms", "max_depth"], |v| assert_eq!(v, Value::from(1)));
check_path(&tmp, &["terms", "nodes_count"], |v| assert_eq!(v, Value::from(1)));
check_path(&tmp, &["terms", "total_size"], |v| assert!(v.gt(&Value::from(150))));
Ok(())
}
#[tokio::test]
async fn define_statement_user_root() -> Result<(), Error> {
let sql = "
DEFINE USER test ON ROOT PASSWORD 'test';
INFO FOR ROOT;
";
let dbs = new_ds().await?;
let ses = Session::owner();
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 2);
let tmp = res.remove(0).result;
tmp.unwrap();
let tmp = res.remove(0).result?;
let define_str = tmp.pick(&["users".into(), "test".into()]).to_string();
assert!(define_str
.strip_prefix('\"')
.unwrap()
.starts_with("DEFINE USER test ON ROOT PASSHASH '$argon2id$"));
Ok(())
}
#[tokio::test]
async fn define_statement_user_ns() -> Result<(), Error> {
let dbs = new_ds().await?;
let ses = Session::owner();
let sql = "
USE NS ns;
DEFINE USER test ON NS PASSWORD 'test';
INFO FOR USER test;
INFO FOR USER test ON NS;
INFO FOR USER test ON NAMESPACE;
INFO FOR USER test ON ROOT;
";
let res = dbs.execute(sql, &ses, None).await?;
let mut res = res.into_iter();
res.next().unwrap().result.unwrap();
res.next().unwrap().result.unwrap();
assert!(res
.next()
.unwrap()
.result
.as_ref()
.unwrap()
.to_string()
.starts_with("\"DEFINE USER test ON NAMESPACE PASSHASH '$argon2id$"));
assert!(res
.next()
.unwrap()
.result
.as_ref()
.unwrap()
.to_string()
.starts_with("\"DEFINE USER test ON NAMESPACE PASSHASH '$argon2id$"));
assert!(res
.next()
.unwrap()
.result
.as_ref()
.unwrap()
.to_string()
.starts_with("\"DEFINE USER test ON NAMESPACE PASSHASH '$argon2id$"));
assert_eq!(
res.next().unwrap().result.as_ref().unwrap_err().to_string(),
"The root user 'test' does not exist"
);
let sql = "
DEFINE USER test ON NS PASSWORD 'test';
";
let res = &mut dbs.execute(sql, &ses, None).await?;
assert!(res.remove(0).result.is_err());
Ok(())
}
#[tokio::test]
async fn define_statement_user_db() -> Result<(), Error> {
let dbs = new_ds().await?;
let ses = Session::owner();
let sql = "
USE NS ns;
USE DB db;
DEFINE USER test ON DB PASSWORD 'test';
INFO FOR USER test;
INFO FOR USER test ON DB;
INFO FOR USER test ON DATABASE;
INFO FOR USER test ON NS;
";
let res = &mut dbs.execute(sql, &ses, None).await?;
res[2].result.as_ref().unwrap();
res[3].result.as_ref().unwrap();
res[4].result.as_ref().unwrap();
res[5].result.as_ref().unwrap();
assert_eq!(
res[6].result.as_ref().unwrap_err().to_string(),
"The user 'test' does not exist in the namespace 'ns'"
);
assert!(res[3]
.result
.as_ref()
.unwrap()
.to_string()
.starts_with("\"DEFINE USER test ON DATABASE PASSHASH '$argon2id$"));
assert!(res[4]
.result
.as_ref()
.unwrap()
.to_string()
.starts_with("\"DEFINE USER test ON DATABASE PASSHASH '$argon2id$"));
assert!(res[5]
.result
.as_ref()
.unwrap()
.to_string()
.starts_with("\"DEFINE USER test ON DATABASE PASSHASH '$argon2id$"));
let sql = "
DEFINE USER test ON DB PASSWORD 'test';
";
let res = &mut dbs.execute(sql, &ses, None).await?;
assert!(res.remove(0).result.is_err());
Ok(())
}
fn check_path<F>(val: &Value, path: &[&str], check: F)
where
F: Fn(Value),
{
let part: Vec<Part> = path.iter().map(|p| Part::from(*p)).collect();
let res = val.walk(&part);
for (i, v) in res {
let mut idiom = Idiom::default();
idiom.0.clone_from(&part);
assert_eq!(idiom, i);
check(v);
}
}
#[tokio::test]
async fn permissions_checks_define_ns() {
let scenario = HashMap::from([
("prepare", ""),
("test", "DEFINE NAMESPACE NS"),
("check", "INFO FOR ROOT"),
]);
let access1 = "{ accesses: { }, namespaces: { NS: 'DEFINE NAMESPACE NS' }, nodes: { }, system: { available_parallelism: 0, cpu_usage: 0.0f, load_average: [0.0f, 0.0f, 0.0f], memory_allocated: 0, memory_usage: 0, physical_cores: 0, threads: 0 }, users: { } }";
let access2 = "{ accesses: { }, namespaces: { }, nodes: { }, system: { available_parallelism: 0, cpu_usage: 0.0f, load_average: [0.0f, 0.0f, 0.0f], memory_allocated: 0, memory_usage: 0, physical_cores: 0, threads: 0 }, users: { } }";
let check_results = [vec![access1], vec![access2]];
let test_cases = [
((().into(), Role::Owner), ("NS", "DB"), true),
((().into(), Role::Editor), ("NS", "DB"), true),
((().into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Owner), ("NS", "DB"), false),
((("NS",).into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Editor), ("NS", "DB"), false),
((("NS",).into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Owner), ("NS", "DB"), false),
((("NS", "DB").into(), Role::Owner), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Editor), ("NS", "DB"), false),
((("NS", "DB").into(), Role::Editor), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Viewer), ("OTHER_NS", "DB"), false),
];
let res = iam_check_cases(test_cases.iter(), &scenario, check_results).await;
assert!(res.is_ok(), "{}", res.unwrap_err());
}
#[tokio::test]
async fn permissions_checks_define_db() {
let scenario =
HashMap::from([("prepare", ""), ("test", "DEFINE DATABASE DB"), ("check", "INFO FOR NS")]);
let check_results = [
vec!["{ accesses: { }, databases: { DB: 'DEFINE DATABASE DB' }, users: { } }"],
vec!["{ accesses: { }, databases: { }, users: { } }"],
];
let test_cases = [
((().into(), Role::Owner), ("NS", "DB"), true),
((().into(), Role::Editor), ("NS", "DB"), true),
((().into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Owner), ("NS", "DB"), true),
((("NS",).into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Editor), ("NS", "DB"), true),
((("NS",).into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Owner), ("NS", "DB"), false),
((("NS", "DB").into(), Role::Owner), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Editor), ("NS", "DB"), false),
((("NS", "DB").into(), Role::Editor), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Viewer), ("OTHER_NS", "DB"), false),
];
let res = iam_check_cases(test_cases.iter(), &scenario, check_results).await;
assert!(res.is_ok(), "{}", res.unwrap_err());
}
#[tokio::test]
async fn permissions_checks_define_function() {
let scenario = HashMap::from([
("prepare", ""),
("test", "DEFINE FUNCTION fn::greet() {RETURN \"Hello\";}"),
("check", "INFO FOR DB"),
]);
let check_results = [
vec!["{ accesses: { }, analyzers: { }, apis: { }, configs: { }, functions: { greet: \"DEFINE FUNCTION fn::greet() { RETURN 'Hello'; } PERMISSIONS FULL\" }, models: { }, params: { }, tables: { }, users: { } }"],
vec!["{ accesses: { }, analyzers: { }, apis: { }, configs: { }, functions: { }, models: { }, params: { }, tables: { }, users: { } }"]
];
let test_cases = [
((().into(), Role::Owner), ("NS", "DB"), true),
((().into(), Role::Editor), ("NS", "DB"), true),
((().into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Owner), ("NS", "DB"), true),
((("NS",).into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Editor), ("NS", "DB"), true),
((("NS",).into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Owner), ("NS", "DB"), true),
((("NS", "DB").into(), Role::Owner), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Editor), ("NS", "DB"), true),
((("NS", "DB").into(), Role::Editor), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Viewer), ("OTHER_NS", "DB"), false),
];
let res = iam_check_cases(test_cases.iter(), &scenario, check_results).await;
assert!(res.is_ok(), "{}", res.unwrap_err());
}
#[tokio::test]
async fn permissions_checks_define_analyzer() {
let scenario = HashMap::from([
("prepare", ""),
("test", "DEFINE ANALYZER analyzer TOKENIZERS BLANK"),
("check", "INFO FOR DB"),
]);
let check_results = [
vec!["{ accesses: { }, analyzers: { analyzer: 'DEFINE ANALYZER analyzer TOKENIZERS BLANK' }, apis: { }, configs: { }, functions: { }, models: { }, params: { }, tables: { }, users: { } }"],
vec!["{ accesses: { }, analyzers: { }, apis: { }, configs: { }, functions: { }, models: { }, params: { }, tables: { }, users: { } }"]
];
let test_cases = [
((().into(), Role::Owner), ("NS", "DB"), true),
((().into(), Role::Editor), ("NS", "DB"), true),
((().into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Owner), ("NS", "DB"), true),
((("NS",).into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Editor), ("NS", "DB"), true),
((("NS",).into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Owner), ("NS", "DB"), true),
((("NS", "DB").into(), Role::Owner), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Editor), ("NS", "DB"), true),
((("NS", "DB").into(), Role::Editor), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Viewer), ("OTHER_NS", "DB"), false),
];
let res = iam_check_cases(test_cases.iter(), &scenario, check_results).await;
assert!(res.is_ok(), "{}", res.unwrap_err());
}
#[tokio::test]
async fn permissions_checks_define_access_root() {
let scenario = HashMap::from([
("prepare", ""),
("test", "DEFINE ACCESS access ON ROOT TYPE JWT ALGORITHM HS512 KEY 'secret'"),
("check", "INFO FOR ROOT"),
]);
let access1 = r#"{ accesses: { access: "DEFINE ACCESS access ON ROOT TYPE JWT ALGORITHM HS512 KEY '[REDACTED]' WITH ISSUER KEY '[REDACTED]' DURATION FOR TOKEN 1h, FOR SESSION NONE" }, namespaces: { }, nodes: { }, system: { available_parallelism: 0, cpu_usage: 0.0f, load_average: [0.0f, 0.0f, 0.0f], memory_allocated: 0, memory_usage: 0, physical_cores: 0, threads: 0 }, users: { } }"#;
let access2 = "{ accesses: { }, namespaces: { }, nodes: { }, system: { available_parallelism: 0, cpu_usage: 0.0f, load_average: [0.0f, 0.0f, 0.0f], memory_allocated: 0, memory_usage: 0, physical_cores: 0, threads: 0 }, users: { } }";
let check_results = [vec![access1], vec![access2]];
let test_cases = [
((().into(), Role::Owner), ("NS", "DB"), true),
((().into(), Role::Editor), ("NS", "DB"), false),
((().into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Owner), ("NS", "DB"), false),
((("NS",).into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Editor), ("NS", "DB"), false),
((("NS",).into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Owner), ("NS", "DB"), false),
((("NS", "DB").into(), Role::Owner), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Editor), ("NS", "DB"), false),
((("NS", "DB").into(), Role::Editor), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Viewer), ("OTHER_NS", "DB"), false),
];
let res = iam_check_cases(test_cases.iter(), &scenario, check_results).await;
assert!(res.is_ok(), "{}", res.unwrap_err());
}
#[tokio::test]
async fn permissions_checks_define_access_ns() {
let scenario = HashMap::from([
("prepare", ""),
("test", "DEFINE ACCESS access ON NS TYPE JWT ALGORITHM HS512 KEY 'secret'"),
("check", "INFO FOR NS"),
]);
let check_results = [
vec!["{ accesses: { access: \"DEFINE ACCESS access ON NAMESPACE TYPE JWT ALGORITHM HS512 KEY '[REDACTED]' WITH ISSUER KEY '[REDACTED]' DURATION FOR TOKEN 1h, FOR SESSION NONE\" }, databases: { }, users: { } }"],
vec!["{ accesses: { }, databases: { }, users: { } }"]
];
let test_cases = [
((().into(), Role::Owner), ("NS", "DB"), true),
((().into(), Role::Editor), ("NS", "DB"), false),
((().into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Owner), ("NS", "DB"), true),
((("NS",).into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Editor), ("NS", "DB"), false),
((("NS",).into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Owner), ("NS", "DB"), false),
((("NS", "DB").into(), Role::Owner), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Editor), ("NS", "DB"), false),
((("NS", "DB").into(), Role::Editor), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Viewer), ("OTHER_NS", "DB"), false),
];
let res = iam_check_cases(test_cases.iter(), &scenario, check_results).await;
assert!(res.is_ok(), "{}", res.unwrap_err());
}
#[tokio::test]
async fn permissions_checks_define_access_db() {
let scenario = HashMap::from([
("prepare", ""),
("test", "DEFINE ACCESS access ON DB TYPE JWT ALGORITHM HS512 KEY 'secret'"),
("check", "INFO FOR DB"),
]);
let check_results = [
vec!["{ accesses: { access: \"DEFINE ACCESS access ON DATABASE TYPE JWT ALGORITHM HS512 KEY '[REDACTED]' WITH ISSUER KEY '[REDACTED]' DURATION FOR TOKEN 1h, FOR SESSION NONE\" }, analyzers: { }, apis: { }, configs: { }, functions: { }, models: { }, params: { }, tables: { }, users: { } }"],
vec!["{ accesses: { }, analyzers: { }, apis: { }, configs: { }, functions: { }, models: { }, params: { }, tables: { }, users: { } }"]
];
let test_cases = [
((().into(), Role::Owner), ("NS", "DB"), true),
((().into(), Role::Editor), ("NS", "DB"), false),
((().into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Owner), ("NS", "DB"), true),
((("NS",).into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Editor), ("NS", "DB"), false),
((("NS",).into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Owner), ("NS", "DB"), true),
((("NS", "DB").into(), Role::Owner), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Editor), ("NS", "DB"), false),
((("NS", "DB").into(), Role::Editor), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Viewer), ("OTHER_NS", "DB"), false),
];
let res = iam_check_cases(test_cases.iter(), &scenario, check_results).await;
assert!(res.is_ok(), "{}", res.unwrap_err());
}
#[tokio::test]
async fn permissions_checks_define_user_root() {
let scenario = HashMap::from([
("prepare", ""),
("test", "DEFINE USER user ON ROOT PASSHASH 'secret' ROLES VIEWER DURATION FOR TOKEN 15m, FOR SESSION 6h"),
("check", "INFO FOR ROOT"),
]);
let check1 = r#"{ accesses: { }, namespaces: { }, nodes: { }, system: { available_parallelism: 0, cpu_usage: 0.0f, load_average: [0.0f, 0.0f, 0.0f], memory_allocated: 0, memory_usage: 0, physical_cores: 0, threads: 0 }, users: { user: "DEFINE USER user ON ROOT PASSHASH 'secret' ROLES VIEWER DURATION FOR TOKEN 15m, FOR SESSION 6h" } }"#;
let check2 = "{ accesses: { }, namespaces: { }, nodes: { }, system: { available_parallelism: 0, cpu_usage: 0.0f, load_average: [0.0f, 0.0f, 0.0f], memory_allocated: 0, memory_usage: 0, physical_cores: 0, threads: 0 }, users: { } }";
let check_results = [vec![check1], vec![check2]];
let test_cases = [
((().into(), Role::Owner), ("NS", "DB"), true),
((().into(), Role::Editor), ("NS", "DB"), false),
((().into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Owner), ("NS", "DB"), false),
((("NS",).into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Editor), ("NS", "DB"), false),
((("NS",).into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Owner), ("NS", "DB"), false),
((("NS", "DB").into(), Role::Owner), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Editor), ("NS", "DB"), false),
((("NS", "DB").into(), Role::Editor), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Viewer), ("OTHER_NS", "DB"), false),
];
let res = iam_check_cases(test_cases.iter(), &scenario, check_results).await;
assert!(res.is_ok(), "{}", res.unwrap_err());
}
#[tokio::test]
async fn permissions_checks_define_user_ns() {
let scenario = HashMap::from([
("prepare", ""),
("test", "DEFINE USER user ON NS PASSHASH 'secret' ROLES VIEWER DURATION FOR TOKEN 15m, FOR SESSION 6h"),
("check", "INFO FOR NS"),
]);
let check_results = [
vec!["{ accesses: { }, databases: { }, users: { user: \"DEFINE USER user ON NAMESPACE PASSHASH 'secret' ROLES VIEWER DURATION FOR TOKEN 15m, FOR SESSION 6h\" } }"],
vec!["{ accesses: { }, databases: { }, users: { } }"]
];
let test_cases = [
((().into(), Role::Owner), ("NS", "DB"), true),
((().into(), Role::Editor), ("NS", "DB"), false),
((().into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Owner), ("NS", "DB"), true),
((("NS",).into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Editor), ("NS", "DB"), false),
((("NS",).into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Owner), ("NS", "DB"), false),
((("NS", "DB").into(), Role::Owner), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Editor), ("NS", "DB"), false),
((("NS", "DB").into(), Role::Editor), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Viewer), ("OTHER_NS", "DB"), false),
];
let res = iam_check_cases(test_cases.iter(), &scenario, check_results).await;
assert!(res.is_ok(), "{}", res.unwrap_err());
}
#[tokio::test]
async fn permissions_checks_define_user_db() {
let scenario = HashMap::from([
("prepare", ""),
("test", "DEFINE USER user ON DB PASSHASH 'secret' ROLES VIEWER DURATION FOR TOKEN 15m, FOR SESSION 6h"),
("check", "INFO FOR DB"),
]);
let check_results = [
vec!["{ accesses: { }, analyzers: { }, apis: { }, configs: { }, functions: { }, models: { }, params: { }, tables: { }, users: { user: \"DEFINE USER user ON DATABASE PASSHASH 'secret' ROLES VIEWER DURATION FOR TOKEN 15m, FOR SESSION 6h\" } }"],
vec!["{ accesses: { }, analyzers: { }, apis: { }, configs: { }, functions: { }, models: { }, params: { }, tables: { }, users: { } }"]
];
let test_cases = [
((().into(), Role::Owner), ("NS", "DB"), true),
((().into(), Role::Editor), ("NS", "DB"), false),
((().into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Owner), ("NS", "DB"), true),
((("NS",).into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Editor), ("NS", "DB"), false),
((("NS",).into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Owner), ("NS", "DB"), true),
((("NS", "DB").into(), Role::Owner), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Editor), ("NS", "DB"), false),
((("NS", "DB").into(), Role::Editor), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Viewer), ("OTHER_NS", "DB"), false),
];
let res = iam_check_cases(test_cases.iter(), &scenario, check_results).await;
assert!(res.is_ok(), "{}", res.unwrap_err());
}
#[tokio::test]
async fn permissions_checks_define_access_record() {
let scenario = HashMap::from([
("prepare", ""),
("test", "DEFINE ACCESS account ON DATABASE TYPE RECORD WITH JWT ALGORITHM HS512 KEY 'secret' DURATION FOR TOKEN 15m, FOR SESSION 12h"),
("check", "INFO FOR DB"),
]);
let check_results = [
vec!["{ accesses: { account: \"DEFINE ACCESS account ON DATABASE TYPE RECORD WITH JWT ALGORITHM HS512 KEY '[REDACTED]' WITH ISSUER KEY '[REDACTED]' DURATION FOR TOKEN 15m, FOR SESSION 12h\" }, analyzers: { }, apis: { }, configs: { }, functions: { }, models: { }, params: { }, tables: { }, users: { } }"],
vec!["{ accesses: { }, analyzers: { }, apis: { }, configs: { }, functions: { }, models: { }, params: { }, tables: { }, users: { } }"]
];
let test_cases = [
((().into(), Role::Owner), ("NS", "DB"), true),
((().into(), Role::Editor), ("NS", "DB"), false),
((().into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Owner), ("NS", "DB"), true),
((("NS",).into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Editor), ("NS", "DB"), false),
((("NS",).into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Owner), ("NS", "DB"), true),
((("NS", "DB").into(), Role::Owner), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Editor), ("NS", "DB"), false),
((("NS", "DB").into(), Role::Editor), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Viewer), ("OTHER_NS", "DB"), false),
];
let res = iam_check_cases(test_cases.iter(), &scenario, check_results).await;
assert!(res.is_ok(), "{}", res.unwrap_err());
}
#[tokio::test]
async fn permissions_checks_define_param() {
let scenario = HashMap::from([
("prepare", ""),
("test", "DEFINE PARAM $param VALUE 'foo'"),
("check", "INFO FOR DB"),
]);
let check_results = [
vec!["{ accesses: { }, analyzers: { }, apis: { }, configs: { }, functions: { }, models: { }, params: { param: \"DEFINE PARAM $param VALUE 'foo' PERMISSIONS FULL\" }, tables: { }, users: { } }"],
vec!["{ accesses: { }, analyzers: { }, apis: { }, configs: { }, functions: { }, models: { }, params: { }, tables: { }, users: { } }"]
];
let test_cases = [
((().into(), Role::Owner), ("NS", "DB"), true),
((().into(), Role::Editor), ("NS", "DB"), true),
((().into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Owner), ("NS", "DB"), true),
((("NS",).into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Editor), ("NS", "DB"), true),
((("NS",).into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Owner), ("NS", "DB"), true),
((("NS", "DB").into(), Role::Owner), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Editor), ("NS", "DB"), true),
((("NS", "DB").into(), Role::Editor), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Viewer), ("OTHER_NS", "DB"), false),
];
let res = iam_check_cases(test_cases.iter(), &scenario, check_results).await;
assert!(res.is_ok(), "{}", res.unwrap_err());
}
#[tokio::test]
async fn permissions_checks_define_table() {
let scenario =
HashMap::from([("prepare", ""), ("test", "DEFINE TABLE TB"), ("check", "INFO FOR DB")]);
let check_results = [
vec!["{ accesses: { }, analyzers: { }, apis: { }, configs: { }, functions: { }, models: { }, params: { }, tables: { TB: 'DEFINE TABLE TB TYPE ANY SCHEMALESS PERMISSIONS NONE' }, users: { } }"],
vec!["{ accesses: { }, analyzers: { }, apis: { }, configs: { }, functions: { }, models: { }, params: { }, tables: { }, users: { } }"]
];
let test_cases = [
((().into(), Role::Owner), ("NS", "DB"), true),
((().into(), Role::Editor), ("NS", "DB"), true),
((().into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Owner), ("NS", "DB"), true),
((("NS",).into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Editor), ("NS", "DB"), true),
((("NS",).into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Owner), ("NS", "DB"), true),
((("NS", "DB").into(), Role::Owner), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Editor), ("NS", "DB"), true),
((("NS", "DB").into(), Role::Editor), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Viewer), ("OTHER_NS", "DB"), false),
];
let res = iam_check_cases(test_cases.iter(), &scenario, check_results).await;
assert!(res.is_ok(), "{}", res.unwrap_err());
}
#[tokio::test]
async fn permissions_checks_define_event() {
let scenario = HashMap::from([
("prepare", ""),
("test", "DEFINE EVENT event ON TABLE TB WHEN true THEN RETURN 'foo'"),
("check", "INFO FOR TABLE TB"),
]);
let check_results = [
vec!["{ events: { event: \"DEFINE EVENT event ON TB WHEN true THEN (RETURN 'foo')\" }, fields: { }, indexes: { }, lives: { }, tables: { } }"],
vec!["{ events: { }, fields: { }, indexes: { }, lives: { }, tables: { } }"]
];
let test_cases = [
((().into(), Role::Owner), ("NS", "DB"), true),
((().into(), Role::Editor), ("NS", "DB"), true),
((().into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Owner), ("NS", "DB"), true),
((("NS",).into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Editor), ("NS", "DB"), true),
((("NS",).into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Owner), ("NS", "DB"), true),
((("NS", "DB").into(), Role::Owner), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Editor), ("NS", "DB"), true),
((("NS", "DB").into(), Role::Editor), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Viewer), ("OTHER_NS", "DB"), false),
];
let res = iam_check_cases(test_cases.iter(), &scenario, check_results).await;
assert!(res.is_ok(), "{}", res.unwrap_err());
}
#[tokio::test]
async fn permissions_checks_define_field() {
let scenario = HashMap::from([
("prepare", ""),
("test", "DEFINE FIELD field ON TABLE TB"),
("check", "INFO FOR TABLE TB"),
]);
let check_results = [
vec!["{ events: { }, fields: { field: 'DEFINE FIELD field ON TB PERMISSIONS FULL' }, indexes: { }, lives: { }, tables: { } }"],
vec!["{ events: { }, fields: { }, indexes: { }, lives: { }, tables: { } }"]
];
let test_cases = [
((().into(), Role::Owner), ("NS", "DB"), true),
((().into(), Role::Editor), ("NS", "DB"), true),
((().into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Owner), ("NS", "DB"), true),
((("NS",).into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Editor), ("NS", "DB"), true),
((("NS",).into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Owner), ("NS", "DB"), true),
((("NS", "DB").into(), Role::Owner), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Editor), ("NS", "DB"), true),
((("NS", "DB").into(), Role::Editor), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Viewer), ("OTHER_NS", "DB"), false),
];
let res = iam_check_cases(test_cases.iter(), &scenario, check_results).await;
assert!(res.is_ok(), "{}", res.unwrap_err());
}
#[tokio::test]
async fn permissions_checks_define_index() {
let scenario = HashMap::from([
("prepare", ""),
("test", "DEFINE INDEX index ON TABLE TB FIELDS field"),
("check", "INFO FOR TABLE TB"),
]);
let check_results = [
vec!["{ events: { }, fields: { }, indexes: { index: 'DEFINE INDEX index ON TB FIELDS field' }, lives: { }, tables: { } }"],
vec!["{ events: { }, fields: { }, indexes: { }, lives: { }, tables: { } }"]
];
let test_cases = [
((().into(), Role::Owner), ("NS", "DB"), true),
((().into(), Role::Editor), ("NS", "DB"), true),
((().into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Owner), ("NS", "DB"), true),
((("NS",).into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Editor), ("NS", "DB"), true),
((("NS",).into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("NS", "DB"), false),
((("NS",).into(), Role::Viewer), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Owner), ("NS", "DB"), true),
((("NS", "DB").into(), Role::Owner), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Owner), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Editor), ("NS", "DB"), true),
((("NS", "DB").into(), Role::Editor), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Editor), ("OTHER_NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "DB"), false),
((("NS", "DB").into(), Role::Viewer), ("NS", "OTHER_DB"), false),
((("NS", "DB").into(), Role::Viewer), ("OTHER_NS", "DB"), false),
];
let res = iam_check_cases(test_cases.iter(), &scenario, check_results).await;
assert!(res.is_ok(), "{}", res.unwrap_err());
}
#[tokio::test]
async fn cross_transaction_caching_uuids_updated() -> Result<(), Error> {
let ds = new_ds().await?;
let cache = ds.get_cache();
let ses = Session::owner().with_ns("test").with_db("test").with_rt(true);
let sql = r"DEFINE TABLE test;".to_owned();
let res = &mut ds.execute(&sql, &ses, None).await?;
assert_eq!(res.len(), 1);
res.remove(0).result.unwrap();
let txn = ds.transaction(TransactionType::Read, LockType::Pessimistic).await?;
let initial = txn.get_tb("test", "test", "test").await?;
let initial_live_query_version = cache.get_live_queries_version("test", "test", "test")?;
drop(txn);
let sql = r"
DEFINE FIELD test ON test;
DEFINE EVENT test ON test WHEN {} THEN {};
DEFINE TABLE view AS SELECT * FROM test;
DEFINE INDEX test ON test FIELDS test;
LIVE SELECT * FROM test;
"
.to_owned();
let res = &mut ds.execute(&sql, &ses, None).await?;
assert_eq!(res.len(), 5);
res.remove(0).result.unwrap();
res.remove(0).result.unwrap();
res.remove(0).result.unwrap();
res.remove(0).result.unwrap();
let lqid = res.remove(0).result?;
assert!(matches!(lqid, Value::Uuid(_)));
let txn = ds.transaction(TransactionType::Read, LockType::Pessimistic).await?;
let after_define = txn.get_tb("test", "test", "test").await?;
let after_define_live_query_version = cache.get_live_queries_version("test", "test", "test")?;
drop(txn);
assert_ne!(initial.cache_fields_ts, after_define.cache_fields_ts);
assert_ne!(initial.cache_events_ts, after_define.cache_events_ts);
assert_ne!(initial.cache_tables_ts, after_define.cache_tables_ts);
assert_ne!(initial.cache_indexes_ts, after_define.cache_indexes_ts);
assert_ne!(initial_live_query_version, after_define_live_query_version);
let sql = r"
REMOVE FIELD test ON test;
REMOVE EVENT test ON test;
REMOVE TABLE view;
REMOVE INDEX test ON test;
KILL $lqid;
"
.to_owned();
let vars = map! { "lqid".to_string() => lqid };
let res = &mut ds.execute(&sql, &ses, Some(vars)).await?;
assert_eq!(res.len(), 5);
res.remove(0).result.unwrap();
res.remove(0).result.unwrap();
res.remove(0).result.unwrap();
res.remove(0).result.unwrap();
res.remove(0).result.unwrap();
let txn = ds.transaction(TransactionType::Read, LockType::Pessimistic).await?;
let after_remove = txn.get_tb("test", "test", "test").await?;
let after_remove_live_query_version = cache.get_live_queries_version("test", "test", "test")?;
drop(txn);
assert_ne!(after_define.cache_fields_ts, after_remove.cache_fields_ts);
assert_ne!(after_define.cache_events_ts, after_remove.cache_events_ts);
assert_ne!(after_define.cache_tables_ts, after_remove.cache_tables_ts);
assert_ne!(after_define.cache_indexes_ts, after_remove.cache_indexes_ts);
assert_ne!(after_define_live_query_version, after_remove_live_query_version);
Ok(())
}