Added full suite of tests & added testing to CI/CD
This commit is contained in:
210
backend/tests/test_todo.py
Normal file
210
backend/tests/test_todo.py
Normal file
@@ -0,0 +1,210 @@
|
||||
import pytest
|
||||
from fastapi import status
|
||||
from fastapi.testclient import TestClient
|
||||
from sqlalchemy.orm import Session
|
||||
from datetime import date
|
||||
|
||||
from tests.helpers import generators
|
||||
from modules.todo import schemas # Import schemas
|
||||
|
||||
# Helper Function
|
||||
def _login_user(db: Session, client: TestClient):
|
||||
user, password = generators.create_user(db)
|
||||
login_rsp = generators.login(db, user.username, password)
|
||||
return user, login_rsp["access_token"], login_rsp["refresh_token"]
|
||||
|
||||
# --- Test CRUD Operations ---
|
||||
|
||||
def test_create_todo(client: TestClient, db: Session):
|
||||
user, access_token, refresh_token = _login_user(db, client)
|
||||
today_date = date.today()
|
||||
# Format the date string to match the expected response format "YYYY-MM-DDTHH:MM:SS"
|
||||
todo_data = {
|
||||
"task": "Test TODO",
|
||||
"date": f"{today_date.isoformat()}T00:00:00",
|
||||
"remind": True
|
||||
}
|
||||
|
||||
response = client.post(
|
||||
"/api/todos/",
|
||||
headers={"Authorization": f"Bearer {access_token}"},
|
||||
cookies={"refresh_token": refresh_token},
|
||||
json=todo_data
|
||||
)
|
||||
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
data = response.json()
|
||||
assert data["task"] == todo_data["task"]
|
||||
assert data["date"] == todo_data["date"]
|
||||
assert data["remind"] == todo_data["remind"]
|
||||
assert data["complete"] is False # Default
|
||||
assert "id" in data
|
||||
assert data["owner_id"] == user.id
|
||||
|
||||
def test_read_todos(client: TestClient, db: Session):
|
||||
user, access_token, refresh_token = _login_user(db, client)
|
||||
# Create some todos for the user
|
||||
client.post("/api/todos/", headers={"Authorization": f"Bearer {access_token}"}, cookies={"refresh_token": refresh_token}, json={"task": "Todo 1"})
|
||||
client.post("/api/todos/", headers={"Authorization": f"Bearer {access_token}"}, cookies={"refresh_token": refresh_token}, json={"task": "Todo 2"})
|
||||
|
||||
# Create a todo for another user
|
||||
other_user, other_password = generators.create_user(db)
|
||||
other_login_rsp = generators.login(db, other_user.username, other_password)
|
||||
other_access_token = other_login_rsp["access_token"]
|
||||
other_refresh_token = other_login_rsp["refresh_token"]
|
||||
client.post("/api/todos/", headers={"Authorization": f"Bearer {other_access_token}"}, cookies={"refresh_token": other_refresh_token}, json={"task": "Other User Todo"})
|
||||
|
||||
|
||||
response = client.get(
|
||||
"/api/todos/",
|
||||
headers={"Authorization": f"Bearer {access_token}"},
|
||||
cookies={"refresh_token": refresh_token}
|
||||
)
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()
|
||||
assert len(data) == 2 # Should only get todos for the logged-in user
|
||||
assert data[0]["task"] == "Todo 1"
|
||||
assert data[1]["task"] == "Todo 2"
|
||||
|
||||
def test_read_single_todo(client: TestClient, db: Session):
|
||||
user, access_token, refresh_token = _login_user(db, client)
|
||||
create_response = client.post(
|
||||
"/api/todos/",
|
||||
headers={"Authorization": f"Bearer {access_token}"},
|
||||
cookies={"refresh_token": refresh_token},
|
||||
json={"task": "Specific Todo"}
|
||||
)
|
||||
todo_id = create_response.json()["id"]
|
||||
|
||||
response = client.get(
|
||||
f"/api/todos/{todo_id}",
|
||||
headers={"Authorization": f"Bearer {access_token}"},
|
||||
cookies={"refresh_token": refresh_token}
|
||||
)
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()
|
||||
assert data["id"] == todo_id
|
||||
assert data["task"] == "Specific Todo"
|
||||
assert data["owner_id"] == user.id
|
||||
|
||||
def test_read_single_todo_not_found(client: TestClient, db: Session):
|
||||
user, access_token, refresh_token = _login_user(db, client)
|
||||
response = client.get(
|
||||
"/api/todos/9999", # Non-existent ID
|
||||
headers={"Authorization": f"Bearer {access_token}"},
|
||||
cookies={"refresh_token": refresh_token}
|
||||
)
|
||||
assert response.status_code == status.HTTP_404_NOT_FOUND
|
||||
|
||||
def test_read_single_todo_forbidden(client: TestClient, db: Session):
|
||||
user, access_token, refresh_token = _login_user(db, client)
|
||||
|
||||
# Create a todo for another user
|
||||
other_user, other_password = generators.create_user(db)
|
||||
other_login_rsp = generators.login(db, other_user.username, other_password)
|
||||
other_access_token = other_login_rsp["access_token"]
|
||||
other_refresh_token = other_login_rsp["refresh_token"]
|
||||
other_create_response = client.post("/api/todos/", headers={"Authorization": f"Bearer {other_access_token}"}, cookies={"refresh_token": other_refresh_token}, json={"task": "Other User Todo"})
|
||||
other_todo_id = other_create_response.json()["id"]
|
||||
|
||||
# Try to access the other user's todo
|
||||
response = client.get(
|
||||
f"/api/todos/{other_todo_id}",
|
||||
headers={"Authorization": f"Bearer {access_token}"}, # Using the first user's token
|
||||
cookies={"refresh_token": refresh_token}
|
||||
)
|
||||
assert response.status_code == status.HTTP_404_NOT_FOUND # Service raises 404 if not found for *this* user
|
||||
|
||||
def test_update_todo(client: TestClient, db: Session):
|
||||
user, access_token, refresh_token = _login_user(db, client)
|
||||
create_response = client.post(
|
||||
"/api/todos/",
|
||||
headers={"Authorization": f"Bearer {access_token}"},
|
||||
cookies={"refresh_token": refresh_token},
|
||||
json={"task": "Update Me"}
|
||||
)
|
||||
todo_id = create_response.json()["id"]
|
||||
|
||||
update_data = {"task": "Updated Task", "complete": True}
|
||||
response = client.put(
|
||||
f"/api/todos/{todo_id}",
|
||||
headers={"Authorization": f"Bearer {access_token}"},
|
||||
cookies={"refresh_token": refresh_token},
|
||||
json=update_data
|
||||
)
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()
|
||||
assert data["id"] == todo_id
|
||||
assert data["task"] == update_data["task"]
|
||||
assert data["complete"] == update_data["complete"]
|
||||
assert data["owner_id"] == user.id
|
||||
|
||||
# Verify update by reading again
|
||||
get_response = client.get(
|
||||
f"/api/todos/{todo_id}",
|
||||
headers={"Authorization": f"Bearer {access_token}"},
|
||||
cookies={"refresh_token": refresh_token}
|
||||
)
|
||||
assert get_response.json()["task"] == update_data["task"]
|
||||
assert get_response.json()["complete"] == update_data["complete"]
|
||||
|
||||
|
||||
def test_update_todo_not_found(client: TestClient, db: Session):
|
||||
user, access_token, refresh_token = _login_user(db, client)
|
||||
update_data = {"task": "Updated Task", "complete": True}
|
||||
response = client.put(
|
||||
"/api/todos/9999", # Non-existent ID
|
||||
headers={"Authorization": f"Bearer {access_token}"},
|
||||
cookies={"refresh_token": refresh_token},
|
||||
json=update_data
|
||||
)
|
||||
assert response.status_code == status.HTTP_404_NOT_FOUND
|
||||
|
||||
def test_delete_todo(client: TestClient, db: Session):
|
||||
user, access_token, refresh_token = _login_user(db, client)
|
||||
create_response = client.post(
|
||||
"/api/todos/",
|
||||
headers={"Authorization": f"Bearer {access_token}"},
|
||||
cookies={"refresh_token": refresh_token},
|
||||
json={"task": "Delete Me"}
|
||||
)
|
||||
todo_id = create_response.json()["id"]
|
||||
|
||||
response = client.delete(
|
||||
f"/api/todos/{todo_id}",
|
||||
headers={"Authorization": f"Bearer {access_token}"},
|
||||
cookies={"refresh_token": refresh_token}
|
||||
)
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK # Delete returns the deleted item
|
||||
assert response.json()["id"] == todo_id
|
||||
|
||||
# Verify deletion by trying to read
|
||||
get_response = client.get(
|
||||
f"/api/todos/{todo_id}",
|
||||
headers={"Authorization": f"Bearer {access_token}"},
|
||||
cookies={"refresh_token": refresh_token}
|
||||
)
|
||||
assert get_response.status_code == status.HTTP_404_NOT_FOUND
|
||||
|
||||
def test_delete_todo_not_found(client: TestClient, db: Session):
|
||||
user, access_token, refresh_token = _login_user(db, client)
|
||||
response = client.delete(
|
||||
"/api/todos/9999", # Non-existent ID
|
||||
headers={"Authorization": f"Bearer {access_token}"},
|
||||
cookies={"refresh_token": refresh_token}
|
||||
)
|
||||
assert response.status_code == status.HTTP_404_NOT_FOUND
|
||||
|
||||
# --- Test Authentication/Authorization ---
|
||||
|
||||
def test_create_todo_unauthorized(client: TestClient):
|
||||
response = client.post("/api/todos/", json={"task": "No Auth"})
|
||||
assert response.status_code == status.HTTP_401_UNAUTHORIZED
|
||||
|
||||
def test_read_todos_unauthorized(client: TestClient):
|
||||
response = client.get("/api/todos/")
|
||||
assert response.status_code == status.HTTP_401_UNAUTHORIZED
|
||||
Reference in New Issue
Block a user