grantforge-api / backend /tests /test_match_program.py
GrantForge Bot
Deploy to Hugging Face
afd56bc
import pytest
from httpx import AsyncClient
from unittest.mock import patch, AsyncMock, MagicMock
@pytest.mark.asyncio
async def test_match_program_success(async_client: AsyncClient, auth_headers):
with patch("endpoints.projects.get_llm") as mock_get_llm, patch(
"core.ncbr_client.ncbr_client.get_active_nabory", new_callable=AsyncMock
) as mock_ncbr, patch(
"core.parp_client.parp_client.get_active_nabory", new_callable=AsyncMock
) as mock_parp:
mock_ncbr.return_value = []
mock_parp.return_value = []
mock_llm = MagicMock()
mock_structured_llm = MagicMock()
# Build fake programs list
class DummyExplanation:
def dict(self):
return {"reason": "It matches", "criteria": ["R&D"], "risks": "None"}
class DummyProgram:
def __init__(self):
self.id = 1
self.name = "FENG.01.01-IP.02-001/23"
self.type = "smart"
self.match = 95
self.chance = 80
self.amount = "10-50 mln PLN"
self.shortDesc = "Ścieżka SMART"
self.fullDesc = "Full description"
self.explanation = DummyExplanation()
self.criteria = ["R&D"]
self.risks = "None"
def dict(self):
return {
"id": self.id,
"name": self.name,
"type": self.type,
"match": self.match,
"chance": self.chance,
"amount": self.amount,
"shortDesc": self.shortDesc,
"fullDesc": self.fullDesc,
"explanation": self.explanation.dict(),
"criteria": self.criteria,
"risks": self.risks,
}
program_mock = DummyProgram()
mock_output = MagicMock()
mock_output.programs = [program_mock]
mock_output.clarifying_questions = ["What is the TRL level?"]
mock_output.model_dump.return_value = {
"programs": [
{
"id": 1,
"name": "FENG.01.01-IP.02-001/23",
"type": "smart",
"match": 95,
"chance": 80,
"amount": "10-50 mln PLN",
"shortDesc": "Ścieżka SMART",
"fullDesc": "Full description",
"explanation": {
"reason": "It matches",
"criteria": ["R&D"],
"risks": "None",
},
"criteria": ["R&D"],
"risks": "None",
}
],
"clarifying_questions": ["What is the TRL level?"],
}
mock_structured_llm.invoke.return_value = mock_output
mock_structured_llm.return_value = mock_output
mock_get_llm.return_value = mock_structured_llm
payload = {
"description": "We are building an AI software.",
"company_type": "sme",
"company_size": "small",
"voivodeship": "mazowieckie",
"innovation_type": "product",
}
response = await async_client.post(
"/api/projects/match-program", json=payload, headers=auth_headers
)
assert response.status_code == 200
data = response.json()
assert "programs" in data
assert "clarifying_questions" in data
assert len(data["programs"]) == 1
assert "What is the TRL level?" in data["clarifying_questions"]
@pytest.mark.asyncio
async def test_match_program_empty_description(async_client: AsyncClient, auth_headers):
payload = {
"description": "",
}
with patch("endpoints.projects.get_llm") as mock_get_llm, patch(
"core.ncbr_client.ncbr_client.get_active_nabory", new_callable=AsyncMock
) as mock_ncbr, patch(
"core.parp_client.parp_client.get_active_nabory", new_callable=AsyncMock
) as mock_parp:
mock_ncbr.return_value = []
mock_parp.return_value = []
mock_llm = MagicMock()
mock_structured_llm = MagicMock()
mock_output = MagicMock()
mock_output.programs = []
mock_output.clarifying_questions = ["Proszę opisać swój projekt."]
mock_output.model_dump.return_value = {
"programs": [],
"clarifying_questions": ["Proszę opisać swój projekt."],
}
mock_structured_llm.invoke.return_value = mock_output
mock_structured_llm.return_value = mock_output
mock_get_llm.return_value = mock_structured_llm
response = await async_client.post(
"/api/projects/match-program", json=payload, headers=auth_headers
)
assert response.status_code == 200
data = response.json()
assert "programs" in data
assert "clarifying_questions" in data
@pytest.mark.asyncio
async def test_match_program_llm_fallback(async_client: AsyncClient, auth_headers):
payload = {
"description": "Fallback test.",
}
with patch("endpoints.projects.get_llm") as mock_get_llm, patch(
"core.ncbr_client.ncbr_client.get_active_nabory", new_callable=AsyncMock
) as mock_ncbr, patch(
"core.parp_client.parp_client.get_active_nabory", new_callable=AsyncMock
) as mock_parp:
mock_ncbr.return_value = []
mock_parp.return_value = []
mock_llm = MagicMock()
mock_structured_llm = MagicMock()
mock_structured_llm.invoke.side_effect = Exception("LLM Error")
mock_structured_llm.side_effect = Exception("LLM Error")
mock_get_llm.return_value = mock_structured_llm
response = await async_client.post(
"/api/projects/match-program", json=payload, headers=auth_headers
)
assert response.status_code == 200
data = response.json()
assert "programs" in data
assert len(data["programs"]) == 3
assert data["programs"][0]["name"] == "Ścieżka SMART (FENG)"