File size: 4,239 Bytes
06b349d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import pytest

from app.models import Book, Status


def seed(client, *books):  # books: (title, author, status) tuples
    store = client.app.state.store
    for title, author, status in books:
        book = Book(id=store._next_id, title=title, author=author, status=status)
        store._books[book.id] = book
        store._next_id += 1


def test_list_empty_returns_empty_array(client):
    r = client.get("/api/books")
    assert r.status_code == 200
    assert r.json() == []


def test_list_all_in_id_order(client):
    seed(
        client,
        ("Dune", "Frank Herbert", Status.READING),
        ("Emma", None, Status.TO_READ),
        ("Ulysses", "James Joyce", Status.DONE),
    )
    r = client.get("/api/books")
    assert r.status_code == 200
    assert r.json() == [
        {"id": 1, "title": "Dune", "author": "Frank Herbert", "status": "reading"},
        {"id": 2, "title": "Emma", "author": None, "status": "to-read"},
        {"id": 3, "title": "Ulysses", "author": "James Joyce", "status": "done"},
    ]


@pytest.mark.parametrize(
    ("status", "expected_ids"),
    [("to-read", [2, 4]), ("reading", [1]), ("done", [3])],
)
def test_list_filtered_by_status(client, status, expected_ids):
    seed(
        client,
        ("Dune", "Frank Herbert", Status.READING),
        ("Emma", None, Status.TO_READ),
        ("Ulysses", "James Joyce", Status.DONE),
        ("Beloved", "Toni Morrison", Status.TO_READ),
    )
    r = client.get("/api/books", params={"status": status})
    assert r.status_code == 200
    body = r.json()
    assert [b["id"] for b in body] == expected_ids
    assert all(b["status"] == status for b in body)


def test_list_invalid_status_gives_422(client):
    r = client.get("/api/books", params={"status": "bogus"})
    assert r.status_code == 422
    detail = r.json()["detail"]
    assert isinstance(detail, list)
    assert detail[0]["loc"] == ["query", "status"]


def test_patch_updates_status_and_persists(client):
    seed(
        client,
        ("Dune", "Frank Herbert", Status.READING),
        ("Emma", None, Status.TO_READ),
    )
    r = client.patch("/api/books/1", json={"status": "done"})
    assert r.status_code == 200
    assert r.json() == {
        "id": 1,
        "title": "Dune",
        "author": "Frank Herbert",
        "status": "done",
    }
    listed = client.get("/api/books").json()
    assert [b["status"] for b in listed] == ["done", "to-read"]


def test_patch_missing_id_gives_404(client):
    seed(client, ("Dune", "Frank Herbert", Status.READING))
    r = client.patch("/api/books/99", json={"status": "done"})
    assert r.status_code == 404
    assert r.json() == {"detail": "Book not found"}


@pytest.mark.parametrize(
    "kwargs",
    [
        {"json": {"status": "bogus"}},
        {"json": {}},
        {},
    ],
    ids=["bad-status", "missing-status", "missing-body"],
)
def test_patch_invalid_body_gives_422(client, kwargs):
    seed(client, ("Dune", "Frank Herbert", Status.READING))
    r = client.patch("/api/books/1", **kwargs)
    assert r.status_code == 422
    assert isinstance(r.json()["detail"], list)
    assert client.get("/api/books").json()[0]["status"] == "reading"


def test_patch_non_integer_id_gives_422(client):
    r = client.patch("/api/books/abc", json={"status": "done"})
    assert r.status_code == 422
    assert r.json()["detail"][0]["loc"] == ["path", "book_id"]


def test_stats_empty_store_all_zero(client):
    r = client.get("/api/stats")
    assert r.status_code == 200
    assert r.json() == {
        "total": 0,
        "by_status": {"to-read": 0, "reading": 0, "done": 0},
    }


def test_stats_counts_seeded_books_and_tracks_patch(client):
    seed(
        client,
        ("Dune", "Frank Herbert", Status.READING),
        ("Emma", None, Status.TO_READ),
        ("Ulysses", "James Joyce", Status.DONE),
        ("Beloved", "Toni Morrison", Status.DONE),
    )
    assert client.get("/api/stats").json() == {
        "total": 4,
        "by_status": {"to-read": 1, "reading": 1, "done": 2},
    }
    client.patch("/api/books/1", json={"status": "done"})
    assert client.get("/api/stats").json() == {
        "total": 4,
        "by_status": {"to-read": 1, "reading": 0, "done": 3},
    }