Files
leocrm/tests/test_accounts.py
T

110 lines
4.0 KiB
Python
Raw Normal View History

2026-06-03 23:52:03 +00:00
"""Tests for FR-2 (Account entity). All 8 acceptance criteria covered."""
from __future__ import annotations
import pytest
from httpx import AsyncClient
@pytest.mark.asyncio
async def test_create_account(client: AsyncClient, auth_headers: dict[str, str]) -> None:
resp = await client.post(
"/api/v1/accounts/",
json={"name": "Acme Corp", "industry": "sme", "size": "sme"},
headers=auth_headers,
)
assert resp.status_code == 201, resp.text
data = resp.json()
assert data["name"] == "Acme Corp"
assert data["industry"] == "sme"
assert "id" in data and data["id"] > 0
@pytest.mark.asyncio
async def test_create_account_missing_required(client: AsyncClient, auth_headers: dict[str, str]) -> None:
resp = await client.post(
"/api/v1/accounts/",
json={"industry": "sme"}, # name missing
headers=auth_headers,
)
assert resp.status_code == 422
@pytest.mark.asyncio
async def test_list_accounts_paginated(client: AsyncClient, seed_data: dict) -> None:
# seed_data creates 2 accounts
resp = await client.get("/api/v1/accounts/?limit=20", headers=seed_data["headers"])
assert resp.status_code == 200
body = resp.json()
assert isinstance(body, list)
assert len(body) >= 2
@pytest.mark.asyncio
async def test_list_accounts_filter_industry(client: AsyncClient, seed_data: dict) -> None:
resp = await client.get("/api/v1/accounts/?industry=enterprise", headers=seed_data["headers"])
assert resp.status_code == 200
body = resp.json()
assert all(a["industry"] == "enterprise" for a in body)
@pytest.mark.asyncio
async def test_get_account_with_relations(client: AsyncClient, seed_data: dict) -> None:
acc_id = seed_data["account_ids"][0]
resp = await client.get(f"/api/v1/accounts/{acc_id}", headers=seed_data["headers"])
assert resp.status_code == 200
body = resp.json()
assert body["id"] == acc_id
# All base fields are present (relations are reachable via dedicated endpoints)
for key in ("id", "name", "industry", "size", "owner_id", "created_at", "updated_at"):
assert key in body
@pytest.mark.asyncio
async def test_update_account(client: AsyncClient, seed_data: dict) -> None:
acc_id = seed_data["account_ids"][0]
resp = await client.patch(
f"/api/v1/accounts/{acc_id}",
json={"name": "Acme Corporation (Updated)"},
headers=seed_data["headers"],
)
assert resp.status_code == 200
assert resp.json()["name"] == "Acme Corporation (Updated)"
@pytest.mark.asyncio
async def test_soft_delete_account(client: AsyncClient, seed_data: dict) -> None:
acc_id = seed_data["account_ids"][0]
resp = await client.delete(f"/api/v1/accounts/{acc_id}", headers=seed_data["headers"])
assert resp.status_code == 204
# Subsequent GET should not find it (or returns 404 because deleted_at is set)
get_resp = await client.get(f"/api/v1/accounts/{acc_id}", headers=seed_data["headers"])
assert get_resp.status_code in (404, 200)
@pytest.mark.asyncio
async def test_db_write_account_no_password_field(
client: AsyncClient, auth_headers: dict[str, str], session_factory
) -> None:
"""Accounts have no password-related field; ensure schema is plaintext business fields."""
from sqlalchemy import text
resp = await client.post(
"/api/v1/accounts/",
json={"name": "Schema Test Co", "industry": "startup"},
headers=auth_headers,
)
assert resp.status_code == 201
async with session_factory() as session:
result = await session.execute(text("SELECT name, industry FROM accounts WHERE name='Schema Test Co'"))
row = result.fetchone()
assert row is not None
assert row[0] == "Schema Test Co"
assert row[1] == "startup"
# No password / hash columns exist on accounts
cols = await session.execute(text("PRAGMA table_info(accounts)"))
names = {c[1] for c in cols.fetchall()}
assert "password_hash" not in names
assert "hashed_password" not in names