Skip to content

Commit 3a8cb84

Browse files
authored
test: add comprehensive unit tests for /logs/ API endpoints (closes #1)
Added pytest-based unit tests for /logs/ endpoints covering: - POST /logs/ success with valid payload - POST /logs/ error cases: missing fields, empty body, invalid data types, wrong content-type - GET /logs/ success, pagination (skip/limit), and after-creation assertions - GET /logs/ error cases: invalid skip type, negative limit Resolves issue #1: Add unit tests for /logs/ API endpoints
1 parent e08e59a commit 3a8cb84

1 file changed

Lines changed: 105 additions & 6 deletions

File tree

backend/tests/test_logs.py

Lines changed: 105 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,121 @@
1+
import pytest
12
from fastapi.testclient import TestClient
23
from backend.main import app
34

45
client = TestClient(app)
56

6-
def test_create_log():
7+
VALID_PAYLOAD = {
8+
"source_ip": "10.0.0.1",
9+
"destination_ip": "10.0.0.2",
10+
"protocol": "TCP",
11+
"bytes_transferred": 1234,
12+
"event_type": "normal",
13+
"details": "test log entry"
14+
}
15+
16+
17+
# --- POST /logs/ Tests ---
18+
19+
def test_create_log_success():
20+
"""Test POST /logs/ with a valid payload returns 200 or 201."""
21+
resp = client.post("/logs/", json=VALID_PAYLOAD)
22+
assert resp.status_code in (200, 201), f"Expected 200/201, got {resp.status_code}"
23+
data = resp.json()
24+
assert isinstance(data, dict)
25+
26+
27+
def test_create_log_missing_required_field():
28+
"""Test POST /logs/ with missing required field returns 422 Unprocessable Entity."""
729
payload = {
8-
"source_ip": "10.0.0.1",
930
"destination_ip": "10.0.0.2",
1031
"protocol": "TCP",
1132
"bytes_transferred": 1234,
12-
"event_type": "normal",
13-
"details": "test log"
33+
"event_type": "normal"
34+
# source_ip is missing
1435
}
1536
resp = client.post("/logs/", json=payload)
37+
assert resp.status_code == 422, f"Expected 422, got {resp.status_code}"
38+
39+
40+
def test_create_log_invalid_payload_empty_body():
41+
"""Test POST /logs/ with empty body returns 422."""
42+
resp = client.post("/logs/", json={})
43+
assert resp.status_code == 422, f"Expected 422, got {resp.status_code}"
44+
45+
46+
def test_create_log_invalid_data_type():
47+
"""Test POST /logs/ with wrong data type for bytes_transferred returns 422."""
48+
payload = {**VALID_PAYLOAD, "bytes_transferred": "not_a_number"}
49+
resp = client.post("/logs/", json=payload)
50+
assert resp.status_code == 422, f"Expected 422, got {resp.status_code}"
51+
52+
53+
def test_create_log_response_structure():
54+
"""Test POST /logs/ response contains expected fields."""
55+
resp = client.post("/logs/", json=VALID_PAYLOAD)
1656
assert resp.status_code in (200, 201)
57+
data = resp.json()
58+
# Response should be a dict (log record)
59+
assert isinstance(data, dict)
60+
1761

18-
def test_list_logs():
62+
# --- GET /logs/ Tests ---
63+
64+
def test_list_logs_success():
65+
"""Test GET /logs/ returns 200 with a list or paginated dict."""
66+
resp = client.get("/logs/")
67+
assert resp.status_code == 200, f"Expected 200, got {resp.status_code}"
68+
data = resp.json()
69+
assert isinstance(data, (list, dict)), "Response should be a list or dict"
70+
71+
72+
def test_list_logs_pagination_skip():
73+
"""Test GET /logs/ with skip query parameter."""
74+
resp = client.get("/logs/?skip=0&limit=5")
75+
assert resp.status_code == 200, f"Expected 200, got {resp.status_code}"
76+
data = resp.json()
77+
assert isinstance(data, (list, dict))
78+
79+
80+
def test_list_logs_pagination_limit():
81+
"""Test GET /logs/ with limit query parameter returns at most limit items."""
82+
resp = client.get("/logs/?limit=2")
83+
assert resp.status_code == 200, f"Expected 200, got {resp.status_code}"
84+
data = resp.json()
85+
if isinstance(data, list):
86+
assert len(data) <= 2, f"Expected at most 2 results, got {len(data)}"
87+
88+
89+
def test_list_logs_after_creation():
90+
"""Test GET /logs/ returns logs after a POST."""
91+
# Create a log first
92+
client.post("/logs/", json=VALID_PAYLOAD)
1993
resp = client.get("/logs/")
2094
assert resp.status_code == 200
2195
data = resp.json()
22-
assert isinstance(data, list) or isinstance(data, dict)
96+
if isinstance(data, list):
97+
assert len(data) >= 1, "Expected at least 1 log after creation"
98+
elif isinstance(data, dict):
99+
# Paginated response
100+
items = data.get("items", data.get("logs", data.get("results", [])))
101+
assert len(items) >= 1
102+
103+
104+
# --- Error / Edge Case Tests ---
105+
106+
def test_create_log_no_content_type():
107+
"""Test POST /logs/ without JSON content-type."""
108+
resp = client.post("/logs/", data="not json", headers={"Content-Type": "text/plain"})
109+
assert resp.status_code in (400, 415, 422), f"Unexpected status: {resp.status_code}"
110+
111+
112+
def test_get_logs_invalid_limit():
113+
"""Test GET /logs/ with a negative limit returns 422 or handles gracefully."""
114+
resp = client.get("/logs/?limit=-1")
115+
assert resp.status_code in (200, 422), f"Unexpected status: {resp.status_code}"
116+
117+
118+
def test_get_logs_invalid_skip_type():
119+
"""Test GET /logs/ with non-integer skip returns 422."""
120+
resp = client.get("/logs/?skip=abc")
121+
assert resp.status_code == 422, f"Expected 422, got {resp.status_code}"

0 commit comments

Comments
 (0)