common-cents-tui/tests/test_csv.py
Joe Arndt ac93e6074b init
2026-05-06 18:25:17 -05:00

82 lines
2.5 KiB
Python

from pathlib import Path
from common_cents.csv_export import write_csv
from common_cents.csv_import import parse_csv
def _row(date, cents, cat, merch=None, notes=None, tags=None):
return {
"date": date,
"cents": cents,
"category": cat,
"merchant": merch,
"notes": notes,
"tags": tags or [],
}
def test_export_then_import_roundtrip(tmp_path: Path):
rows = [
_row("2026-01-01", 100, "A", "M", None, ["x", "y"]),
_row("2026-01-02", 200, "B", None, 'Hello, "world"'),
_row("2026-01-03", 300, "C:D", None, None, ["t1", "t2", "t3"]),
]
p = tmp_path / "out.csv"
write_csv(p, rows)
result = parse_csv(p)
assert result.errors == []
assert len(result.rows) == 3
assert result.rows[0].tags == ["x", "y"]
assert result.rows[1].notes == 'Hello, "world"'
assert result.rows[2].tags == ["t1", "t2", "t3"]
def test_parse_csv_missing_required(tmp_path: Path):
p = tmp_path / "bad.csv"
p.write_text("DATE,MERCHANT\n2026-01-01,Joe\n")
result = parse_csv(p)
assert result.errors
assert "CATEGORY" in result.errors[0]
assert "CENTS" in result.errors[0]
def test_parse_csv_missing_optional_warns(tmp_path: Path):
p = tmp_path / "ok.csv"
p.write_text("DATE,CENTS,CATEGORY,MERCHANT\n2026-01-01,100,Food,Joe\n")
result = parse_csv(p)
assert result.errors == []
assert any("NOTES" in w and "TAGS" in w for w in result.warnings)
assert len(result.rows) == 1
def test_parse_csv_invalid_date(tmp_path: Path):
p = tmp_path / "bad.csv"
p.write_text("DATE,CENTS,CATEGORY,MERCHANT\nnot-a-date,100,Food,\n")
result = parse_csv(p)
assert any("invalid date" in e for e in result.errors)
assert result.rows == []
def test_parse_csv_zero_cents_rejected(tmp_path: Path):
p = tmp_path / "bad.csv"
p.write_text("DATE,CENTS,CATEGORY,MERCHANT\n2026-01-01,0,Food,\n")
result = parse_csv(p)
assert any("positive" in e for e in result.errors)
def test_parse_csv_comma_formatted_cents(tmp_path: Path):
p = tmp_path / "ok.csv"
p.write_text(
'DATE,CENTS,CATEGORY,MERCHANT\n2026-01-01,"14,901",Food,\n'
)
result = parse_csv(p)
assert result.errors == []
assert result.rows[0].cents == 14901
def test_export_sorts_nothing_within_row(tmp_path: Path):
"""write_csv preserves caller's tag order; sorting is the DB layer's job."""
p = tmp_path / "out.csv"
write_csv(p, [_row("2026-01-01", 100, "A", None, None, ["zeta", "alpha"])])
text = p.read_text()
assert '"zeta,alpha"' in text