#[path = "testutils/mod.rs"]
mod testutils;
use graphlite::Value;
use std::sync::OnceLock;
use testutils::test_fixture::TestFixture;
static SECURITY_FIXTURE: OnceLock<TestFixture> = OnceLock::new();
fn get_security_fixture() -> &'static TestFixture {
SECURITY_FIXTURE
.get_or_init(|| TestFixture::empty().expect("Failed to create security test fixture"))
}
#[test]
fn test_role_lifecycle() {
let fixture = get_security_fixture();
fixture.assert_query_succeeds("CREATE ROLE 'data_scientist'");
fixture.assert_query_succeeds("CREATE ROLE 'analyst'");
fixture.assert_query_succeeds("CREATE ROLE 'viewer'");
fixture.assert_query_fails("CREATE ROLE 'data_scientist'", "already exists");
let if_not_exists_result = fixture.query("CREATE ROLE IF NOT EXISTS 'data_scientist'");
if if_not_exists_result.is_ok() {
fixture.assert_query_succeeds("CREATE ROLE IF NOT EXISTS 'new_role'");
} else {
fixture.assert_query_succeeds("CREATE ROLE 'new_role'");
}
fixture.assert_query_succeeds("DROP ROLE 'new_role'");
fixture.assert_query_succeeds("DROP ROLE IF EXISTS 'nonexistent_role'");
fixture.assert_query_succeeds("DROP ROLE IF EXISTS 'analyst'");
fixture.assert_query_fails("DROP ROLE 'nonexistent_role'", "not found");
}
#[test]
fn test_role_listing() {
let fixture = get_security_fixture();
let if_not_exists_result = fixture.query("CREATE ROLE IF NOT EXISTS 'test_role_1'");
if if_not_exists_result.is_ok() {
fixture.assert_query_succeeds("CREATE ROLE IF NOT EXISTS 'test_role_2'");
fixture.assert_query_succeeds("CREATE ROLE IF NOT EXISTS 'test_role_3'");
} else {
fixture.assert_query_succeeds("CREATE ROLE 'test_role_1'");
fixture.assert_query_succeeds("CREATE ROLE 'test_role_2'");
fixture.assert_query_succeeds("CREATE ROLE 'test_role_3'");
}
let result = fixture.assert_query_succeeds("CALL gql.list_roles()");
assert!(!result.rows.is_empty(), "Should have at least some roles");
assert_eq!(
result.variables.len(),
3,
"Should have 3 columns: role_name, description, created_at"
);
assert_eq!(result.variables[0], "role_name");
assert_eq!(result.variables[1], "description");
assert_eq!(result.variables[2], "created_at");
let role_names: Vec<String> = result
.rows
.iter()
.filter_map(|row| {
row.values.get("role_name").and_then(|v| match v {
Value::String(s) => Some(s.clone()),
_ => None,
})
})
.collect();
assert!(
role_names.contains(&"test_role_1".to_string()),
"Should contain test_role_1"
);
assert!(
role_names.contains(&"test_role_2".to_string()),
"Should contain test_role_2"
);
assert!(
role_names.contains(&"test_role_3".to_string()),
"Should contain test_role_3"
);
}
#[test]
fn test_user_lifecycle() {
let fixture = get_security_fixture();
fixture.assert_query_succeeds("CREATE USER 'alice' PASSWORD 'password123'");
fixture.assert_query_succeeds("CREATE USER 'bob' PASSWORD 'secret456'");
fixture.assert_query_succeeds("CREATE USER 'charlie' PASSWORD 'pass789'");
fixture.assert_query_fails("CREATE USER 'alice' PASSWORD 'newpass'", "already exists");
let if_not_exists_result =
fixture.query("CREATE USER IF NOT EXISTS 'alice' PASSWORD 'ignored'");
if if_not_exists_result.is_ok() {
fixture.assert_query_succeeds("CREATE USER IF NOT EXISTS 'diana' PASSWORD 'newpass'");
} else {
fixture.assert_query_succeeds("CREATE USER 'diana' PASSWORD 'newpass'");
}
fixture.assert_query_succeeds("DROP USER 'diana'");
fixture.assert_query_succeeds("DROP USER IF EXISTS 'nonexistent_user'");
fixture.assert_query_succeeds("DROP USER IF EXISTS 'charlie'");
fixture.assert_query_fails("DROP USER 'nonexistent_user'", "not found");
}
#[test]
fn test_user_listing() {
let fixture = get_security_fixture();
let if_not_exists_result =
fixture.query("CREATE USER IF NOT EXISTS 'test_user_1' PASSWORD 'pass1'");
if if_not_exists_result.is_ok() {
fixture.assert_query_succeeds("CREATE USER IF NOT EXISTS 'test_user_2' PASSWORD 'pass2'");
fixture.assert_query_succeeds("CREATE USER IF NOT EXISTS 'test_user_3' PASSWORD 'pass3'");
} else {
fixture.assert_query_succeeds("CREATE USER 'test_user_1' PASSWORD 'pass1'");
fixture.assert_query_succeeds("CREATE USER 'test_user_2' PASSWORD 'pass2'");
fixture.assert_query_succeeds("CREATE USER 'test_user_3' PASSWORD 'pass3'");
}
let result = fixture.assert_query_succeeds("CALL gql.list_users()");
assert!(!result.rows.is_empty(), "Should have at least some users");
assert_eq!(
result.variables.len(),
5,
"Should have 5 columns: username, email, active, created_at, roles"
);
assert_eq!(result.variables[0], "username");
assert_eq!(result.variables[1], "email");
assert_eq!(result.variables[2], "active");
assert_eq!(result.variables[3], "created_at");
assert_eq!(result.variables[4], "roles");
let usernames: Vec<String> = result
.rows
.iter()
.filter_map(|row| {
row.values.get("username").and_then(|v| match v {
Value::String(s) => Some(s.clone()),
_ => None,
})
})
.collect();
assert!(
usernames.contains(&"test_user_1".to_string()),
"Should contain test_user_1"
);
assert!(
usernames.contains(&"test_user_2".to_string()),
"Should contain test_user_2"
);
assert!(
usernames.contains(&"test_user_3".to_string()),
"Should contain test_user_3"
);
}
#[test]
fn test_role_assignment() {
let fixture = get_security_fixture();
let if_not_exists_result = fixture.query("CREATE ROLE IF NOT EXISTS 'admin'");
if if_not_exists_result.is_ok() {
fixture.assert_query_succeeds("CREATE ROLE IF NOT EXISTS 'editor'");
} else {
let admin_result = fixture.query("CREATE ROLE 'admin'");
if admin_result.is_err() {}
let editor_result = fixture.query("CREATE ROLE 'editor'");
if editor_result.is_err() {}
}
let if_not_exists_result =
fixture.query("CREATE USER IF NOT EXISTS 'testuser' PASSWORD 'password'");
if if_not_exists_result.is_err() {
fixture.assert_query_succeeds("CREATE USER 'testuser' PASSWORD 'password'");
}
let grant_result = fixture.query("GRANT ROLE 'admin' TO 'testuser'");
match grant_result {
Ok(_) => {
fixture.assert_query_succeeds("GRANT ROLE 'editor' TO 'testuser'");
let result = fixture.assert_query_succeeds("CALL gql.list_users()");
let test_user_row = result.rows.iter().find(|row| {
if let Some(Value::String(username)) = row.values.get("username") {
username == "testuser"
} else {
false
}
});
if let Some(user_row) = test_user_row {
if let Some(Value::String(roles)) = user_row.values.get("roles") {
assert!(roles.contains("admin"), "User should have admin role");
assert!(roles.contains("editor"), "User should have editor role");
}
}
fixture.assert_query_succeeds("REVOKE ROLE 'editor' FROM 'testuser'");
}
Err(_) => {}
}
}
#[test]
fn test_authentication() {
let fixture = get_security_fixture();
let if_not_exists_result =
fixture.query("CREATE USER IF NOT EXISTS 'authtest' PASSWORD 'testpass123'");
if if_not_exists_result.is_err() {
fixture.assert_query_succeeds("CREATE USER 'authtest' PASSWORD 'testpass123'");
}
let auth_result = fixture.query("CALL gql.authenticate_user('authtest', 'testpass123')");
match auth_result {
Ok(result) => {
assert!(
!result.rows.is_empty(),
"Authentication should return user info"
);
fixture.assert_query_fails(
"CALL gql.authenticate_user('authtest', 'wrongpass')",
"Authentication failed",
);
fixture.assert_query_fails(
"CALL gql.authenticate_user('nonexistent', 'anypass')",
"Authentication failed",
);
}
Err(_) => {}
}
}
#[test]
fn test_security_edge_cases() {
let fixture = get_security_fixture();
let empty_role_result = fixture.query("CREATE ROLE ''");
if empty_role_result.is_err() {
} else {
}
let if_not_exists_result = fixture.query("CREATE ROLE IF NOT EXISTS 'role-with-dashes'");
if if_not_exists_result.is_ok() {
fixture.assert_query_succeeds("CREATE ROLE IF NOT EXISTS 'role_with_underscores'");
let long_role_name =
"very_long_role_name_that_exceeds_normal_length_limits_to_test_boundary_conditions";
fixture.assert_query_succeeds(&format!("CREATE ROLE IF NOT EXISTS '{}'", long_role_name));
fixture.assert_query_succeeds("CREATE ROLE IF NOT EXISTS 'CaseTest'");
fixture.assert_query_succeeds("CREATE ROLE IF NOT EXISTS 'casetest'");
} else {
fixture.assert_query_succeeds("CREATE ROLE 'role-with-dashes'");
fixture.assert_query_succeeds("CREATE ROLE 'role_with_underscores'");
let long_role_name =
"very_long_role_name_that_exceeds_normal_length_limits_to_test_boundary_conditions";
fixture.assert_query_succeeds(&format!("CREATE ROLE '{}'", long_role_name));
fixture.assert_query_succeeds("CREATE ROLE 'CaseTest'");
fixture.assert_query_succeeds("CREATE ROLE 'casetest'");
}
let result = fixture.assert_query_succeeds("CALL gql.list_roles()");
let role_names: Vec<String> = result
.rows
.iter()
.filter_map(|row| {
row.values.get("role_name").and_then(|v| match v {
Value::String(s) => Some(s.clone()),
_ => None,
})
})
.collect();
assert!(
role_names.contains(&"CaseTest".to_string()),
"Should contain CaseTest"
);
assert!(
role_names.contains(&"casetest".to_string()),
"Should contain casetest"
);
}
#[test]
fn test_procedure_argument_validation() {
let fixture = get_security_fixture();
fixture.assert_query_succeeds("CALL gql.list_roles()");
fixture.assert_query_succeeds("CALL gql.list_users()");
fixture.assert_query_fails(
"CALL gql.authenticate_user('onlyonearg')",
"expects exactly 2 arguments",
);
fixture.assert_query_fails(
"CALL gql.authenticate_user('user', 'pass', 'extra')",
"expects exactly 2 arguments",
);
fixture.assert_query_fails(
"CALL gql.authenticate_user(123, 'pass')",
"must be a string",
);
fixture.assert_query_fails(
"CALL gql.authenticate_user('user', 456)",
"must be a string",
);
}
#[test]
fn test_transaction_integrity() {
let fixture = get_security_fixture();
fixture.assert_query_succeeds("BEGIN");
fixture.assert_query_succeeds("CREATE ROLE 'tx_test_role'");
let result = fixture.assert_query_succeeds("CALL gql.list_roles()");
let role_names: Vec<String> = result
.rows
.iter()
.filter_map(|row| {
row.values.get("role_name").and_then(|v| match v {
Value::String(s) => Some(s.clone()),
_ => None,
})
})
.collect();
assert!(
role_names.contains(&"tx_test_role".to_string()),
"Role should exist in transaction"
);
fixture.assert_query_succeeds("ROLLBACK");
let result_after_rollback = fixture.assert_query_succeeds("CALL gql.list_roles()");
let role_names_after: Vec<String> = result_after_rollback
.rows
.iter()
.filter_map(|row| {
row.values.get("role_name").and_then(|v| match v {
Value::String(s) => Some(s.clone()),
_ => None,
})
})
.collect();
if role_names_after.contains(&"tx_test_role".to_string()) {
let _cleanup = fixture.query("DROP ROLE 'tx_test_role'");
} else {
}
fixture.assert_query_succeeds("BEGIN");
fixture.assert_query_succeeds("CREATE ROLE 'committed_role'");
fixture.assert_query_succeeds("COMMIT");
let result_after_commit = fixture.assert_query_succeeds("CALL gql.list_roles()");
let role_names_committed: Vec<String> = result_after_commit
.rows
.iter()
.filter_map(|row| {
row.values.get("role_name").and_then(|v| match v {
Value::String(s) => Some(s.clone()),
_ => None,
})
})
.collect();
assert!(
role_names_committed.contains(&"committed_role".to_string()),
"Role should persist after commit"
);
}