Skip to content

Use Isolated Test Database for Reliable and Consistent Test Runs #230

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ POSTGRES_PORT=5432
POSTGRES_DB=ai_platform
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_DB_TEST=ai_platform_test

SENTRY_DSN=

Expand Down
6 changes: 5 additions & 1 deletion .github/workflows/continuous_integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ jobs:
POSTGRES_DB: ai_platform
ports:
- 5432:5432
options: --health-cmd "pg_isready -U postgres" --health-interval 10s --health-timeout 5s --health-retries 5
options: >-
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@avirajsingh7

Is this intentional?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kurund was part of my previous commit where I was modifying this file.
Either way it is fine

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be good to revert then

--health-cmd "pg_isready -U postgres"
--health-interval 10s
--health-timeout 5s
--health-retries 5

strategy:
matrix:
Expand Down
17 changes: 17 additions & 0 deletions backend/app/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class Settings(BaseSettings):
POSTGRES_USER: str
POSTGRES_PASSWORD: str = ""
POSTGRES_DB: str = ""
POSTGRES_DB_TEST: str | None = None

@computed_field # type: ignore[prop-decorator]
@property
Expand All @@ -60,6 +61,22 @@ def SQLALCHEMY_DATABASE_URI(self) -> PostgresDsn:
path=self.POSTGRES_DB,
)

@computed_field # type: ignore[prop-decorator]
@property
def SQLALCHEMY_TEST_DATABASE_URI(self) -> PostgresDsn:
if not self.POSTGRES_DB_TEST:
raise ValueError(
"POSTGRES_DB_TEST is not set but is required for test configuration."
)
return MultiHostUrl.build(
scheme="postgresql+psycopg",
username=self.POSTGRES_USER,
password=self.POSTGRES_PASSWORD,
host=self.POSTGRES_SERVER,
port=self.POSTGRES_PORT,
path=self.POSTGRES_DB_TEST,
)

EMAIL_RESET_TOKEN_EXPIRE_HOURS: int = 48
EMAIL_TEST_USER: EmailStr

Expand Down
65 changes: 41 additions & 24 deletions backend/app/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,59 @@

import pytest
from fastapi.testclient import TestClient
from sqlmodel import Session, delete
from sqlmodel import Session, SQLModel, create_engine, text

from app.core.config import settings
from app.core.db import engine, init_db
from app.main import app
from app.models import (
APIKey,
Assistant,
Organization,
Project,
ProjectUser,
User,
OpenAI_Thread,
Credential,
Collection,
)
from app.api.deps import get_db
from app.tests.utils.user import authentication_token_from_email
from app.tests.utils.utils import get_superuser_token_headers


def recreate_test_db():
test_db_name = settings.POSTGRES_DB_TEST
if test_db_name is None:
raise ValueError(
"POSTGRES_DB_TEST is not set but is required for test configuration."
)
with engine.connect() as conn:
conn.execution_options(isolation_level="AUTOCOMMIT")
# Disconnect other connections to the test DB
conn.execute(
text(
f"""
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE datname = '{test_db_name}' AND pid <> pg_backend_pid()
"""
)
)
conn.execute(text(f"DROP DATABASE IF EXISTS {test_db_name}"))
conn.execute(text(f"CREATE DATABASE {test_db_name}"))


recreate_test_db()
test_engine = create_engine(str(settings.SQLALCHEMY_TEST_DATABASE_URI))


@pytest.fixture(scope="session", autouse=True)
def db() -> Generator[Session, None, None]:
with Session(engine) as session:
with Session(test_engine) as session:
SQLModel.metadata.create_all(test_engine)
init_db(session)
yield session
# Delete data in reverse dependency order
session.execute(delete(ProjectUser)) # Many-to-many relationship
session.execute(delete(Assistant))
session.execute(delete(Credential))
session.execute(delete(Project))
session.execute(delete(Organization))
session.execute(delete(APIKey))
session.execute(delete(User))
session.execute(delete(OpenAI_Thread))
session.execute(delete(Collection))
session.commit()


# Override the get_db dependency to use test session
@pytest.fixture(scope="session", autouse=True)
def override_get_db(db: Session):
def _get_test_db():
yield db

app.dependency_overrides[get_db] = _get_test_db
yield
app.dependency_overrides.clear()


@pytest.fixture(scope="module")
Expand Down
2 changes: 2 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ services:
- POSTGRES_SERVER=db
- POSTGRES_PORT=${POSTGRES_PORT}
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_DB_TEST=${POSTGRES_DB_TEST}
- POSTGRES_USER=${POSTGRES_USER?Variable not set}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD?Variable not set}
- SENTRY_DSN=${SENTRY_DSN}
Expand Down Expand Up @@ -108,6 +109,7 @@ services:
- POSTGRES_SERVER=db
- POSTGRES_PORT=${POSTGRES_PORT}
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_DB_TEST=${POSTGRES_DB_TEST}
- POSTGRES_USER=${POSTGRES_USER?Variable not set}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD?Variable not set}
- SENTRY_DSN=${SENTRY_DSN}
Expand Down
Loading