File size: 5,496 Bytes
e327f0d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""
Backend API testleri.

Coverage:
  - Health endpoint
  - Version endpoint
  - Auth: dev mode + (mocked) prod mode
  - Inspect sync (mock ML)
  - CORS preflight
  - 404/403 davranisi
"""
from __future__ import annotations

import io

import pytest
from fastapi.testclient import TestClient


# ---------------- Health & version ----------------

def test_health_ok(client: TestClient):
    r = client.get("/health")
    assert r.status_code == 200
    body = r.json()
    assert body["status"] == "ok"
    assert body["ml_loaded"] is True
    assert "timestamp" in body
    assert "version" in body


def test_version_endpoint(client: TestClient):
    r = client.get("/api/v1/version")
    assert r.status_code == 200
    body = r.json()
    assert "version" in body
    assert "git_sha" in body
    assert "build_time" in body
    assert "environment" in body


# ---------------- Auth ----------------

def test_dev_mode_allows_no_api_key(client: TestClient):
    """API_KEYS bos => dev mode => key olmadan calismali."""
    r = client.get("/api/v1/inspect", params={"page": 1, "page_size": 10})
    # 200: list bos olabilir ama auth gecti
    assert r.status_code == 200
    body = r.json()
    assert "items" in body
    assert body["page"] == 1


def test_prod_mode_requires_api_key(client: TestClient, monkeypatch):
    """API_KEYS dolu => header olmadan 401."""
    from config import settings
    monkeypatch.setattr(settings, "api_keys", ["valid-key-123"])
    monkeypatch.setattr(settings, "environment", "production")

    r = client.get("/api/v1/inspect")
    assert r.status_code == 401
    assert "X-API-Key" in r.json()["detail"]


def test_prod_mode_invalid_api_key(client: TestClient, monkeypatch):
    from config import settings
    monkeypatch.setattr(settings, "api_keys", ["valid-key-123"])
    monkeypatch.setattr(settings, "environment", "production")

    r = client.get("/api/v1/inspect", headers={"X-API-Key": "wrong"})
    assert r.status_code == 403


def test_prod_mode_valid_api_key(client: TestClient, monkeypatch):
    from config import settings
    monkeypatch.setattr(settings, "api_keys", ["valid-key-123"])
    monkeypatch.setattr(settings, "environment", "production")

    r = client.get("/api/v1/inspect", headers={"X-API-Key": "valid-key-123"})
    assert r.status_code == 200


# ---------------- CORS ----------------

def test_cors_preflight_localhost_web(client: TestClient):
    """Next.js dev origin'inden preflight gecmeli."""
    r = client.options(
        "/api/v1/inspect",
        headers={
            "Origin": "http://localhost:3000",
            "Access-Control-Request-Method": "POST",
            "Access-Control-Request-Headers": "x-api-key,content-type",
        },
    )
    assert r.status_code in (200, 204)
    assert r.headers.get("access-control-allow-origin") == "http://localhost:3000"
    assert "POST" in r.headers.get("access-control-allow-methods", "")


def test_cors_preflight_tauri(client: TestClient):
    """Tauri desktop origin'i kabul edilmeli."""
    r = client.options(
        "/api/v1/inspect",
        headers={
            "Origin": "http://tauri.localhost",
            "Access-Control-Request-Method": "POST",
            "Access-Control-Request-Headers": "x-api-key",
        },
    )
    assert r.status_code in (200, 204)
    assert r.headers.get("access-control-allow-origin") == "http://tauri.localhost"


def test_cors_preflight_vercel_regex(client: TestClient):
    """*.vercel.app origin'i regex ile kabul edilmeli."""
    r = client.options(
        "/api/v1/inspect",
        headers={
            "Origin": "https://my-app-abc123.vercel.app",
            "Access-Control-Request-Method": "GET",
            "Access-Control-Request-Headers": "x-api-key",
        },
    )
    assert r.status_code in (200, 204)
    assert "vercel.app" in (r.headers.get("access-control-allow-origin") or "")


# ---------------- Inspect ----------------

def _png_bytes() -> bytes:
    """Minimal gecerli 1x1 PNG."""
    import base64
    return base64.b64decode(
        b"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII="
    )


def test_sync_single_inspection(client: TestClient):
    """Tek goruntu sync inspection — mock ML donduruyor."""
    img = _png_bytes()
    r = client.post(
        "/api/v1/inspect/sync",
        files={"file": ("test.png", img, "image/png")},
    )
    assert r.status_code == 200, r.text
    body = r.json()
    assert "inspection_id" in body
    assert "result" in body
    assert body["result"]["summary"]["total_parts_inspected"] == 0


def test_sync_rejects_non_image(client: TestClient):
    """Image olmayan MIME tipi reddedilmeli."""
    r = client.post(
        "/api/v1/inspect/sync",
        files={"file": ("test.txt", b"not an image", "text/plain")},
    )
    assert r.status_code == 400


def test_inspect_404_for_missing(client: TestClient):
    r = client.get("/api/v1/inspect/00000000-0000-0000-0000-000000000000")
    assert r.status_code == 404
    assert "bulunamadi" in r.json()["detail"].lower()


def test_inspect_async_mode_returns_202(client: TestClient):
    img = _png_bytes()
    r = client.post(
        "/api/v1/inspect?mode=async",
        files=[("files", ("test1.png", img, "image/png"))],
    )
    assert r.status_code == 202, r.text
    body = r.json()
    assert body["status"] == "queued"
    assert body["status_url"].startswith("/api/v1/inspect/")
    assert "X-Inspection-Id" in r.headers