feat: implement complete CMT backend with API endpoints and test suite

- Add 7 core API endpoints: users, transactions, partners, products, inventory, payments, credit
- Implement role-based authentication (admin/write/read-only access)
- Add comprehensive database models with proper relationships
- Include full test coverage for all endpoints and business logic
- Set up Alembic migrations and Docker configuration
- Configure FastAPI app with CORS and database integration
This commit is contained in:
2025-09-14 21:04:07 +02:00
parent 49c813778b
commit c086f64363
48 changed files with 6992 additions and 126 deletions
+216
View File
@@ -0,0 +1,216 @@
import pytest
from fastapi.testclient import TestClient
from sqlmodel import Session, SQLModel, create_engine
from sqlmodel.pool import StaticPool
from app.main import app
from app.core.db import get_session
from app.schemas.models import User, Partner, Product, Transaction
from app.schemas.base import UserRole, PartnerType, TransactionType, TransactionStatus
from app.core.auth import get_password_hash
@pytest.fixture(name="session")
def session_fixture():
"""Create a test database session."""
engine = create_engine(
"sqlite:///:memory:", # Use in-memory database for each test
connect_args={"check_same_thread": False},
poolclass=StaticPool,
)
SQLModel.metadata.create_all(engine)
with Session(engine) as session:
yield session
@pytest.fixture(name="client")
def client_fixture(session: Session):
"""Create a test client with dependency override."""
def get_session_override():
return session
app.dependency_overrides[get_session] = get_session_override
client = TestClient(app)
yield client
app.dependency_overrides.clear()
@pytest.fixture(name="admin_user")
def admin_user_fixture(session: Session):
"""Create an admin user for testing."""
admin_user = User(
username="testadmin",
password_hash=get_password_hash("adminpassword"),
role=UserRole.ADMIN
)
session.add(admin_user)
session.commit()
session.refresh(admin_user)
return admin_user
@pytest.fixture(name="write_user")
def write_user_fixture(session: Session):
"""Create a write user for testing."""
write_user = User(
username="writeuser",
password_hash=get_password_hash("writepassword"),
role=UserRole.WRITE
)
session.add(write_user)
session.commit()
session.refresh(write_user)
return write_user
@pytest.fixture(name="read_only_user")
def read_only_user_fixture(session: Session):
"""Create a read-only user for testing."""
read_only_user = User(
username="readuser",
password_hash=get_password_hash("readpassword"),
role=UserRole.READ_ONLY
)
session.add(read_only_user)
session.commit()
session.refresh(read_only_user)
return read_only_user
@pytest.fixture(name="admin_token")
def admin_token_fixture(client: TestClient, admin_user: User):
"""Get admin authentication token."""
response = client.post("/api/v1/users/login", json={
"username": "testadmin",
"password": "adminpassword"
})
return response.json()["access_token"]
@pytest.fixture(name="write_token")
def write_token_fixture(client: TestClient, write_user: User):
"""Get write user authentication token."""
response = client.post("/api/v1/users/login", json={
"username": "writeuser",
"password": "writepassword"
})
return response.json()["access_token"]
@pytest.fixture(name="read_only_token")
def read_only_token_fixture(client: TestClient, read_only_user: User):
"""Get read-only user authentication token."""
response = client.post("/api/v1/users/login", json={
"username": "readuser",
"password": "readpassword"
})
return response.json()["access_token"]
@pytest.fixture(name="sample_partner")
def sample_partner_fixture(session: Session):
"""Create a sample partner for testing."""
partner = Partner(
tin_number=123456789,
names="Test Partner Ltd",
type=PartnerType.CLIENT,
phone_number="0123456789"
)
session.add(partner)
session.commit()
session.refresh(partner)
return partner
@pytest.fixture(name="sample_product")
def sample_product_fixture(session: Session):
"""Create a sample product for testing."""
product = Product(
product_code="PROD001",
product_name="Test Product",
purchase_price=100,
selling_price=150
)
session.add(product)
session.commit()
session.refresh(product)
return product
@pytest.fixture(name="sample_transaction")
def sample_transaction_fixture(session: Session, sample_partner, admin_user):
"""Create a sample transaction for testing."""
transaction = Transaction(
partner_id=sample_partner.id,
transcation_type=TransactionType.SALE,
transaction_status=TransactionStatus.UNPAID,
total_amount=1000,
created_by=admin_user.id,
updated_by=admin_user.id
)
session.add(transaction)
session.commit()
session.refresh(transaction)
return transaction
@pytest.fixture(name="multiple_partners")
def multiple_partners_fixture(session: Session):
"""Create multiple partners for testing."""
partners = [
Partner(
tin_number=100000001,
names="Client Partner One",
type=PartnerType.CLIENT,
phone_number="0111111111"
),
Partner(
tin_number=200000002,
names="Supplier Partner Two",
type=PartnerType.SUPPLIER,
phone_number="0222222222"
),
Partner(
tin_number=300000003,
names="Client Partner Three",
type=PartnerType.CLIENT,
phone_number="0333333333"
)
]
for partner in partners:
session.add(partner)
session.commit()
for partner in partners:
session.refresh(partner)
return partners
@pytest.fixture(name="multiple_products")
def multiple_products_fixture(session: Session):
"""Create multiple products for testing."""
products = [
Product(
product_code="ITEM001",
product_name="Product One",
purchase_price=50,
selling_price=75
),
Product(
product_code="ITEM002",
product_name="Product Two",
purchase_price=200,
selling_price=250
),
Product(
product_code="ITEM003",
product_name="Product Three",
purchase_price=1000,
selling_price=1200
)
]
for product in products:
session.add(product)
session.commit()
for product in products:
session.refresh(product)
return products