fix: use in-memory checkpointer for API tests to avoid database dependency
- Replace PostgreSQL AsyncPostgresSaver with MemorySaver in test fixtures - Create test_app fixture with test_lifespan using in-memory dependencies - Mock LLM model to avoid requiring API keys during tests - Update test_api.py to use test_app fixture instead of production app - Fix test_root endpoint URL to match root_path configuration - All API tests now pass without requiring external services (0.06s vs 30s+ timeout) This change allows tests to run in isolation without PostgreSQL or API keys, making them faster and more reliable for CI/CD and local development.
This commit is contained in:
@@ -5,7 +5,9 @@ from naliiabot.bot.factories.llm_factory import LLMFactory
|
||||
from naliiabot.bot.prompts.load_prompt import get_prompt_template
|
||||
from naliiabot.bot.tools.naliia_tools import NaliiaTools
|
||||
from naliiabotapi.api.v1.endpoints.chat import router as chat_router
|
||||
from naliiabotapi.api.v1.webhooks.chat_hook import router as chat_hook_router
|
||||
from naliiabotapi.api.v1.webhooks.chat_hook import (
|
||||
router as chat_hook_router,
|
||||
)
|
||||
import naliiabotapi.api.dependencies as deps
|
||||
from contextlib import asynccontextmanager
|
||||
from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
|
||||
@@ -30,7 +32,7 @@ async def lifespan(app: FastAPI):
|
||||
model=model,
|
||||
config=config,
|
||||
checkpointer=checkpointer,
|
||||
tools=naliia_tools
|
||||
tools=naliia_tools,
|
||||
)
|
||||
|
||||
yield
|
||||
@@ -42,10 +44,11 @@ app = FastAPI(
|
||||
title="NaliiaBot API",
|
||||
description=(
|
||||
"API for NaliiaBot, a chatbot that provides "
|
||||
"customer service and related topics."),
|
||||
"customer service and related topics."
|
||||
),
|
||||
version="1.0.0",
|
||||
root_path="/agent/api",
|
||||
lifespan=lifespan
|
||||
lifespan=lifespan,
|
||||
)
|
||||
|
||||
app.include_router(chat_router)
|
||||
|
||||
@@ -1,18 +1,86 @@
|
||||
import pytest
|
||||
from unittest.mock import Mock
|
||||
from unittest.mock import Mock, MagicMock
|
||||
import os
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
from src.naliiabot.bot.agent.agent import Agent, AgentConfig
|
||||
from src.naliiabot.bot.factories.llm_factory import LLMFactory
|
||||
from httpx import ASGITransport, AsyncClient
|
||||
from naliiabotapi.main import app
|
||||
from fastapi import FastAPI
|
||||
from langgraph.checkpoint.memory import MemorySaver
|
||||
from langchain_core.messages import AIMessage
|
||||
from naliiabot.bot.prompts.load_prompt import get_prompt_template
|
||||
from naliiabot.bot.tools.naliia_tools import NaliiaTools
|
||||
import naliiabotapi.api.dependencies as deps
|
||||
from naliiabotapi.api.v1.endpoints.chat import router as chat_router
|
||||
from naliiabotapi.api.v1.webhooks.chat_hook import router as chat_hook_router
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def test_lifespan(app: FastAPI):
|
||||
"""Test lifespan that uses in-memory checkpointer instead of PostgreSQL."""
|
||||
# Use in-memory checkpointer for tests
|
||||
checkpointer = MemorySaver()
|
||||
|
||||
# Use a mock model for tests to avoid needing API keys
|
||||
# The mock needs to return proper AIMessage objects
|
||||
mock_model = MagicMock()
|
||||
|
||||
def mock_invoke(*args, **kwargs):
|
||||
"""Mock invoke that returns a proper AIMessage."""
|
||||
return AIMessage(content="This is a test response from the mock model.")
|
||||
|
||||
mock_model.invoke = mock_invoke
|
||||
mock_model.bind_tools = Mock(return_value=mock_model)
|
||||
|
||||
naliia_prompt = get_prompt_template("NALIIA_PROMPT")
|
||||
naliia_tools = NaliiaTools().get_tools()
|
||||
config = AgentConfig(system_prompt=naliia_prompt)
|
||||
|
||||
deps.agent_instance = Agent(
|
||||
model=mock_model,
|
||||
config=config,
|
||||
checkpointer=checkpointer,
|
||||
tools=naliia_tools,
|
||||
)
|
||||
|
||||
yield
|
||||
|
||||
# Cleanup
|
||||
deps.agent_instance = None
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def test_app():
|
||||
"""Create a test FastAPI app with in-memory dependencies."""
|
||||
app = FastAPI(
|
||||
title="NaliiaBot API - Test",
|
||||
description="Test API for NaliiaBot",
|
||||
version="1.0.0",
|
||||
root_path="/agent/api",
|
||||
lifespan=test_lifespan,
|
||||
)
|
||||
|
||||
app.include_router(chat_router)
|
||||
app.include_router(chat_hook_router)
|
||||
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
return {"Hello": "World"}
|
||||
|
||||
@app.get("/health")
|
||||
def health_check():
|
||||
return {"status": "ok"}
|
||||
|
||||
return app
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
async def async_client():
|
||||
async def async_client(test_app):
|
||||
"""Fixture para un cliente HTTP asíncrono."""
|
||||
|
||||
async with AsyncClient(
|
||||
transport=ASGITransport(app=app),
|
||||
transport=ASGITransport(app=test_app),
|
||||
base_url="http://localhost:8010"
|
||||
) as client:
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from src.naliiabotapi.main import app
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client():
|
||||
with TestClient(app) as client:
|
||||
def client(test_app):
|
||||
"""Create a test client using the test app with in-memory dependencies."""
|
||||
with TestClient(test_app) as client:
|
||||
yield client
|
||||
|
||||
|
||||
def test_root(client):
|
||||
response = client.get("/")
|
||||
response = client.get("/agent/api")
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
@@ -22,7 +22,9 @@ def test_health(client):
|
||||
|
||||
|
||||
def test_chat(client):
|
||||
response = client.post("/chat", json={"messages": "Hello, how are you?"})
|
||||
response = client.post(
|
||||
"/chat", json={"messages": "Hello, how are you?"}
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json() is not None
|
||||
|
||||
Reference in New Issue
Block a user