paper-dynasty-database/tests/test_api_packs.py
Cal Corum 64be5eabdc feat: add initial test suite with pytest (#28)
- Add SQLITE_DB_PATH env var to db_engine.py for test isolation
- Create tests/conftest.py with in-memory SQLite fixture and sample data helpers
- Add tests/test_dependencies.py: unit tests for valid_token, mround, param_char, get_req_url
- Add tests/test_card_pricing.py: tests for Player.change_on_sell/buy and get_all_pos
- Add tests/test_api_packs.py: integration tests for GET/POST/DELETE /api/v2/packs
- Add requirements-test.txt with pytest and httpx
- Add test job to CI workflow (build now requires tests to pass first)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 16:35:53 -06:00

131 lines
4.7 KiB
Python

"""
Integration tests for the /api/v2/packs endpoints.
Uses FastAPI's TestClient (starlette) with an isolated SQLite test database.
The db_transaction fixture in conftest.py rolls back all changes after each test.
"""
import pytest
from starlette.testclient import TestClient
from app.main import app
TEST_TOKEN = "test_token_12345"
AUTH_HEADERS = {"Authorization": f"Bearer {TEST_TOKEN}"}
@pytest.fixture
def client():
"""Return a TestClient for the FastAPI app."""
return TestClient(app, raise_server_exceptions=True)
class TestGetPacks:
"""Tests for GET /api/v2/packs."""
def test_empty_database_returns_404(self, client):
"""With no packs in the DB, the endpoint should return 404."""
resp = client.get("/api/v2/packs")
assert resp.status_code == 404
def test_returns_pack_list(self, client, sample_pack):
"""With at least one pack, GET /packs returns count and list."""
resp = client.get("/api/v2/packs")
assert resp.status_code == 200
data = resp.json()
assert "count" in data
assert data["count"] >= 1
assert "packs" in data
assert len(data["packs"]) >= 1
def test_filter_by_team_id(self, client, sample_pack, sample_team):
"""team_id filter should return only packs for that team."""
resp = client.get(f"/api/v2/packs?team_id={sample_team.id}")
assert resp.status_code == 200
data = resp.json()
for pack in data["packs"]:
assert pack["team"]["id"] == sample_team.id
def test_filter_by_invalid_team_returns_404(self, client, sample_pack):
"""Filtering by a non-existent team_id should return 404."""
resp = client.get("/api/v2/packs?team_id=999999")
assert resp.status_code == 404
def test_filter_opened_false(self, client, sample_pack):
"""opened=false should return only packs with no open_time."""
resp = client.get("/api/v2/packs?opened=false")
assert resp.status_code == 200
data = resp.json()
for pack in data["packs"]:
assert pack["open_time"] is None
class TestGetOnePack:
"""Tests for GET /api/v2/packs/{pack_id}."""
def test_get_existing_pack(self, client, sample_pack):
"""GET with a valid pack_id should return that pack's data."""
resp = client.get(f"/api/v2/packs/{sample_pack.id}")
assert resp.status_code == 200
data = resp.json()
assert data["id"] == sample_pack.id
def test_get_nonexistent_pack_returns_404(self, client):
"""GET with an unknown pack_id should return 404."""
resp = client.get("/api/v2/packs/999999")
assert resp.status_code == 404
class TestPostPack:
"""Tests for POST /api/v2/packs — requires auth token."""
def test_post_without_token_returns_401(
self, client, sample_team, sample_pack_type
):
"""POST without Authorization header should return 422 (missing field)."""
payload = {
"packs": [{"team_id": sample_team.id, "pack_type_id": sample_pack_type.id}]
}
resp = client.post("/api/v2/packs", json=payload)
# FastAPI returns 422 when required OAuth2 token is missing
assert resp.status_code == 422
def test_post_with_invalid_token_returns_401(
self, client, sample_team, sample_pack_type
):
"""POST with a wrong token should return 401."""
payload = {
"packs": [{"team_id": sample_team.id, "pack_type_id": sample_pack_type.id}]
}
resp = client.post(
"/api/v2/packs",
json=payload,
headers={"Authorization": "Bearer wrong_token"},
)
assert resp.status_code == 401
def test_post_with_valid_token_creates_packs(
self, client, sample_team, sample_pack_type
):
"""POST with valid token and payload should create the packs (returns 200)."""
payload = {
"packs": [{"team_id": sample_team.id, "pack_type_id": sample_pack_type.id}]
}
resp = client.post("/api/v2/packs", json=payload, headers=AUTH_HEADERS)
# The router raises HTTPException(200) on success
assert resp.status_code == 200
class TestDeletePack:
"""Tests for DELETE /api/v2/packs/{pack_id} — requires auth token."""
def test_delete_nonexistent_pack_returns_404(self, client):
"""Deleting a pack that doesn't exist should return 404."""
resp = client.delete("/api/v2/packs/999999", headers=AUTH_HEADERS)
assert resp.status_code == 404
def test_delete_without_token_returns_401_or_422(self, client, sample_pack):
"""DELETE without a token should fail auth."""
resp = client.delete(f"/api/v2/packs/{sample_pack.id}")
assert resp.status_code in (401, 422)