File size: 9,027 Bytes
aaa634c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import os
import sys
import io
import zipfile
from fastapi.testclient import TestClient

# Add workspace root to python path
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from main import app, get_supabase_client

# Define mock classes to intercept Supabase client interactions
class MockResponse:
    def __init__(self, data):
        self.data = data

class MockQueryBuilder:
    def __init__(self, table_name, mock_client):
        self.table_name = table_name
        self.mock_client = mock_client
        self.filters = {}

    def select(self, columns):
        return self

    def eq(self, column, value):
        self.filters[column] = value
        return self

    def insert(self, data):
        if self.table_name == 'problems':
            if not isinstance(data, list):
                data = [data]
            inserted = []
            for item in data:
                item_copy = item.copy()
                if 'id' not in item_copy:
                    item_copy['id'] = f"mock-prob-id-{len(self.mock_client.problems) + 1}"
                self.mock_client.problems.append(item_copy)
                inserted.append(item_copy)
            self.last_result = inserted
        elif self.table_name == 'user_reviews':
            if not isinstance(data, list):
                data = [data]
            inserted = []
            for item in data:
                item_copy = item.copy()
                if 'id' not in item_copy:
                    item_copy['id'] = f"mock-rev-id-{len(self.mock_client.reviews) + 1}"
                self.mock_client.reviews.append(item_copy)
                inserted.append(item_copy)
            self.last_result = inserted
        return self

    def update(self, data):
        self.update_data = data
        return self

    def execute(self):
        if hasattr(self, 'last_result'):
            return MockResponse(self.last_result)
        
        if self.table_name == 'problems':
            name_filter = self.filters.get('name')
            id_filter = self.filters.get('id')
            
            # If update was called
            if hasattr(self, 'update_data'):
                updated = []
                for p in self.mock_client.problems:
                    if id_filter and p.get('id') == id_filter:
                        p.update(self.update_data)
                        updated.append(p)
                return MockResponse(updated)
                
            matches = []
            for p in self.mock_client.problems:
                if name_filter and p.get('name') != name_filter:
                    continue
                if id_filter and p.get('id') != id_filter:
                    continue
                p_copy = p.copy()
                p_reviews = [r for r in self.mock_client.reviews if r.get('problem_id') == p.get('id')]
                p_copy['user_reviews'] = p_reviews
                matches.append(p_copy)
            return MockResponse(matches)
            
        elif self.table_name == 'user_reviews':
            return MockResponse(self.mock_client.reviews)
            
        return MockResponse([])

class MockSupabaseClient:
    def __init__(self):
        self.problems = []
        self.reviews = []
        self.current_user_id = "mock_user_123"
        self.auth = self

    def get_user(self, token):
        class MockUser:
            id = "mock_user_123"
        class MockUserRes:
            user = MockUser()
        return MockUserRes()

    def table(self, table_name):
        return MockQueryBuilder(table_name, self)

# Create a shared mock client state
shared_mock_client = MockSupabaseClient()

# Override FastAPI dependency
def mock_get_supabase_client():
    return shared_mock_client

app.dependency_overrides[get_supabase_client] = mock_get_supabase_client

client = TestClient(app)

