feat: comprehensive test infrastructure improvements
- Fix unit test imports: add env setup in conftest.py before module imports - Add 24 new auth router tests (RBAC, preferences, password validation) - Add 16 new tests for litellm and chat_summary routers - Apply black formatting and ruff linting across codebase - Add pre-commit hooks configuration (black, ruff, file checks) - Increase CI coverage threshold from 40% to 50% Test Results: - 206 tests passing (91 unit + 115 integration) - Coverage: 58.79% on core modules - auth.py: 57% → 85%, litellm.py: 23% → 87%, chat_summary.py: 41% → 100% - auth_service: 96.51%, config: 100%, rbac: 93.48%
This commit is contained in:
@@ -1,27 +1,29 @@
|
||||
"""
|
||||
OPC Projects and Invoicing Router
|
||||
"""
|
||||
from fastapi import APIRouter, Depends, HTTPException, Response, Request
|
||||
from typing import Optional
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from fastapi import APIRouter, HTTPException, Request, Response
|
||||
from pydantic import BaseModel
|
||||
|
||||
from db import opc_db
|
||||
from rbac import User
|
||||
from services import pdf_service
|
||||
from datetime import datetime
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
class ProjectCreate(BaseModel):
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
client_id: Optional[int] = None
|
||||
description: str | None = None
|
||||
client_id: int | None = None
|
||||
|
||||
|
||||
class InvoiceGenerate(BaseModel):
|
||||
project_id: int
|
||||
client_id: int
|
||||
invoice_number: Optional[str] = None
|
||||
invoice_number: str | None = None
|
||||
|
||||
|
||||
@router.get("/projects")
|
||||
@@ -45,11 +47,16 @@ async def create_project(
|
||||
user: User = request.state.user
|
||||
pool = await opc_db.get_pool()
|
||||
async with pool.acquire() as conn:
|
||||
row = await conn.fetchrow("""
|
||||
row = await conn.fetchrow(
|
||||
"""
|
||||
INSERT INTO projects (name, description, client_id)
|
||||
VALUES ($1, $2, $3)
|
||||
RETURNING *
|
||||
""", project.name, project.description, project.client_id)
|
||||
""",
|
||||
project.name,
|
||||
project.description,
|
||||
project.client_id,
|
||||
)
|
||||
return dict(row)
|
||||
|
||||
|
||||
@@ -69,18 +76,23 @@ async def list_clients(
|
||||
async def create_client(
|
||||
request: Request,
|
||||
name: str,
|
||||
email: Optional[str] = None,
|
||||
company: Optional[str] = None,
|
||||
email: str | None = None,
|
||||
company: str | None = None,
|
||||
):
|
||||
"""Create a new client"""
|
||||
user: User = request.state.user
|
||||
pool = await opc_db.get_pool()
|
||||
async with pool.acquire() as conn:
|
||||
row = await conn.fetchrow("""
|
||||
row = await conn.fetchrow(
|
||||
"""
|
||||
INSERT INTO clients (name, email, company)
|
||||
VALUES ($1, $2, $3)
|
||||
RETURNING *
|
||||
""", name, email, company)
|
||||
""",
|
||||
name,
|
||||
email,
|
||||
company,
|
||||
)
|
||||
return dict(row)
|
||||
|
||||
|
||||
@@ -100,11 +112,14 @@ async def generate_invoice(
|
||||
client = dict(client_row)
|
||||
|
||||
# Get time entries for project
|
||||
time_rows = await conn.fetch("""
|
||||
time_rows = await conn.fetch(
|
||||
"""
|
||||
SELECT * FROM time_entries
|
||||
WHERE project_id = $1 AND billable = TRUE
|
||||
ORDER BY started_at
|
||||
""", invoice.project_id)
|
||||
""",
|
||||
invoice.project_id,
|
||||
)
|
||||
|
||||
if not time_rows:
|
||||
raise HTTPException(status_code=400, detail="No billable time entries found")
|
||||
@@ -116,7 +131,7 @@ async def generate_invoice(
|
||||
project_id=invoice.project_id,
|
||||
client=client,
|
||||
time_entries=time_entries,
|
||||
invoice_number=invoice.invoice_number
|
||||
invoice_number=invoice.invoice_number,
|
||||
)
|
||||
|
||||
# Return PDF
|
||||
@@ -124,9 +139,7 @@ async def generate_invoice(
|
||||
return Response(
|
||||
content=pdf_bytes,
|
||||
media_type="application/pdf",
|
||||
headers={
|
||||
"Content-Disposition": f"attachment; filename={filename}"
|
||||
}
|
||||
headers={"Content-Disposition": f"attachment; filename={filename}"},
|
||||
)
|
||||
|
||||
|
||||
@@ -146,5 +159,5 @@ async def get_project_time(
|
||||
"total_minutes": total_minutes,
|
||||
"total_hours": round(total_minutes / 60, 2),
|
||||
"billable_minutes": billable_minutes,
|
||||
"billable_hours": round(billable_minutes / 60, 2)
|
||||
"billable_hours": round(billable_minutes / 60, 2),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user