| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182 |
- """Coverage for bundled fictional bill files and the explicit CSV CLI."""
- from __future__ import annotations
- from pathlib import Path
- import pytest
- from main import _parse_goal, parser
- from src.agents.coordinator import MoneyMirrorCoordinator
- from src.tools import CSVImportTool
- from .fakes import FakeRuntime
- ROOT = Path(__file__).resolve().parents[1]
- SAMPLE_FILES = tuple(ROOT / "data" / f"sample_{index:02d}.csv" for index in range(1, 6))
- def test_all_bundled_sample_files_exist_and_import() -> None:
- importer = CSVImportTool()
- assert len(SAMPLE_FILES) == 5
- for index, path in enumerate(SAMPLE_FILES, start=1):
- assert path.is_file(), f"sample-{index:02d} is missing its CSV: {path}"
- transactions = importer.load(path)
- assert transactions, f"sample-{index:02d} should contain transactions"
- assert not importer.last_errors, f"sample-{index:02d} has invalid rows: {importer.last_errors}"
- def test_samples_produce_distinct_data_grounded_quest_signals(tmp_path) -> None:
- quest_ids: dict[str, set[str]] = {}
- subscription_merchants: dict[str, set[str]] = {}
- for index, path in enumerate(SAMPLE_FILES, start=1):
- sample_id = f"sample-{index:02d}"
- coordinator = MoneyMirrorCoordinator(tmp_path / f"{sample_id}.db", runtime=FakeRuntime())
- try:
- report = coordinator.analyze_csv(path)
- assert report.summary["income"] > 0
- assert report.summary["expense"] > 0
- assert report.quests
- quest_ids[sample_id] = {quest.quest_id for quest in report.quests}
- subscription_merchants[sample_id] = {item["merchant"] for item in report.subscriptions}
- finally:
- coordinator.close()
- assert "weekend_wallet_shield" in quest_ids["sample-03"]
- assert "payday_cooldown" in quest_ids["sample-04"]
- assert "learning_loot_log" in quest_ids["sample-02"]
- assert "subscription_hunter" in quest_ids["sample-05"]
- assert "房东-六月房租" not in subscription_merchants["sample-04"]
- assert "腾讯视频会员" in subscription_merchants["sample-05"]
- assert len({tuple(sorted(ids)) for ids in quest_ids.values()}) >= 4
- def test_csv_path_is_required_and_demo_flag_is_removed() -> None:
- command = parser()
- args = command.parse_args(["--csv", "bill.csv"])
- assert args.csv == Path("bill.csv")
- assert args.interactive is False
- assert not hasattr(args, "demo")
- interactive_args = command.parse_args(["--interactive", "--csv", "bill.csv"])
- assert interactive_args.interactive is True
- with pytest.raises(SystemExit):
- command.parse_args([])
- with pytest.raises(SystemExit):
- command.parse_args(["--demo", "--csv", "bill.csv"])
- def test_cli_goal_parsing_is_explicit_and_validated() -> None:
- travel = _parse_goal("三个月旅行基金|travel|10000|2800|2026-10-31")
- assert travel.goal_type == "travel"
- assert travel.target_amount == 10000
- assert travel.current_amount == 2800
- category = _parse_goal("本月娱乐限额|category_limit|800|0|2026-08-31|娱乐|800")
- assert category.category == "娱乐"
- assert category.monthly_limit == 800
- with pytest.raises(ValueError, match="格式"):
- _parse_goal("格式不完整|travel")
- with pytest.raises(ValueError, match="category_limit"):
- _parse_goal("娱乐限额|category_limit|800|0|2026-08-31")
|