Complete Todo application implementation
- Update database path configuration - Add test script for API functionality - Update README with testing instructions - Add requests package to requirements generated with BackendIM... (backend.im)
This commit is contained in:
parent
e852047583
commit
6da81ed97c
12
README.md
12
README.md
@ -9,6 +9,7 @@ A simple Todo application built with FastAPI and SQLite.
|
|||||||
- Database migrations with Alembic
|
- Database migrations with Alembic
|
||||||
- Health check endpoint
|
- Health check endpoint
|
||||||
- OpenAPI documentation
|
- OpenAPI documentation
|
||||||
|
- Test script for API functionality
|
||||||
|
|
||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
@ -32,6 +33,7 @@ todoapplication/
|
|||||||
├── alembic.ini # Alembic configuration
|
├── alembic.ini # Alembic configuration
|
||||||
├── main.py # Application entry point
|
├── main.py # Application entry point
|
||||||
├── requirements.txt # Python dependencies
|
├── requirements.txt # Python dependencies
|
||||||
|
├── test_api.py # API testing script
|
||||||
```
|
```
|
||||||
|
|
||||||
## API Endpoints
|
## API Endpoints
|
||||||
@ -81,3 +83,13 @@ Create a new migration:
|
|||||||
```bash
|
```bash
|
||||||
alembic revision -m "your migration message"
|
alembic revision -m "your migration message"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
|
||||||
|
Run the API tests (make sure the server is running first):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python test_api.py
|
||||||
|
```
|
||||||
|
|
||||||
|
This will test all the CRUD operations for the Todo API.
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import os
|
||||||
|
|
||||||
class Settings(BaseModel):
|
class Settings(BaseModel):
|
||||||
API_V1_STR: str = "/api/v1"
|
API_V1_STR: str = "/api/v1"
|
||||||
PROJECT_NAME: str = "Todo API"
|
PROJECT_NAME: str = "Todo API"
|
||||||
PROJECT_DESCRIPTION: str = "A simple Todo API built with FastAPI and SQLite"
|
PROJECT_DESCRIPTION: str = "A simple Todo API built with FastAPI and SQLite"
|
||||||
VERSION: str = "0.1.0"
|
VERSION: str = "0.1.0"
|
||||||
|
|
||||||
# Database settings
|
# Database settings
|
||||||
DB_DIR: Path = Path("/app") / "storage" / "db"
|
BASE_DIR: Path = Path(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||||
|
DB_DIR: Path = BASE_DIR / "storage" / "db"
|
||||||
SQLALCHEMY_DATABASE_URL: Optional[str] = None
|
SQLALCHEMY_DATABASE_URL: Optional[str] = None
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
|
@ -4,4 +4,5 @@ sqlalchemy==2.0.27
|
|||||||
pydantic==2.5.3
|
pydantic==2.5.3
|
||||||
alembic==1.12.1
|
alembic==1.12.1
|
||||||
python-dotenv==1.0.0
|
python-dotenv==1.0.0
|
||||||
pathlib==1.0.1
|
pathlib==1.0.1
|
||||||
|
requests==2.31.0
|
151
test_api.py
Normal file
151
test_api.py
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Test script for the Todo API
|
||||||
|
This script tests all the CRUD operations for the Todo API
|
||||||
|
"""
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
from typing import Dict, Any, Optional
|
||||||
|
|
||||||
|
BASE_URL = "http://localhost:8000/api/v1"
|
||||||
|
|
||||||
|
def print_success(message: str) -> None:
|
||||||
|
"""Print a success message in green color"""
|
||||||
|
print(f"\033[92m✓ {message}\033[0m")
|
||||||
|
|
||||||
|
def print_error(message: str) -> None:
|
||||||
|
"""Print an error message in red color"""
|
||||||
|
print(f"\033[91m✗ {message}\033[0m")
|
||||||
|
|
||||||
|
def print_info(message: str) -> None:
|
||||||
|
"""Print an info message in blue color"""
|
||||||
|
print(f"\033[94m• {message}\033[0m")
|
||||||
|
|
||||||
|
def test_health_endpoint() -> bool:
|
||||||
|
"""Test the health endpoint"""
|
||||||
|
print_info("Testing health endpoint...")
|
||||||
|
try:
|
||||||
|
response = requests.get("http://localhost:8000/health")
|
||||||
|
if response.status_code == 200:
|
||||||
|
data = response.json()
|
||||||
|
if data.get("status") == "healthy":
|
||||||
|
print_success("Health endpoint is working")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print_error(f"Health endpoint returned unexpected data: {data}")
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
print_error(f"Health endpoint returned status code {response.status_code}")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
print_error(f"Error testing health endpoint: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def create_todo(title: str, description: Optional[str] = None, completed: bool = False) -> Dict[str, Any]:
|
||||||
|
"""Create a new todo and return the created todo data"""
|
||||||
|
print_info(f"Creating todo: {title}")
|
||||||
|
todo_data = {
|
||||||
|
"title": title,
|
||||||
|
"description": description,
|
||||||
|
"completed": completed
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.post(f"{BASE_URL}/todos/", json=todo_data)
|
||||||
|
if response.status_code == 201:
|
||||||
|
todo = response.json()
|
||||||
|
print_success(f"Todo created with ID: {todo['id']}")
|
||||||
|
return todo
|
||||||
|
else:
|
||||||
|
print_error(f"Failed to create todo. Status code: {response.status_code}")
|
||||||
|
print_error(f"Response: {response.text}")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def get_todo(todo_id: int) -> Dict[str, Any]:
|
||||||
|
"""Get a specific todo by ID"""
|
||||||
|
print_info(f"Getting todo with ID: {todo_id}")
|
||||||
|
response = requests.get(f"{BASE_URL}/todos/{todo_id}")
|
||||||
|
if response.status_code == 200:
|
||||||
|
todo = response.json()
|
||||||
|
print_success(f"Got todo: {todo['title']}")
|
||||||
|
return todo
|
||||||
|
else:
|
||||||
|
print_error(f"Failed to get todo. Status code: {response.status_code}")
|
||||||
|
print_error(f"Response: {response.text}")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def get_all_todos() -> list:
|
||||||
|
"""Get all todos"""
|
||||||
|
print_info("Getting all todos")
|
||||||
|
response = requests.get(f"{BASE_URL}/todos/")
|
||||||
|
if response.status_code == 200:
|
||||||
|
todos = response.json()
|
||||||
|
print_success(f"Got {len(todos)} todos")
|
||||||
|
return todos
|
||||||
|
else:
|
||||||
|
print_error(f"Failed to get todos. Status code: {response.status_code}")
|
||||||
|
print_error(f"Response: {response.text}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
def update_todo(todo_id: int, data: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
"""Update a specific todo"""
|
||||||
|
print_info(f"Updating todo with ID: {todo_id}")
|
||||||
|
response = requests.put(f"{BASE_URL}/todos/{todo_id}", json=data)
|
||||||
|
if response.status_code == 200:
|
||||||
|
todo = response.json()
|
||||||
|
print_success(f"Updated todo: {todo['title']}")
|
||||||
|
return todo
|
||||||
|
else:
|
||||||
|
print_error(f"Failed to update todo. Status code: {response.status_code}")
|
||||||
|
print_error(f"Response: {response.text}")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def delete_todo(todo_id: int) -> bool:
|
||||||
|
"""Delete a specific todo"""
|
||||||
|
print_info(f"Deleting todo with ID: {todo_id}")
|
||||||
|
response = requests.delete(f"{BASE_URL}/todos/{todo_id}")
|
||||||
|
if response.status_code == 204:
|
||||||
|
print_success(f"Deleted todo with ID: {todo_id}")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print_error(f"Failed to delete todo. Status code: {response.status_code}")
|
||||||
|
print_error(f"Response: {response.text}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def run_tests() -> None:
|
||||||
|
"""Run all tests"""
|
||||||
|
print_info("Starting API tests")
|
||||||
|
|
||||||
|
# Test health endpoint
|
||||||
|
if not test_health_endpoint():
|
||||||
|
print_error("Health endpoint test failed. Make sure the server is running.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Test creating a todo
|
||||||
|
todo = create_todo("Test Todo", "This is a test todo", False)
|
||||||
|
if not todo:
|
||||||
|
print_error("Failed to create todo. Stopping tests.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
todo_id = todo["id"]
|
||||||
|
|
||||||
|
# Test getting a specific todo
|
||||||
|
get_todo(todo_id)
|
||||||
|
|
||||||
|
# Test getting all todos
|
||||||
|
get_all_todos()
|
||||||
|
|
||||||
|
# Test updating a todo
|
||||||
|
updated_todo = update_todo(todo_id, {"title": "Updated Test Todo", "completed": True})
|
||||||
|
if not updated_todo:
|
||||||
|
print_error("Failed to update todo.")
|
||||||
|
|
||||||
|
# Test deleting a todo
|
||||||
|
if not delete_todo(todo_id):
|
||||||
|
print_error("Failed to delete todo.")
|
||||||
|
|
||||||
|
print_info("All tests completed")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
run_tests()
|
Loading…
x
Reference in New Issue
Block a user