281 lines
9.0 KiB
Python
281 lines
9.0 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""
|
||
|
|
Test script to validate refactored backend endpoints
|
||
|
|
Tests all CRUD operations for profiles, songs, and plans
|
||
|
|
"""
|
||
|
|
import requests
|
||
|
|
import json
|
||
|
|
import sys
|
||
|
|
from datetime import datetime
|
||
|
|
|
||
|
|
BASE_URL = "http://localhost:8080/api"
|
||
|
|
session = requests.Session()
|
||
|
|
|
||
|
|
# Color output
|
||
|
|
GREEN = '\033[92m'
|
||
|
|
RED = '\033[91m'
|
||
|
|
RESET = '\033[0m'
|
||
|
|
BLUE = '\033[94m'
|
||
|
|
|
||
|
|
test_results = []
|
||
|
|
|
||
|
|
def log_test(name, passed, details=""):
|
||
|
|
"""Log test result"""
|
||
|
|
status = f"{GREEN}✓ PASS{RESET}" if passed else f"{RED}✗ FAIL{RESET}"
|
||
|
|
print(f"{status} - {name}")
|
||
|
|
if details and not passed:
|
||
|
|
print(f" {details}")
|
||
|
|
test_results.append((name, passed, details))
|
||
|
|
|
||
|
|
def test_health():
|
||
|
|
"""Test health endpoint"""
|
||
|
|
try:
|
||
|
|
response = session.get(f"{BASE_URL}/health")
|
||
|
|
passed = response.status_code == 200 and response.json().get('status') == 'ok'
|
||
|
|
log_test("Health Check", passed)
|
||
|
|
return passed
|
||
|
|
except Exception as e:
|
||
|
|
log_test("Health Check", False, str(e))
|
||
|
|
return False
|
||
|
|
|
||
|
|
def test_profile_crud():
|
||
|
|
"""Test profile CRUD operations"""
|
||
|
|
print(f"\n{BLUE}=== Testing Profile CRUD ==={RESET}")
|
||
|
|
|
||
|
|
# CREATE
|
||
|
|
try:
|
||
|
|
profile_data = {
|
||
|
|
"name": "Test Profile",
|
||
|
|
"first_name": "Test",
|
||
|
|
"last_name": "User",
|
||
|
|
"email": "test@example.com",
|
||
|
|
"default_key": "C"
|
||
|
|
}
|
||
|
|
response = session.post(f"{BASE_URL}/profiles", json=profile_data)
|
||
|
|
passed = response.status_code == 200
|
||
|
|
profile_id = response.json().get('id') if passed else None
|
||
|
|
log_test("Profile Create", passed, response.text if not passed else "")
|
||
|
|
|
||
|
|
if not passed:
|
||
|
|
return False
|
||
|
|
except Exception as e:
|
||
|
|
log_test("Profile Create", False, str(e))
|
||
|
|
return False
|
||
|
|
|
||
|
|
# READ (GET ALL)
|
||
|
|
try:
|
||
|
|
response = session.get(f"{BASE_URL}/profiles")
|
||
|
|
passed = response.status_code == 200 and isinstance(response.json(), list)
|
||
|
|
log_test("Profile List", passed, response.text if not passed else "")
|
||
|
|
except Exception as e:
|
||
|
|
log_test("Profile List", False, str(e))
|
||
|
|
return False
|
||
|
|
|
||
|
|
# UPDATE
|
||
|
|
try:
|
||
|
|
update_data = {"notes": "Updated notes from refactored test"}
|
||
|
|
response = session.put(f"{BASE_URL}/profiles/{profile_id}", json=update_data)
|
||
|
|
passed = response.status_code == 200
|
||
|
|
log_test("Profile Update", passed, response.text if not passed else "")
|
||
|
|
except Exception as e:
|
||
|
|
log_test("Profile Update", False, str(e))
|
||
|
|
return False
|
||
|
|
|
||
|
|
# DELETE
|
||
|
|
try:
|
||
|
|
response = session.delete(f"{BASE_URL}/profiles/{profile_id}")
|
||
|
|
passed = response.status_code == 200
|
||
|
|
log_test("Profile Delete", passed, response.text if not passed else "")
|
||
|
|
except Exception as e:
|
||
|
|
log_test("Profile Delete", False, str(e))
|
||
|
|
return False
|
||
|
|
|
||
|
|
return True
|
||
|
|
|
||
|
|
def test_song_crud():
|
||
|
|
"""Test song CRUD operations"""
|
||
|
|
print(f"\n{BLUE}=== Testing Song CRUD ==={RESET}")
|
||
|
|
|
||
|
|
# CREATE
|
||
|
|
try:
|
||
|
|
song_data = {
|
||
|
|
"title": "Test Song",
|
||
|
|
"artist": "Test Artist",
|
||
|
|
"lyrics": "Test lyrics content",
|
||
|
|
"chords": "C G Am F"
|
||
|
|
}
|
||
|
|
response = session.post(f"{BASE_URL}/songs", json=song_data)
|
||
|
|
passed = response.status_code == 200
|
||
|
|
song_id = response.json().get('id') if passed else None
|
||
|
|
log_test("Song Create", passed, response.text if not passed else "")
|
||
|
|
|
||
|
|
if not passed:
|
||
|
|
return False
|
||
|
|
except Exception as e:
|
||
|
|
log_test("Song Create", False, str(e))
|
||
|
|
return False
|
||
|
|
|
||
|
|
# READ (SEARCH)
|
||
|
|
try:
|
||
|
|
response = session.get(f"{BASE_URL}/songs?q=Test")
|
||
|
|
passed = response.status_code == 200 and isinstance(response.json(), list)
|
||
|
|
log_test("Song Search", passed, response.text if not passed else "")
|
||
|
|
except Exception as e:
|
||
|
|
log_test("Song Search", False, str(e))
|
||
|
|
return False
|
||
|
|
|
||
|
|
# READ (GET ONE)
|
||
|
|
try:
|
||
|
|
response = session.get(f"{BASE_URL}/songs/{song_id}")
|
||
|
|
passed = response.status_code == 200
|
||
|
|
log_test("Song Get", passed, response.text if not passed else "")
|
||
|
|
except Exception as e:
|
||
|
|
log_test("Song Get", False, str(e))
|
||
|
|
return False
|
||
|
|
|
||
|
|
# UPDATE
|
||
|
|
try:
|
||
|
|
update_data = {"chords": "C G Am F G"}
|
||
|
|
response = session.put(f"{BASE_URL}/songs/{song_id}", json=update_data)
|
||
|
|
passed = response.status_code == 200
|
||
|
|
log_test("Song Update", passed, response.text if not passed else "")
|
||
|
|
except Exception as e:
|
||
|
|
log_test("Song Update", False, str(e))
|
||
|
|
return False
|
||
|
|
|
||
|
|
# DELETE
|
||
|
|
try:
|
||
|
|
response = session.delete(f"{BASE_URL}/songs/{song_id}")
|
||
|
|
passed = response.status_code == 200
|
||
|
|
log_test("Song Delete", passed, response.text if not passed else "")
|
||
|
|
except Exception as e:
|
||
|
|
log_test("Song Delete", False, str(e))
|
||
|
|
return False
|
||
|
|
|
||
|
|
return True
|
||
|
|
|
||
|
|
def test_plan_crud():
|
||
|
|
"""Test plan CRUD operations"""
|
||
|
|
print(f"\n{BLUE}=== Testing Plan CRUD ==={RESET}")
|
||
|
|
|
||
|
|
# CREATE
|
||
|
|
try:
|
||
|
|
plan_data = {
|
||
|
|
"date": datetime.now().strftime("%Y-%m-%d"),
|
||
|
|
"notes": "Test plan from refactored code"
|
||
|
|
}
|
||
|
|
response = session.post(f"{BASE_URL}/plans", json=plan_data)
|
||
|
|
passed = response.status_code == 200
|
||
|
|
plan_id = response.json().get('id') if passed else None
|
||
|
|
log_test("Plan Create", passed, response.text if not passed else "")
|
||
|
|
|
||
|
|
if not passed:
|
||
|
|
return False
|
||
|
|
except Exception as e:
|
||
|
|
log_test("Plan Create", False, str(e))
|
||
|
|
return False
|
||
|
|
|
||
|
|
# READ
|
||
|
|
try:
|
||
|
|
response = session.get(f"{BASE_URL}/plans")
|
||
|
|
passed = response.status_code == 200 and isinstance(response.json(), list)
|
||
|
|
log_test("Plan List", passed, response.text if not passed else "")
|
||
|
|
except Exception as e:
|
||
|
|
log_test("Plan List", False, str(e))
|
||
|
|
return False
|
||
|
|
|
||
|
|
# READ (GET ONE)
|
||
|
|
try:
|
||
|
|
response = session.get(f"{BASE_URL}/plans/{plan_id}")
|
||
|
|
passed = response.status_code == 200
|
||
|
|
log_test("Plan Get", passed, response.text if not passed else "")
|
||
|
|
except Exception as e:
|
||
|
|
log_test("Plan Get", False, str(e))
|
||
|
|
return False
|
||
|
|
|
||
|
|
# UPDATE
|
||
|
|
try:
|
||
|
|
update_data = {"notes": "Updated plan notes"}
|
||
|
|
response = session.put(f"{BASE_URL}/plans/{plan_id}", json=update_data)
|
||
|
|
passed = response.status_code == 200
|
||
|
|
log_test("Plan Update", passed, response.text if not passed else "")
|
||
|
|
except Exception as e:
|
||
|
|
log_test("Plan Update", False, str(e))
|
||
|
|
return False
|
||
|
|
|
||
|
|
# DELETE
|
||
|
|
try:
|
||
|
|
response = session.delete(f"{BASE_URL}/plans/{plan_id}")
|
||
|
|
passed = response.status_code == 200
|
||
|
|
log_test("Plan Delete", passed, response.text if not passed else "")
|
||
|
|
except Exception as e:
|
||
|
|
log_test("Plan Delete", False, str(e))
|
||
|
|
return False
|
||
|
|
|
||
|
|
return True
|
||
|
|
|
||
|
|
def test_validation():
|
||
|
|
"""Test input validation"""
|
||
|
|
print(f"\n{BLUE}=== Testing Input Validation ==={RESET}")
|
||
|
|
|
||
|
|
# Test invalid profile (missing name)
|
||
|
|
try:
|
||
|
|
response = session.post(f"{BASE_URL}/profiles", json={})
|
||
|
|
passed = response.status_code == 400
|
||
|
|
log_test("Validation: Missing Profile Name", passed)
|
||
|
|
except Exception as e:
|
||
|
|
log_test("Validation: Missing Profile Name", False, str(e))
|
||
|
|
|
||
|
|
# Test invalid song (missing title)
|
||
|
|
try:
|
||
|
|
response = session.post(f"{BASE_URL}/songs", json={"title": ""})
|
||
|
|
passed = response.status_code == 400
|
||
|
|
log_test("Validation: Missing Song Title", passed)
|
||
|
|
except Exception as e:
|
||
|
|
log_test("Validation: Missing Song Title", False, str(e))
|
||
|
|
|
||
|
|
# Test invalid ID format
|
||
|
|
try:
|
||
|
|
long_id = "x" * 300
|
||
|
|
response = session.put(f"{BASE_URL}/profiles/{long_id}", json={})
|
||
|
|
passed = response.status_code == 400
|
||
|
|
log_test("Validation: Invalid ID Format", passed, f"Expected 400, got {response.status_code}")
|
||
|
|
except Exception as e:
|
||
|
|
log_test("Validation: Invalid ID Format", False, str(e))
|
||
|
|
|
||
|
|
def print_summary():
|
||
|
|
"""Print test summary"""
|
||
|
|
print(f"\n{BLUE}{'='*60}{RESET}")
|
||
|
|
print(f"{BLUE}TEST SUMMARY{RESET}")
|
||
|
|
print(f"{BLUE}{'='*60}{RESET}")
|
||
|
|
|
||
|
|
passed = sum(1 for _, p, _ in test_results if p)
|
||
|
|
total = len(test_results)
|
||
|
|
|
||
|
|
print(f"Total Tests: {total}")
|
||
|
|
print(f"Passed: {GREEN}{passed}{RESET}")
|
||
|
|
print(f"Failed: {RED}{total - passed}{RESET}")
|
||
|
|
print(f"Success Rate: {(passed/total*100):.1f}%")
|
||
|
|
|
||
|
|
if passed == total:
|
||
|
|
print(f"\n{GREEN}✓ ALL TESTS PASSED - Refactoring successful!{RESET}")
|
||
|
|
return 0
|
||
|
|
else:
|
||
|
|
print(f"\n{RED}✗ Some tests failed - Review errors above{RESET}")
|
||
|
|
return 1
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
print(f"{BLUE}{'='*60}{RESET}")
|
||
|
|
print(f"{BLUE}Refactored Backend Test Suite{RESET}")
|
||
|
|
print(f"{BLUE}{'='*60}{RESET}")
|
||
|
|
|
||
|
|
# Run tests
|
||
|
|
test_health()
|
||
|
|
test_profile_crud()
|
||
|
|
test_song_crud()
|
||
|
|
test_plan_crud()
|
||
|
|
test_validation()
|
||
|
|
|
||
|
|
# Print summary and exit
|
||
|
|
sys.exit(print_summary())
|