File size: 6,254 Bytes
37a6ee1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import os
import sys
import io
import time
from fastapi.testclient import TestClient

# Ensure parent directory is in python path
current_dir = os.path.dirname(os.path.abspath(__file__))
if current_dir not in sys.path:
    sys.path.insert(0, current_dir)

from backend.main import app
from backend import config

client = TestClient(app)

def test_health():
    print("\n--- Test Health Endpoint ---")
    response = client.get("/health")
    assert response.status_code == 200
    data = response.json()
    assert data["status"] == "healthy"
    assert data["groq_configured"] is True
    assert "sales" in data["sample_datasets"]
    print("[OK] Health endpoint returned healthy state.")

def test_get_datasets():
    print("\n--- Test Get Datasets ---")
    response = client.get("/datasets")
    assert response.status_code == 200
    data = response.json()
    assert "datasets" in data
    assert data["total"] >= 3
    
    # Verify sample datasets exist
    sample_names = {ds["db_name"] for ds in data["datasets"] if ds["is_sample"]}
    assert "sales" in sample_names
    assert "employees" in sample_names
    assert "ecommerce" in sample_names
    print(f"[OK] Retrieved {data['total']} datasets successfully.")

def test_get_schema():
    print("\n--- Test Get Schema ---")
    # Valid dataset
    response = client.get("/datasets/employees/schema")
    assert response.status_code == 200
    data = response.json()
    assert data["db_name"] == "employees"
    assert data["table_name"] == "data"
    assert len(data["columns"]) > 0
    assert data["row_count"] == 25
    print("[OK] Schema for employees retrieved successfully.")

    # Invalid dataset (404)
    response = client.get("/datasets/nonexistent_db/schema")
    assert response.status_code == 404
    print("[OK] Non-existent schema query correctly returned 404.")

    # Path traversal validation (400)
    response = client.get("/datasets/invalid-name-with-dash/schema")
    assert response.status_code == 400
    print("[OK] Invalid db_name with dash correctly returned 400.")

def test_upload_and_delete_flow():
    print("\n--- Test CSV Upload, Preview, and Delete Flow ---")
    # 1. Create a dummy CSV file
    csv_content = (
        "id,name,role,level\n"
        "1,Alice,Engineer,L1\n"
        "2,Bob,Product Manager,L2\n"
        "3,Charlie,Designer,L1\n"
    )
    file_payload = {"file": ("test_upload_dataset.csv", io.BytesIO(csv_content.encode("utf-8")), "text/csv")}

    # 2. Upload the CSV file
    response = client.post("/upload", files=file_payload)
    assert response.status_code == 200
    data = response.json()
    assert data["success"] is True
    assert data["db_name"] == "test_upload_dataset"
    assert data["row_count"] == 3
    assert "name" in data["columns"]
    print("[OK] Uploaded CSV dataset successfully.")

    # 3. Upload duplicate CSV file (expected 409)
    file_payload_dup = {"file": ("test_upload_dataset.csv", io.BytesIO(csv_content.encode("utf-8")), "text/csv")}
    response_dup = client.post("/upload", files=file_payload_dup)
    assert response_dup.status_code == 409
    assert "already uploaded" in response_dup.json()["detail"]
    print("[OK] Duplicate upload correctly rejected with 409 Conflict.")

    # 4. Preview the uploaded dataset
    response_preview = client.get("/datasets/test_upload_dataset/preview")
    assert response_preview.status_code == 200
    preview_data = response_preview.json()
    assert preview_data["row_count"] == 3
    assert len(preview_data["rows"]) == 3
    assert preview_data["columns"] == ["id", "name", "role", "level"]
    print("[OK] Previewed uploaded dataset successfully.")

    # 5. Delete the uploaded dataset
    response_delete = client.delete("/datasets/test_upload_dataset")
    assert response_delete.status_code == 200
    assert response_delete.json()["success"] is True
    print("[OK] Deleted uploaded dataset successfully.")

    # 6. Try deleting a sample dataset (expected 403)
    response_del_sample = client.delete("/datasets/employees")
    assert response_del_sample.status_code == 403
    print("[OK] Deleting sample dataset correctly rejected with 403 Forbidden.")

def test_query_pipeline():
    print("\n--- Test Query Pipeline ---")
    # Standard query
    payload = {
        "question": "What is the average revenue for products in the Clothing category?",
        "db_name": "sales"
    }
    response = client.post("/query", json=payload)
    assert response.status_code == 200
    data = response.json()
    assert data["question"] == payload["question"]
    assert data["db_name"] == "sales"
    assert "results" in data
    assert len(data["results"]["columns"]) > 0
    assert "insight" in data
    assert "X-Process-Time" in response.headers
    print(f"[OK] Standard query returned successfully in {response.headers['X-Process-Time']}.")
    print(f"  SQL: {data['sql']}")
    print(f"  Insight: {data['insight']}")

    # Validation: min length constraint on question (expected 422)
    payload_short = {
        "question": "a",
        "db_name": "sales"
    }
    response_short = client.post("/query", json=payload_short)
    assert response_short.status_code == 422
    print("[OK] Question less than 3 characters correctly rejected with 422.")

    # Validation: max length constraint on question (expected 422)
    payload_long = {
        "question": "a" * 301,
        "db_name": "sales"
    }
    response_long = client.post("/query", json=payload_long)
    assert response_long.status_code == 422
    print("[OK] Question greater than 300 characters correctly rejected with 422.")

    # Validation: db_name pattern constraint (expected 422)
    payload_bad_db = {
        "question": "What is the average revenue?",
        "db_name": "sales-invalid-dash"
    }
    response_bad_db = client.post("/query", json=payload_bad_db)
    assert response_bad_db.status_code == 422
    print("[OK] Database name with invalid format correctly rejected with 422.")

if __name__ == "__main__":
    print("Starting integration tests for API routes...")
    test_health()
    test_get_datasets()
    test_get_schema()
    test_upload_and_delete_flow()
    test_query_pipeline()
    print("\nAll API integration tests passed successfully!")