T4: Repository pattern — ModelRepository, JsonFileRepository, ExcelRepository

- application/repositories/model_repository.py: ModelRepository Protocol
  with load(path) and save(model, path)
- infrastructure/repositories/json_file_repository.py: JsonFileRepository
  (вынесен из FinancialModel.save/load)
- infrastructure/repositories/excel_repository.py: ExcelRepository
  (бывший ExcelSync, переименован)
- FinancialModel.save/load удалены — теперь только данные
- infrastructure/sync/ удалён (ExcelSync перенесён в repositories/)
- infrastructure/cli/main.py и config.py используют JsonFileRepository +
  ExcelRepository через локальные singletons
- test_excel_sync.py переименован в test для ExcelRepository
- Новый test_repositories.py: 5 тестов для JsonFileRepository + протокол
- test_model.py: использован JsonFileRepository

Tests: 68/68 pass (было 63 + 5 новых).
This commit is contained in:
2026-10-08 16:50:06 +03:00
parent 363d440d53
commit 3f7268d14c
12 changed files with 190 additions and 92 deletions
+56
View File
@@ -0,0 +1,56 @@
from decimal import Decimal
from pathlib import Path
from domain import Account, Asset, FinancialModel, Transaction
from application.repositories import ModelRepository
from infrastructure.repositories import JsonFileRepository
class TestJsonFileRepository:
def test_save_load_roundtrip(self, tmp_path: Path):
repo = JsonFileRepository()
model = FinancialModel(
accounts=[Account(name="Test", balance=Decimal("100.50"))],
transactions=[Transaction(amount=Decimal("33.33"), category="income")],
)
p = tmp_path / "model.json"
repo.save(model, p)
assert p.exists()
loaded = repo.load(p)
assert len(loaded.accounts) == 1
assert loaded.accounts[0].name == "Test"
assert loaded.accounts[0].balance == Decimal("100.50")
assert loaded.transactions[0].amount == Decimal("33.33")
def test_version_field_persisted(self, tmp_path: Path):
repo = JsonFileRepository()
model = FinancialModel()
p = tmp_path / "v.json"
repo.save(model, p)
import json
with open(p) as f:
data = json.load(f)
assert data["version"] == 1
def test_creates_parent_dirs(self, tmp_path: Path):
repo = JsonFileRepository()
model = FinancialModel()
p = tmp_path / "nested" / "dir" / "model.json"
repo.save(model, p)
assert p.exists()
def test_implements_protocol(self):
repo = JsonFileRepository()
assert isinstance(repo, ModelRepository)
def test_decimal_preserved(self, tmp_path: Path):
repo = JsonFileRepository()
model = FinancialModel(
assets=[Asset(name="Stocks", value=Decimal("12345.67"), growth_rate=Decimal("8.5"))]
)
p = tmp_path / "decimal.json"
repo.save(model, p)
loaded = repo.load(p)
assert loaded.assets[0].value == Decimal("12345.67")
assert loaded.assets[0].growth_rate == Decimal("8.5")