# tests/unit/services/test_company_service.py """Unit tests for CompanyService.""" import uuid import pytest from app.modules.tenancy.exceptions import CompanyNotFoundException, UserNotFoundException from app.modules.tenancy.services.company_service import company_service from app.modules.tenancy.models import Company from app.modules.tenancy.schemas.company import ( CompanyCreate, CompanyUpdate, CompanyTransferOwnership, ) # ============================================================================= # FIXTURES # ============================================================================= @pytest.fixture def unverified_company(db, test_user): """Create an unverified test company.""" unique_id = str(uuid.uuid4())[:8] company = Company( name=f"Unverified Company {unique_id}", owner_user_id=test_user.id, contact_email=f"unverified{unique_id}@company.com", is_active=True, is_verified=False, ) db.add(company) db.commit() db.refresh(company) return company @pytest.fixture def inactive_company(db, test_user): """Create an inactive test company.""" unique_id = str(uuid.uuid4())[:8] company = Company( name=f"Inactive Company {unique_id}", owner_user_id=test_user.id, contact_email=f"inactive{unique_id}@company.com", is_active=False, is_verified=True, ) db.add(company) db.commit() db.refresh(company) return company # ============================================================================= # CREATE TESTS # ============================================================================= @pytest.mark.unit @pytest.mark.tenancy class TestCompanyServiceCreate: """Test suite for company creation.""" def test_create_company_with_new_owner(self, db): """Test creating a company with a new owner user.""" unique_id = str(uuid.uuid4())[:8] company_data = CompanyCreate( name=f"New Company {unique_id}", owner_email=f"newowner{unique_id}@example.com", contact_email=f"contact{unique_id}@company.com", description="A new test company", ) company, owner, temp_password = company_service.create_company_with_owner( db, company_data ) db.commit() assert company is not None assert company.name == f"New Company {unique_id}" assert company.is_active is True assert company.is_verified is False assert owner is not None assert owner.email == f"newowner{unique_id}@example.com" assert temp_password is not None # New user gets temp password def test_create_company_with_existing_owner(self, db, test_user): """Test creating a company with an existing user as owner.""" unique_id = str(uuid.uuid4())[:8] company_data = CompanyCreate( name=f"Existing Owner Company {unique_id}", owner_email=test_user.email, contact_email=f"contact{unique_id}@company.com", ) company, owner, temp_password = company_service.create_company_with_owner( db, company_data ) db.commit() assert company is not None assert owner.id == test_user.id assert temp_password is None # Existing user doesn't get new password # ============================================================================= # READ TESTS # ============================================================================= @pytest.mark.unit @pytest.mark.tenancy class TestCompanyServiceRead: """Test suite for reading companies.""" def test_get_company_by_id_success(self, db, test_company): """Test getting a company by ID.""" company = company_service.get_company_by_id(db, test_company.id) assert company is not None assert company.id == test_company.id assert company.name == test_company.name def test_get_company_by_id_not_found(self, db): """Test getting a non-existent company raises exception.""" with pytest.raises(CompanyNotFoundException) as exc_info: company_service.get_company_by_id(db, 99999) assert "99999" in str(exc_info.value) def test_get_companies_paginated(self, db, test_company, other_company): """Test getting paginated list of companies.""" companies, total = company_service.get_companies(db, skip=0, limit=10) assert len(companies) >= 2 assert total >= 2 def test_get_companies_with_search(self, db, test_company): """Test searching companies by name.""" # Get the unique part of the company name search_term = test_company.name.split()[0] # "Test" companies, total = company_service.get_companies( db, skip=0, limit=100, search=search_term ) assert len(companies) >= 1 assert any(c.id == test_company.id for c in companies) def test_get_companies_filter_by_active(self, db, test_company, inactive_company): """Test filtering companies by active status.""" active_companies, _ = company_service.get_companies( db, skip=0, limit=100, is_active=True ) inactive_companies, _ = company_service.get_companies( db, skip=0, limit=100, is_active=False ) assert all(c.is_active for c in active_companies) assert all(not c.is_active for c in inactive_companies) def test_get_companies_filter_by_verified(self, db, test_company, unverified_company): """Test filtering companies by verified status.""" verified_companies, _ = company_service.get_companies( db, skip=0, limit=100, is_verified=True ) unverified_companies, _ = company_service.get_companies( db, skip=0, limit=100, is_verified=False ) assert all(c.is_verified for c in verified_companies) assert all(not c.is_verified for c in unverified_companies) # ============================================================================= # UPDATE TESTS # ============================================================================= @pytest.mark.unit @pytest.mark.tenancy class TestCompanyServiceUpdate: """Test suite for updating companies.""" def test_update_company_success(self, db, test_company): """Test updating company information.""" unique_id = str(uuid.uuid4())[:8] update_data = CompanyUpdate( name=f"Updated Company {unique_id}", description="Updated description", ) updated = company_service.update_company(db, test_company.id, update_data) db.commit() assert updated.name == f"Updated Company {unique_id}" assert updated.description == "Updated description" def test_update_company_partial(self, db, test_company): """Test partial update of company.""" original_name = test_company.name update_data = CompanyUpdate(description="Only description updated") updated = company_service.update_company(db, test_company.id, update_data) db.commit() assert updated.name == original_name # Name unchanged assert updated.description == "Only description updated" def test_update_company_not_found(self, db): """Test updating non-existent company raises exception.""" update_data = CompanyUpdate(name="New Name") with pytest.raises(CompanyNotFoundException): company_service.update_company(db, 99999, update_data) # ============================================================================= # DELETE TESTS # ============================================================================= @pytest.mark.unit @pytest.mark.tenancy class TestCompanyServiceDelete: """Test suite for deleting companies.""" def test_delete_company_success(self, db, test_user): """Test deleting a company.""" # Create a company to delete unique_id = str(uuid.uuid4())[:8] company = Company( name=f"To Delete {unique_id}", owner_user_id=test_user.id, contact_email=f"delete{unique_id}@company.com", is_active=True, ) db.add(company) db.commit() company_id = company.id # Delete it company_service.delete_company(db, company_id) db.commit() # Verify it's gone with pytest.raises(CompanyNotFoundException): company_service.get_company_by_id(db, company_id) def test_delete_company_not_found(self, db): """Test deleting non-existent company raises exception.""" with pytest.raises(CompanyNotFoundException): company_service.delete_company(db, 99999) # ============================================================================= # TOGGLE TESTS # ============================================================================= @pytest.mark.unit @pytest.mark.tenancy class TestCompanyServiceToggle: """Test suite for toggling company status.""" def test_toggle_verification_on(self, db, unverified_company): """Test setting verification to True.""" result = company_service.toggle_verification(db, unverified_company.id, True) db.commit() assert result.is_verified is True def test_toggle_verification_off(self, db, test_company): """Test setting verification to False.""" result = company_service.toggle_verification(db, test_company.id, False) db.commit() assert result.is_verified is False def test_toggle_active_on(self, db, inactive_company): """Test setting active status to True.""" result = company_service.toggle_active(db, inactive_company.id, True) db.commit() assert result.is_active is True def test_toggle_active_off(self, db, test_company): """Test setting active status to False.""" result = company_service.toggle_active(db, test_company.id, False) db.commit() assert result.is_active is False # ============================================================================= # OWNERSHIP TRANSFER TESTS # ============================================================================= @pytest.mark.unit @pytest.mark.tenancy class TestCompanyServiceOwnershipTransfer: """Test suite for company ownership transfer.""" def test_transfer_ownership_success(self, db, test_company, other_user): """Test successful ownership transfer.""" original_owner_id = test_company.owner_user_id transfer_data = CompanyTransferOwnership( new_owner_user_id=other_user.id, transfer_reason="Testing ownership transfer", ) company, old_owner, new_owner = company_service.transfer_ownership( db, test_company.id, transfer_data ) db.commit() assert company.owner_user_id == other_user.id assert old_owner.id == original_owner_id assert new_owner.id == other_user.id def test_transfer_ownership_to_same_owner_fails(self, db, test_company, test_user): """Test transfer to same owner raises error.""" transfer_data = CompanyTransferOwnership( new_owner_user_id=test_user.id, transfer_reason="This should fail", ) with pytest.raises(ValueError) as exc_info: company_service.transfer_ownership(db, test_company.id, transfer_data) assert "current owner" in str(exc_info.value).lower() def test_transfer_ownership_to_nonexistent_user_fails(self, db, test_company): """Test transfer to non-existent user raises exception.""" transfer_data = CompanyTransferOwnership( new_owner_user_id=99999, transfer_reason="This should fail", ) with pytest.raises(UserNotFoundException): company_service.transfer_ownership(db, test_company.id, transfer_data) def test_transfer_ownership_nonexistent_company_fails(self, db, other_user): """Test transfer on non-existent company raises exception.""" transfer_data = CompanyTransferOwnership( new_owner_user_id=other_user.id, transfer_reason="This should fail", ) with pytest.raises(CompanyNotFoundException): company_service.transfer_ownership(db, 99999, transfer_data)