Spaces:
Sleeping
Sleeping
| """Tests for Batch C — snapshot gap audit, regulation schemas, tenant audit log.""" | |
| import pytest | |
| from httpx import ASGITransport, AsyncClient | |
| from core.audit.snapshot_gap_audit import run_snapshot_gap_audit | |
| from core.regulation.schema_registry import get_schema, list_schema_ids | |
| from core.search.regulation_engine import regulation_engine | |
| from server import app | |
| SAMPLE_DOCUMENT = """ | |
| # Wniosek o dofinansowanie | |
| ## Opis projektu i innowacji | |
| Projekt zakłada rozwój innowacyjnego oprogramowania SaaS na poziomie TRL 6. | |
| Komponent B+R obejmuje badania nad modelem predykcyjnym. | |
| ## Harmonogram i kamienie milowe | |
| Q1: prototyp, Q2: pilotaż, Q3: wdrożenie komercyjne. | |
| ## Budżet i kwalifikowalność kosztów | |
| - Wynagrodzenia personelu B+R: 120 000 PLN | |
| - Oprogramowanie i licencje: 30 000 PLN | |
| - Szkolenia: 10 000 PLN | |
| ## Analiza rynku i wdrożenie | |
| Rynek MŚP w Polsce, plan sprzedaży subskrypcji. | |
| Status MŚP: mikroprzedsiębiorstwo zgodnie z 651/2014. | |
| """ | |
| def test_snapshot_gap_audit_detects_complete_smart_document(): | |
| rules = regulation_engine.get_structured_rules_for_program("SMART") | |
| report = run_snapshot_gap_audit( | |
| SAMPLE_DOCUMENT, | |
| "SMART", | |
| structured_rules=rules, | |
| company_profile={"size": "mikro", "nip": "5250000000"}, | |
| ) | |
| assert report.overall_score >= 70 | |
| assert not report.has_critical_gaps | |
| assert report.innovation_status.get("adequate") is True | |
| assert report.sme_status.get("eligible_signal") is True | |
| def test_snapshot_gap_audit_flags_missing_sections_and_ineligible_costs(): | |
| bad_doc = """ | |
| ## Budżet | |
| Zakup samochodu osobowego dla prezesa: 80 000 PLN | |
| Marketing i reklama: 50 000 PLN | |
| Status MŚP: mikroprzedsiębiorstwo. | |
| """ | |
| report = run_snapshot_gap_audit( | |
| bad_doc, | |
| "SMART", | |
| company_profile={"size": "duże"}, | |
| ) | |
| assert report.overall_score < 80 | |
| assert any(g.category == "costs" for g in report.gaps) | |
| assert any(g.category == "sections" for g in report.gaps) | |
| assert any(g.code == "SME_STATUS_CONFLICT" for g in report.gaps) | |
| def test_snapshot_gap_audit_rejects_short_document(): | |
| report = run_snapshot_gap_audit("krótki", "PARP") | |
| assert report.overall_score == 0 | |
| assert report.has_critical_gaps | |
| def test_regulation_schema_registry(): | |
| ids = list_schema_ids() | |
| assert "program_rules" in ids | |
| assert "gap_audit_report" in ids | |
| schema = get_schema("gap_audit_report") | |
| assert schema and schema["type"] == "object" | |
| async def test_api_regulation_schemas(auth_headers): | |
| async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client: | |
| resp = await client.get("/api/regulation/schemas", headers=auth_headers) | |
| assert resp.status_code == 200 | |
| data = resp.json() | |
| assert "program_rules" in data["schema_ids"] | |
| assert "schemas" in data | |
| async def test_api_gap_report_json(auth_headers): | |
| async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client: | |
| resp = await client.post( | |
| "/api/audit/gap-report", | |
| headers=auth_headers, | |
| json={ | |
| "program_name": "SMART", | |
| "document_text": SAMPLE_DOCUMENT, | |
| "export_format": "json", | |
| "company_profile": {"size": "mikro"}, | |
| }, | |
| ) | |
| assert resp.status_code == 200 | |
| body = resp.json() | |
| assert body["program"] == "SMART" | |
| assert "overall_score" in body | |
| assert "gaps" in body | |
| async def test_api_audit_logs_after_gap_report(auth_headers): | |
| async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client: | |
| await client.post( | |
| "/api/audit/gap-report", | |
| headers=auth_headers, | |
| json={ | |
| "program_name": "PARP", | |
| "document_text": SAMPLE_DOCUMENT, | |
| "export_format": "json", | |
| }, | |
| ) | |
| logs = await client.get("/api/audit-logs", headers=auth_headers) | |
| assert logs.status_code == 200 | |
| items = logs.json()["items"] | |
| assert any(i["action"] == "gap_audit_completed" for i in items) | |
| async def test_collaborator_add_remove_creates_audit_logs(auth_headers): | |
| from core.projects.models import Project | |
| from core.subscription.db import SessionLocal | |
| db = SessionLocal() | |
| try: | |
| project = Project( | |
| clerk_user_id="test_clerk_id_e2e", | |
| title="Audit Collab Test", | |
| program_type="SMART", | |
| ) | |
| db.add(project) | |
| db.commit() | |
| db.refresh(project) | |
| project_id = project.id | |
| finally: | |
| db.close() | |
| async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client: | |
| add = await client.post( | |
| f"/api/audit/collaborators/{project_id}", | |
| headers=auth_headers, | |
| json={"collaborator_user_id": "collab_user_99", "role": "viewer"}, | |
| ) | |
| assert add.status_code == 200 | |
| remove = await client.delete( | |
| f"/api/audit/collaborators/{project_id}/collab_user_99", | |
| headers=auth_headers, | |
| ) | |
| assert remove.status_code == 200 | |
| logs = await client.get("/api/audit-logs", headers=auth_headers) | |
| actions = [i["action"] for i in logs.json()["items"]] | |
| assert "collaborator_added" in actions | |
| assert "collaborator_removed" in actions | |