grantforge-api / backend /tests /test_data_completeness.py
GrantForge Bot
Deploy sha-565ad85979610064f6d1c18ab3b6404357d61073 — source build (no GHCR)
ce8f04a
Raw
History Blame Contribute Delete
5.94 kB
"""Pre-gen data completeness gate."""
from __future__ import annotations
from core.projects.data_completeness import (
STATUS_MISSING,
STATUS_READY,
STATUS_STRUCTURE,
apply_project_facts,
evaluate_data_completeness,
)
from core.projects.instrument_schema import build_instrument_schema
def test_eurogranty_missing_target_blocks():
schema = build_instrument_schema(program_name="Granty na Eurogranty FENG.02.12")
r = evaluate_data_completeness(
external_context={
"company_data": {
"nip": "8721990161",
"name": "AUTOPROCES",
"pkd_codes": ["28.29.Z"],
"msp_status": "mikro",
"de_minimis": 2738,
"closed_financial_year": True,
"revenue_3y": [1, 2, 3],
"employment_fte": 2,
},
"grounding_mode": "regulation",
"program_dossier": {"readiness": {"level": "partial"}},
},
instrument_schema=schema,
description="Przygotowanie do aplikacji UE",
)
assert r["status"] == STATUS_MISSING
assert r["full_autopilot_allowed"] is False
fields = {m["field"] for m in r["missing_fields"]}
assert "eurogrant_call" in fields or "eurogrant_programme" in fields
def test_structure_only_allows_generate():
r = evaluate_data_completeness(
external_context={
"grounding_mode": "structure_only",
# complete eligibility so structure-only is not blocked by spine
"company_data": {
"nip": "8721990161",
"msp_status": "mikro",
"de_minimis_manual_eur": 1000,
},
},
grounding_mode="structure_only",
)
assert r["status"] == STATUS_STRUCTURE
assert r["ready_to_generate"] is True
assert r["full_autopilot_allowed"] is True
assert r.get("eligibility_spine_enforced") is True
def test_structure_only_still_gates_full_autopilot_on_large_firm(monkeypatch):
"""structure_only must not fail-open Full Autopilot past eligibility spine."""
monkeypatch.setenv("ENABLE_ELIGIBILITY_SPINE", "true")
r = evaluate_data_completeness(
external_context={
"grounding_mode": "structure_only",
"company_data": {
"nip": "8721990161",
"msp_status": "duza",
"de_minimis_manual_eur": 1000,
},
},
grounding_mode="structure_only",
)
assert r["status"] == STATUS_STRUCTURE
assert r["ready_to_generate"] is True # structural consent retained
assert r["full_autopilot_allowed"] is False
assert r.get("eligibility_spine_enforced") is True
assert any("duża" in b.lower() or "duza" in b.lower() for b in (r.get("eligibility") or {}).get("blockers") or []) or any(
"duż" in w.lower() or "duza" in w.lower() or "large" in w.lower() or "MŚP" in w or "duża" in w
for w in (r.get("warnings") or [])
)
def test_structure_only_gates_exhausted_de_minimis(monkeypatch):
monkeypatch.setenv("ENABLE_ELIGIBILITY_SPINE", "true")
r = evaluate_data_completeness(
external_context={
"grounding_mode": "structure_only",
"company_data": {
"nip": "8721990161",
"msp_status": "mikro",
"sudop": {"configured": True, "de_minimis_total_eur": 300_000},
},
},
grounding_mode="structure_only",
)
assert r["ready_to_generate"] is True
assert r["full_autopilot_allowed"] is False
assert (r.get("eligibility") or {}).get("de_minimis", {}).get("exhausted") is True
def test_structure_only_gates_unknown_de_minimis(monkeypatch):
monkeypatch.setenv("ENABLE_ELIGIBILITY_SPINE", "true")
r = evaluate_data_completeness(
external_context={
"grounding_mode": "structure_only",
"company_data": {
"nip": "8721990161",
"msp_status": "mikro",
# no sudop / manual de minimis
},
},
grounding_mode="structure_only",
)
assert r["full_autopilot_allowed"] is False
assert r.get("eligibility_spine_enforced") is True
def test_apply_facts_then_ready_eurogranty():
schema = build_instrument_schema(program_name="Granty na Eurogranty")
ext = {
"company_data": {
"nip": "8721990161",
"name": "AUTOPROCES",
"pkd_codes": ["28.29.Z"],
"msp_status": "mikro",
"de_minimis": 1000,
"closed_financial_year": True,
"revenue_3y": {"2023": 1, "2024": 2, "2025": 3},
"employment_fte": 3,
},
"grounding_mode": "regulation",
"program_dossier": {"readiness": {"level": "ready"}},
}
ext = apply_project_facts(
ext,
{
"eurogrant_programme": "Horizon Europe Cluster 4",
"eurogrant_call": "HORIZON-CL4-2026-AI-MANUFACTURING",
"eurogrant_role": "samodzielny",
"specialist_analysis_yes_no": "tak — analiza partnerów i stanu techniki",
"lump_sum_amount_pln": 110852,
},
)
r = evaluate_data_completeness(
external_context=ext,
instrument_schema=schema,
description="Przygotowanie wniosku Horizon dla AI in manufacturing",
)
assert r["status"] == STATUS_READY
assert r["full_autopilot_allowed"] is True
assert not r["missing_fields"]
def test_missing_nip_blocks():
schema = build_instrument_schema(program_type="SMART", program_name="SMART")
r = evaluate_data_completeness(
external_context={"company_data": {}, "grounding_mode": "regulation"},
instrument_schema=schema,
description="Projekt innowacyjny",
)
assert r["full_autopilot_allowed"] is False
assert any(m["field"] == "nip" for m in r["missing_fields"])