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:
@@ -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
|
||||
Reference in New Issue
Block a user