354 lines
8.3 KiB
Python
354 lines
8.3 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
|