def test_explorer_endpoints():
    print("=== Running Explorer REST API Tests ===")
    headers = {"Authorization": "Bearer mock-token-123"}
    
    # 1. Create Problem 1 (Two Sum)
    print("\n1. Testing POST /api/explorer/problems/create (Two Sum)")
    create_payload = {
        "name": "Two Sum",
        "pattern": "Two Pointers",
        "difficulty": "Easy",
        "reference_code": "def twoSum(nums, target):\n    pass",
        "description": "Find two numbers in the array that sum to target."
    }
    res = client.post("/api/explorer/problems/create", json=create_payload, headers=headers)
    print("Response status:", res.status_code)
    print("Response JSON:", res.json())
    assert res.status_code == 200
    assert res.json()["success"] is True
    prob1_id = res.json()["problem_id"]
    
    # Verify review was created
    assert len(shared_mock_client.reviews) == 1
    assert shared_mock_client.reviews[0]["problem_id"] == prob1_id
    assert shared_mock_client.reviews[0]["box_level"] == 1

    # 2. Try to create duplicate problem
    print("\n2. Testing duplicate prevention")
    res_dup = client.post("/api/explorer/problems/create", json=create_payload, headers=headers)
    print("Response status:", res_dup.status_code)
    print("Response JSON:", res_dup.json())
    assert res_dup.status_code == 400
    assert "already exists" in res_dup.json()["detail"]

    # 3. Create Problem 2 (Three Sum)
    print("\n3. Testing POST /api/explorer/problems/create (Three Sum)")
    create_payload_2 = {
        "name": "Three Sum",
        "pattern": "Two Pointers",
        "difficulty": "Medium",
        "reference_code": "def threeSum(nums):\n    pass",
        "description": "Find all unique triplets that sum to zero."
    }
    res2 = client.post("/api/explorer/problems/create", json=create_payload_2, headers=headers)
    assert res2.status_code == 200
    prob2_id = res2.json()["problem_id"]

    # 4. List Problems
    print("\n4. Testing GET /api/explorer/problems")
    res_list = client.get("/api/explorer/problems", headers=headers)
    print("Response status:", res_list.status_code)
    problems_list = res_list.json()
    print("List count:", len(problems_list))
    for p in problems_list:
        print(f"  - Problem: {p['name']}, Box: {p['box_level']}, Description: '{p['description']}', Code length: {len(p['reference_code'])}")
    assert res_list.status_code == 200
    assert len(problems_list) == 2
    assert problems_list[0]["box_level"] == 1
    assert problems_list[0]["description"] == "Find two numbers in the array that sum to target."

    # 5. Update reference code for Two Sum
    print("\n5. Testing PUT /api/explorer/problems/{id}")
    new_code = "def twoSum(nums, target):\n    # Optimized solution\n    seen = {}\n    for i, num in enumerate(nums):\n        diff = target - num\n        if diff in seen:\n            return [seen[diff], i]\n        seen[num] = i\n    return []"
    update_payload = {
        "name": "Two Sum",
        "pattern": "Two Pointers",
        "difficulty": "Easy",
        "reference_code": new_code,
        "description": "Find two numbers in the array that sum to target (Optimized)."
    }
    res_update = client.put(f"/api/explorer/problems/{prob1_id}", json=update_payload, headers=headers)
    print("Response status:", res_update.status_code)
    print("Response JSON:", res_update.json())
    assert res_update.status_code == 200
    assert res_update.json()["success"] is True

    # Verify update in listing
    res_list2 = client.get("/api/explorer/problems", headers=headers)
    problems_list2 = res_list2.json()
    two_sum_updated = next(p for p in problems_list2 if p["id"] == prob1_id)
    print("Updated Two Sum code length:", len(two_sum_updated["reference_code"]))
    print("Updated Two Sum description:", two_sum_updated["description"])
    assert "Optimized solution" in two_sum_updated["reference_code"]
    assert "Optimized" in two_sum_updated["description"]

    # 6. Export ZIP of solutions
    print("\n6. Testing GET /api/explorer/export (ZIP download)")
    res_export = client.get("/api/explorer/export", headers=headers)
    print("Response status:", res_export.status_code)
    print("Content-Type:", res_export.headers.get("content-type"))
    assert res_export.status_code == 200
    assert res_export.headers.get("content-type") == "application/zip"
    
    # Validate Zip structure
    zip_bytes = io.BytesIO(res_export.content)
    with zipfile.ZipFile(zip_bytes, "r") as zf:
        namelist = zf.namelist()
        print("Files in ZIP:", namelist)
        assert "Two_Sum.py" in namelist
        assert "Three_Sum.py" in namelist
        
        # Read content from ZIP
        two_sum_zip_code = zf.read("Two_Sum.py").decode("utf-8")
        print("Two_Sum.py contents in ZIP:")
        print(two_sum_zip_code)
        assert "Optimized solution" in two_sum_zip_code
        assert "# Description:" in two_sum_zip_code
        assert "Find two numbers in the array that sum to target" in two_sum_zip_code

    print("\n=== ALL TESTS PASSED SUCCESSFULLY ===")

if __name__ == "__main__":
    test_explorer_endpoints()