Repair ContractClock persistence and receipt safety

This commit is contained in:
2026-07-18 05:27:54 -03:00
parent 78f8677e6f
commit 21284e8ab4
3 changed files with 88 additions and 23 deletions

View File

@@ -1,6 +1,5 @@
import base64
import importlib.util
import os
import sys
from pathlib import Path
@@ -27,7 +26,7 @@ def test_full_stack_product_contract_card_and_manifest():
frontend = (ROOT / "frontend" / "src" / "App.jsx").read_text(encoding="utf-8")
a2a_client = (ROOT / "frontend" / "src" / "a2a.js").read_text(encoding="utf-8")
assert "version: 0.1.5" in manifest
assert "version: 0.1.6" in manifest
assert "public: false" in manifest
assert "frontend:" in manifest and "mount: /app" in manifest
assert "resources:" in manifest and "databases:" in manifest
@@ -45,7 +44,7 @@ def test_full_stack_product_contract_card_and_manifest():
assert migrations and all(path.read_text(encoding="utf-8").strip() for path in migrations)
card = load_local_project(ROOT).agent_cls().card()
assert card.version == "0.1.5"
assert card.version == "0.1.6"
skill_names = {skill.name for skill in card.skills}
assert {"analyze_contract", "get_contract_deadlines", "analyze_contract_upload_base64", "analyze_contract_file"} <= skill_names
analyze_schema = next(skill.input_schema for skill in card.skills if skill.name == "analyze_contract")
@@ -85,6 +84,31 @@ def test_explicit_date_only_success_and_failure():
assert bad.ok is False
assert bad.code == "ambiguous_date"
multiple_dates = module._analyze_contract_text(
contract_id="studio-contract-multiple-dates",
title="Multiple dates",
text=(
"This Agreement is effective on 2025-01-01 and renews automatically "
"on 2026-12-31 unless either party gives at least 60 days written notice."
),
)
assert [d.model_dump(include={"kind", "date"}) for d in multiple_dates.deadlines] == [
{"kind": "renewal", "date": "2026-12-31"},
{"kind": "notice", "date": "2026-11-01"},
]
separate_lines = module._analyze_contract_text(
contract_id="studio-contract-separate-lines",
title="Separate clauses",
text=(
"Agreement renews on 2026-12-31\n"
"60 days written notice applies to another obligation"
),
)
assert [d.model_dump(include={"kind", "date"}) for d in separate_lines.deadlines] == [
{"kind": "renewal", "date": "2026-12-31"},
]
@pytest.mark.asyncio
async def test_success_failure_upload_and_reload_with_fake_postgres(monkeypatch):
@@ -106,7 +130,7 @@ async def test_success_failure_upload_and_reload_with_fake_postgres(monkeypatch)
params = params or ()
normalized = " ".join(sql.split())
if normalized.startswith("SET"):
return
raise AssertionError("timeouts must be configured in connection options")
if "INSERT INTO contracts" in normalized:
tenant, contract_id, title, source_hash = params
key = (tenant, contract_id)
@@ -158,6 +182,7 @@ async def test_success_failure_upload_and_reload_with_fake_postgres(monkeypatch)
class FakePsycopg:
def connect(self, *args, **kwargs):
assert "statement_timeout=5000" in kwargs.get("options", "")
assert "idle_in_transaction_session_timeout=5000" in kwargs.get("options", "")
return FakeConnection()
monkeypatch.setenv("DATABASE_URL", "postgresql://example.invalid/db")
@@ -176,6 +201,9 @@ async def test_success_failure_upload_and_reload_with_fake_postgres(monkeypatch)
assert success.ok is True
assert success.receipt_id and success.saved_at
assert len(store["receipts"]) == 1
receipt_payload = __import__("json").loads(store["receipts"][0][-1])
assert "source_text" not in str(receipt_payload)
assert receipt_payload["result"]["deadline_count"] == 2
reload_result = await agent.invoke("get_contract_deadlines", ctx, contract_id="studio-contract-v1")
assert reload_result.ok is True