Files
resolutionflow/backend/tests/test_target_lists.py
chihlasm 0c2d4ba685 feat: add target_lists table, schema, and CRUD endpoints
Introduces the TargetList model (team-scoped JSONB target arrays),
Pydantic schemas, and full CRUD REST API at /target-lists/.
Includes Alembic migration and 5 integration tests (TDD).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-17 11:16:40 -05:00

108 lines
3.5 KiB
Python

"""Tests for target lists CRUD."""
import pytest
from httpx import AsyncClient
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.team import Team
from app.models.user import User
from sqlalchemy import select
@pytest.fixture
async def auth_headers(client: AsyncClient, test_db: AsyncSession, test_user: dict):
"""Override auth_headers to ensure the test user has a team_id assigned."""
# Fetch the user from DB and assign a team
result = await test_db.execute(select(User).where(User.email == test_user["email"]))
user = result.scalar_one()
# Create a team and assign the user to it
team = Team(name="Test Team")
test_db.add(team)
await test_db.flush()
user.team_id = team.id
await test_db.commit()
# Re-login to get a fresh token
login_data = {
"email": test_user["email"],
"password": test_user["password"],
}
resp = await client.post("/api/v1/auth/login/json", json=login_data)
assert resp.status_code == 200
token_data = resp.json()
return {"Authorization": f"Bearer {token_data['access_token']}"}
@pytest.mark.asyncio
async def test_create_target_list(client: AsyncClient, auth_headers: dict):
resp = await client.post(
"/api/v1/target-lists/",
json={
"name": "RDS Farm A",
"description": "Production RDS servers",
"targets": [
{"label": "RDS-01", "notes": "192.168.1.10"},
{"label": "RDS-02", "notes": "192.168.1.11"},
],
},
headers=auth_headers,
)
assert resp.status_code == 201, resp.text
data = resp.json()
assert data["name"] == "RDS Farm A"
assert len(data["targets"]) == 2
@pytest.mark.asyncio
async def test_list_target_lists(client: AsyncClient, auth_headers: dict):
resp = await client.get("/api/v1/target-lists/", headers=auth_headers)
assert resp.status_code == 200
assert isinstance(resp.json(), list)
@pytest.mark.asyncio
async def test_get_target_list(client: AsyncClient, auth_headers: dict):
create = await client.post(
"/api/v1/target-lists/",
json={"name": "Get Test", "targets": [{"label": "SRV-01"}]},
headers=auth_headers,
)
list_id = create.json()["id"]
resp = await client.get(f"/api/v1/target-lists/{list_id}", headers=auth_headers)
assert resp.status_code == 200
assert resp.json()["name"] == "Get Test"
@pytest.mark.asyncio
async def test_update_target_list(client: AsyncClient, auth_headers: dict):
create = await client.post(
"/api/v1/target-lists/",
json={"name": "Old Name", "targets": [{"label": "SRV-01"}]},
headers=auth_headers,
)
list_id = create.json()["id"]
resp = await client.put(
f"/api/v1/target-lists/{list_id}",
json={"name": "New Name", "targets": [{"label": "SRV-01"}, {"label": "SRV-02"}]},
headers=auth_headers,
)
assert resp.status_code == 200
assert resp.json()["name"] == "New Name"
assert len(resp.json()["targets"]) == 2
@pytest.mark.asyncio
async def test_delete_target_list(client: AsyncClient, auth_headers: dict):
create = await client.post(
"/api/v1/target-lists/",
json={"name": "To Delete", "targets": [{"label": "X"}]},
headers=auth_headers,
)
list_id = create.json()["id"]
resp = await client.delete(f"/api/v1/target-lists/{list_id}", headers=auth_headers)
assert resp.status_code == 204
get = await client.get(f"/api/v1/target-lists/{list_id}", headers=auth_headers)
assert get.status_code == 404