cuatrolabs-tracker-ms / test_tasks_api.py
Michael-Antony's picture
feat: implement tasks API with GET today and PATCH status endpoints
32eb084
"""
Test script for Tasks API endpoints.
Tests both GET /tasks/today and PATCH /tasks/:id/status endpoints.
"""
import asyncio
import asyncpg
import os
import requests
import time
from datetime import datetime, timedelta
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
# Configuration
API_BASE_URL = os.getenv("API_BASE_URL", "http://localhost:8003")
DB_HOST = os.getenv("DB_HOST", "localhost")
DB_PORT = int(os.getenv("DB_PORT", "5432"))
DB_NAME = os.getenv("DB_NAME", "cuatrolabs")
DB_USER = os.getenv("DB_USER", "postgres")
DB_PASSWORD = os.getenv("DB_PASSWORD", "")
# Test data
TEST_MERCHANT_ID = "550e8400-e29b-41d4-a716-446655440000"
TEST_EMPLOYEE_ID = "660e8400-e29b-41d4-a716-446655440001"
def get_auth_token():
"""Read JWT token from test_token.txt"""
try:
with open("test_token.txt", "r") as f:
return f.read().strip()
except FileNotFoundError:
print("❌ test_token.txt not found. Run generate_test_token.py first.")
exit(1)
async def create_test_task():
"""Create a test task in the database"""
conn = await asyncpg.connect(
host=DB_HOST,
port=DB_PORT,
database=DB_NAME,
user=DB_USER,
password=DB_PASSWORD
)
try:
# Delete existing test tasks
await conn.execute("""
DELETE FROM trans.scm_tasks
WHERE merchant_id = $1::uuid
AND assigned_to = $2::uuid
""", TEST_MERCHANT_ID, TEST_EMPLOYEE_ID)
# Create new test task
scheduled_time = datetime.now() + timedelta(hours=2)
task_id = await conn.fetchval("""
INSERT INTO trans.scm_tasks (
merchant_id, assigned_to, title, description,
status, latitude, longitude, address, scheduled_at
) VALUES (
$1::uuid, $2::uuid, $3, $4, $5, $6, $7, $8, $9
) RETURNING id
""",
TEST_MERCHANT_ID,
TEST_EMPLOYEE_ID,
"Test Task - Visit Customer",
"Deliver products and collect payment",
"not_started",
19.0760,
72.8777,
"123 Test Street, Mumbai",
scheduled_time
)
print(f"βœ… Test task created: {task_id}")
return str(task_id)
finally:
await conn.close()
def test_get_today_tasks(token):
"""Test GET /tracker/tasks/today endpoint"""
print("\n" + "="*80)
print("TEST 1: GET /tracker/tasks/today")
print("="*80)
url = f"{API_BASE_URL}/tracker/tasks/today"
headers = {"Authorization": f"Bearer {token}"}
print(f"Request: GET {url}")
response = requests.get(url, headers=headers)
print(f"Status Code: {response.status_code}")
print(f"Response: {response.json()}")
if response.status_code == 200:
data = response.json()
if data.get("success") and data.get("count", 0) > 0:
print("βœ… Test passed: Tasks retrieved successfully")
return data["tasks"][0]["id"] # Return first task ID for next tests
else:
print("⚠️ No tasks found for today")
return None
else:
print("❌ Test failed")
return None
def test_update_task_status(token, task_id, new_status, should_succeed=True):
"""Test PATCH /tracker/tasks/:id/status endpoint"""
print("\n" + "="*80)
print(f"TEST: Update task status to '{new_status}'")
print("="*80)
url = f"{API_BASE_URL}/tracker/tasks/{task_id}/status"
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
payload = {
"status": new_status,
"timestamp": int(time.time() * 1000), # Current time in milliseconds
"latitude": 19.0760 + (0.001 * hash(new_status) % 10), # Slightly different location
"longitude": 72.8777 + (0.001 * hash(new_status) % 10)
}
print(f"Request: PATCH {url}")
print(f"Payload: {payload}")
response = requests.patch(url, headers=headers, json=payload)
print(f"Status Code: {response.status_code}")
print(f"Response: {response.json()}")
if should_succeed:
if response.status_code == 200:
print(f"βœ… Test passed: Status updated to '{new_status}'")
return True
else:
print(f"❌ Test failed: Expected success but got {response.status_code}")
return False
else:
if response.status_code in [400, 404]:
print(f"βœ… Test passed: Invalid transition correctly rejected")
return True
else:
print(f"❌ Test failed: Expected error but got {response.status_code}")
return False
def test_invalid_task_id(token):
"""Test with invalid task ID"""
print("\n" + "="*80)
print("TEST: Invalid task ID (should return 404)")
print("="*80)
fake_task_id = "00000000-0000-0000-0000-000000000000"
url = f"{API_BASE_URL}/tracker/tasks/{fake_task_id}/status"
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
payload = {
"status": "in_progress",
"timestamp": int(time.time() * 1000),
"latitude": 19.0760,
"longitude": 72.8777
}
print(f"Request: PATCH {url}")
response = requests.patch(url, headers=headers, json=payload)
print(f"Status Code: {response.status_code}")
print(f"Response: {response.json()}")
if response.status_code == 404:
print("βœ… Test passed: Invalid task ID correctly rejected")
return True
else:
print(f"❌ Test failed: Expected 404 but got {response.status_code}")
return False
async def main():
"""Run all tests"""
print("="*80)
print("TASKS API TEST SUITE")
print("="*80)
print(f"API Base URL: {API_BASE_URL}")
print(f"Database: {DB_HOST}:{DB_PORT}/{DB_NAME}")
print("="*80)
# Get auth token
print("\n[1/9] Getting authentication token...")
token = get_auth_token()
print("βœ… Token loaded")
# Create test task
print("\n[2/9] Creating test task...")
task_id = await create_test_task()
# Test 1: Get today's tasks
print("\n[3/9] Testing GET /tracker/tasks/today...")
retrieved_task_id = test_get_today_tasks(token)
if not retrieved_task_id:
print("\n❌ Cannot continue tests without a task ID")
return
# Use the retrieved task ID for remaining tests
task_id = retrieved_task_id
# Test 2: Update to in_progress (valid transition)
print("\n[4/9] Testing status update: not_started β†’ in_progress...")
test_update_task_status(token, task_id, "in_progress", should_succeed=True)
# Test 3: Update to completed (valid transition)
print("\n[5/9] Testing status update: in_progress β†’ completed...")
test_update_task_status(token, task_id, "completed", should_succeed=True)
# Test 4: Reopen task (valid transition)
print("\n[6/9] Testing status update: completed β†’ in_progress...")
test_update_task_status(token, task_id, "in_progress", should_succeed=True)
# Test 5: Complete again (valid transition)
print("\n[7/9] Testing status update: in_progress β†’ completed...")
test_update_task_status(token, task_id, "completed", should_succeed=True)
# Test 6: Invalid transition (should fail)
print("\n[8/9] Testing invalid transition: completed β†’ not_started...")
test_update_task_status(token, task_id, "not_started", should_succeed=False)
# Test 7: Invalid task ID
print("\n[9/9] Testing with invalid task ID...")
test_invalid_task_id(token)
# Summary
print("\n" + "="*80)
print("TEST SUITE COMPLETED")
print("="*80)
print("\nAll tests executed. Review results above.")
print("\nTo verify in database:")
print(f" SELECT * FROM trans.scm_tasks WHERE id = '{task_id}'::uuid;")
print("="*80)
if __name__ == "__main__":
asyncio.run(main())