Spaces:
Sleeping
Sleeping
| 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() | |