Skip to content

Commit 4add2d2

Browse files
committed
Mock at the HTTP level, not internal functions
We want our integration tests to check that: 1. When prometheus returns a given bit of data 2. Do we produce the output we desire? Earlier, we were mocking an internal function that would always return the 'correct' data. Ultimately this just tests that our mocks work, rather than if our logic is correct. This commit uses https://pytest-httpserver.readthedocs.io to instead: 1. Mock the response from the prometheus server 2. Validate that the output is what we want This helps us catch issues with our logic more directly
1 parent 6a6268a commit 4add2d2

5 files changed

Lines changed: 104 additions & 58 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ dev = [
2121
"mystmd>=1.6.0",
2222
"pytest>=8.4.1",
2323
"ruff>=0.12.5",
24+
"pytest-httpserver"
2425
]
2526

2627
[tool.isort]

src/jupyterhub_cost_monitoring/prometheus.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -287,19 +287,7 @@ def query_user_groups(
287287
except requests.exceptions.RequestException as e:
288288
self.log.exception(f"HTTP request failed: {e}")
289289
raise
290-
result = self._process_user_groups(response, hub_name, user_name, group_name)
291-
return result
292290

293-
def _process_user_groups(
294-
self,
295-
response: requests.Response,
296-
hub_name: str | None = None,
297-
user_name: str | None = None,
298-
group_name: str | None = None,
299-
) -> list[dict]:
300-
"""
301-
Process the response from the Prometheus server to extract user group information. Note that only the most recent date of user group membership is used.
302-
"""
303291
result = []
304292
unique_keys = set()
305293
for data in response["data"]["result"]:

tests/data/prometheus-groups.json

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
{
2+
"status": "success",
3+
"data": {
4+
"resultType": "matrix",
5+
"result": [
6+
{
7+
"metric": {
8+
"namespace": "staging",
9+
"usergroup": "group_1",
10+
"username": "user_1",
11+
"username_escaped": "user_1"
12+
},
13+
"values": [
14+
[
15+
1785024000,
16+
"1"
17+
]
18+
]
19+
},
20+
{
21+
"metric": {
22+
"namespace": "staging",
23+
"usergroup": "group_1",
24+
"username": "user_2",
25+
"username_escaped": "user_2"
26+
},
27+
"values": [
28+
[
29+
1785024000,
30+
"1"
31+
]
32+
]
33+
},
34+
{
35+
"metric": {
36+
"namespace": "staging",
37+
"usergroup": "group_1",
38+
"username": "user_3",
39+
"username_escaped": "user_3"
40+
},
41+
"values": [
42+
[
43+
1785024000,
44+
"1"
45+
]
46+
]
47+
},
48+
{
49+
"metric": {
50+
"namespace": "staging",
51+
"usergroup": "group_2",
52+
"username": "user_4",
53+
"username_escaped": "user_4"
54+
},
55+
"values": [
56+
[
57+
1785024000,
58+
"1"
59+
]
60+
]
61+
}
62+
]
63+
}
64+
}
Lines changed: 9 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,26 @@
11
[
22
{
3-
"date": "2025-09-01",
4-
"hub": "prod",
5-
"usergroup": "group_1",
6-
"username": "user_1",
7-
"username_escaped": "user_1"
8-
},
9-
{
10-
"date": "2025-09-02",
11-
"hub": "prod",
12-
"usergroup": "group_1",
3+
"hub": "staging",
134
"username": "user_1",
14-
"username_escaped": "user_1"
5+
"username_escaped": "user_1",
6+
"usergroup": "group_1"
157
},
168
{
17-
"date": "2025-09-01",
189
"hub": "staging",
19-
"usergroup": "group_2",
2010
"username": "user_2",
21-
"username_escaped": "user_2"
11+
"username_escaped": "user_2",
12+
"usergroup": "group_1"
2213
},
2314
{
24-
"date": "2025-09-02",
2515
"hub": "staging",
26-
"usergroup": "group_2",
27-
"username": "user_2",
28-
"username_escaped": "user_2"
29-
},
30-
{
31-
"date": "2025-09-01",
32-
"hub": "prod",
33-
"usergroup": "group_1",
3416
"username": "user_3",
35-
"username_escaped": "user_3"
36-
},
37-
{
38-
"date": "2025-09-02",
39-
"hub": "prod",
40-
"usergroup": "group_1",
41-
"username": "user_3",
42-
"username_escaped": "user_3"
43-
},
44-
{
45-
"date": "2025-09-01",
46-
"hub": "staging",
47-
"usergroup": "group_2",
48-
"username": "user_4",
49-
"username_escaped": "user_4"
17+
"username_escaped": "user_3",
18+
"usergroup": "group_1"
5019
},
5120
{
52-
"date": "2025-09-02",
5321
"hub": "staging",
54-
"usergroup": "group_2",
5522
"username": "user_4",
56-
"username_escaped": "user_4"
23+
"username_escaped": "user_4",
24+
"usergroup": "group_2"
5725
}
5826
]

tests/test_integration.py

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
import json
12
import logging
23
from collections import defaultdict
4+
from datetime import timedelta
35

46
import pytest
7+
from pytest_httpserver import HTTPServer
58

69
from src.jupyterhub_cost_monitoring.const_cost_aws import (
710
GRANULARITY_DAILY,
@@ -11,6 +14,7 @@
1114
from src.jupyterhub_cost_monitoring.query_cost_aws import (
1215
query_total_costs_per_component,
1316
)
17+
from jupyterhub_cost_monitoring.prometheus import Prometheus
1418

1519
logger = logging.getLogger(__name__)
1620

@@ -37,19 +41,40 @@ def test_get_usage_data(mock_prometheus_usage, env_vars):
3741
assert len(response) > 0
3842

3943

40-
def test_get_user_group_info(mock_prometheus_user_group_info, env_vars):
44+
def test_get_user_group_info(httpserver: HTTPServer):
4145
"""
4246
Test mocked Prometheus user group info json data retrieval.
4347
"""
44-
from src.jupyterhub_cost_monitoring.query_usage import query_user_groups
4548

46-
response = query_user_groups(
49+
prometheus = Prometheus()
50+
prometheus.host = httpserver.host
51+
prometheus.port = httpserver.port
52+
53+
now_date = get_now_date() - timedelta(days=1)
54+
55+
date_range = DateRange(start_date=now_date, end_date=now_date)
56+
start, end = date_range.prometheus_range
57+
58+
with open("tests/data/prometheus-groups.json") as f:
59+
httpserver.expect_request(
60+
"/api/v1/query_range",
61+
query_string={
62+
"query": USER_GROUP_INFO,
63+
"start": start,
64+
"end": end,
65+
"step": "1d",
66+
},
67+
).respond_with_data(f.read())
68+
69+
response = prometheus.query_user_groups(
4770
hub_name=None,
4871
user_name=None,
4972
group_name=None,
5073
)
51-
logger.info(f"User group info: {response}")
52-
assert len(response) > 0
74+
75+
with open("tests/data/test_output_user_group_info.json") as f:
76+
expected_response = json.load(f)
77+
assert expected_response == response
5378

5479

5580
def test_get_cost_component_data(mock_ce, env_vars):

0 commit comments

Comments
 (0)