File size: 3,605 Bytes
cf35fad
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
from __future__ import annotations

import pytest
from httpx import AsyncClient

from conftest import generate_valid_transaction_data


pytestmark = [pytest.mark.asyncio, pytest.mark.validation]


async def test_invalid_email_format_returns_422(client: AsyncClient) -> None:
    """Invalid emails should be rejected during registration."""

    response = await client.post("/api/auth/register", json={"email": "bad-email", "password": "StrongPass123"})

    assert response.status_code == 422, f"Invalid email should return 422, got {response.text}"


@pytest.mark.parametrize(

    ("payload", "field"),

    [

        ({"amount": "10.00", "type": "invalid", "category": "Food", "date": "2024-01-01"}, "type"),

        ({"amount": "10.00", "type": "income", "category": "Food", "date": "invalid-date"}, "date"),

        ({"amount": "not-a-number", "type": "income", "category": "Food", "date": "2024-01-01"}, "amount"),

    ],

)
async def test_invalid_transaction_fields_return_422(

    admin_client: AsyncClient,

    payload: dict[str, str],

    field: str,

) -> None:
    """Malformed transaction input should return the normalized validation payload."""

    response = await admin_client.post("/api/transactions", json=payload)

    assert response.status_code == 422, f"Invalid field {field} should return 422, got {response.text}"
    assert any(error["field"] == field for error in response.json()["errors"]), f"422 payload should include the {field} field"


async def test_422_response_has_correct_format(client: AsyncClient) -> None:
    """Validation errors should follow the app's normalized response format."""

    response = await client.post("/api/auth/register", json={"email": "still-bad"})

    assert response.status_code == 422, f"Invalid registration payload should return 422, got {response.text}"
    payload = response.json()
    assert payload["detail"] == "Validation error", "Validation response should use the shared detail string"
    assert isinstance(payload["errors"], list) and payload["errors"], "Validation response should include a non-empty errors list"


async def test_extra_fields_are_ignored_on_registration(client: AsyncClient) -> None:
    """Unexpected fields should be ignored under the current Pydantic configuration."""

    response = await client.post(
        "/api/auth/register",
        json={
            "email": "extrafields@example.com",
            "password": "StrongPass123",
            "full_name": "Extra Fields",
            "ignored_field": "value",
        },
    )

    assert response.status_code == 201, f"Extra fields should be ignored, got {response.text}"
    assert "ignored_field" not in response.json(), "Ignored fields should not be echoed back in the response"


async def test_amount_precision_validation(admin_client: AsyncClient) -> None:
    """Amounts with too many decimal places should fail validation."""

    response = await admin_client.post("/api/transactions", json=generate_valid_transaction_data(amount="10.999"))

    assert response.status_code == 422, f"Invalid decimal precision should return 422, got {response.text}"


async def test_end_date_before_start_date_returns_422(analyst_client: AsyncClient) -> None:
    """Invalid date ranges should fail query validation."""

    response = await analyst_client.get(
        "/api/transactions",
        params={"start_date": "2024-01-10", "end_date": "2024-01-01"},
    )

    assert response.status_code == 422, f"Invalid date range should return 422, got {response.text}"