mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-25 00:36:31 +02:00
test: bootstrap pytest environment for backend
This commit is contained in:
parent
47e6a7f29e
commit
10a6ba6924
10 changed files with 126 additions and 0 deletions
|
|
@ -70,6 +70,21 @@ dependencies = [
|
|||
[dependency-groups]
|
||||
dev = [
|
||||
"ruff>=0.12.5",
|
||||
"pytest>=8.0",
|
||||
"pytest-asyncio>=0.25",
|
||||
"pytest-mock>=3.14",
|
||||
]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
asyncio_mode = "auto"
|
||||
asyncio_default_fixture_loop_scope = "session"
|
||||
testpaths = ["tests"]
|
||||
markers = [
|
||||
"unit: pure logic tests, no DB or external services",
|
||||
"integration: tests that require a real PostgreSQL database",
|
||||
]
|
||||
filterwarnings = [
|
||||
"ignore::UserWarning:chonkie",
|
||||
]
|
||||
|
||||
[tool.ruff]
|
||||
|
|
|
|||
0
surfsense_backend/tests/__init__.py
Normal file
0
surfsense_backend/tests/__init__.py
Normal file
16
surfsense_backend/tests/conftest.py
Normal file
16
surfsense_backend/tests/conftest.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_user_id() -> str:
|
||||
return "00000000-0000-0000-0000-000000000001"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_search_space_id() -> int:
|
||||
return 1
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_connector_id() -> int:
|
||||
return 42
|
||||
0
surfsense_backend/tests/integration/__init__.py
Normal file
0
surfsense_backend/tests/integration/__init__.py
Normal file
46
surfsense_backend/tests/integration/conftest.py
Normal file
46
surfsense_backend/tests/integration/conftest.py
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
|
||||
import os
|
||||
|
||||
import pytest_asyncio
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
|
||||
from sqlalchemy.pool import NullPool
|
||||
|
||||
from app.db import Base
|
||||
|
||||
_DEFAULT_TEST_DB = "postgresql+asyncpg://postgres:postgres@localhost:5432/surfsense_test"
|
||||
TEST_DATABASE_URL = os.environ.get("TEST_DATABASE_URL", _DEFAULT_TEST_DB)
|
||||
|
||||
|
||||
@pytest_asyncio.fixture(scope="session")
|
||||
async def async_engine():
|
||||
engine = create_async_engine(TEST_DATABASE_URL, poolclass=NullPool, echo=False)
|
||||
|
||||
async with engine.begin() as conn:
|
||||
await conn.execute(text("CREATE EXTENSION IF NOT EXISTS vector"))
|
||||
await conn.run_sync(Base.metadata.create_all)
|
||||
|
||||
yield engine
|
||||
|
||||
async with engine.begin() as conn:
|
||||
await conn.run_sync(Base.metadata.drop_all)
|
||||
|
||||
await engine.dispose()
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def db_session(async_engine) -> AsyncSession:
|
||||
# Bind the session to a connection that holds an outer transaction.
|
||||
# join_transaction_mode="create_savepoint" makes session.commit() release
|
||||
# a SAVEPOINT instead of committing the outer transaction, so the final
|
||||
# transaction.rollback() undoes everything — including commits made by the
|
||||
# service under test — leaving the DB clean for the next test.
|
||||
async with async_engine.connect() as conn:
|
||||
transaction = await conn.begin()
|
||||
async with AsyncSession(
|
||||
bind=conn,
|
||||
expire_on_commit=False,
|
||||
join_transaction_mode="create_savepoint",
|
||||
) as session:
|
||||
yield session
|
||||
await transaction.rollback()
|
||||
0
surfsense_backend/tests/unit/__init__.py
Normal file
0
surfsense_backend/tests/unit/__init__.py
Normal file
0
surfsense_backend/tests/unit/adapters/__init__.py
Normal file
0
surfsense_backend/tests/unit/adapters/__init__.py
Normal file
49
surfsense_backend/tests/unit/conftest.py
Normal file
49
surfsense_backend/tests/unit/conftest.py
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
from unittest.mock import AsyncMock, MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
_EMBEDDING_DIM = 4 # keep vectors tiny in tests; real model uses 768+
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_session() -> AsyncMock:
|
||||
session = AsyncMock()
|
||||
session.add = MagicMock() # synchronous in real SQLAlchemy
|
||||
session.execute = AsyncMock()
|
||||
session.scalar = AsyncMock()
|
||||
session.scalars = AsyncMock()
|
||||
session.flush = AsyncMock()
|
||||
session.commit = AsyncMock()
|
||||
session.rollback = AsyncMock()
|
||||
session.refresh = AsyncMock()
|
||||
return session
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_llm() -> AsyncMock:
|
||||
llm = AsyncMock()
|
||||
llm.ainvoke = AsyncMock(return_value=MagicMock(content="Mocked summary."))
|
||||
return llm
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_embedding_model() -> MagicMock:
|
||||
model = MagicMock()
|
||||
model.embed = MagicMock(
|
||||
side_effect=lambda texts: [[0.1] * _EMBEDDING_DIM for _ in texts]
|
||||
)
|
||||
return model
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_chunker() -> MagicMock:
|
||||
chunker = MagicMock()
|
||||
chunker.chunk = MagicMock(return_value=["chunk one", "chunk two"])
|
||||
return chunker
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_code_chunker() -> MagicMock:
|
||||
chunker = MagicMock()
|
||||
chunker.chunk = MagicMock(return_value=["chunk one", "chunk two"])
|
||||
return chunker
|
||||
Loading…
Add table
Add a link
Reference in a new issue