Enable pytest-asyncio tests in CI (#3)

* Set asyncio_mode="auto" in pytest.ini config

Xref https://pytest-asyncio.readthedocs.io/en/latest/reference/configuration.html#asyncio-mode

* Add test-python GitHub Actions CI workflow

* Add `--color=yes` flag to force GitHub Actions logs to have color

Xref https://github.com/pytest-dev/pytest/issues/7443

* Refactor contents of test_api.py slightly

Using some ruff rules

* Delete test_api.py

* Make test_get_place async

* Make get_place async, and call .ainvoke method on it
This commit is contained in:
Wei Ji
2025-12-04 13:09:02 +00:00
committed by GitHub
parent 93b405cda4
commit 2d5af2adb9
5 changed files with 54 additions and 53 deletions
+39
View File
@@ -0,0 +1,39 @@
name: Test Python
on:
push:
branches:
- main
pull_request:
permissions:
contents: read # Required to checkout repository
jobs:
pytest:
name: Pytest
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.13"]
steps:
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:
python-version: ${{ matrix.python-version }}
- name: Install a specific version of uv
uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
with:
version: "latest"
- name: Install dependencies
run: uv sync
- name: Run pytest
run: uv run pytest --verbose
+4
View File
@@ -33,3 +33,7 @@ build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel] [tool.hatch.build.targets.wheel]
packages = ["src/geo_assistant"] packages = ["src/geo_assistant"]
[tool.pytest.ini_options]
addopts = "--color=yes"
asyncio_mode = "auto"
+7 -5
View File
@@ -1,10 +1,11 @@
from typing import Annotated
import duckdb
import json import json
from langchain_core.tools import tool from typing import Annotated
from langgraph.types import Command
import duckdb
from langchain_core.messages import ToolMessage from langchain_core.messages import ToolMessage
from langchain_core.tools import tool
from langchain_core.tools.base import InjectedToolCallId from langchain_core.tools.base import InjectedToolCallId
from langgraph.types import Command
def create_database_connection(): def create_database_connection():
@@ -15,6 +16,7 @@ def create_database_connection():
Returns: Returns:
Configured DuckDB connection Configured DuckDB connection
""" """
connection = duckdb.connect() connection = duckdb.connect()
connection.execute("INSTALL spatial;") connection.execute("INSTALL spatial;")
@@ -25,7 +27,7 @@ def create_database_connection():
@tool @tool
def get_place( async def get_place(
place_name: str, tool_call_id: Annotated[str, InjectedToolCallId] = "" place_name: str, tool_call_id: Annotated[str, InjectedToolCallId] = ""
) -> Command: ) -> Command:
"""Get place location from Overture Maps based on user input place name.""" """Get place location from Overture Maps based on user input place name."""
-45
View File
@@ -1,45 +0,0 @@
import pytest
import pytest_asyncio
from httpx import AsyncClient, ASGITransport
from uuid import uuid4
from geo_assistant.api.app import app
from geo_assistant.agent.graph import create_graph
@pytest_asyncio.fixture
async def initialized_app():
"""Initialize the app's chatbot before testing"""
# Manually initialize the chatbot as the lifespan would
app.state.chatbot = await create_graph()
yield app
# Cleanup if needed
if hasattr(app.state, "chatbot"):
del app.state.chatbot
@pytest.mark.asyncio
async def test_hello_world(initialized_app):
"""Hello world test for the API"""
async with AsyncClient(
transport=ASGITransport(app=initialized_app), base_url="http://test"
) as client:
thread_id = uuid4()
response = await client.post(
"/chat",
json={
"agent_state_input": {
"messages": [{"content": "Hello, world!", "type": "human"}],
"feature_collection": None,
},
"thread_id": str(thread_id),
},
)
assert response.status_code == 200
assert response.headers["content-type"] == "application/x-ndjson; charset=utf-8"
# Read the streaming response
content = response.text
assert content is not None
assert len(content) > 0
+4 -3
View File
@@ -1,14 +1,15 @@
from langchain_core.tools.base import ToolCall from langchain_core.tools.base import ToolCall
from geo_assistant.tools.overture import get_place from geo_assistant.tools.overture import get_place
def test_get_place(): async def test_get_place():
command = get_place.invoke( command = await get_place.ainvoke(
ToolCall( ToolCall(
name="get_place", name="get_place",
type="tool_call", type="tool_call",
id="test_id", id="test_id",
args={"place_name": "Neighboourhood Cafe Lisbon"}, args={"place_name": "Neighbourhood Cafe Lisbon"},
) )
) )
assert "place" in command.update assert "place" in command.update