File size: 8,141 Bytes
32eb084
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
"""
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())