Files
mt5cli/tests/test_examples.py
Daichi Narushima a5c2aa8e8f test: consolidate duplicate test cases into parameterized tests (#91)
* test: consolidate duplicate test cases into parameterized tests

- Combine test_symbol_info, test_symbol_info_tick, test_market_book into test_symbol_command with parametrize
- Combine test_order_check variants into test_order_request with parametrize
- Combine test_order_send variants into test_order_request with parametrize
- Consolidate grafana view skip tests into test_grafana_view_skipped_when_required_cols_missing
- Consolidate grafana index skip tests into test_index_skipped_when_cols_missing
- Consolidate _NoOp method tests into test_method_is_noop
- Consolidate record_*_update noop tests into test_record_update_noop_before_configure

This improves test maintainability by reducing duplication while maintaining coverage.

* test: parametrize remaining duplicated cases

* test: parameterize remaining duplicate test cases

* test: parameterize remaining duplicate test cases

* test: parameterize remaining duplicate test cases

* test: parameterize remaining duplicate test cases

* test: parameterize count and start_pos validation tests

* test: parameterize remaining duplicate test cases

- tests/test_utils.py: merge valid string/integer cases for
  parse_timeframe and parse_tick_flags.
- tests/test_cli.py: consolidate collect-history --if-exists append/fail
  tests and drop the duplicate collect-history --dataset ticks default
  flags=-1 assertion.
- tests/test_trading.py: parametrize buy/sell projected margin ratio,
  invalid place_market_order mode validation, core
  calculate_volume_by_margin boundary cases, and zero-ratio
  calculate_margin_and_volume sizing cases.

* test: parameterize remaining duplicate test cases

Consolidate the last high-value duplicate test cases outside the
already-changed areas while keeping semantics and 100% coverage.

- tests/test_sdk.py:TestMt5CliClient - merge copy_rates_range,
  copy_ticks_from, history_orders, and latest_rates delegation/
  normalization tests into test_method_delegates_with_normalization
  parameterized by call/expected_method/expected_kwargs with readable
  ids for each normalization path.
- tests/test_sdk.py:TestSubstituteEnvPlaceholders - merge
  brace/whole-dollar/plain/partial substitution cases into
  test_substitute_env_placeholders, and missing-env cases into
  test_substitute_env_placeholders_raises_on_missing_env.
- tests/test_trading.py:TestVolumeAndExecution - merge buy/sell
  trailing-stop main cases into test_calculate_trailing_stop_updates_by_side,
  opposite-side invalid-quote cases into
  test_calculate_trailing_stop_updates_ignores_opposite_side_price,
  and the two mixed-positions scenarios into the parametrized
  test_calculate_trailing_stop_updates_mixed_positions_skip_invalid_side.
  All ids explicitly describe the side and behavior.

* test: parameterize collect-history and telemetry update tests

Consolidate duplicate default-vs-ticks collect-history cases in CLI and
SDK tests, and merge history/snapshot record_*_update success and
failure tests in test_telemetry.py.

Co-authored-by: Cursor <cursoragent@cursor.com>

* test: parameterize telemetry record_*_state gauge tests

Co-authored-by: Cursor <cursoragent@cursor.com>

* test: parameterize Grafana example file checks

* test: centralize remaining parametrized cases

* test: add parametrized replacements for remaining cases

* test: fix remaining parametrized lint

* Fix Ruff warnings

* test: remove duplicate parametrization sweep file

* test: remove duplicate-test deselection hook

* test: restore conftest formatting

* test: add remaining parametrized regression coverage

* test: fix lint issues in parametrization completion tests

* test: avoid dynamic SQL in parametrization completion tests

* test: format parametrization completion tests

* test: fix lint and pyright issues in parametrization coverage

* Fix a Pyright error

* Update .agents/skills/pr-feedback-triage/SKILL.md

* test: dissolve parametrize-completion catch-all into owning test files

Move the remaining consolidated cases from test_parametrize_completion.py
into their owning modules and delete the file:

- test_cli.py: merge snapshot/grafana-schema --publish-copy gating into
  one parametrized test.
- test_trading.py: merge calculate_spread_ratio numeric/numeric-string
  cases; fold non-finite volume_max into
  test_normalize_order_volume_deterministic; merge estimate_order_margin
  invalid-volume cases (0.0/nan/inf); merge calculate_positions_margin
  invalid-row filtering cases.
- test_history.py: parametrize resolve_history_tick_flags and
  resolve_granularity_name.
- test_sdk.py: parametrize collect_latest_closed_rates_for_accounts
  empty-effective-frame cases.
- test_grafana.py: merge account/terminal snapshot insert tests inside
  TestSnapshotInserts.

All remaining cases in the removed file were exact duplicates of
existing coverage, so no new test scenarios are introduced. 100%
coverage and all checks (ruff, pyright, pytest, local-qa) pass.

* test: parameterize remaining account-event, snapshot, and CLI edge cases

Consolidates six more pairs of duplicate tests flagged during PR #91
review into pytest.mark.parametrize tables (incremental history_deals
account-event edge cases, dedup scope column variants, snapshot view
status filtering, snapshot kwargs forwarding, injected-client
lifecycle, and close-positions --yes gate), preserving explicit ids
and 100% coverage.

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

* test: consolidate build_config login coercion into parametrized cases

Fold separate backward-compat and env-expansion tests into named
pytest.param rows for clearer failure output.

Co-authored-by: Cursor <cursoragent@cursor.com>

* test: consolidate deduplication and publish test cases into parametrized variants

Merge related test cases into single parametrized tests:
- Consolidate collect-history validation tests into a single parametrized case
- Merge snapshot-view skip conditions into one test with parametrized inputs
- Combine publish_grafana_copy target variations into one parametrized test
- Consolidate incremental-start and drop-duplicates error cases
- Remove duplicate test_drop_duplicates_rejects_invalid_identifiers

Reduces test file duplication while maintaining full coverage.

* test: consolidate remaining review-identified parameterized duplicates

Merge close-position filter, margin-ratio suppress/reraise, SQLite path
validation, and grafana_symbol_pnl schema cases into clearer parametrized tests.

Co-authored-by: Cursor <cursoragent@cursor.com>

* test: restore default if_exists coverage in SQLite append test

The default-append parametrized case now omits if_exists so the API default
remains exercised instead of passing IfExists.APPEND explicitly.

Co-authored-by: Cursor <cursoragent@cursor.com>

* test: tighten trading parametrization cleanup

* test: consolidate remaining parameterized cases

* test: consolidate additional duplicate cases into parametrized tests

Merge overlapping success-path tests in contracts, history, SDK, and
trading modules while preserving 100% coverage and explicit test ids.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: agent <agent@localhost>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-03 03:54:26 +09:00

93 lines
3.4 KiB
Python

"""Tests for example files in examples/grafana/."""
from __future__ import annotations
import json
from pathlib import Path
from typing import TYPE_CHECKING
import pytest
if TYPE_CHECKING:
from collections.abc import Iterator
_EXAMPLES_DIR = Path(__file__).parent.parent / "examples" / "grafana"
_DASHBOARDS_DIR = _EXAMPLES_DIR / "dashboards"
def _dashboard_json_files() -> list[Path]:
"""Return bundled Grafana dashboard JSON files in deterministic order."""
return sorted(_DASHBOARDS_DIR.glob("*.json"))
@pytest.fixture(params=_dashboard_json_files(), ids=lambda path: path.name)
def dashboard_path(request: pytest.FixtureRequest) -> Iterator[Path]:
"""Yield one bundled Grafana dashboard JSON file per test case."""
return request.param
class TestGrafanaExamples:
"""Validate structure and content of bundled Grafana example files."""
def test_dashboard_json_files_are_present(self) -> None:
"""At least one dashboard JSON file is bundled."""
assert _dashboard_json_files(), "No dashboard JSON files found"
def test_dashboard_json_file_is_valid_json(self, dashboard_path: Path) -> None:
"""Each dashboard JSON file parses to a JSON object."""
content = dashboard_path.read_text(encoding="utf-8")
obj = json.loads(content)
assert isinstance(obj, dict), (
f"{dashboard_path.name} root must be a JSON object"
)
@pytest.mark.parametrize("private_pattern", ["password", "api_key", "apikey"])
def test_dashboard_json_has_no_private_placeholders(
self,
dashboard_path: Path,
private_pattern: str,
) -> None:
"""Dashboard JSON files contain no obvious credential placeholders."""
content = dashboard_path.read_text(encoding="utf-8").lower()
assert private_pattern not in content, (
f"{dashboard_path.name} contains {private_pattern!r}"
)
def test_dashboard_json_uses_grafana_views(self, dashboard_path: Path) -> None:
"""Each dashboard JSON file queries grafana_* views."""
content = dashboard_path.read_text(encoding="utf-8")
assert "grafana_" in content, (
f"{dashboard_path.name} must contain queries against grafana_* views"
)
@pytest.mark.parametrize("field", ["uid", "title"])
def test_dashboard_json_has_required_field(
self,
dashboard_path: Path,
field: str,
) -> None:
"""Each dashboard JSON file has a non-empty uid and title field."""
obj = json.loads(dashboard_path.read_text(encoding="utf-8"))
assert obj.get(field), f"{dashboard_path.name} must have a {field}"
def test_expected_dashboards_present(self) -> None:
"""The three expected dashboard files are present."""
names = {p.name for p in _dashboard_json_files()}
assert "mt5cli-overview.json" in names
assert "mt5cli-trades.json" in names
assert "mt5cli-market.json" in names
@pytest.mark.parametrize(
"relative_path",
[
"README.md",
"compose.yml",
"provisioning/datasources/mt5cli-sqlite.yml",
"provisioning/dashboards/mt5cli.yml",
],
ids=["readme", "compose", "datasource-provisioning", "dashboard-provisioning"],
)
def test_expected_file_exists(self, relative_path: str) -> None:
"""Bundled Grafana example support files are present."""
assert (_EXAMPLES_DIR / relative_path).is_file()