Files
dengwenhao 99e42f8be4
Deploy TodoList / deploy (push) Successful in 4s
style: format Python files with Ruff
2026-07-31 10:26:51 +08:00

370 lines
8.2 KiB
Python

import pytest
from fastapi.testclient import TestClient
from sqlalchemy.exc import SQLAlchemyError
from models import Todo
from tests.fakes import FakeSession
@pytest.mark.parametrize(
"invalid_task",
[
"",
" ",
"a" * 201,
],
)
def test_create_todo_rejects_invalid_task(
client: TestClient,
invalid_task: str,
) -> None:
response = client.post(
"/todos",
json={"task": invalid_task},
)
assert response.status_code == 422
def test_create_todo_rejects_missing_task(
client: TestClient,
) -> None:
response = client.post(
"/todos",
json={},
)
assert response.status_code == 422
error = response.json()["detail"][0]
assert error["loc"] == ["body", "task"]
assert error["type"] == "missing"
def test_create_todo_success(
client: TestClient,
fake_session: FakeSession,
) -> None:
response = client.post(
"/todos",
json={"task": "学习单元测试"},
)
assert response.status_code == 200
assert response.json() == {
"id": 1,
"task": "学习单元测试",
"completion": False,
}
assert fake_session.committed is True
assert fake_session.added_todo.task == "学习单元测试"
def test_patch_missing_todo_returns_404(
client: TestClient,
fake_session: FakeSession,
) -> None:
fake_session.todo_to_return = None
response = client.patch(
"/todos/999",
json={"task": "修改后的任务"},
)
assert response.status_code == 404
assert response.json() == {
"detail": "Todo not found",
}
assert fake_session.committed is False
assert fake_session.rolled_back is False
def test_patch_existing_todo_updates_task(
client: TestClient,
fake_session: FakeSession,
) -> None:
fake_session.todo_to_return = Todo(
id=1,
task="旧任务",
completion=False,
)
response = client.patch(
"/todos/1",
json={"task": "新任务"},
)
assert response.status_code == 200
assert response.json() == {
"id": 1,
"task": "新任务",
"completion": False,
}
assert fake_session.committed is True
assert fake_session.todo_to_return.task == "新任务"
def test_patch_existing_todo_updates_completion_only(
client: TestClient,
fake_session: FakeSession,
) -> None:
fake_session.todo_to_return = Todo(
id=1,
task="保留原任务",
completion=False,
)
response = client.patch(
"/todos/1",
json={"completion": True},
)
assert response.status_code == 200
assert response.json() == {
"id": 1,
"task": "保留原任务",
"completion": True,
}
assert fake_session.committed is True
assert fake_session.todo_to_return.task == "保留原任务"
assert fake_session.todo_to_return.completion is True
def test_delete_existing_todo_success(
client: TestClient,
fake_session: FakeSession,
) -> None:
existing_todo = Todo(
id=1,
task="准备删除的任务",
completion=False,
)
fake_session.todo_to_return = existing_todo
response = client.delete("/todos/1")
assert response.status_code == 200
assert response.json() == {
"id": 1,
"task": "准备删除的任务",
"completion": False,
}
assert fake_session.committed is True
assert fake_session.deleted_todo is existing_todo
def test_delete_missing_todo_returns_404(
client: TestClient,
fake_session: FakeSession,
) -> None:
response = client.delete("/todos/999")
assert response.status_code == 404
assert response.json() == {
"detail": "Todo not found",
}
assert fake_session.deleted_todo is None
assert fake_session.committed is False
def test_create_todo_rolls_back_when_commit_fails(
client: TestClient,
fake_session: FakeSession,
) -> None:
fake_session.should_fail_commit = True
with pytest.raises(SQLAlchemyError):
client.post(
"/todos",
json={"task": "会提交失败的任务"},
)
assert fake_session.rolled_back is True
assert fake_session.committed is False
def test_patch_todo_rolls_back_when_commit_fails(
client: TestClient,
fake_session: FakeSession,
) -> None:
fake_session.todo_to_return = Todo(
id=1,
task="旧任务",
completion=False,
)
fake_session.should_fail_commit = True
with pytest.raises(SQLAlchemyError):
client.patch(
"/todos/1",
json={"task": "新任务"},
)
assert fake_session.rolled_back is True
assert fake_session.committed is False
def test_delete_todo_rolls_back_when_commit_fails(
client: TestClient,
fake_session: FakeSession,
) -> None:
existing_todo = Todo(
id=1,
task="准备删除的任务",
completion=False,
)
fake_session.todo_to_return = existing_todo
fake_session.should_fail_commit = True
with pytest.raises(SQLAlchemyError):
client.delete("/todos/1")
assert fake_session.deleted_todo is existing_todo
assert fake_session.rolled_back is True
assert fake_session.committed is False
def test_get_todos_returns_todo_list(
client: TestClient,
fake_session: FakeSession,
) -> None:
fake_session.todos_to_return = [
Todo(
id=1,
task="学习单元测试",
completion=False,
),
Todo(
id=2,
task="完成接口测试",
completion=True,
),
]
response = client.get("/todos")
assert response.status_code == 200
assert response.json() == [
{
"id": 1,
"task": "学习单元测试",
"completion": False,
},
{
"id": 2,
"task": "完成接口测试",
"completion": True,
},
]
def test_get_todos_returns_empty_list(
client: TestClient,
fake_session: FakeSession,
) -> None:
fake_session.todos_to_return = []
response = client.get("/todos")
assert response.status_code == 200
assert response.json() == []
@pytest.mark.parametrize(
"invalid_task",
[
"",
" ",
"a" * 201,
],
)
def test_patch_todo_rejects_invalid_task(
client: TestClient,
fake_session: FakeSession,
invalid_task: str,
) -> None:
fake_session.todo_to_return = Todo(
id=1,
task="原任务",
completion=False,
)
response = client.patch(
"/todos/1",
json={"task": invalid_task},
)
assert response.status_code == 422
def test_patch_todo_rejects_empty_body(
client: TestClient,
fake_session: FakeSession,
) -> None:
fake_session.todo_to_return = Todo(
id=1,
task="原任务",
completion=False,
)
response = client.patch(
"/todos/1",
json={},
)
assert response.status_code == 422
assert fake_session.committed is False
def test_patch_todo_accepts_completion_false(
client: TestClient,
fake_session: FakeSession,
) -> None:
fake_session.todo_to_return = Todo(
id=1,
task="已有任务",
completion=True,
)
response = client.patch(
"/todos/1",
json={"completion": False},
)
assert response.status_code == 200
assert fake_session.todo_to_return.completion is False
assert response.json()["completion"] is False
assert fake_session.committed is True
def test_patch_todo_updates_task_and_completion(
client: TestClient,
fake_session: FakeSession,
) -> None:
existing_todo = Todo(
id=1,
task="原任务",
completion=False,
)
fake_session.todo_to_return = existing_todo
response = client.patch(
"/todos/1",
json={
"task": "修改后的任务",
"completion": True,
},
)
assert response.status_code == 200
assert response.json() == {
"id": 1,
"task": "修改后的任务",
"completion": True,
}
assert existing_todo.task == "修改后的任务"
assert existing_todo.completion is True
assert fake_session.committed is True