# tests/unit/services/test_admin_service.py import pytest 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 @pytest.mark.unit @pytest.mark.admin class TestAdminService: """Test suite for AdminService following the application's testing patterns""" def setup_method(self): """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) assert len(users) >= 2 # test_user + test_admin user_ids = [user.id for user in users] assert test_user.id in user_ids assert test_admin.id in user_ids 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) assert len(users_second_page) == 1 assert users[0].id != users_second_page[0].id def test_toggle_user_status_deactivate(self, db, test_user, test_admin): """Test deactivating a user""" assert test_user.is_active is True user, message = self.service.toggle_user_status(db, test_user.id, test_admin.id) assert user.id == test_user.id assert user.is_active is False 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""" # First deactivate the user test_user.is_active = False db.commit() user, message = self.service.toggle_user_status(db, test_user.id, test_admin.id) assert user.id == test_user.id assert user.is_active is True 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(UserNotFoundException) as exc_info: self.service.toggle_user_status(db, 99999, test_admin.id) exception = exc_info.value assert exception.error_code == "USER_NOT_FOUND" assert "99999" in exception.message 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) 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) assert total >= 1 assert len(shops) >= 1 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 test_shop.is_verified = False db.commit() shop, message = self.service.verify_shop(db, test_shop.id) assert shop.id == test_shop.id assert shop.is_verified is True 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(ShopNotFoundException) as exc_info: self.service.verify_shop(db, 99999) 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""" result = self.service.get_marketplace_import_jobs(db, skip=0, limit=10) assert len(result) >= 1 # Find our test job in the results test_job = next( (job for job in result if job.job_id == test_marketplace_job.id), None ) assert test_job is not None 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