fix: address settlement display and documentation review findings
Rework Issue 18 / PR 19, source review 76: C11-F01 and D02-F02/D03-F03/D04-F04. Preserve the approved contract and add named display, schema, policy, ownership and signature regressions.
This commit is contained in:
@@ -182,3 +182,90 @@ def test_settlement_draft_runtime_cancel_and_recovery_contract() -> None:
|
||||
source = ast.parse(text("jarca_ledger_mcp/application/settlement_draft_service.py"))
|
||||
cancel = next(n for n in source.body if isinstance(n, ast.FunctionDef) and n.name == "cancel_settlement_draft")
|
||||
assert [arg.arg for arg in cancel.args.args] == ["draft_id", "expected_version", "cancelled_by", "reason", "db_path"]
|
||||
|
||||
|
||||
def _comprehensive_section(doc, heading):
|
||||
assert heading in doc, f"missing documented contract section: {heading}"
|
||||
return doc.split(heading, 1)[1].split("\n### ", 1)[0]
|
||||
|
||||
|
||||
def _comprehensive_table(section):
|
||||
rows = {}
|
||||
for line in section.splitlines():
|
||||
if not line.startswith("|") or re.match(r"^\|\s*:?-+", line):
|
||||
continue
|
||||
cells = [c.strip().strip("` ") for c in line.strip().strip("|").split("|")]
|
||||
if len(cells) == 4 and cells[0] not in {"Column", "Field", "field"}:
|
||||
rows[cells[0]] = cells[1:]
|
||||
return rows
|
||||
|
||||
|
||||
def test_settlement_cashflow_model_documents_schema_and_legacy_reproduction(tmp_path):
|
||||
import sqlite3
|
||||
from jarca_ledger_mcp.db import init_db
|
||||
doc = (ROOT / "docs/data-model.md").read_text(encoding="utf-8")
|
||||
names = {
|
||||
"asset_snapshot_runs": ("settlement_month", "currency", "income_total", "expense_total", "net_cashflow", "total_assets", "total_liabilities", "net_worth", "cashflow_policy_json", "aggregation_version", "snapshot_schema_version", "source_snapshot_json"),
|
||||
"settlement_drafts": ("cashflow_policy_json", "aggregation_version"),
|
||||
"settlement_cashflow_snapshots": ("id", "settlement_run_id", "cashflow_kind", "category", "subcategory", "amount", "transaction_count", "currency"),
|
||||
}
|
||||
database = tmp_path / "synthetic-doc-schema.sqlite3"
|
||||
init_db(str(database))
|
||||
with sqlite3.connect(database) as conn:
|
||||
for table, columns in names.items():
|
||||
section = _comprehensive_section(doc, f"### {table} cashflow field contract")
|
||||
observed = _comprehensive_table(section)
|
||||
ddl = {r[1]: [r[2], "NOT NULL" if r[3] else "NULL", r[4] if r[4] is not None else "none"] for r in conn.execute(f"PRAGMA table_info({table})") if r[1] in columns}
|
||||
assert observed == ddl, f"field types/nullability/defaults differ from actual DDL: {table}"
|
||||
reproduction = _comprehensive_section(doc, "### Comprehensive snapshot evidence inputs")
|
||||
for field in ("source_snapshot_json", "transactions", "assets", "transaction_date", "originalcashflow_kind", "applied_amount", "valuation_id", "valuation_date", "authority_source", "cashflow_policy_json", "legacy", "NULL"):
|
||||
assert field in reproduction, field
|
||||
for constraint in ("ON DELETE RESTRICT", "UNIQUE", "transaction_count > 0", "idx_settlement_cashflow_snapshots_run"):
|
||||
assert constraint in doc, constraint
|
||||
|
||||
|
||||
def test_settlement_cashflow_interface_specifies_valid_policy_review_and_response_examples():
|
||||
from jarca_ledger_mcp.application.settlement_cashflow_service import _normalize_cashflow_policy
|
||||
doc = (ROOT / "docs/interface-contract.md").read_text(encoding="utf-8")
|
||||
policy_section = _comprehensive_section(doc, "### Comprehensive cashflow policy input")
|
||||
match = re.search(r"```json\s*(.*?)\s*```", policy_section, re.S)
|
||||
assert match, "policy input needs an executable JSON example"
|
||||
policy = json.loads(match.group(1))
|
||||
assert _normalize_cashflow_policy(policy) == policy
|
||||
assert set(policy["savings_categories"][0]) == {"category", "subcategory"}
|
||||
assert set(policy["category_decisions"][0]) == {"category", "subcategory", "cashflow_kind", "reason"}
|
||||
for field in ("income", "expense", "excluded", "duplicate", "nonempty", "expected_version", "review_fingerprint", "aggregation_version", "comprehensive-v1"):
|
||||
assert field in policy_section, field
|
||||
outputs = _comprehensive_section(doc, "### Comprehensive settlement response contract")
|
||||
totals = _comprehensive_table(outputs)
|
||||
keys = ("income_total", "expense_total", "net_cashflow", "total_assets", "total_liabilities", "net_worth")
|
||||
assert set(keys) <= set(totals)
|
||||
for key in keys:
|
||||
assert totals[key] == ["integer", "required", "null"], key
|
||||
for field in ("cashflow_items", "cashflow_blockers", "transaction_id", "reason", "validation_error", "cashflow review blockers must be resolved", "final_response", "cashflow_policy", "source_snapshot", "refund"):
|
||||
assert field in outputs, field
|
||||
examples = [json.loads(s) for s in re.findall(r"```json\s*(.*?)\s*```", outputs, re.S)]
|
||||
review = next((x for x in examples if isinstance(x, dict) and "cashflow_blockers" in x and "review_fingerprint" in x), None)
|
||||
assert review is not None, "review response example must use actual root fields"
|
||||
assert review["success"] is True and all(type(review[key]) is int for key in keys)
|
||||
assert review["expense_total"] < 0, "signed-refund example must retain a negative expense total"
|
||||
assert all(set(x) == {"transaction_id", "reason"} for x in review["cashflow_blockers"])
|
||||
saved = next((x for x in examples if isinstance(x, dict) and x.get("source") == "settlement_snapshot"), None)
|
||||
assert saved is not None and saved["success"] is True and "run_id" in saved
|
||||
assert all(key in saved for key in keys)
|
||||
|
||||
|
||||
def test_settlement_cashflow_private_service_has_explicit_ownership_and_test_mapping():
|
||||
doc = (ROOT / "docs/project-structure.md").read_text(encoding="utf-8")
|
||||
rows = [line for line in doc.splitlines() if line.startswith("|") and "settlement_cashflow_service.py" in line]
|
||||
assert len(rows) == 1, "private aggregation owner must have an explicit responsibility row"
|
||||
for field in ("private", "_normalize_cashflow_policy", "_review_cashflow", "settlement_draft_service.py", "test_settlement_cashflow_policy.py", "test_comprehensive_settlement_adapters.py"):
|
||||
assert field in rows[0], field
|
||||
|
||||
def test_cashflow_policy_documented_signature_matches_public_api():
|
||||
from jarca_ledger_mcp import service
|
||||
import inspect
|
||||
section = _comprehensive_section((ROOT / "docs/interface-contract.md").read_text(), "### Comprehensive cashflow policy input")
|
||||
assert list(inspect.signature(service.set_settlement_cashflow_policy).parameters) == ["draft_id", "expected_version", "updated_by", "cashflow_policy", "db_path"]
|
||||
assert "set_settlement_cashflow_policy(draft_id, expected_version, updated_by, cashflow_policy, db_path=None)" in section
|
||||
assert "set_settlement_draft_cashflow_policy(" not in section
|
||||
|
||||
Reference in New Issue
Block a user