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:
ManikandanandClaude Opus 5.5 committed 2026-10-23 18:38:17 +05:30
commit ba15a574ab
205 files changed
+56772

No files matched your search

+168
View File
@@ -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 "&lt;b&gt;timeout&lt;/b&gt;" 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())