Spaces:
Sleeping
Sleeping
| """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"]) | |