Spaces:
Running
Running
| # Test cases for LLM Code Deployment API | |
| import pytest | |
| import json | |
| import tempfile | |
| import os | |
| from unittest.mock import Mock, patch | |
| from flask import Flask | |
| # Import the app | |
| import sys | |
| sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | |
| from app import app | |
| from src.config import Config | |
| from src.utils import validate_json_structure, decode_attachments | |
| from src.readme_generator import generate_readme | |
| def client(): | |
| """Flask test client""" | |
| app.config['TESTING'] = True | |
| with app.test_client() as client: | |
| yield client | |
| def sample_request_data(): | |
| """Sample valid request data""" | |
| return { | |
| "email": "test@example.com", | |
| "secret": "test-secret", | |
| "task": "test-task-123", | |
| "round": 1, | |
| "nonce": "test-nonce-456", | |
| "brief": "Create a simple todo list application with Bootstrap styling", | |
| "evaluation_url": "https://httpbin.org/post", | |
| "checks": ["Must have #add-task button", "Must display task count"], | |
| "attachments": [] | |
| } | |
| class TestAPI: | |
| """Test API endpoints""" | |
| def test_health_endpoint(self, client): | |
| """Test health check endpoint""" | |
| response = client.get('/health') | |
| assert response.status_code == 200 | |
| data = json.loads(response.data) | |
| assert data['status'] == 'healthy' | |
| def test_root_endpoint(self, client): | |
| """Test root endpoint""" | |
| response = client.get('/') | |
| assert response.status_code == 200 | |
| data = json.loads(response.data) | |
| assert 'service' in data | |
| assert 'endpoints' in data | |
| def test_api_endpoint_missing_json(self, client): | |
| """Test API endpoint with missing JSON""" | |
| response = client.post('/api') | |
| assert response.status_code == 400 | |
| data = json.loads(response.data) | |
| assert 'error' in data | |
| def test_api_endpoint_missing_fields(self, client): | |
| """Test API endpoint with missing required fields""" | |
| response = client.post('/api', json={}) | |
| assert response.status_code == 400 | |
| data = json.loads(response.data) | |
| assert 'Missing required fields' in data['error'] | |
| def test_api_endpoint_invalid_secret(self, mock_validate_secret, client, sample_request_data): | |
| """Test API endpoint with invalid secret""" | |
| mock_validate_secret.return_value = False | |
| response = client.post('/api', json=sample_request_data) | |
| assert response.status_code == 401 | |
| data = json.loads(response.data) | |
| assert data['error'] == 'Invalid secret' | |
| def test_api_endpoint_invalid_round(self, mock_validate_secret, client, sample_request_data): | |
| """Test API endpoint with invalid round number""" | |
| mock_validate_secret.return_value = True # Make secret validation pass | |
| sample_request_data['round'] = 3 | |
| response = client.post('/api', json=sample_request_data) | |
| assert response.status_code == 400 | |
| data = json.loads(response.data) | |
| assert 'Round must be 1 or 2' in data['error'] | |
| class TestValidation: | |
| """Test validation functions""" | |
| def test_validate_json_structure_valid(self, sample_request_data): | |
| """Test validation with valid data""" | |
| errors = validate_json_structure(sample_request_data) | |
| assert len(errors) == 0 | |
| def test_validate_json_structure_missing_fields(self): | |
| """Test validation with missing fields""" | |
| data = {"email": "test@example.com"} | |
| errors = validate_json_structure(data) | |
| assert len(errors) > 0 | |
| assert any("Missing required field" in error for error in errors) | |
| def test_validate_json_structure_invalid_email(self): | |
| """Test validation with invalid email""" | |
| data = { | |
| "email": "invalid-email", | |
| "secret": "test", | |
| "task": "test", | |
| "round": 1, | |
| "nonce": "test", | |
| "brief": "test", | |
| "evaluation_url": "https://example.com" | |
| } | |
| errors = validate_json_structure(data) | |
| assert any("Invalid email format" in error for error in errors) | |
| def test_validate_json_structure_invalid_url(self): | |
| """Test validation with invalid URL""" | |
| data = { | |
| "email": "test@example.com", | |
| "secret": "test", | |
| "task": "test", | |
| "round": 1, | |
| "nonce": "test", | |
| "brief": "test", | |
| "evaluation_url": "not-a-url" | |
| } | |
| errors = validate_json_structure(data) | |
| assert any("Evaluation URL must start with" in error for error in errors) | |
| class TestUtilities: | |
| """Test utility functions""" | |
| def test_decode_attachments_empty(self): | |
| """Test decoding empty attachments""" | |
| result = decode_attachments([], "test-task") | |
| assert result == [] | |
| def test_decode_attachments_valid(self): | |
| """Test decoding valid base64 attachments""" | |
| import base64 | |
| test_content = "Hello, world!" | |
| encoded_content = base64.b64encode(test_content.encode()).decode() | |
| attachments = [{ | |
| "filename": "test.txt", | |
| "content": encoded_content | |
| }] | |
| result = decode_attachments(attachments, "test-task") | |
| assert len(result) == 1 | |
| # Read the file and verify content | |
| with open(result[0], 'r') as f: | |
| content = f.read() | |
| assert content == test_content | |
| # Cleanup | |
| os.unlink(result[0]) | |
| def test_generate_readme(self): | |
| """Test README generation""" | |
| brief = "Create a todo list application" | |
| task_id = "test-123" | |
| readme = generate_readme(brief, task_id) | |
| assert task_id in readme | |
| assert brief in readme | |
| assert "# " in readme # Has heading | |
| assert "## " in readme # Has subheadings | |
| assert "MIT License" in readme | |
| class TestConfig: | |
| """Test configuration""" | |
| def test_validate_secret_empty_list(self): | |
| """Test secret validation with empty valid secrets""" | |
| with patch.object(Config, 'VALID_SECRETS', ['']): | |
| assert Config.validate_secret("any-secret") == True | |
| def test_validate_secret_valid(self): | |
| """Test secret validation with valid secret""" | |
| with patch.object(Config, 'VALID_SECRETS', ['secret1', 'secret2']): | |
| assert Config.validate_secret("secret1") == True | |
| assert Config.validate_secret("secret2") == True | |
| def test_validate_secret_invalid(self): | |
| """Test secret validation with invalid secret""" | |
| with patch.object(Config, 'VALID_SECRETS', ['secret1', 'secret2']): | |
| assert Config.validate_secret("invalid") == False | |
| assert Config.validate_secret("") == False | |
| def test_get_repo_name(self): | |
| """Test repository name generation""" | |
| task_id = "test-123" | |
| email = "student@example.com" | |
| repo_name = Config.get_repo_name(task_id, email) | |
| assert task_id in repo_name | |
| assert "student-task-" in repo_name | |
| assert len(repo_name) > len(task_id) | |
| # Integration test data | |
| def create_test_request_file(): | |
| """Create a test request file for CLI testing""" | |
| test_data = { | |
| "email": "test@example.com", | |
| "secret": "test-secret", | |
| "task": "cli-test-123", | |
| "round": 1, | |
| "nonce": "cli-test-nonce", | |
| "brief": "Create a simple calculator application with basic arithmetic operations", | |
| "evaluation_url": "https://httpbin.org/post", | |
| "checks": [ | |
| "Must have calculator buttons for digits 0-9", | |
| "Must have operation buttons (+, -, *, /)", | |
| "Must have equals button", | |
| "Must display calculation result" | |
| ], | |
| "attachments": [] | |
| } | |
| with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f: | |
| json.dump(test_data, f, indent=2) | |
| return f.name | |
| if __name__ == '__main__': | |
| # Run tests | |
| pytest.main([__file__, '-v']) | |
| # Create test request file | |
| test_file = create_test_request_file() | |
| print(f"\nTest request file created: {test_file}") | |
| print(f"To test CLI: python cli.py {test_file}") |