Admin service tests update
This commit is contained in:
@@ -90,4 +90,5 @@ pytest_plugins = [
|
||||
"tests.fixtures.product_fixtures",
|
||||
"tests.fixtures.shop_fixtures",
|
||||
"tests.fixtures.marketplace_fixtures",
|
||||
"tests.fixtures.testing_fixtures",
|
||||
]
|
||||
|
||||
54
tests/fixtures/testing_fixtures.py
vendored
Normal file
54
tests/fixtures/testing_fixtures.py
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
# tests/fixtures/testing_fixtures.py
|
||||
"""
|
||||
Testing utility fixtures for edge cases and error handling.
|
||||
|
||||
This module provides fixtures for:
|
||||
- Empty database sessions for edge case testing
|
||||
- Mock database sessions for error simulation
|
||||
- Additional testing utilities
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import Mock
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def empty_db(db):
|
||||
"""Empty database session for edge case testing"""
|
||||
from sqlalchemy import text
|
||||
|
||||
# Clear only the tables that are relevant for admin service testing
|
||||
# In order to respect foreign key constraints
|
||||
tables_to_clear = [
|
||||
"marketplace_import_jobs", # Has foreign keys to shops and users
|
||||
"shop_products", # Has foreign keys to shops and products
|
||||
"stock", # Fixed: singular not plural
|
||||
"products", # Referenced by shop_products
|
||||
"shops", # Has foreign key to users
|
||||
"users" # Base table
|
||||
]
|
||||
|
||||
for table in tables_to_clear:
|
||||
try:
|
||||
db.execute(text(f"DELETE FROM {table}"))
|
||||
except Exception:
|
||||
# If table doesn't exist or delete fails, continue
|
||||
pass
|
||||
|
||||
db.commit()
|
||||
return db
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def db_with_error():
|
||||
"""Database session that raises errors for testing error handling"""
|
||||
mock_db = Mock()
|
||||
|
||||
# Configure the mock to raise SQLAlchemy errors on query operations
|
||||
mock_db.query.side_effect = SQLAlchemyError("Database connection failed")
|
||||
mock_db.add.side_effect = SQLAlchemyError("Database insert failed")
|
||||
mock_db.commit.side_effect = SQLAlchemyError("Database commit failed")
|
||||
mock_db.rollback.return_value = None
|
||||
|
||||
return mock_db
|
||||
@@ -1,7 +1,14 @@
|
||||
# tests/unit/services/test_admin_service.py
|
||||
import pytest
|
||||
from fastapi import HTTPException
|
||||
|
||||
from app.exceptions import (
|
||||
UserNotFoundException,
|
||||
UserStatusChangeException,
|
||||
CannotModifySelfException,
|
||||
ShopNotFoundException,
|
||||
ShopVerificationException,
|
||||
AdminOperationException,
|
||||
)
|
||||
from app.services.admin_service import AdminService
|
||||
from models.database.marketplace import MarketplaceImportJob
|
||||
from models.database.shop import Shop
|
||||
@@ -16,6 +23,7 @@ class TestAdminService:
|
||||
"""Setup method following the same pattern as product service tests"""
|
||||
self.service = AdminService()
|
||||
|
||||
# User Management Tests
|
||||
def test_get_all_users(self, db, test_user, test_admin):
|
||||
"""Test getting all users with pagination"""
|
||||
users = self.service.get_all_users(db, skip=0, limit=10)
|
||||
@@ -28,7 +36,6 @@ class TestAdminService:
|
||||
def test_get_all_users_with_pagination(self, db, test_user, test_admin):
|
||||
"""Test user pagination works correctly"""
|
||||
users = self.service.get_all_users(db, skip=0, limit=1)
|
||||
|
||||
assert len(users) == 1
|
||||
|
||||
users_second_page = self.service.get_all_users(db, skip=1, limit=1)
|
||||
@@ -43,7 +50,8 @@ class TestAdminService:
|
||||
|
||||
assert user.id == test_user.id
|
||||
assert user.is_active is False
|
||||
assert f"{user.username} has been deactivated" in message
|
||||
assert test_user.username in message
|
||||
assert "deactivated" in message
|
||||
|
||||
def test_toggle_user_status_activate(self, db, test_user, test_admin):
|
||||
"""Test activating a user"""
|
||||
@@ -55,24 +63,37 @@ class TestAdminService:
|
||||
|
||||
assert user.id == test_user.id
|
||||
assert user.is_active is True
|
||||
assert f"{user.username} has been activated" in message
|
||||
assert test_user.username in message
|
||||
assert "activated" in message
|
||||
|
||||
def test_toggle_user_status_user_not_found(self, db, test_admin):
|
||||
"""Test toggle user status when user not found"""
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
with pytest.raises(UserNotFoundException) as exc_info:
|
||||
self.service.toggle_user_status(db, 99999, test_admin.id)
|
||||
|
||||
assert exc_info.value.status_code == 404
|
||||
assert "User not found" in str(exc_info.value.detail)
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "USER_NOT_FOUND"
|
||||
assert "99999" in exception.message
|
||||
|
||||
def test_toggle_user_status_cannot_deactivate_self(self, db, test_admin):
|
||||
"""Test that admin cannot deactivate their own account"""
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
def test_toggle_user_status_cannot_modify_self(self, db, test_admin):
|
||||
"""Test that admin cannot modify their own account"""
|
||||
with pytest.raises(CannotModifySelfException) as exc_info:
|
||||
self.service.toggle_user_status(db, test_admin.id, test_admin.id)
|
||||
|
||||
assert exc_info.value.status_code == 400
|
||||
assert "Cannot deactivate your own account" in str(exc_info.value.detail)
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "CANNOT_MODIFY_SELF"
|
||||
assert "deactivate account" in exception.message
|
||||
|
||||
def test_toggle_user_status_cannot_modify_admin(self, db, test_admin, another_admin):
|
||||
"""Test that admin cannot modify another admin"""
|
||||
with pytest.raises(UserStatusChangeException) as exc_info:
|
||||
self.service.toggle_user_status(db, another_admin.id, test_admin.id)
|
||||
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "USER_STATUS_CHANGE_FAILED"
|
||||
assert "Cannot modify another admin user" in exception.message
|
||||
|
||||
# Shop Management Tests
|
||||
def test_get_all_shops(self, db, test_shop):
|
||||
"""Test getting all shops with total count"""
|
||||
shops, total = self.service.get_all_shops(db, skip=0, limit=10)
|
||||
@@ -82,6 +103,18 @@ class TestAdminService:
|
||||
shop_codes = [shop.shop_code for shop in shops]
|
||||
assert test_shop.shop_code in shop_codes
|
||||
|
||||
def test_get_all_shops_with_pagination(self, db, test_shop, verified_shop):
|
||||
"""Test shop pagination works correctly"""
|
||||
shops, total = self.service.get_all_shops(db, skip=0, limit=1)
|
||||
|
||||
assert total >= 2
|
||||
assert len(shops) == 1
|
||||
|
||||
shops_second_page, _ = self.service.get_all_shops(db, skip=1, limit=1)
|
||||
assert len(shops_second_page) >= 0
|
||||
if len(shops_second_page) > 0:
|
||||
assert shops[0].id != shops_second_page[0].id
|
||||
|
||||
def test_verify_shop_mark_verified(self, db, test_shop):
|
||||
"""Test marking shop as verified"""
|
||||
# Ensure shop starts unverified
|
||||
@@ -92,18 +125,52 @@ class TestAdminService:
|
||||
|
||||
assert shop.id == test_shop.id
|
||||
assert shop.is_verified is True
|
||||
assert f"{shop.shop_code} has been verified" in message
|
||||
assert test_shop.shop_code in message
|
||||
assert "verified" in message
|
||||
|
||||
def test_verify_shop_mark_unverified(self, db, verified_shop):
|
||||
"""Test marking verified shop as unverified"""
|
||||
shop, message = self.service.verify_shop(db, verified_shop.id)
|
||||
|
||||
assert shop.id == verified_shop.id
|
||||
assert shop.is_verified is False
|
||||
assert verified_shop.shop_code in message
|
||||
assert "unverified" in message
|
||||
|
||||
def test_verify_shop_not_found(self, db):
|
||||
"""Test verify shop when shop not found"""
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
with pytest.raises(ShopNotFoundException) as exc_info:
|
||||
self.service.verify_shop(db, 99999)
|
||||
|
||||
assert exc_info.value.status_code == 404
|
||||
assert "Shop not found" in str(exc_info.value.detail)
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "SHOP_NOT_FOUND"
|
||||
assert "99999" in exception.message
|
||||
|
||||
def test_toggle_shop_status_deactivate(self, db, test_shop):
|
||||
"""Test deactivating a shop"""
|
||||
original_status = test_shop.is_active
|
||||
|
||||
shop, message = self.service.toggle_shop_status(db, test_shop.id)
|
||||
|
||||
assert shop.id == test_shop.id
|
||||
assert shop.is_active != original_status
|
||||
assert test_shop.shop_code in message
|
||||
if original_status:
|
||||
assert "deactivated" in message
|
||||
else:
|
||||
assert "activated" in message
|
||||
|
||||
def test_toggle_shop_status_not_found(self, db):
|
||||
"""Test toggle shop status when shop not found"""
|
||||
with pytest.raises(ShopNotFoundException) as exc_info:
|
||||
self.service.toggle_shop_status(db, 99999)
|
||||
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "SHOP_NOT_FOUND"
|
||||
|
||||
# Marketplace Import Jobs Tests
|
||||
def test_get_marketplace_import_jobs_no_filters(self, db, test_marketplace_job):
|
||||
"""Test getting marketplace import jobs without filters using fixture"""
|
||||
"""Test getting marketplace import jobs without filters"""
|
||||
result = self.service.get_marketplace_import_jobs(db, skip=0, limit=10)
|
||||
|
||||
assert len(result) >= 1
|
||||
@@ -115,3 +182,127 @@ class TestAdminService:
|
||||
assert test_job.marketplace == test_marketplace_job.marketplace
|
||||
assert test_job.shop_name == test_marketplace_job.shop_name
|
||||
assert test_job.status == test_marketplace_job.status
|
||||
|
||||
def test_get_marketplace_import_jobs_with_marketplace_filter(self, db, test_marketplace_job):
|
||||
"""Test filtering marketplace import jobs by marketplace"""
|
||||
result = self.service.get_marketplace_import_jobs(
|
||||
db, marketplace=test_marketplace_job.marketplace, skip=0, limit=10
|
||||
)
|
||||
|
||||
assert len(result) >= 1
|
||||
for job in result:
|
||||
assert test_marketplace_job.marketplace.lower() in job.marketplace.lower()
|
||||
|
||||
def test_get_marketplace_import_jobs_with_shop_filter(self, db, test_marketplace_job):
|
||||
"""Test filtering marketplace import jobs by shop name"""
|
||||
result = self.service.get_marketplace_import_jobs(
|
||||
db, shop_name=test_marketplace_job.shop_name, skip=0, limit=10
|
||||
)
|
||||
|
||||
assert len(result) >= 1
|
||||
for job in result:
|
||||
assert test_marketplace_job.shop_name.lower() in job.shop_name.lower()
|
||||
|
||||
def test_get_marketplace_import_jobs_with_status_filter(self, db, test_marketplace_job):
|
||||
"""Test filtering marketplace import jobs by status"""
|
||||
result = self.service.get_marketplace_import_jobs(
|
||||
db, status=test_marketplace_job.status, skip=0, limit=10
|
||||
)
|
||||
|
||||
assert len(result) >= 1
|
||||
for job in result:
|
||||
assert job.status == test_marketplace_job.status
|
||||
|
||||
def test_get_marketplace_import_jobs_pagination(self, db, test_marketplace_job):
|
||||
"""Test marketplace import jobs pagination"""
|
||||
result_page1 = self.service.get_marketplace_import_jobs(db, skip=0, limit=1)
|
||||
result_page2 = self.service.get_marketplace_import_jobs(db, skip=1, limit=1)
|
||||
|
||||
assert len(result_page1) >= 0
|
||||
assert len(result_page2) >= 0
|
||||
|
||||
if len(result_page1) > 0 and len(result_page2) > 0:
|
||||
assert result_page1[0].job_id != result_page2[0].job_id
|
||||
|
||||
# Statistics Tests
|
||||
def test_get_user_statistics(self, db, test_user, test_admin):
|
||||
"""Test getting user statistics"""
|
||||
stats = self.service.get_user_statistics(db)
|
||||
|
||||
assert "total_users" in stats
|
||||
assert "active_users" in stats
|
||||
assert "inactive_users" in stats
|
||||
assert "activation_rate" in stats
|
||||
|
||||
assert isinstance(stats["total_users"], int)
|
||||
assert isinstance(stats["active_users"], int)
|
||||
assert isinstance(stats["inactive_users"], int)
|
||||
assert isinstance(stats["activation_rate"], (int, float))
|
||||
|
||||
assert stats["total_users"] >= 2 # test_user + test_admin
|
||||
assert stats["active_users"] + stats["inactive_users"] == stats["total_users"]
|
||||
|
||||
def test_get_shop_statistics(self, db, test_shop):
|
||||
"""Test getting shop statistics"""
|
||||
stats = self.service.get_shop_statistics(db)
|
||||
|
||||
assert "total_shops" in stats
|
||||
assert "active_shops" in stats
|
||||
assert "verified_shops" in stats
|
||||
assert "verification_rate" in stats
|
||||
|
||||
assert isinstance(stats["total_shops"], int)
|
||||
assert isinstance(stats["active_shops"], int)
|
||||
assert isinstance(stats["verified_shops"], int)
|
||||
assert isinstance(stats["verification_rate"], (int, float))
|
||||
|
||||
assert stats["total_shops"] >= 1
|
||||
|
||||
# Error Handling Tests
|
||||
def test_get_all_users_database_error(self, db_with_error, test_admin):
|
||||
"""Test handling database errors in get_all_users"""
|
||||
with pytest.raises(AdminOperationException) as exc_info:
|
||||
self.service.get_all_users(db_with_error, skip=0, limit=10)
|
||||
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "ADMIN_OPERATION_FAILED"
|
||||
assert "get_all_users" in exception.message
|
||||
|
||||
def test_get_all_shops_database_error(self, db_with_error):
|
||||
"""Test handling database errors in get_all_shops"""
|
||||
with pytest.raises(AdminOperationException) as exc_info:
|
||||
self.service.get_all_shops(db_with_error, skip=0, limit=10)
|
||||
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "ADMIN_OPERATION_FAILED"
|
||||
assert "get_all_shops" in exception.message
|
||||
|
||||
# Edge Cases
|
||||
def test_get_all_users_empty_database(self, empty_db):
|
||||
"""Test getting users when database is empty"""
|
||||
users = self.service.get_all_users(empty_db, skip=0, limit=10)
|
||||
assert len(users) == 0
|
||||
|
||||
def test_get_all_shops_empty_database(self, empty_db):
|
||||
"""Test getting shops when database is empty"""
|
||||
shops, total = self.service.get_all_shops(empty_db, skip=0, limit=10)
|
||||
assert len(shops) == 0
|
||||
assert total == 0
|
||||
|
||||
def test_user_statistics_empty_database(self, empty_db):
|
||||
"""Test user statistics when no users exist"""
|
||||
stats = self.service.get_user_statistics(empty_db)
|
||||
|
||||
assert stats["total_users"] == 0
|
||||
assert stats["active_users"] == 0
|
||||
assert stats["inactive_users"] == 0
|
||||
assert stats["activation_rate"] == 0
|
||||
|
||||
def test_shop_statistics_empty_database(self, empty_db):
|
||||
"""Test shop statistics when no shops exist"""
|
||||
stats = self.service.get_shop_statistics(empty_db)
|
||||
|
||||
assert stats["total_shops"] == 0
|
||||
assert stats["active_shops"] == 0
|
||||
assert stats["verified_shops"] == 0
|
||||
assert stats["verification_rate"] == 0
|
||||
|
||||
Reference in New Issue
Block a user