forked from ChelseaKR/fare-policy-assistant
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_console.py
More file actions
312 lines (254 loc) · 12.3 KB
/
Copy pathtest_console.py
File metadata and controls
312 lines (254 loc) · 12.3 KB
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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
"""Agency operator console (EXP-09): auth, version history/diff, config
actions against the rider Lambda, and the eval-report passthrough.
The AWS-facing actions (pin, embed-config) are exercised against a fake
Lambda client (`_client_factory`), never real AWS, so the suite stays
offline like the rest of the repo's tests.
"""
from __future__ import annotations
import json
import pytest
from tests.conftest import make_chunk
from web import console
AUTH = {"authorization": "Bearer test-token"}
@pytest.fixture(autouse=True)
def token(monkeypatch):
monkeypatch.setenv("FPA_CONSOLE_TOKEN", "test-token")
def _event(method="GET", path="/console/api/status", headers=None, body=None, qs=None):
return {
"requestContext": {"http": {"method": method}},
"rawPath": path,
"headers": headers if headers is not None else dict(AUTH),
"body": json.dumps(body) if body is not None else None,
"queryStringParameters": qs,
}
class FakeLambdaClient:
def __init__(self, env: dict | None = None):
self.env = dict(env or {"FPA_PROVIDER": "bedrock"})
self.updated_with = None
def get_function_configuration(self, FunctionName): # noqa: N803 (boto3 shape)
return {"Environment": {"Variables": dict(self.env)}}
def update_function_configuration(self, FunctionName, Environment): # noqa: N803
self.updated_with = FunctionName
self.env = dict(Environment["Variables"])
return {}
@pytest.fixture
def fake_client(monkeypatch):
client = FakeLambdaClient()
monkeypatch.setattr(console, "_client_factory", lambda: client)
monkeypatch.setenv("FPA_RIDER_FUNCTION_NAME", "fare-policy-assistant-demo")
yield client
monkeypatch.setattr(console, "_client_factory", None)
class TestAuth:
def test_missing_token_env_fails_closed(self, monkeypatch):
monkeypatch.delenv("FPA_CONSOLE_TOKEN", raising=False)
resp = console.console_handler(_event())
assert resp["statusCode"] == 401
def test_missing_header_rejected(self):
resp = console.console_handler(_event(headers={}))
assert resp["statusCode"] == 401
def test_wrong_token_rejected(self):
resp = console.console_handler(_event(headers={"authorization": "Bearer nope"}))
assert resp["statusCode"] == 401
def test_correct_token_accepted(self):
resp = console.console_handler(_event())
assert resp["statusCode"] == 200
def test_console_page_is_public_but_contains_no_operator_data(self):
resp = console.console_handler(_event(method="GET", path="/console", headers={}))
assert resp["statusCode"] == 200
assert "Agency operator console" in resp["body"]
assert "test-token" not in resp["body"]
def test_console_api_still_requires_auth_when_page_is_public(self):
resp = console.console_handler(_event(method="GET", path="/console/api/status", headers={}))
assert resp["statusCode"] == 401
def test_console_page_passes_structural_a11y(self):
from web.a11y import check_html
assert check_html(console.CONSOLE_HTML) == []
def test_console_treats_pass_rate_as_zero_to_one_hundred_percentage(self):
# Eval reports already store pass_rate on a 0–100 scale. The browser must
# format that value directly rather than turn 95.2 into 9520%.
assert "Number(s.pass_rate).toFixed(1)" in console.CONSOLE_HTML
assert "s.pass_rate * 100" not in console.CONSOLE_HTML
assert "s.pass_rate*100" not in console.CONSOLE_HTML
def test_unknown_route_404(self):
resp = console.console_handler(_event(path="/console/api/nope"))
assert resp["statusCode"] == 404
class TestStatus:
def test_status_reports_corpus_and_pin(self, monkeypatch):
monkeypatch.delenv("FPA_PINNED_CORPUS_VERSION", raising=False)
resp = console.console_handler(_event(path="/console/api/status"))
data = json.loads(resp["body"])
assert len(data["corpus"]["corpus_version"]) == 12
assert data["pinned"] is None
assert data["embed_ancestors"] == "'self'"
class TestVersionsAndDiff:
@pytest.fixture(autouse=True)
def history(self, monkeypatch):
old_chunk = make_chunk(text="Old fare text.")
new_chunk = make_chunk(text="New fare text, updated.")
added_chunk = make_chunk(
chunk_id="mst-fares#new", doc_id="mst-fares-new", text="Brand new program."
)
versions = [
{
"commit": "aaaaaaaaaaaa",
"committed_at": "2026-06-01T00:00:00+00:00",
"corpus_version": "aaaaaaaaaaaa",
"agencies": ["MST"],
"documents": 1,
"chunks": [old_chunk.__dict__],
},
{
"commit": "bbbbbbbbbbbb",
"committed_at": "2026-07-01T00:00:00+00:00",
"corpus_version": "bbbbbbbbbbbb",
"agencies": ["MST"],
"documents": 2,
"chunks": [new_chunk.__dict__, added_chunk.__dict__],
},
]
monkeypatch.setattr(console, "_load_version_history", lambda: versions)
def test_versions_list_omits_chunk_payload(self):
resp = console.console_handler(_event(path="/console/api/versions"))
data = json.loads(resp["body"])
assert len(data["versions"]) == 2
assert "chunks" not in data["versions"][0]
assert data["versions"][0]["corpus_version"] == "aaaaaaaaaaaa"
def test_diff_by_corpus_version(self):
resp = console.console_handler(
_event(
path="/console/api/diff",
qs={"from": "aaaaaaaaaaaa", "to": "bbbbbbbbbbbb"},
)
)
assert resp["statusCode"] == 200
data = json.loads(resp["body"])
assert data["added"] == ["mst-fares-new"]
assert data["changed"] == ["mst-fares"]
assert data["removed"] == []
def test_diff_by_commit(self):
resp = console.console_handler(
_event(path="/console/api/diff", qs={"from": "aaaaaaaaaaaa", "to": "bbbbbbbbbbbb"})
)
assert resp["statusCode"] == 200
def test_diff_missing_params_400(self):
resp = console.console_handler(_event(path="/console/api/diff", qs=None))
assert resp["statusCode"] == 400
def test_diff_unknown_version_404(self):
resp = console.console_handler(
_event(path="/console/api/diff", qs={"from": "aaaaaaaaaaaa", "to": "doesnotexist"})
)
assert resp["statusCode"] == 404
class TestPin:
def test_pin_updates_rider_env_without_clobbering_other_keys(self, fake_client):
fake_client.env["FPA_ANSWER_MODEL"] = "keep-me"
resp = console.console_handler(
_event(
method="POST",
path="/console/api/pin",
body={"corpus_version": "deadbeefcafe"},
)
)
assert resp["statusCode"] == 200
data = json.loads(resp["body"])
assert data["pinned"] == "deadbeefcafe"
assert fake_client.updated_with == "fare-policy-assistant-demo"
assert fake_client.env["FPA_ANSWER_MODEL"] == "keep-me"
assert fake_client.env["FPA_PINNED_CORPUS_VERSION"] == "deadbeefcafe"
def test_pin_rejects_non_hex_version(self, fake_client):
resp = console.console_handler(
_event(method="POST", path="/console/api/pin", body={"corpus_version": "not hex!"})
)
assert resp["statusCode"] == 400
assert fake_client.updated_with is None
def test_pin_missing_field_400(self, fake_client):
resp = console.console_handler(_event(method="POST", path="/console/api/pin", body={}))
assert resp["statusCode"] == 400
def test_pin_without_rider_function_name_500(self, monkeypatch, fake_client):
monkeypatch.delenv("FPA_RIDER_FUNCTION_NAME", raising=False)
resp = console.console_handler(
_event(method="POST", path="/console/api/pin", body={"corpus_version": "abc123"})
)
assert resp["statusCode"] == 500
class TestEmbedConfig:
def test_get_defaults_to_self(self, monkeypatch):
monkeypatch.delenv("FPA_EMBED_ANCESTORS", raising=False)
resp = console.console_handler(_event(path="/console/api/embed-config"))
assert json.loads(resp["body"])["ancestors"] == "'self'"
def test_post_updates_rider_env(self, fake_client):
resp = console.console_handler(
_event(
method="POST",
path="/console/api/embed-config",
body={"ancestors": "https://mst.org https://sbmtd.gov"},
)
)
assert resp["statusCode"] == 200
data = json.loads(resp["body"])
assert data["ancestors"] == "https://mst.org https://sbmtd.gov"
assert fake_client.env["FPA_EMBED_ANCESTORS"] == "https://mst.org https://sbmtd.gov"
def test_post_rejects_non_https_origin(self, fake_client):
resp = console.console_handler(
_event(
method="POST",
path="/console/api/embed-config",
body={"ancestors": "http://insecure.example"},
)
)
assert resp["statusCode"] == 400
assert fake_client.updated_with is None
def test_post_rejects_empty(self, fake_client):
resp = console.console_handler(
_event(method="POST", path="/console/api/embed-config", body={"ancestors": " "})
)
assert resp["statusCode"] == 400
class TestEvalReport:
def test_no_runs_returns_404(self, monkeypatch, tmp_path):
monkeypatch.setattr(console.config, "EVAL_RUNS_DIR", tmp_path / "does-not-exist")
resp = console.console_handler(_event(path="/console/api/eval-report"))
assert resp["statusCode"] == 404
def test_latest_run_returned(self, monkeypatch, tmp_path):
runs = tmp_path / "runs"
older = runs / "20260101T000000Z"
newer = runs / "20260201T000000Z"
older.mkdir(parents=True)
newer.mkdir(parents=True)
(older / "summary.json").write_text(json.dumps({"run_at": "old"}), encoding="utf-8")
(newer / "summary.json").write_text(json.dumps({"run_at": "new"}), encoding="utf-8")
monkeypatch.setattr(console.config, "EVAL_RUNS_DIR", runs)
resp = console.console_handler(_event(path="/console/api/eval-report"))
assert json.loads(resp["body"])["run_at"] == "new"
def test_skips_run_dir_without_summary(self, monkeypatch, tmp_path):
runs = tmp_path / "runs"
incomplete = runs / "20260301T000000Z"
complete = runs / "20260201T000000Z"
incomplete.mkdir(parents=True)
complete.mkdir(parents=True)
(complete / "summary.json").write_text(json.dumps({"run_at": "complete"}), encoding="utf-8")
monkeypatch.setattr(console.config, "EVAL_RUNS_DIR", runs)
resp = console.console_handler(_event(path="/console/api/eval-report"))
assert json.loads(resp["body"])["run_at"] == "complete"
class TestLoadVersionHistory:
def test_reads_static_file_when_present(self, monkeypatch, tmp_path):
path = tmp_path / "version_history.json"
path.write_text(json.dumps({"versions": [{"commit": "abc", "corpus_version": "abc"}]}))
monkeypatch.setattr(console, "VERSION_HISTORY_PATH", path)
assert console._load_version_history() == [{"commit": "abc", "corpus_version": "abc"}]
def test_falls_back_to_live_git_query_when_file_absent(self, monkeypatch, tmp_path):
monkeypatch.setattr(console, "VERSION_HISTORY_PATH", tmp_path / "missing.json")
called = {}
def fake_version_history():
called["ran"] = True
return [{"commit": "live"}]
monkeypatch.setattr("assistant.corpus.version_history", fake_version_history)
assert console._load_version_history() == [{"commit": "live"}]
assert called.get("ran")
class TestErrorHandling:
def test_unexpected_exception_is_500_and_logs_no_content(self, monkeypatch, capsys):
def boom(event):
raise ValueError("SECRET-detail")
monkeypatch.setitem(console._ROUTES, ("GET", "/console/api/status"), boom)
resp = console.console_handler(_event(path="/console/api/status"))
assert resp["statusCode"] == 500
out = capsys.readouterr().out
assert "SECRET-detail" not in out
assert "ValueError" in out