- RBAC: ensure_admin_role() guarantees the Admin role carries is_admin=True (startup self-heal + create_admin promotion + role-is_admin UI checkbox in admin/roles); update_role refuses to strip is_admin from the last admin role. - WS: broadcast_maintenance_event is now awaited (3 sites) so maintenance events actually reach clients (was an un-awaited coroutine RuntimeWarning). - Pagination: MAX_PAGINATION_PAGES cap + clear error in fetch_paginated_data to stop runaway loops on huge environments. - Discovery: find_affected_dashboards and translate datasource picker filter datasets/dashboards server-side (table_name/id filters, opr operator per Superset OpenAPI) instead of full scans that hit the pagination token cap; fallback to full scan when filters are rejected; virtual-dataset dedupe.
205 lines
8.0 KiB
Python
205 lines
8.0 KiB
Python
# #region Test.CreateAdmin.TestCreateAdmin [C:2] [TYPE Module] [SEMANTICS test,admin,user,cli]
|
|
# @BRIEF Tests for create_admin.py script (mocked DB, no real auth.db).
|
|
# @RELATION BINDS_TO -> [Tooling.CreateAdmin.CreateAdminScript]
|
|
# @TEST_EDGE: create_admin_new_user -> Creates user and role successfully
|
|
# @TEST_EDGE: create_admin_user_exists -> Returns "exists"
|
|
# @TEST_EDGE: create_admin_existing_role -> Reuses existing Admin role
|
|
# @TEST_EDGE: create_admin_email_normalized -> Strips whitespace from email
|
|
# @TEST_EDGE: create_admin_db_error -> Raises on DB failure
|
|
# @TEST_EDGE: create_admin_no_email -> Works with email=None
|
|
# @TEST_EDGE: create_admin_empty_email -> Normalizes empty string to None
|
|
import sys
|
|
from pathlib import Path
|
|
from unittest.mock import MagicMock, patch, PropertyMock
|
|
|
|
import pytest
|
|
|
|
sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src"))
|
|
|
|
|
|
# =============================================================================
|
|
# create_admin
|
|
# =============================================================================
|
|
|
|
class TestCreateAdmin:
|
|
"""Tests for create_admin.create_admin()."""
|
|
|
|
@patch("src.scripts.create_admin.AuthSessionLocal")
|
|
@patch("src.scripts.create_admin.get_password_hash", return_value="hashed_pwd")
|
|
def test_creates_new_user(self, mock_hash, MockSession):
|
|
"""Happy: creates admin user and role."""
|
|
from src.scripts.create_admin import create_admin
|
|
|
|
# Mock DB session
|
|
mock_db = MagicMock()
|
|
MockSession.return_value = mock_db
|
|
|
|
# No existing Admin role, no existing user
|
|
mock_db.query.return_value.filter.return_value.first.side_effect = [
|
|
None, # Role query -> no role
|
|
None, # User query -> no user
|
|
]
|
|
|
|
result = create_admin("admin", "secret123", "admin@test.com")
|
|
|
|
assert result == "created"
|
|
# add() was called at least once (for role and/or user)
|
|
assert mock_db.add.called
|
|
assert mock_db.commit.call_count >= 1
|
|
mock_db.close.assert_called_once()
|
|
|
|
@patch("src.scripts.create_admin.AuthSessionLocal")
|
|
@patch("src.scripts.create_admin.get_password_hash", return_value="hashed_pwd")
|
|
def test_user_already_exists(self, mock_hash, MockSession):
|
|
"""Edge: existing user returns 'exists' AND is promoted to the Admin role."""
|
|
from src.scripts.create_admin import create_admin
|
|
|
|
mock_db = MagicMock()
|
|
MockSession.return_value = mock_db
|
|
|
|
# Existing Admin role with admin flag
|
|
existing_role = MagicMock()
|
|
existing_role.name = "Admin"
|
|
existing_role.is_admin = True
|
|
# Existing user WITHOUT admin role yet (real list so append is observable)
|
|
existing_user = MagicMock()
|
|
existing_user.username = "admin"
|
|
existing_user.roles = []
|
|
|
|
def query_side_effect(model):
|
|
mock_query = MagicMock()
|
|
if model.__name__ == "Role":
|
|
mock_query.filter.return_value.first.return_value = existing_role
|
|
elif model.__name__ == "User":
|
|
mock_query.filter.return_value.first.return_value = existing_user
|
|
return mock_query
|
|
|
|
mock_db.query.side_effect = query_side_effect
|
|
|
|
result = create_admin("admin", "secret123")
|
|
|
|
assert result == "exists"
|
|
# The pre-existing user must be promoted to admin, not silently skipped.
|
|
assert existing_role in existing_user.roles
|
|
mock_db.close.assert_called_once()
|
|
|
|
@patch("src.scripts.create_admin.AuthSessionLocal")
|
|
@patch("src.scripts.create_admin.get_password_hash", return_value="hashed_pwd")
|
|
def test_user_already_admin_no_duplicate(self, mock_hash, MockSession):
|
|
"""Edge: existing user who is already admin stays admin (no duplicate role)."""
|
|
from src.scripts.create_admin import create_admin
|
|
|
|
mock_db = MagicMock()
|
|
MockSession.return_value = mock_db
|
|
|
|
existing_role = MagicMock()
|
|
existing_role.name = "Admin"
|
|
existing_role.is_admin = True
|
|
existing_user = MagicMock()
|
|
existing_user.username = "admin"
|
|
existing_user.roles = [existing_role]
|
|
|
|
def query_side_effect(model):
|
|
mock_query = MagicMock()
|
|
if model.__name__ == "Role":
|
|
mock_query.filter.return_value.first.return_value = existing_role
|
|
elif model.__name__ == "User":
|
|
mock_query.filter.return_value.first.return_value = existing_user
|
|
return mock_query
|
|
|
|
mock_db.query.side_effect = query_side_effect
|
|
|
|
result = create_admin("admin", "secret123")
|
|
|
|
assert result == "exists"
|
|
assert existing_user.roles.count(existing_role) == 1
|
|
mock_db.close.assert_called_once()
|
|
|
|
@patch("src.scripts.create_admin.AuthSessionLocal")
|
|
@patch("src.scripts.create_admin.get_password_hash", return_value="hashed_pwd")
|
|
def test_email_normalized_strip(self, mock_hash, MockSession):
|
|
"""Edge: email with whitespace is normalized."""
|
|
from src.scripts.create_admin import create_admin
|
|
|
|
mock_db = MagicMock()
|
|
MockSession.return_value = mock_db
|
|
|
|
existing_role = MagicMock()
|
|
existing_role.name = "Admin"
|
|
mock_db.query.return_value.filter.return_value.first.side_effect = [
|
|
existing_role, # Role exists
|
|
None, # No existing user
|
|
]
|
|
|
|
result = create_admin("admin2", "secret123", " admin@test.com ")
|
|
|
|
assert result == "created"
|
|
# Check the user was created with stripped email
|
|
added_user = [a for a in mock_db.add.call_args_list if "User" in str(a)]
|
|
assert added_user # User was added
|
|
|
|
@patch("src.scripts.create_admin.AuthSessionLocal")
|
|
@patch("src.scripts.create_admin.get_password_hash", return_value="hashed_pwd")
|
|
def test_empty_email_normalized_to_none(self, mock_hash, MockSession):
|
|
"""Edge: empty email string is normalized to None."""
|
|
from src.scripts.create_admin import create_admin
|
|
|
|
mock_db = MagicMock()
|
|
MockSession.return_value = mock_db
|
|
|
|
existing_role = MagicMock()
|
|
existing_role.name = "Admin"
|
|
mock_db.query.return_value.filter.return_value.first.side_effect = [
|
|
existing_role, # Role exists
|
|
None, # No existing user
|
|
]
|
|
|
|
result = create_admin("admin3", "secret123", " ")
|
|
|
|
assert result == "created"
|
|
|
|
@patch("src.scripts.create_admin.AuthSessionLocal")
|
|
@patch("src.scripts.create_admin.get_password_hash", return_value="hashed_pwd")
|
|
def test_no_email(self, mock_hash, MockSession):
|
|
"""Edge: email=None works without error."""
|
|
from src.scripts.create_admin import create_admin
|
|
|
|
mock_db = MagicMock()
|
|
MockSession.return_value = mock_db
|
|
|
|
existing_role = MagicMock()
|
|
existing_role.name = "Admin"
|
|
mock_db.query.return_value.filter.return_value.first.side_effect = [
|
|
existing_role,
|
|
None,
|
|
]
|
|
|
|
result = create_admin("admin4", "secret123", None)
|
|
|
|
assert result == "created"
|
|
|
|
@patch("src.scripts.create_admin.AuthSessionLocal")
|
|
@patch("src.scripts.create_admin.get_password_hash", return_value="hashed_pwd")
|
|
def test_db_error_rollback_and_raise(self, mock_hash, MockSession):
|
|
"""Edge: DB error triggers rollback and re-raises."""
|
|
from src.scripts.create_admin import create_admin
|
|
|
|
mock_db = MagicMock()
|
|
MockSession.return_value = mock_db
|
|
|
|
# No existing role + no existing user -> will hit commit when creating user
|
|
mock_db.query.return_value.filter.return_value.first.side_effect = [
|
|
None, # Role query -> no role
|
|
None, # User query -> no user
|
|
]
|
|
|
|
# First commit (for role) succeeds, second commit (for user) fails
|
|
mock_db.commit.side_effect = [None, Exception("DB failure")]
|
|
|
|
with pytest.raises(Exception, match="DB failure"):
|
|
create_admin("admin", "secret123")
|
|
|
|
mock_db.rollback.assert_called_once()
|
|
mock_db.close.assert_called_once()
|
|
# #endregion Test.CreateAdmin.TestCreateAdmin
|