Skip to content

Commit b034f4c

Browse files
committed
eli-389 updating tests to use matchers
1 parent f4e8122 commit b034f4c

File tree

1 file changed

+48
-28
lines changed

1 file changed

+48
-28
lines changed

tests/unit/middleware/test_security_headers.py

Lines changed: 48 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
import pytest
66
from flask import Flask
77
from flask.testing import FlaskClient
8+
from hamcrest import assert_that, contains_string, equal_to, has_entries, is_
89

910
from eligibility_signposting_api.middleware import SecurityHeadersMiddleware
1011

1112

12-
class TestError(Exception):
13-
"""Test exception for error handling tests."""
13+
class MiddlewareTestError(Exception):
14+
"""Custom exception for middleware error handling tests."""
1415

1516

1617
@pytest.fixture
@@ -26,9 +27,9 @@ def test_route():
2627
@app.route("/error")
2728
def error_route():
2829
msg = "Test error"
29-
raise TestError(msg)
30+
raise MiddlewareTestError(msg)
3031

31-
@app.errorhandler(TestError)
32+
@app.errorhandler(MiddlewareTestError)
3233
def handle_value_error(e):
3334
return {"error": str(e)}, HTTPStatus.INTERNAL_SERVER_ERROR
3435

@@ -48,28 +49,49 @@ def test_security_headers_present_on_successful_response(self, client: FlaskClie
4849
"""Test that security headers are added to successful responses."""
4950
response = client.get("/test")
5051

51-
assert response.status_code == HTTPStatus.OK
52-
assert response.headers.get("Cache-Control") == "no-store, private"
53-
assert response.headers.get("Strict-Transport-Security") == "max-age=31536000; includeSubDomains"
54-
assert response.headers.get("X-Content-Type-Options") == "nosniff"
52+
assert_that(response.status_code, is_(equal_to(HTTPStatus.OK)))
53+
assert_that(
54+
dict(response.headers),
55+
has_entries(
56+
{
57+
"Cache-Control": "no-store, private",
58+
"Strict-Transport-Security": "max-age=31536000; includeSubDomains",
59+
"X-Content-Type-Options": "nosniff",
60+
}
61+
),
62+
)
5563

5664
def test_security_headers_present_on_error_response(self, client: FlaskClient) -> None:
5765
"""Test that security headers are added to error responses."""
5866
response = client.get("/error")
5967

60-
assert response.status_code == HTTPStatus.INTERNAL_SERVER_ERROR
61-
assert response.headers.get("Cache-Control") == "no-store, private"
62-
assert response.headers.get("Strict-Transport-Security") == "max-age=31536000; includeSubDomains"
63-
assert response.headers.get("X-Content-Type-Options") == "nosniff"
68+
assert_that(response.status_code, is_(equal_to(HTTPStatus.INTERNAL_SERVER_ERROR)))
69+
assert_that(
70+
dict(response.headers),
71+
has_entries(
72+
{
73+
"Cache-Control": "no-store, private",
74+
"Strict-Transport-Security": "max-age=31536000; includeSubDomains",
75+
"X-Content-Type-Options": "nosniff",
76+
}
77+
),
78+
)
6479

6580
def test_security_headers_present_on_404(self, client: FlaskClient) -> None:
6681
"""Test that security headers are added to 404 responses."""
6782
response = client.get("/nonexistent")
6883

69-
assert response.status_code == HTTPStatus.NOT_FOUND
70-
assert response.headers.get("Cache-Control") == "no-store, private"
71-
assert response.headers.get("Strict-Transport-Security") == "max-age=31536000; includeSubDomains"
72-
assert response.headers.get("X-Content-Type-Options") == "nosniff"
84+
assert_that(response.status_code, is_(equal_to(HTTPStatus.NOT_FOUND)))
85+
assert_that(
86+
dict(response.headers),
87+
has_entries(
88+
{
89+
"Cache-Control": "no-store, private",
90+
"Strict-Transport-Security": "max-age=31536000; includeSubDomains",
91+
"X-Content-Type-Options": "nosniff",
92+
}
93+
),
94+
)
7395

7496
def test_all_expected_headers_are_present(self, client: FlaskClient) -> None:
7597
"""Test that all expected security headers are present."""
@@ -91,25 +113,23 @@ def test_cache_control_prevents_caching(self, client: FlaskClient) -> None:
91113
response = client.get("/test")
92114

93115
cache_control = response.headers.get("Cache-Control")
94-
assert cache_control is not None
95-
assert "no-store" in cache_control, "Response should not be stored in any cache"
96-
assert "private" in cache_control, "Response should be marked as private"
116+
assert_that(cache_control, contains_string("no-store"))
117+
assert_that(cache_control, contains_string("private"))
97118

98119
def test_hsts_header_enforces_https(self, client: FlaskClient) -> None:
99120
"""Test that HSTS header is properly configured."""
100121
response = client.get("/test")
101122

102123
hsts = response.headers.get("Strict-Transport-Security")
103-
assert hsts is not None
104-
assert "max-age=31536000" in hsts, "HSTS should be valid for 1 year"
105-
assert "includeSubDomains" in hsts, "HSTS should apply to all subdomains"
124+
assert_that(hsts, contains_string("max-age=31536000"))
125+
assert_that(hsts, contains_string("includeSubDomains"))
106126

107127
def test_content_type_options_prevents_sniffing(self, client: FlaskClient) -> None:
108128
"""Test that X-Content-Type-Options prevents MIME sniffing."""
109129
response = client.get("/test")
110130

111131
content_type_options = response.headers.get("X-Content-Type-Options")
112-
assert content_type_options == "nosniff", "Should prevent MIME type sniffing"
132+
assert_that(content_type_options, is_(equal_to("nosniff")))
113133

114134
def test_middleware_init_app_method(self) -> None:
115135
"""Test that middleware can be initialized separately using init_app."""
@@ -119,11 +139,11 @@ def test_middleware_init_app_method(self) -> None:
119139

120140
@app.route("/test")
121141
def test_route():
122-
return {"status": "ok"}, 200
142+
return {"status": "ok"}, HTTPStatus.OK
123143

124144
with app.test_client() as client:
125145
response = client.get("/test")
126-
assert response.headers.get("Cache-Control") == "no-store, private"
146+
assert_that(response.headers.get("Cache-Control"), is_(equal_to("no-store, private")))
127147

128148
def test_existing_headers_are_not_overridden(self) -> None:
129149
"""Test that existing headers are not overridden by middleware."""
@@ -134,13 +154,13 @@ def test_existing_headers_are_not_overridden(self) -> None:
134154
def test_route():
135155
from flask import make_response
136156

137-
resp = make_response({"status": "ok"}, 200)
157+
resp = make_response({"status": "ok"}, HTTPStatus.OK)
138158
resp.headers["Cache-Control"] = "public, max-age=3600"
139159
return resp
140160

141161
with app.test_client() as client:
142162
response = client.get("/test")
143163
# Should keep the custom Cache-Control value
144-
assert response.headers.get("Cache-Control") == "public, max-age=3600"
164+
assert_that(response.headers.get("Cache-Control"), is_(equal_to("public, max-age=3600")))
145165
# But other headers should still be added
146-
assert response.headers.get("X-Content-Type-Options") == "nosniff"
166+
assert_that(response.headers.get("X-Content-Type-Options"), is_(equal_to("nosniff")))

0 commit comments

Comments
 (0)