Skip to content

test(TEST-007): render-path tests for fundamentals_panel + scan_view#98

Merged
DoRmAmMu1997 merged 2 commits into
mainfrom
test/test-007-ui-render-tests
Jul 11, 2026
Merged

test(TEST-007): render-path tests for fundamentals_panel + scan_view#98
DoRmAmMu1997 merged 2 commits into
mainfrom
test/test-007-ui-render-tests

Conversation

@DoRmAmMu1997

@DoRmAmMu1997 DoRmAmMu1997 commented Jul 10, 2026

Copy link
Copy Markdown
Owner

Ticket scope (TEST-007)

Goal: close the test gap left by REF-002 (#97) — ui/fundamentals_panel.py (271 lines) and ui/scan_view.py (200 lines) landed with zero direct tests, dipping measured coverage 89.15% → 88.07% against the 87% CI floor.

In scope

  • Direct render-path tests for every public/render helper in both modules, using the house pattern ("UI tests monkeypatch the module that actually reads st" — AGENTS.md §10): a per-file _FakeStreamlit recording fake set onto the module's own st global.
  • Exercising the REAL helpers wherever practical (_is_eligible_for_fundamentals, _format_data_freshness, _render_results_with_chart, _has_rating_column); faking only true externals (agent, load_universe, chart builder, audit recorder).
  • Locking the security-relevant behaviors these modules own: the AUTH-003 export gate, the OBS-003 export audit event, and secret redaction of failure/agent messages.

Out of scope (deliberate)

  • No production code changes — if a test needed a source change, the test was reshaped instead.
  • No app.py orchestration tests (already covered by tests/test_app_orchestration.py; its re-export identity and _eligible_symbols_set.clear() seams are preserved untouched).
  • Further app.py slimming (that is the follow-up ticket REF-003, next PR in this wave).

This is ticket 1 of a 10-ticket second improvement wave (TEST-007, REF-003, AI-006, IPO-006, TEST-006, PERF-002, DEPLOY-004, QUAL-006, QUAL-007, DOC-003), each shipped as its own PR off main.

Summary

REF-002 (#97) moved ~470 lines of rendering out of app.py into ui/fundamentals_panel.py and ui/scan_view.py. Both modules count toward the 87% coverage floor but had no direct tests. This PR adds render-path tests for both modules and restores the headroom.

Test-only change: two new files, no production code touched.

What's covered

tests/test_app_fundamentals_panel.py

  • Panel hidden without a selected symbol.
  • Criteria-mode vs universal-mode captions, including the UI-002 "why is this symbol different" wording.
  • Cached-verdict flow: primary button disabled, "Re-run analysis" secondary, session-cache provenance caption, humanized freshness (Data fetched: 06 Jul 2026, 08:15 UTC).
  • Click-through runs the agent in the symbol-deterministic mode and caches the verdict under fundamentals_verdict::{symbol}::{model}::{mode}.
  • FundamentalsUsageLimitError → gentle warning, not an error.
  • Agent failure → redacted error message.
  • Invalid cached verdict is cleared and re-rendered clean.
  • Eligibility on the REAL _is_eligible_for_fundamentals: union across hemant_super_45 + nifty_100, tolerance for a missing universe CSV — with _eligible_symbols_set.clear() around every test so the st.cache_data state can't leak into other suites.
  • All four _format_data_freshness branches.

tests/test_app_scan_view.py

  • Empty-results branch (warning shown, chart/export/fundamentals all skipped, diagnostics still render).
  • Happy path chains the charted symbol into the fundamentals panel.
  • AUTH-003 export gate: a viewer never reaches st.download_button or the CSV bytes build.
  • OBS-003: export click records the audit event (user email, row count, file name).
  • Failure expanders render fetch/compute failures with redacted messages.
  • The two-widget table/dropdown chart sync on the REAL _render_results_with_chart: fresh table click wins, stale (persistent) selection does not clobber a fresh dropdown pick, a dropped symbol falls back to the first row, and no-symbol-column / no-chart-builder return None.
  • _has_rating_column truth table.

Gates (all green, local, Python 3.13)

Gate Result
pytest -q --cov=backend --cov=screeners --cov=ui --cov-fail-under=87 1,411 passed; coverage 88.07% → 89.34%
pre_commit validate-config clean
compileall clean
ruff check clean
mypy (119 files) clean
bandit clean
pip_audit -r constraints.txt no known vulnerabilities

constraints.txt / pyproject.toml diff vs main: empty (as required).

Review

/code-review: Approve — verified the five risk points (cache_data leakage handled by explicit clear(), button-response queue ordering, dataframes[-2:] safety, namespace patching of imported names, chart-sync state transitions). /security-review: no findings (test-only diff; adds locks on the AUTH-003 gate, OBS-003 audit event, and secret redaction).

🤖 Generated with Claude Code

REF-002 (#97) moved ~470 lines of rendering into ui/fundamentals_panel.py
and ui/scan_view.py, which count toward the 87% coverage floor but had no
direct tests (measured coverage dipped 89.15 -> 88.07). These tests cover
the real render paths with the house pattern (monkeypatch the module's own
`st` global with a recording fake):

- fundamentals_panel: hidden without a symbol; criteria vs universal
  captions (incl. the UI-002 "why is this symbol different" wording);
  cached-verdict flow (disabled primary, Re-run button, session-cache
  provenance caption, humanized freshness); click-through runs the agent
  in the symbol-deterministic mode and caches the verdict; usage-limit ->
  gentle warning; agent failure -> redacted error; invalid cached verdict
  cleared; eligibility union/missing-universe behavior on the REAL helper
  (faked load_universe, cache cleared around each test); the four
  _format_data_freshness branches.
- scan_view: empty-results branch; happy path chains chart symbol into the
  fundamentals panel; AUTH-003 export gate (viewer never reaches the
  button); export click records the OBS-003 audit event; failure expanders
  render with redacted messages; and the two-widget table/dropdown chart
  sync (fresh click wins, stale selection does not, dropped symbol falls
  back to first, no-chart/no-symbol returns None).

Coverage: 88.07 -> 89.34% (floor 87); 1,411 tests pass. Gates: pre-commit
validate, compileall, ruff, mypy (119 files), bandit, pip-audit all clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@DoRmAmMu1997 DoRmAmMu1997 left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-review (posted as COMMENT — the bot runs as the PR author and cannot APPROVE). Verdict: Approve.

Risk points checked against the real modules:

  1. st.cache_data leakage_eligible_symbols_set is a real @st.cache_data(ttl=600) function; the eligibility tests call the REAL helper, so its cache is cleared both before each test and in a finally: block. Without that, a cached universe set from these tests could bleed into any later suite that touches eligibility. Verified the full 1,411-test run passes in one process.
  2. Button-response queue ordering_FakeColumn.button pops from a per-test queue; the panel renders primary-then-secondary deterministically (fundamentals_panel.py:126-144), so queue order is stable. Asserted on recorded labels, not just positions.
  3. fake_st.dataframes[-2:] — safe because the failure-expander test renders exactly the results frame plus the two failure frames; the slice can't pick up the results table since assertions check the message column, which only the failure frames have.
  4. Namespace patchingrecord_audit_event, _redact_secrets, _render_fundamentals_panel, and _render_cached_symbol_chart are patched on ui.scan_view / ui.fundamentals_panel (the namespaces that read them), matching the house pattern; the originals are untouched for other suites.
  5. Chart-sync state machine — the four transitions (default-to-first, fresh-click-wins, stale-selection-ignored, dropped-symbol-fallback) mirror scan_view.py:198-225 exactly, including the chart_prev_table_row_{key} bookkeeping that distinguishes a fresh click from a persistent selection.

Gates: 1,411 passed, coverage 89.34% (floor 87), ruff/mypy/bandit/pip-audit/pre-commit/compileall clean. No production code touched; constraints/pyproject diff vs main is empty.

Copy link
Copy Markdown
Owner Author

Codex review plan before fixes:

  • Track the selected chart symbol as well as the selected row index so a reordered result table cannot display a chart for the wrong stock.
  • Redact cached-verdict validation failures before they cross into Streamlit.
  • Add real re-run-button coverage proving force_refresh=True, cache replacement, and refreshed rendering, and strengthen the secret-redaction assertions.

I’ll implement these on this branch with TDD, run the full repository/browser/security gates, push the follow-up, and report the final evidence here.

@DoRmAmMu1997 DoRmAmMu1997 left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex review findings for TEST-007. These are reproducible behavior/security gaps in the paths this test PR is intended to protect.

Comment thread tests/test_app_scan_view.py
Comment thread tests/test_app_fundamentals_panel.py
Comment thread tests/test_app_fundamentals_panel.py
Co-authored-by: Hemant <hemantdhamija@gmail.com>
Co-authored-by: Codex <codex@openai.com>
@DoRmAmMu1997 DoRmAmMu1997 merged commit a2ed75c into main Jul 11, 2026
3 checks passed
@DoRmAmMu1997 DoRmAmMu1997 deleted the test/test-007-ui-render-tests branch July 11, 2026 13:21
@DoRmAmMu1997

Copy link
Copy Markdown
Owner Author

Completion verdict

Complete — no remaining reportable findings. This is a completion comment, not an approval.

Commit a7be039 fixed symbol-stable chart selection when result ordering changes, redacted cached and agent errors, and made force-refresh perform a real rerun. Verification completed with 1,413 tests passed, 89.10% coverage, desktop and mobile browser QA, confirmation that the secret sentinel was absent, and green hosted CI.

A formal diff-security review found no remaining reportable findings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant