Skip to content

test: mirror data/ingestion tests + fix layout parity (#55) - #58

Merged
alihaskar merged 5 commits into
alihaskar:masterfrom
tschm:fix-test-layout-55
Jul 24, 2026
Merged

alihaskar merged 5 commits into
alihaskar:masterfrom
tschm:fix-test-layout-55

Conversation

@tschm

@tschm tschm commented Jul 20, 2026 •

Copy link
Copy Markdown
Contributor

Fixes the check_test_layout.py parity failure for the data package.

Root cause

The migration to the mirrored tests/<pkg>/** layout created mirrors for api and core but never created the data mirror — the flat tests/test_data_ingestion.py and tests/test_data_slicing.py were deleted without replacement, so data/ingestion.py had no test file at all.

Two problems compounded it:

  1. Missing mirror. Restored both deleted suites, merged into a single tests/pycharting/data/test_ingestion.py. Organized to satisfy the checker's class-parity rule (each Test<Class> must map to a source class):

    • TestDataValidationError — every case that raises DataValidationError
    • TestDataManager — construction, properties, repr, and get_chunk slicing (folded in, since get_chunk is a DataManager method)
    • module-level functions — validate_input behaviour (it's a free function, so unconstrained)
  2. .gitignore swallowed the dir. An unanchored data/ rule (meant for local sample data) matched tests/pycharting/data/ — which is why the mirror could never be committed. Anchored it to /data/.

Verification

  • check_test_layout.py → exits 0 ("tests mirror sources 1:1")
  • Full suite: 146 passed, coverage 100% (data/ingestion.py 181/181)
  • No module is tested by two files (the flat duplicate is gone).

Closes #55

Also in this PR

Merged #57 and addressed three further quality issues (see the PR comment for the full summary):

Closes #45
Closes #47
Closes #49

🤖 Generated with Claude Code

tschm and others added 3 commits July 20, 2026 11:33
…haskar#56, alihaskar#50)

- mkdocs.yml: replace placeholder site_description with the real
  description from pyproject.toml (alihaskar#56)
- interface.py: split the broad except in plot() so DataValidationError
  is reported with stage="validation" (fixable input) separately from
  server/runtime failures reported with stage="server" (alihaskar#50)
- test_interface.py: assert the validation stage is reported

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Splitting the broad except in plot() left the generic (server/runtime)
except branch unexercised once validation errors took their own branch,
dropping interface.py coverage below 100%. Add a test that forces a
ChartServer startup failure and asserts stage="server".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The migration to the mirrored tests/<pkg>/** layout never created the
data package mirror, so check_test_layout.py failed with a missing
tests/pycharting/data/test_ingestion.py. Two problems compounded it:

- The flat tests/test_data_ingestion.py and tests/test_data_slicing.py
  were deleted in the migration without a mirrored replacement. Restore
  them, merged into tests/pycharting/data/test_ingestion.py with the
  class layout the checker enforces: TestDataValidationError and
  TestDataManager (get_chunk slicing folded in), plus module-level
  functions for validate_input behaviour.
- .gitignore had an unanchored `data/` rule (for local sample data)
  that silently ignored tests/pycharting/data/. Anchor it to `/data/`.

check_test_layout.py now exits 0 and data/ingestion.py stays at 100%.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 20, 2026 08:10

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Restores test-layout parity for pycharting.data by reintroducing a mirrored ingestion test suite under tests/pycharting/data/ and fixing a .gitignore rule that previously prevented that directory from being committed.

Changes:

  • Add tests/pycharting/data/test_ingestion.py to mirror and cover src/pycharting/data/ingestion.py (validation + DataManager.get_chunk).
  • Add tests/pycharting/data/__init__.py to complete the mirrored test package structure.
  • Anchor the .gitignore data/ rule to /data/ so it no longer matches tests/pycharting/data/.

Reviewed changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated 3 comments.

File Description
tests/pycharting/data/test_ingestion.py Adds the missing mirrored ingestion/validation/slicing test suite for data/ingestion.py.
tests/pycharting/data/init.py Creates the mirrored tests.pycharting.data test package.
.gitignore Anchors data/ ignore rule to the repo root to avoid ignoring the mirrored tests directory.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


# Verify timestamps are in the correct range (milliseconds since epoch)
# For 2024-01-01, timestamps should be around 1704067200000 (ms)
expected_first_ts = int(pd.Timestamp("2024-01-01").timestamp() * 1000)
Comment on lines +722 to +741
def test_get_chunk_performance_large_dataset(self):
"""Test performance with large dataset (100k points)."""
n = 100000
index = np.arange(n)
open_data = np.random.uniform(100, 200, n)
high = open_data + np.random.uniform(0, 10, n)
low = open_data - np.random.uniform(0, 10, n)
close = np.random.uniform(low, high)

dm = DataManager(index, open_data, high, low, close)

# Measure slicing performance
start_time = time.time()
chunk = dm.get_chunk(10000, 20000) # 10k points
elapsed_ms = (time.time() - start_time) * 1000

# Should be well under 100ms for 10k points
assert elapsed_ms < 100, f"Slicing took {elapsed_ms:.2f}ms, expected <100ms"
assert len(chunk["index"]) == 10000

Comment on lines +742 to +761
def test_get_chunk_performance_small_slice_large_dataset(self):
"""Test performance of small slice from large dataset."""
n = 100000
index = np.arange(n)
open_data = np.random.uniform(100, 200, n)
high = open_data + np.random.uniform(0, 10, n)
low = open_data - np.random.uniform(0, 10, n)
close = np.random.uniform(low, high)

dm = DataManager(index, open_data, high, low, close)

# Small slice should be extremely fast
start_time = time.time()
chunk = dm.get_chunk(50000, 50100) # Just 100 points
elapsed_ms = (time.time() - start_time) * 1000

# Should be very fast (< 10ms)
assert elapsed_ms < 10, f"Small slice took {elapsed_ms:.2f}ms, expected <10ms"
assert len(chunk["index"]) == 100

tschm and others added 2 commits July 20, 2026 12:14
…alihaskar#45, alihaskar#47, alihaskar#49)

alihaskar#45 — Test the pragma-excluded websocket/auto-shutdown paths
Removed the 5 `# pragma: no cover` markers on the concurrency-critical paths
(websocket heartbeat, _monitor_connection stale/disconnect branches, _run_server
exception handler, and plot()'s blocking loop) and covered them with tests that
drive the handlers directly — no real sockets or timing races. Coverage 100%.

alihaskar#47 — Eliminate test-suite flakiness (xdist worker crash, sleep-based readiness)
Added tests/pycharting/conftest.py with:
- a `wait_until` polling helper replacing fixed `time.sleep` readiness waits, and
- a `FakeUvicornServer` / `fake_uvicorn` fixture so behaviour tests exercise the
  full ChartServer lifecycle without binding real ports.
Only the two genuine integration tests still start a real server (now waited on
via health polling). Suite went from ~88s to ~9s and passes 10/10 consecutive
runs under `-n auto` with no worker crashes.

alihaskar#49 — Type the session registry and route responses
- `_data_managers` is now `dict[str, DataManager]`.
- Route responses use concrete Pydantic models (InitDataResponse, SessionInfo,
  SessionListResponse, DeleteSessionResponse, StatusResponse).
- `plot()` returns a `PlotResult` TypedDict and `get_server_status()` a
  `ServerStatus` TypedDict instead of `dict[str, Any]`.
This surfaced (and fixed) an unchecked-None in `_repr_html_`. `ty` stays clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@tschm

tschm commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

Updated: merged #57 + addressed #45, #47, #49

This branch now contains the original data/ingestion test-layout fix (#55), the merge of #57, and fixes for three further quality issues.

Merged #57 (no conflicts) — brings in #56 (mkdocs description), #50 (staged plot() error handling), #54.

#49 — Type the API boundary

  • _data_managers → dict[str, DataManager]
  • Route responses use concrete Pydantic models (InitDataResponse, SessionInfo, SessionListResponse, DeleteSessionResponse, StatusResponse)
  • plot() → PlotResult TypedDict; get_server_status() → ServerStatus TypedDict (was dict[str, Any])
  • Surfaced and fixed an unchecked-None in _repr_html_. ty stays clean.

#45 — Cover the pragma-excluded websocket/auto-shutdown paths

  • Removed all 5 # pragma: no cover markers on the concurrency-critical paths: websocket heartbeat, _monitor_connection (stale + disconnect branches), _run_server exception handler, and plot()'s blocking loop
  • Covered them by driving the handlers directly (scripted fake websocket, direct _monitor_connection calls with patched sleep, fake shutdown events) — no real sockets, no timing races

#47 — Eliminate test-suite flakiness

  • New tests/pycharting/conftest.py: a wait_until polling helper (replaces fixed time.sleep readiness) and a FakeUvicornServer / fake_uvicorn fixture so behaviour tests exercise the full ChartServer lifecycle without binding real ports
  • Only the two genuine integration tests still start a real server (now health-polled)

Verification

  • make test: 154 passed, coverage 100% (≥90 gate met)
  • 10/10 consecutive -n auto runs, all exit 0, no worker crashes
  • Suite runtime ~88s → ~9s
  • ty clean · ruff check + format clean · pre-commit hooks pass

Closes #45
Closes #47
Closes #49

🤖 Generated with Claude Code

@alihaskar
alihaskar merged commit 421e38f into alihaskar:master Jul 24, 2026
26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants