""" Comprehensive API testing suite. Tests API endpoints to improve coverage. """ import pytest from datetime import datetime, timedelta, date from decimal import Decimal import json # ============================================================================ # Timer API Tests # ============================================================================ @pytest.mark.api @pytest.mark.integration def test_start_timer_api(authenticated_client, project): """Test starting a timer via API.""" response = authenticated_client.post('/api/timer/start', json={ 'project_id': project.id, 'notes': 'Working on feature' }) # Should succeed or return appropriate status assert response.status_code in [200, 201, 404, 405] @pytest.mark.api @pytest.mark.integration def test_get_timer_status(authenticated_client): """Test getting timer status.""" response = authenticated_client.get('/api/timer/status') # Should return status or appropriate error assert response.status_code in [200, 404] # ============================================================================ # Project API Tests # ============================================================================ @pytest.mark.api @pytest.mark.integration def test_get_projects_list(authenticated_client): """Test getting list of projects.""" response = authenticated_client.get('/api/projects') # Should return projects list or appropriate error assert response.status_code in [200, 404] @pytest.mark.api @pytest.mark.integration def test_get_project_details(authenticated_client, project): """Test getting project details.""" response = authenticated_client.get(f'/api/projects/{project.id}') # Should return project details or appropriate error assert response.status_code in [200, 404] # ============================================================================ # Time Entry API Tests # ============================================================================ @pytest.mark.api @pytest.mark.integration def test_get_time_entries(authenticated_client): """Test getting time entries list.""" response = authenticated_client.get('/api/time-entries') # Should return time entries or appropriate error assert response.status_code in [200, 404] @pytest.mark.api @pytest.mark.integration def test_get_time_entry_details(authenticated_client, time_entry): """Test getting time entry details.""" response = authenticated_client.get(f'/api/time-entries/{time_entry.id}') # Should return time entry details or appropriate error assert response.status_code in [200, 404] # ============================================================================ # Client API Tests # ============================================================================ @pytest.mark.api @pytest.mark.integration def test_get_clients_list(authenticated_client): """Test getting list of clients.""" response = authenticated_client.get('/api/clients') # Should return clients list or appropriate error assert response.status_code in [200, 404] @pytest.mark.api @pytest.mark.integration def test_get_client_details(authenticated_client, test_client): """Test getting client details.""" response = authenticated_client.get(f'/api/clients/{test_client.id}') # Should return client details or appropriate error assert response.status_code in [200, 404] # ============================================================================ # Invoice API Tests # ============================================================================ @pytest.mark.api @pytest.mark.integration def test_get_invoices_list(authenticated_client): """Test getting list of invoices.""" response = authenticated_client.get('/api/invoices') # Should return invoices list or appropriate error assert response.status_code in [200, 404] @pytest.mark.api @pytest.mark.integration def test_get_invoice_details(authenticated_client, invoice): """Test getting invoice details.""" response = authenticated_client.get(f'/api/invoices/{invoice.id}') # Should return invoice details or appropriate error assert response.status_code in [200, 404] # ============================================================================ # Report API Tests # ============================================================================ @pytest.mark.api @pytest.mark.integration def test_get_time_report(authenticated_client): """Test getting time report.""" response = authenticated_client.get('/api/reports/time', query_string={ 'start_date': (datetime.utcnow() - timedelta(days=7)).strftime('%Y-%m-%d'), 'end_date': datetime.utcnow().strftime('%Y-%m-%d') }) # Should return report or appropriate error assert response.status_code in [200, 404, 500] @pytest.mark.api @pytest.mark.integration def test_get_project_report(authenticated_client, project): """Test getting project report.""" response = authenticated_client.get(f'/api/reports/projects/{project.id}') # Should return report or appropriate error assert response.status_code in [200, 404] # ============================================================================ # Task API Tests # ============================================================================ @pytest.mark.api @pytest.mark.integration def test_get_tasks_list(authenticated_client): """Test getting list of tasks.""" response = authenticated_client.get('/api/tasks') # Should return tasks list or appropriate error (400 is also valid if params are required) assert response.status_code in [200, 400, 404] @pytest.mark.api @pytest.mark.integration def test_get_task_details(authenticated_client, task): """Test getting task details.""" response = authenticated_client.get(f'/api/tasks/{task.id}') # Should return task details or appropriate error assert response.status_code in [200, 404] @pytest.mark.api @pytest.mark.integration def test_get_project_tasks_excludes_done_and_cancelled(authenticated_client, project, user, app): """Test that /api/projects//tasks excludes done and cancelled tasks.""" from app.models import Task from app import db # Create tasks with different statuses active_task = Task( name='Active Task', project_id=project.id, status='todo', created_by=user.id ) in_progress_task = Task( name='In Progress Task', project_id=project.id, status='in_progress', created_by=user.id ) review_task = Task( name='Review Task', project_id=project.id, status='review', created_by=user.id ) done_task = Task( name='Done Task', project_id=project.id, status='done', created_by=user.id ) cancelled_task = Task( name='Cancelled Task', project_id=project.id, status='cancelled', created_by=user.id ) db.session.add_all([active_task, in_progress_task, review_task, done_task, cancelled_task]) db.session.commit() # Get tasks for the project response = authenticated_client.get(f'/api/projects/{project.id}/tasks') assert response.status_code == 200 data = json.loads(response.data) assert 'tasks' in data assert data['success'] is True # Verify only active tasks are returned task_names = [t['name'] for t in data['tasks']] assert 'Active Task' in task_names assert 'In Progress Task' in task_names assert 'Review Task' in task_names assert 'Done Task' not in task_names assert 'Cancelled Task' not in task_names # Verify we got exactly 3 tasks assert len(data['tasks']) == 3 # ============================================================================ # Settings API Tests # ============================================================================ @pytest.mark.api @pytest.mark.integration def test_get_settings(authenticated_client): """Test getting application settings.""" response = authenticated_client.get('/api/settings') # Should return settings or appropriate error assert response.status_code in [200, 404] # ============================================================================ # Analytics API Tests # ============================================================================ @pytest.mark.api @pytest.mark.integration def test_get_dashboard_stats(authenticated_client): """Test getting dashboard statistics.""" response = authenticated_client.get('/api/analytics/dashboard') # Should return stats or appropriate error assert response.status_code in [200, 404, 500] # ============================================================================ # Search API Tests # ============================================================================ @pytest.mark.api @pytest.mark.integration def test_search_api(authenticated_client): """Test search API endpoint.""" response = authenticated_client.get('/api/search', query_string={ 'q': 'test' }) # Should return search results or appropriate error assert response.status_code in [200, 400, 404] # ============================================================================ # Export API Tests # ============================================================================ @pytest.mark.api @pytest.mark.integration def test_export_time_entries(authenticated_client): """Test exporting time entries.""" response = authenticated_client.get('/api/export/time-entries', query_string={ 'format': 'csv', 'start_date': (datetime.utcnow() - timedelta(days=7)).strftime('%Y-%m-%d'), 'end_date': datetime.utcnow().strftime('%Y-%m-%d') }) # Should return export or appropriate error assert response.status_code in [200, 404, 500]