Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@

USE_MOCK_SERVICES = os.getenv("USE_MOCK_SERVICES", "true").lower() == "true"

DATABASE_URL = os.getenv("DATABASE_URL", "postgresql://user:password@localhost:5432/orion_db")
# Have just added async driver ('+asyncpg') to URL to match app - Lucas
DATABASE_URL = os.getenv("DATABASE_URL", "postgresql+asyncpg://user:password@localhost:5432/orion_db")

JWT_SECRET_KEY = os.getenv("JWT_SECRET_KEY", "your-secret-key-here")
JWT_ALGORITHM = os.getenv("JWT_ALGORITHM", "HS256")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
)
logger = logging.getLogger(__name__)

Base.metadata.create_all(bind=engine)
# Base.metadata.create_all(bind=engine.sync_engine)

app = FastAPI(
title="Project Orion Backend API",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
from app.services.player_client import get_player_data
from app.services.crowd_client import get_crowd_data

from sqlalchemy import select

router = APIRouter()

ALLOWED_EXTENSIONS = {".mp4", ".avi", ".mov"}
Expand Down Expand Up @@ -44,16 +46,17 @@ async def process_video(job_id: str, file_path: str):
status = "done"
error = None

job = db.query(Job).filter(Job.job_id == job_id).first()
result = await db.execute(select(Job).where(Job.job_id == job_id))
job = result.scalar_one_or_none()
if job:
job.status = status
job.player_result = player_data
job.crowd_result = crowd_data
job.error = error
job.updated_at = datetime.now(timezone.utc)
db.commit()
await db.commit()
finally:
db.close()
await db.close()
if os.path.exists(file_path):
os.remove(file_path)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
from datetime import datetime, timezone
from types import SimpleNamespace

from app.main import app
from app.auth.dependencies import get_current_user

# Bypass authentication
def override_get_current_user():
return {"sub": "test_user", "role": "admin"}

# Mock DB record
def make_job(
job_id="job-123",
status="done",
user_id="test_user",
player_result=None,
crowd_result=None,
error=None,
video_path="uploads/test.mp4",
):
now = datetime.now(timezone.utc)

return SimpleNamespace(
job_id=job_id,
status=status,
user_id=user_id,
player_result=player_result,
crowd_result=crowd_result,
error=error,
video_path=video_path,
created_at=now,
updated_at=now,
)

# Test: Get job status (success)
def test_get_status_success(client, mock_db):
app.dependency_overrides[get_current_user] = override_get_current_user
fake_job = make_job(
status="done",
player_result={"players": 10},
crowd_result={"crowd": 50},
)
mock_db.query.return_value.filter.return_value.first.return_value = fake_job # Mock DB query to return fake job
response = client.get("/status/job-123") # Send GET request to endpoint
assert response.status_code == 200
data = response.json()
assert data["job_id"] == "job-123"
assert data["status"] == "done"
assert "results" in data

# Test: Job status not found
def test_get_status_not_found(client, mock_db):
app.dependency_overrides[get_current_user] = override_get_current_user

mock_db.query.return_value.filter.return_value.first.return_value = None

response = client.get("/status/missing-job")

assert response.status_code == 404
assert response.json()["detail"] == "Job not found"

# Test: List jobs (pagination)
def test_list_jobs_with_pagination(client, mock_db):
app.dependency_overrides[get_current_user] = override_get_current_user

# Create fake jobs list
jobs = [
make_job(job_id="job-1"),
make_job(job_id="job-2"),
make_job(job_id="job-3"),
]

mock_db.query.return_value.count.return_value = 3
mock_db.query.return_value.order_by.return_value.offset.return_value.limit.return_value.all.return_value = jobs[:2]

response = client.get("/jobs?page=1&limit=2")
assert response.status_code == 200
data = response.json()

# Check pagination fields
assert data["total"] == 3
assert data["page"] == 1
assert data["limit"] == 2
assert len(data["jobs"]) == 2 # only 2 returned

# Test: Get job details
def test_get_job_success(client, mock_db):
app.dependency_overrides[get_current_user] = override_get_current_user

# Fake completed job
fake_job = make_job(
status="done",
player_result={"players": 10},
crowd_result={"crowd": 50},
)

mock_db.query.return_value.filter.return_value.first.return_value = fake_job

response = client.get("/jobs/job-123")
assert response.status_code == 200
data = response.json()
# Validate job data
assert data["job_id"] == "job-123"
assert data["status"] == "done"
assert "results" in data

# Test Get job not found
def test_get_job_not_found(client, mock_db):
app.dependency_overrides[get_current_user] = override_get_current_user

# No job returned
mock_db.query.return_value.filter.return_value.first.return_value = None

response = client.get("/jobs/missing-job")
assert response.status_code == 404
assert response.json()["detail"] == "Job not found"

# Test: Retry job (success case)
def test_retry_job_success(client, mock_db, monkeypatch):
app.dependency_overrides[get_current_user] = override_get_current_user

# Partial job (only one result missing)
fake_job = make_job(
status="partial",
player_result=None,
crowd_result={"crowd": 50},
)

mock_db.query.return_value.filter.return_value.first.return_value = fake_job

# Mock async player service response
async def fake_player_data(video_path):
return {"players": 12}

# Replace real service call with fake one
monkeypatch.setattr("app.routes.jobs.get_player_data", fake_player_data)

response = client.post("/jobs/job-123/retry")
assert response.status_code == 200
data = response.json()

# Job should now be completed
assert data["job_id"] == "job-123"
assert data["status"] == "done"

# Test: Retry invalid job
def test_retry_job_not_partial(client, mock_db):
app.dependency_overrides[get_current_user] = override_get_current_user

# Job already complete
fake_job = make_job(
status="done",
player_result={"players": 10},
crowd_result={"crowd": 50},
)

mock_db.query.return_value.filter.return_value.first.return_value = fake_job

response = client.post("/jobs/job-123/retry")

# Should fail
assert response.status_code == 400
assert response.json()["detail"] == "Only partial jobs can be retried"

# Test: Delete job
def test_delete_job_success(client, mock_db):
app.dependency_overrides[get_current_user] = override_get_current_user

fake_job = make_job()

# Mock DB returning a job
mock_db.query.return_value.filter.return_value.first.return_value = fake_job

response = client.delete("/jobs/job-123")
assert response.status_code == 200
assert response.json()["message"] == "job deleted"

# Ensure DB methods were called
mock_db.delete.assert_called_once_with(fake_job)
mock_db.commit.assert_called()
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from app.main import app
from app.auth.dependencies import get_current_user

def override_get_current_user():
return {"sub": "test_user", "role": "admin"}

async def fake_process_video(job_id, file_path):
return None

def test_upload_valid_file(client, monkeypatch):
app.dependency_overrides[get_current_user] = override_get_current_user
monkeypatch.setattr("app.routes.upload.process_video", fake_process_video)
response = client.post("/upload", files={"file": ("test.mp4", b"fake video content", "video/mp4")})
assert response.status_code == 200
assert "job_id" in response.json()
assert response.json()["status"] == "processing"

def test_upload_invalid_file_type(client):
app.dependency_overrides[get_current_user] = override_get_current_user
response = client.post("/upload", files={"file": ("text.txt", b"dummy,data", "text/plain")})
assert response.status_code == 400
assert "invalid" in str(response.json()).lower()

def test_missing_file(client):
app.dependency_overrides[get_current_user] = override_get_current_user
response = client.post("/upload", files={})
assert response.status_code == 422
assert "file" in str(response.json())
Loading