Fix Zotero table models for Zotero 9:
- Remove stale Annotations/Highlights/Transaction* models
- Add ItemAnnotations, RetractedItems, DeletedCollections,
DeletedSearches, DbDebug1
- Fix ItemAttachments, Libraries, Users column mismatches
New test files covering all major modules:
- cli/{bib,prisma,rec,zot,mail,run} deep exercising tests
- mail/{droplet,postmark,resend,cloudflare} lifecycle tests
- bib/{iom,oig,pincite,sync,regulations_gov,email_ingest,format,store}
- prisma/{vpn,fetch,export,llm,screen,eligibility,extract,project,ingest,flow}
- aco/lake/{unity,quality,deploy} + api/aco coverage gaps
- zot/{ops,db,extract,duck} + rec/{report,engine,base,pricers}
- pfs/{pipe,rules,eq,files}
Add pytest-xdist for parallel test execution.
Tracks #353
132 lines
4.4 KiB
Python
132 lines
4.4 KiB
Python
"""Tests for rec.report — markdown and JSON formatters."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
|
|
import polars as pl
|
|
|
|
from rec.base import Reconciliation
|
|
from rec.engine import reconcile
|
|
from rec.report import _polars_to_markdown, _top_diffs, as_json, as_markdown
|
|
|
|
|
|
class TestAsMarkdown:
|
|
def test_contains_header(self, fake_pricer) -> None:
|
|
r = reconcile(fake_pricer, con=None, year=2025)
|
|
md = as_markdown(r)
|
|
assert "# Reconciliation — `fake` CY2025" in md
|
|
|
|
def test_contains_counts_table(self, fake_pricer) -> None:
|
|
r = reconcile(fake_pricer, con=None, year=2025)
|
|
md = as_markdown(r)
|
|
assert "## Counts" in md
|
|
assert "Ground-truth rows" in md
|
|
assert "Calculated rows" in md
|
|
assert "Matched (both sides)" in md
|
|
|
|
def test_contains_top_deltas(self, fake_pricer) -> None:
|
|
r = reconcile(fake_pricer, con=None, year=2025)
|
|
md = as_markdown(r)
|
|
assert "## Top" in md
|
|
# A002 has the 1¢ delta; should appear in top deltas table
|
|
assert "A002" in md
|
|
|
|
def test_perfect_status_line(self, fake_pricer) -> None:
|
|
r = reconcile(fake_pricer, con=None, year=2025, tolerance_cents=1)
|
|
md = as_markdown(r)
|
|
# With tolerance=1¢, both matched rows are exact — but there's
|
|
# still a gt_only and a calc_only so is_perfect is False.
|
|
assert "Status:" in md
|
|
|
|
|
|
class TestAsJson:
|
|
def test_valid_json(self, fake_pricer) -> None:
|
|
r = reconcile(fake_pricer, con=None, year=2025)
|
|
payload = json.loads(as_json(r))
|
|
assert isinstance(payload, dict)
|
|
assert payload["system"] == "fake"
|
|
assert payload["year"] == 2025
|
|
assert payload["matched_rows"] == 2
|
|
assert payload["exact_matches"] == 1
|
|
assert payload["near_matches"] == 2
|
|
assert payload["pct_exact"] == 50.0
|
|
|
|
def test_top_deltas_embedded(self, fake_pricer) -> None:
|
|
r = reconcile(fake_pricer, con=None, year=2025)
|
|
payload = json.loads(as_json(r))
|
|
assert "top_deltas" in payload
|
|
assert isinstance(payload["top_deltas"], list)
|
|
assert len(payload["top_deltas"]) >= 1
|
|
|
|
|
|
# ── Gap coverage — missed lines ────────────────────────────────
|
|
|
|
|
|
class TestMarkdownWarnings:
|
|
"""Lines 44-48: warnings block rendered in markdown."""
|
|
|
|
def test_warnings_rendered(self, fake_pricer) -> None:
|
|
|
|
r = reconcile(fake_pricer, con=None, year=2025)
|
|
# Create a reconciliation with warnings
|
|
r_with_warnings = Reconciliation(
|
|
system=r.system,
|
|
year=r.year,
|
|
ground_truth_rows=r.ground_truth_rows,
|
|
calculated_rows=r.calculated_rows,
|
|
matched_rows=r.matched_rows,
|
|
ground_truth_only=r.ground_truth_only,
|
|
calculated_only=r.calculated_only,
|
|
exact_matches=r.exact_matches,
|
|
near_matches=r.near_matches,
|
|
deltas=r.deltas,
|
|
tolerance_cents=r.tolerance_cents,
|
|
warnings=("some warning message",),
|
|
)
|
|
md = as_markdown(r_with_warnings)
|
|
assert "## Warnings" in md
|
|
assert "some warning message" in md
|
|
|
|
|
|
class TestMarkdownEmptyDeltas:
|
|
"""Lines 57-60: empty deltas shows 'No non-zero deltas' message."""
|
|
|
|
def test_empty_deltas(self) -> None:
|
|
r = Reconciliation(
|
|
system="x",
|
|
year=2025,
|
|
ground_truth_rows=1,
|
|
calculated_rows=1,
|
|
matched_rows=1,
|
|
ground_truth_only=0,
|
|
calculated_only=0,
|
|
exact_matches=1,
|
|
near_matches=1,
|
|
deltas=pl.DataFrame(),
|
|
)
|
|
md = as_markdown(r)
|
|
assert "No non-zero deltas" in md
|
|
|
|
|
|
class TestTopDiffsEdges:
|
|
"""Lines 97, 102: _top_diffs edge cases."""
|
|
|
|
def test_missing_abs_max_delta(self) -> None:
|
|
df = pl.DataFrame({"a": [1, 2]})
|
|
result = _top_diffs(df, 10)
|
|
assert result.height == 0
|
|
|
|
def test_no_is_exact_column(self) -> None:
|
|
df = pl.DataFrame({"abs_max_delta": [1.0, 2.0]})
|
|
result = _top_diffs(df, 10)
|
|
assert result.height == 2
|
|
|
|
|
|
class TestPolarsToMarkdownEmpty:
|
|
"""Line 109: _polars_to_markdown returns '*(empty)*' for empty df."""
|
|
|
|
def test_empty(self) -> None:
|
|
df = pl.DataFrame({"a": []}).cast({"a": pl.Int64})
|
|
assert _polars_to_markdown(df) == "*(empty)*"
|