# #region 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 -> [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'.""" from src.scripts.create_admin import create_admin mock_db = MagicMock() MockSession.return_value = mock_db # Existing Admin role existing_role = MagicMock() existing_role.name = "Admin" # Existing user existing_user = MagicMock() existing_user.username = "admin" 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" 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 TestCreateAdmin