Spaces:
Sleeping
Sleeping
| """F1 generation consent + dossier gate (pure unit tests on shipped functions).""" | |
| from __future__ import annotations | |
| import os | |
| from types import SimpleNamespace | |
| import pytest | |
| from core.projects.generation_consent import ( | |
| ERROR_CODE, | |
| GROUNDING_BLOCKED, | |
| GROUNDING_REGULATION, | |
| GROUNDING_STRUCTURE, | |
| STRUCTURE_EXPORT_REASON, | |
| STRUCTURE_ONLY_PROMPT, | |
| apply_structure_consent, | |
| build_generation_gate, | |
| clear_structure_consent, | |
| consent_required, | |
| project_generation_meta, | |
| resolve_grounding_mode, | |
| sync_grounding_after_dossier_update, | |
| ) | |
| def _flags_on(monkeypatch): | |
| monkeypatch.setenv("REQUIRE_DOSSIER_OR_CONSENT", "true") | |
| monkeypatch.setenv("ALLOW_STRUCTURE_WITHOUT_REGULATION", "true") | |
| def test_consent_without_ack_rejected(): | |
| ext, err = apply_structure_consent({}, ack=False, mode="structure_without_regulation", clerk_user_id="u1") | |
| assert err | |
| assert "ack" in err.lower() or "potwierdzenie" in err.lower() | |
| assert "generation_consent" not in ext | |
| def test_blind_without_consent_blocked(): | |
| ext = {"program_dossier": {"readiness": {"level": "blind"}}} | |
| gate = build_generation_gate(ext) | |
| assert gate["allowed"] is False | |
| assert gate["grounding_mode"] == GROUNDING_BLOCKED | |
| assert gate["code"] == ERROR_CODE | |
| assert gate["consent_required"] is True | |
| assert consent_required(ext) is True | |
| def test_blind_with_valid_consent_structure_only(): | |
| ext = {"program_dossier": {"readiness": {"level": "blind"}}} | |
| ext2, err = apply_structure_consent( | |
| ext, ack=True, mode="structure_without_regulation", clerk_user_id="user_abc", grant_id="g1" | |
| ) | |
| assert err is None | |
| assert ext2["generation_consent"]["mode"] == "structure_without_regulation" | |
| assert ext2["generation_consent"]["granted_by"] == "user_abc" | |
| assert ext2["generation_consent"]["granted_at"] | |
| gate = build_generation_gate(ext2) | |
| assert gate["allowed"] is True | |
| assert gate["grounding_mode"] == GROUNDING_STRUCTURE | |
| assert gate["structure_prompt"] | |
| assert "NIE cytuj regulaminu" in gate["structure_prompt"] | |
| assert gate["human_review_required"] is True | |
| assert STRUCTURE_EXPORT_REASON in (gate["export_reason"] or "") | |
| def test_after_non_blind_consent_cleared_to_regulation(): | |
| ext = { | |
| "program_dossier": {"readiness": {"level": "blind"}}, | |
| "generation_consent": { | |
| "mode": "structure_without_regulation", | |
| "granted_at": "2026-01-01T00:00:00+00:00", | |
| "granted_by": "u", | |
| }, | |
| "grounding_mode": "structure_only", | |
| } | |
| # Simulate dossier became ready | |
| ext["program_dossier"] = { | |
| "readiness": {"level": "ready"}, | |
| "regulation_documents": [ | |
| {"url": "https://x.pl/reg.pdf", "role": "regulamin", "score": 40}, | |
| {"url": "https://x.pl/rwp.pdf", "role": "rwp", "score": 38}, | |
| ], | |
| } | |
| ext["precise_regulation_url"] = "https://x.pl/reg.pdf" | |
| synced = sync_grounding_after_dossier_update(ext) | |
| assert "generation_consent" not in synced | |
| assert resolve_grounding_mode(synced) == GROUNDING_REGULATION | |
| assert synced["grounding_mode"] == GROUNDING_REGULATION | |
| def test_ready_does_not_need_consent(): | |
| ext = { | |
| "program_dossier": {"readiness": {"level": "ready"}}, | |
| "precise_regulation_url": "https://x.pl/a.pdf", | |
| "regulation_documents": [ | |
| {"url": "https://x.pl/a.pdf", "role": "regulamin", "score": 40}, | |
| {"url": "https://x.pl/b.pdf", "role": "rwp", "score": 30}, | |
| ], | |
| } | |
| assert resolve_grounding_mode(ext) == GROUNDING_REGULATION | |
| assert consent_required(ext) is False | |
| _, err = apply_structure_consent(ext, ack=True, mode="structure_without_regulation", clerk_user_id="u") | |
| assert err # not needed | |
| def test_flag_off_allows_generation(): | |
| os.environ["REQUIRE_DOSSIER_OR_CONSENT"] = "false" | |
| ext = {"program_dossier": {"readiness": {"level": "blind"}}} | |
| assert resolve_grounding_mode(ext) == GROUNDING_REGULATION | |
| assert build_generation_gate(ext)["allowed"] is True | |
| os.environ["REQUIRE_DOSSIER_OR_CONSENT"] = "true" | |
| def test_clear_consent_returns_blocked_when_blind(): | |
| ext = { | |
| "program_dossier": {"readiness": {"level": "blind"}}, | |
| "generation_consent": { | |
| "mode": "structure_without_regulation", | |
| "granted_at": "2026-01-01T00:00:00+00:00", | |
| "granted_by": "u", | |
| }, | |
| } | |
| cleared = clear_structure_consent(ext) | |
| assert "generation_consent" not in cleared | |
| assert resolve_grounding_mode(cleared) == GROUNDING_BLOCKED | |
| def test_project_generation_meta_fields(): | |
| ext = {"program_dossier": {"readiness": {"level": "blind"}}} | |
| meta = project_generation_meta(ext) | |
| assert meta["consent_required"] is True | |
| assert meta["generation_allowed"] is False | |
| assert meta["dossier_readiness"] == "blind" | |
| def test_export_gate_structure_only_human_review(): | |
| from core.projects.export_gate import evaluate_export_gate_with_guidance | |
| project = SimpleNamespace( | |
| external_context={ | |
| "program_dossier": {"readiness": {"level": "blind"}}, | |
| "generation_consent": { | |
| "mode": "structure_without_regulation", | |
| "granted_at": "2026-01-01T00:00:00+00:00", | |
| "granted_by": "u", | |
| }, | |
| "grounding_mode": "structure_only", | |
| }, | |
| final_document_audit_result={ | |
| "status": "completed", | |
| "export_status": "ok", | |
| "is_approved": True, | |
| "human_review_required": True, | |
| }, | |
| ) | |
| gate = evaluate_export_gate_with_guidance(project) | |
| assert gate.get("structure_only") is True | |
| assert gate.get("human_review_required") is True | |
| assert any("strukturalny" in (g.get("reason") or "").lower() or "regulaminu" in (g.get("reason") or "").lower() for g in gate.get("guidance") or []) | |
| def test_structure_prompt_constant(): | |
| assert "DO WERYFIKACJI" in STRUCTURE_ONLY_PROMPT or "WERYFIKACJI" in STRUCTURE_ONLY_PROMPT | |
| assert "NIE cytuj" in STRUCTURE_ONLY_PROMPT or "nie cytuj" in STRUCTURE_ONLY_PROMPT.lower() | |
| def test_dossier_stats_on_empty_db(monkeypatch): | |
| """Stats aggregation pure path with mock session.""" | |
| from core.grants import dossier_ops | |
| class FakeQuery: | |
| def filter(self, *a, **k): | |
| return self | |
| def limit(self, n): | |
| return self | |
| def all(self): | |
| return [] | |
| class FakeDB: | |
| def query(self, *a, **k): | |
| return FakeQuery() | |
| dist = dossier_ops.compute_readiness_distribution(FakeDB(), limit=10) | |
| assert dist["total"] == 0 | |
| assert "pct_blind" in dist | |
| assert "flags" in dist | |
| def test_soft_check_allows_structure_only_full_autopilot(): | |
| """F1: structure_only consent must not be blocked by regulation soft-check.""" | |
| from endpoints.generator import _soft_check_regulation_for_full_autopilot | |
| project = SimpleNamespace( | |
| external_context={ | |
| "program_dossier": {"readiness": {"level": "blind"}}, | |
| "generation_consent": { | |
| "mode": "structure_without_regulation", | |
| "granted_at": "2026-01-01T00:00:00+00:00", | |
| "granted_by": "u", | |
| }, | |
| "grounding_mode": "structure_only", | |
| "regulation_pending": True, | |
| "regulation_source": "none", | |
| } | |
| ) | |
| assert _soft_check_regulation_for_full_autopilot(project) is None | |
| def test_f3_structure_only_skips_regulation_section_seed(monkeypatch): | |
| """F3: required_sections from advisor_brief only seed under regulation mode.""" | |
| from endpoints import generator as gen_mod | |
| calls = {"reg": 0, "tmpl": 0} | |
| class FakeSection: | |
| def __init__(self, st="desc", content=""): | |
| self.section_type = st | |
| self.content = content | |
| self.order = 0 | |
| class FakeQ: | |
| def __init__(self, rows): | |
| self._rows = rows | |
| def filter(self, *a, **k): | |
| return self | |
| def order_by(self, *a, **k): | |
| return self | |
| def all(self): | |
| return self._rows | |
| class FakeDB: | |
| def query(self, model): | |
| name = getattr(model, "__name__", str(model)) | |
| if "Template" in name: | |
| return FakeQ([]) | |
| return FakeQ([]) # no existing sections | |
| def commit(self): | |
| pass | |
| def fake_seed_reg(db, pid, required, program_type): | |
| calls["reg"] += 1 | |
| return [FakeSection("from_reg")], {"from_reg": "From regulation"} | |
| def fake_seed_tmpl(db, pid, program_type): | |
| calls["tmpl"] += 1 | |
| return [FakeSection("from_tmpl", "x" * 20)] | |
| monkeypatch.setattr( | |
| "core.projects.section_seeder.seed_sections_from_regulation", | |
| fake_seed_reg, | |
| raising=False, | |
| ) | |
| # patch where used inside function | |
| import core.projects.section_seeder as seeder | |
| monkeypatch.setattr(seeder, "seed_sections_from_regulation", fake_seed_reg) | |
| monkeypatch.setattr(seeder, "seed_sections_from_templates", fake_seed_tmpl) | |
| project = SimpleNamespace( | |
| program_type="SMART", | |
| external_context={ | |
| "program_dossier": {"readiness": {"level": "blind"}}, | |
| "generation_consent": { | |
| "mode": "structure_without_regulation", | |
| "granted_at": "2026-01-01T00:00:00+00:00", | |
| "granted_by": "u", | |
| }, | |
| "grounding_mode": "structure_only", | |
| "required_sections": ["Sekcja z advisor_brief", "Inna"], | |
| }, | |
| ) | |
| plan, _ = gen_mod._ensure_sections_plan_from_project(FakeDB(), project, "p1") | |
| assert calls["reg"] == 0, "structure_only must not seed from regulation required_sections" | |
| assert calls["tmpl"] == 1 | |
| assert plan # templates used | |
| # regulation mode uses required_sections | |
| calls["reg"] = calls["tmpl"] = 0 | |
| project_reg = SimpleNamespace( | |
| program_type="SMART", | |
| external_context={ | |
| "program_dossier": { | |
| "readiness": {"level": "ready"}, | |
| "regulation_documents": [ | |
| {"url": "https://x.pl/a.pdf", "role": "regulamin", "score": 40}, | |
| {"url": "https://x.pl/b.pdf", "role": "rwp", "score": 30}, | |
| ], | |
| }, | |
| "precise_regulation_url": "https://x.pl/a.pdf", | |
| "required_sections": ["Sekcja A", "Sekcja B"], | |
| }, | |
| ) | |
| gen_mod._ensure_sections_plan_from_project(FakeDB(), project_reg, "p2") | |
| assert calls["reg"] == 1 | |
| assert calls["tmpl"] == 0 | |