Move letzshop-related functionality from tenancy to marketplace module: - Move admin letzshop routes to marketplace/routes/api/admin_letzshop.py - Move letzshop schemas to marketplace/schemas/letzshop.py - Remove letzshop code from tenancy module (admin_vendors, vendor_service) - Update model exports and imports Add comprehensive unit tests for vendor services: - test_company_service.py: Company management operations - test_platform_service.py: Platform management operations - test_vendor_domain_service.py: Vendor domain operations - test_vendor_team_service.py: Vendor team management Update module definitions: - billing, messaging, payments: Minor definition updates Add architecture proposals documentation: - Module dependency redesign session notes - Decouple modules implementation plan - Module decoupling proposal Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
356 lines
12 KiB
Python
356 lines
12 KiB
Python
# 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)
|