File size: 4,854 Bytes
53e1531
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""
Shortlist — API Endpoint Tests

Tests for the FastAPI application endpoints.
Uses TestClient for synchronous testing.
"""

import pytest
from fastapi.testclient import TestClient

from app.main import app


@pytest.fixture
def client():
    """Create a test client for the FastAPI app."""
    return TestClient(app)


class TestHealthCheck:
    """Tests for the /health endpoint."""

    def test_health_returns_200(self, client):
        response = client.get("/health")
        assert response.status_code == 200

    def test_health_returns_status(self, client):
        response = client.get("/health")
        data = response.json()
        assert data["status"] == "healthy"
        assert "version" in data
        assert "environment" in data

    def test_health_has_security_headers(self, client):
        response = client.get("/health")
        assert response.headers.get("X-Content-Type-Options") == "nosniff"
        assert response.headers.get("X-Frame-Options") == "DENY"
        assert response.headers.get("X-XSS-Protection") == "1; mode=block"


class TestCORS:
    """Tests for CORS configuration."""

    def test_cors_allows_configured_origin(self, client):
        response = client.options(
            "/health",
            headers={
                "Origin": "http://localhost:3000",
                "Access-Control-Request-Method": "GET",
            },
        )
        assert response.headers.get("access-control-allow-origin") == "http://localhost:3000"

    def test_cors_blocks_unknown_origin(self, client):
        response = client.options(
            "/health",
            headers={
                "Origin": "http://evil.com",
                "Access-Control-Request-Method": "GET",
            },
        )
        # Unknown origins should not get CORS headers
        assert response.headers.get("access-control-allow-origin") != "http://evil.com"


class TestAuthRequiredEndpoints:
    """Tests that protected endpoints require authentication."""

    def test_jd_analyze_requires_auth(self, client):
        response = client.post("/api/v1/jd/analyze", json={
            "jd_text": "x" * 100,
            "role": "Backend Engineer",
            "company_type": "startup",
        })
        assert response.status_code == 401

    def test_capstone_generate_requires_auth(self, client):
        response = client.post("/api/v1/capstone/generate", json={
            "analysis_id": "test-id",
        })
        assert response.status_code == 401

    def test_repo_analyze_requires_auth(self, client):
        response = client.post("/api/v1/repo/analyze", json={
            "github_url": "https://github.com/test/repo",
        })
        assert response.status_code == 401

    def test_scaffold_generate_requires_auth(self, client):
        response = client.post("/api/v1/scaffold/generate", json={
            "project_title": "Test Project",
            "project_description": "A test project description here",
        })
        assert response.status_code == 401

    def test_portfolio_optimize_requires_auth(self, client):
        response = client.post("/api/v1/portfolio/optimize", json={
            "project_title": "Test",
            "project_description": "A test project description here",
        })
        assert response.status_code == 401


class TestInputValidation:
    """Tests for request validation (Pydantic enforcement)."""

    def test_jd_rejects_short_text(self, client):
        """JD text under 50 chars should be rejected (auth blocks first with fake token)."""
        response = client.post(
            "/api/v1/jd/analyze",
            json={
                "jd_text": "too short",
                "role": "Engineer",
                "company_type": "startup",
            },
            headers={"Authorization": "Bearer fake-token"},
        )
        # Auth runs before validation, so we get 401 with fake token
        assert response.status_code in (401, 422)

    def test_jd_rejects_invalid_company_type(self, client):
        """Invalid company type should be rejected (auth blocks first with fake token)."""
        response = client.post(
            "/api/v1/jd/analyze",
            json={
                "jd_text": "x" * 100,
                "role": "Engineer",
                "company_type": "invalid_type",
            },
            headers={"Authorization": "Bearer fake-token"},
        )
        assert response.status_code in (401, 422)

    def test_repo_rejects_invalid_github_url(self, client):
        """Non-GitHub URLs should be rejected (auth blocks first with fake token)."""
        response = client.post(
            "/api/v1/repo/analyze",
            json={"github_url": "https://gitlab.com/user/repo"},
            headers={"Authorization": "Bearer fake-token"},
        )
        assert response.status_code in (401, 422)