Initial import: eCW Charge Validation bot and results Portal
Snapshot of the working project as of 2026-10-23 (local 2d4d4fc), one commit with no earlier history: the tracked files were de-identified first (synthetic claim ids and patient data only; see Context.md section 7). Credentials, run logs, screenshots, claim reports and client workbooks are gitignored and not included. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
ba15a574ab
205 files changed
+56772
No files matched your search
@@ -0,0 +1,168 @@
|
||||
r"""
|
||||
The Portal's Runs pages — `/runs`, `/runs/<id>`, `/runs/<id>/shot/<n>`
|
||||
(spec 2026-10-22 Stage 1, EEWA-31), against events files in a temp folder
|
||||
and the fake repository / runner of test_web_app.py.
|
||||
|
||||
python tests\test_runs_page.py
|
||||
|
||||
Claim numbers here are made up.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import json
|
||||
import pathlib
|
||||
import tempfile
|
||||
|
||||
ROOT = pathlib.Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(ROOT))
|
||||
sys.path.insert(0, str(ROOT / "src"))
|
||||
sys.path.insert(0, str(ROOT / "tests"))
|
||||
|
||||
from test_web_app import FakeRepo, FakeRunner, signed_in # noqa: E402
|
||||
|
||||
CASES = []
|
||||
RUN = "20261022_140000"
|
||||
|
||||
|
||||
def case(fn):
|
||||
CASES.append(fn)
|
||||
return fn
|
||||
|
||||
|
||||
def ev(t, kind, **f):
|
||||
return {"t": f"2026-10-22T{t}.000", "kind": kind, **f}
|
||||
|
||||
|
||||
def _setup(events, runner=None, run_id=RUN):
|
||||
d = pathlib.Path(tempfile.mkdtemp())
|
||||
(d / f"events_{run_id}.jsonl").write_text(
|
||||
"".join(json.dumps(e) + "\n" for e in events), encoding="utf-8")
|
||||
shots = d / "Screenshots"
|
||||
shots.mkdir()
|
||||
client, _ = signed_in(FakeRepo(), runner or FakeRunner(), logs_dir=d,
|
||||
screenshots_root=shots)
|
||||
return client, d, shots
|
||||
|
||||
|
||||
START = ev("14:00:00", "run_start", source="portal", started_by="asha",
|
||||
mode="VERIFY", claim_limit=2, test_sheet="", pid=1, log="x")
|
||||
ERROR_RUN = [
|
||||
START,
|
||||
ev("14:00:01", "login_attempt_start", attempt=1),
|
||||
ev("14:00:10", "login_step", attempt=1, step="twofa_requested"),
|
||||
ev("14:00:52", "login_step", attempt=1, step="mail_received",
|
||||
mail_kind="link"),
|
||||
ev("14:01:00", "login_attempt_end", attempt=1, result="ok", message=""),
|
||||
ev("14:01:10", "queue", claims=["9001", "9002"]),
|
||||
ev("14:01:11", "claim_start", n=1, total=2, claim="9001"),
|
||||
ev("14:01:20", "claim_error", claim="9001", step="search",
|
||||
message="<b>timeout</b>"),
|
||||
ev("14:01:21", "claim_result", n=1, claim="9001", category="Review"),
|
||||
ev("14:01:22", "run_error", step="claims", exit_code=5,
|
||||
message="Target closed", traceback="Traceback (most recent)\nX"),
|
||||
ev("14:01:23", "run_end", exit_code=5),
|
||||
]
|
||||
|
||||
|
||||
@case
|
||||
def the_runs_list_shows_a_run_with_its_result_and_counts():
|
||||
client, _, _ = _setup(ERROR_RUN)
|
||||
page = client.get("/runs").text
|
||||
assert f'href="/runs/{RUN}"' in page, page
|
||||
assert "asha" in page and "Run Error" in page and "exit 5" in page
|
||||
assert "1 attempt · 59s" in page, "login count + time"
|
||||
|
||||
|
||||
@case
|
||||
def the_runs_list_filters_and_rejects_a_bad_filter():
|
||||
client, _, _ = _setup(ERROR_RUN)
|
||||
assert RUN not in client.get("/runs?status=Done").text
|
||||
assert RUN in client.get("/runs?status=Run+Error").text
|
||||
assert RUN not in client.get("/runs?day_from=2026-10-23").text
|
||||
assert client.get("/runs?day_from=yesterday").status_code == 400
|
||||
assert client.get("/runs?status=Bogus").status_code == 400
|
||||
|
||||
|
||||
@case
|
||||
def a_runs_page_has_login_errors_not_reached_and_timeline():
|
||||
client, _, _ = _setup(ERROR_RUN)
|
||||
page = client.get(f"/runs/{RUN}").text
|
||||
assert "eCW Login Attempts" in page and "42s" in page, "2FA wait"
|
||||
assert "Run Error" in page and "Traceback (most recent)" in page
|
||||
assert "Claim Errors (1)" in page
|
||||
assert "Not Reached (1)" in page and "9002" in page
|
||||
assert "Timeline" in page and "Run ended, exit 5" in page
|
||||
assert 'http-equiv="refresh"' not in page, "an ended run does not poll"
|
||||
|
||||
|
||||
@case
|
||||
def event_text_reaches_the_page_escaped():
|
||||
client, _, _ = _setup(ERROR_RUN)
|
||||
page = client.get(f"/runs/{RUN}").text
|
||||
assert "<b>timeout</b>" not in page
|
||||
assert "<b>timeout</b>" in page
|
||||
|
||||
|
||||
@case
|
||||
def a_running_run_refreshes_every_five_seconds():
|
||||
runner = FakeRunner(running=True)
|
||||
st = runner._st
|
||||
runner._st = lambda: {**st(), "log_path": rf"D:\x\Logs\run_{RUN}.log"}
|
||||
client, _, _ = _setup(ERROR_RUN[:7], runner=runner)
|
||||
page = client.get(f"/runs/{RUN}").text
|
||||
assert "Running" in page, page
|
||||
assert '<meta http-equiv="refresh" content="5">' in page
|
||||
|
||||
|
||||
@case
|
||||
def an_unknown_or_bad_run_is_404():
|
||||
client, _, _ = _setup(ERROR_RUN)
|
||||
assert client.get("/runs/20261022_140001").status_code == 404
|
||||
assert client.get("/runs/..%5C..%5Cx").status_code in (404, 400)
|
||||
|
||||
|
||||
@case
|
||||
def a_screenshot_is_served_and_nothing_else():
|
||||
d = pathlib.Path(tempfile.mkdtemp())
|
||||
client, d, shots = _setup([START])
|
||||
png = shots / "140100_failed.png"
|
||||
png.write_bytes(b"\x89PNG")
|
||||
(d / f"events_{RUN}.jsonl").write_text(
|
||||
json.dumps(START) + "\n" + json.dumps(
|
||||
ev("14:01:00", "screenshot", path=str(png), tag="failed")) + "\n",
|
||||
encoding="utf-8")
|
||||
r = client.get(f"/runs/{RUN}/shot/0")
|
||||
assert r.status_code == 200 and r.content == b"\x89PNG", r.status_code
|
||||
assert client.get(f"/runs/{RUN}/shot/1").status_code == 404
|
||||
page = client.get(f"/runs/{RUN}").text
|
||||
assert f'/runs/{RUN}/shot/0' in page
|
||||
|
||||
|
||||
@case
|
||||
def every_page_links_the_runs_page_and_carries_no_inline_style():
|
||||
client, _, _ = _setup(ERROR_RUN)
|
||||
for path in ("/", "/runs", f"/runs/{RUN}"):
|
||||
page = client.get(path).text
|
||||
assert 'href="/runs"' in page, path
|
||||
assert " style=" not in page, path
|
||||
|
||||
|
||||
def main() -> int:
|
||||
failures = 0
|
||||
for fn in CASES:
|
||||
name = fn.__name__.replace("_", " ")
|
||||
try:
|
||||
fn()
|
||||
print(f" ok {name}")
|
||||
except AssertionError as exc:
|
||||
print(f" FAIL {name}: {exc}")
|
||||
failures += 1
|
||||
except Exception as exc:
|
||||
print(f" ERROR {name}: {type(exc).__name__}: {exc}")
|
||||
failures += 1
|
||||
print(f"\n{len(CASES) - failures}/{len(CASES)} passed")
|
||||
return 1 if failures else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in new issue
Block a user