Skip to content

Commit cde9d10

Browse files
authored
Merge pull request #10 from getmarkus/cm-branch-9
refactor(tests): enhance test structure with fixtures for DB sessions
2 parents ea763df + 4883279 commit cde9d10

File tree

6 files changed

+142
-88
lines changed

6 files changed

+142
-88
lines changed

main.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,27 +21,28 @@
2121
# https://betterstack.com/community/guides/logging/loguru/
2222

2323
env = os.getenv("PYTHON_TEMPLATE_ENV", ".env")
24-
running = False
25-
2624

2725
def isRunning() -> bool:
28-
logger.info(f"running: {running}")
26+
#logger.info(f"App state: {dict(app.state.__dict__)}")
27+
running = getattr(app.state, "running", False)
28+
logger.info(f"Running state: {running}")
2929
return running
3030

3131

3232
# https://fastapi.tiangolo.com/advanced/events/
3333
@asynccontextmanager
3434
async def lifespan(app: FastAPI):
35-
global running
36-
running = True
35+
app.state.running = True
3736
logger.info("Lifespan started")
37+
logger.info(f"App state: {dict(app.state.__dict__)}")
38+
logger.info(app.state.running)
3839

3940
# Initialize database if using SQLModel
4041
if Settings.get_settings().execution_mode == "sqlmodel" and not Settings.get_settings().migrate_database:
4142
init_db()
4243

4344
yield
44-
running = False
45+
app.state.running = False
4546
logger.info("Lifespan stopped")
4647

4748

@@ -164,7 +165,6 @@ async def add_process_time_header(request, call_next):
164165
async def root(
165166
settings: Annotated[Settings, Depends(Settings.get_settings)],
166167
) -> Dict[str, Any]:
167-
logger.info(f"running: {running}")
168168
return {
169169
"app_name": settings.project_name,
170170
"system_time": datetime.datetime.now(),

src/app/usecases/analyze_issue.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ def __init__(
2323
def analyze(self) -> Issue:
2424
logger.info(f"analyzing issue: {self.issue_number}")
2525
issue = self.repo.get_by_id(self.issue_number)
26+
logger.info(f"issue: {issue}")
2627
if issue.issue_number == 0:
2728
raise NotFoundException(
2829
message="Issue not found",

src/interface_adapters/endpoints/issues.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
# https://fastapi.tiangolo.com/tutorial/dependencies/
2020
def configure_repository(
2121
session: Annotated[Session, Depends(get_db)]
22-
#session: SessionDep,
2322
) -> IssueRepository:
2423
execution_mode = Settings.get_settings().execution_mode
2524
if execution_mode == "in-memory":

tests/test_issues.py

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,51 @@
1+
import pytest
12
from fastapi.testclient import TestClient
3+
from sqlmodel import Session, SQLModel, create_engine
4+
from sqlmodel.pool import StaticPool
25

3-
from src.interface_adapters.endpoints import issues
6+
from main import app
7+
from src.domain.issue import Issue, IssueState
8+
from src.resource_adapters.persistence.sqlmodel.database import get_db
9+
from src.resource_adapters.persistence.sqlmodel.issues import SQLModelIssueRepository
410

5-
client = TestClient(issues.router)
11+
12+
# https://sqlmodel.tiangolo.com/tutorial/fastapi/tests/
13+
@pytest.fixture(name="session")
14+
def session_fixture():
15+
engine = create_engine(
16+
"sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool
17+
)
18+
SQLModel.metadata.create_all(engine)
19+
with Session(engine) as session:
20+
yield session
21+
22+
23+
@pytest.fixture(name="client")
24+
def client_fixture(session: Session):
25+
def get_session_override():
26+
return session
27+
28+
app.dependency_overrides[get_db] = get_session_override
29+
app.state.running = True
30+
client = TestClient(app)
31+
yield client
32+
app.dependency_overrides.clear()
33+
app.state.running = False
634

735

836
class TestAnalyzeIssue:
9-
def test_analyze_issue(self):
37+
def test_analyze_issue(self, client: TestClient, session: Session):
1038
# Test case 1: Successful analysis
11-
response = client.post("/issues/123/analyze")
12-
assert response.status_code == 200
13-
assert response.json() == {"version": 1, "issue_number": 123}
39+
issue_number = 1
40+
test_issue = Issue(issue_number=issue_number, issue_state=IssueState.OPEN)
41+
42+
repository = SQLModelIssueRepository(session)
43+
44+
repository.add(test_issue)
45+
46+
response = client.post("/issues/1/analyze")
47+
#assert response.status_code == 200
48+
assert response.json() == {"version": 1, "issue_number": 1}
1449

1550
# Test case 2: Invalid issue number
1651
try:

tests/test_main.py

Lines changed: 53 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,59 @@
1+
import pytest
12
from fastapi.testclient import TestClient
3+
from sqlmodel import Session, SQLModel, create_engine
4+
from sqlmodel.pool import StaticPool
25

36
from main import app
7+
from src.resource_adapters.persistence.sqlmodel.database import get_db
8+
9+
10+
# https://sqlmodel.tiangolo.com/tutorial/fastapi/tests/
11+
@pytest.fixture(name="session")
12+
def session_fixture():
13+
engine = create_engine(
14+
"sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool
15+
)
16+
SQLModel.metadata.create_all(engine)
17+
with Session(engine) as session:
18+
yield session
19+
20+
21+
@pytest.fixture(name="client")
22+
def client_fixture(session: Session):
23+
def get_session_override():
24+
return session
25+
26+
app.dependency_overrides[get_db] = get_session_override
27+
app.state.running = True
28+
client = TestClient(app)
29+
yield client
30+
app.dependency_overrides.clear()
31+
app.state.running = False
432

533

634
class TestHealth:
7-
def test_root(self):
8-
with TestClient(app) as client:
9-
response = client.get("/")
10-
assert response.status_code == 200
11-
assert response.json()["app_name"] == "python-template"
12-
13-
def test_health(self):
14-
with TestClient(app) as client:
15-
response = client.get("/health")
16-
assert response.status_code == 200
17-
18-
def test_startup(self):
19-
with TestClient(app) as client:
20-
response = client.get("/startup")
21-
assert response.status_code == 200
22-
23-
def test_readiness(self):
24-
with TestClient(app) as client:
25-
response = client.get("/readiness")
26-
assert response.status_code == 200
27-
28-
def test_liveness(self):
29-
with TestClient(app) as client:
30-
response = client.get("/liveness")
31-
assert response.status_code == 200
32-
33-
def test_smoke(self):
34-
with TestClient(app) as client:
35-
response = client.get("/smoke")
36-
assert response.status_code == 200
35+
36+
def test_root(self, client: TestClient):
37+
response = client.get("/")
38+
assert response.status_code == 200
39+
assert response.json()["app_name"] == "python-template"
40+
41+
def test_health(self, client: TestClient):
42+
response = client.get("/health")
43+
assert response.status_code == 200
44+
45+
def test_startup(self, client: TestClient):
46+
response = client.get("/startup")
47+
assert response.status_code == 200
48+
49+
def test_readiness(self, client: TestClient):
50+
response = client.get("/readiness")
51+
assert response.status_code == 200
52+
53+
def test_liveness(self, client: TestClient):
54+
response = client.get("/liveness")
55+
assert response.status_code == 200
56+
57+
def test_smoke(self, client: TestClient):
58+
response = client.get("/smoke")
59+
assert response.status_code == 200

tests/test_sqlmodel_repository.py

Lines changed: 40 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,111 +1,107 @@
11
import pytest
2-
from sqlmodel import SQLModel
2+
from sqlmodel import Session, SQLModel, create_engine
3+
from sqlmodel.pool import StaticPool
34

45
from src.domain.issue import Issue, IssueState
5-
from src.resource_adapters.persistence.sqlmodel.database import get_engine
6-
from src.resource_adapters.persistence.sqlmodel.unit_of_work import SQLModelUnitOfWork
6+
from src.resource_adapters.persistence.sqlmodel.issues import SQLModelIssueRepository
77

88

9-
@pytest.fixture
10-
def uow():
11-
"""Create a SQLModelUnitOfWork with in-memory database."""
12-
# Reset the global engine for each test
13-
import src.resource_adapters.persistence.sqlmodel.database as db
14-
15-
db._engine = None
16-
17-
# Create a fresh database
18-
database_url = "sqlite://"
19-
uow = SQLModelUnitOfWork(database_url=database_url)
20-
engine = get_engine(database_url)
21-
SQLModel.metadata.create_all(engine)
22-
return uow
23-
24-
25-
class TestSQLModelRepository:
9+
# https://sqlmodel.tiangolo.com/tutorial/fastapi/tests/
10+
class TestSQLModelIssueRepository:
2611
"""Test suite for SQLModel-based issue repository."""
2712

28-
@pytest.mark.anyio
29-
async def test_add_and_get_issue(self, uow):
13+
@pytest.fixture(name="session")
14+
def session_fixture(self):
15+
engine = create_engine(
16+
"sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool
17+
)
18+
SQLModel.metadata.create_all(engine)
19+
with Session(engine) as session:
20+
yield session
21+
22+
def test_add_and_get_issue(self, session: Session):
3023
"""Test adding and retrieving an issue."""
3124
issue_number = 1
3225
test_issue = Issue(issue_number=issue_number, issue_state=IssueState.OPEN)
3326

27+
uow = SQLModelIssueRepository(session)
28+
3429
with uow:
35-
await uow.issues.add(test_issue)
30+
uow.add(test_issue)
3631
uow.commit()
3732

3833
with uow:
39-
retrieved_issue = await uow.issues.get_by_id(issue_number)
34+
retrieved_issue = uow.get_by_id(issue_number)
4035
assert retrieved_issue.issue_number == issue_number
4136
assert retrieved_issue.issue_state == IssueState.OPEN
4237

43-
@pytest.mark.anyio
44-
async def test_list_issues(self, uow):
38+
def test_list_issues(self, session: Session):
4539
"""Test listing all issues."""
4640
issue_number = 1
4741
test_issue = Issue(issue_number=issue_number, issue_state=IssueState.OPEN)
4842

43+
uow = SQLModelIssueRepository(session)
44+
4945
with uow:
50-
await uow.issues.add(test_issue)
46+
uow.add(test_issue)
5147
uow.commit()
5248

5349
with uow:
54-
issues = await uow.issues.list()
50+
issues = uow.list()
5551
assert len(issues) == 1
5652
assert issues[0].issue_number == issue_number
5753

58-
@pytest.mark.anyio
59-
async def test_update_issue(self, uow):
54+
def test_update_issue(self, session: Session):
6055
"""Test updating an issue's state."""
6156
issue_number = 1
6257
test_issue = Issue(issue_number=issue_number, issue_state=IssueState.OPEN)
6358

59+
uow = SQLModelIssueRepository(session)
6460
with uow:
65-
await uow.issues.add(test_issue)
61+
uow.add(test_issue)
6662
uow.commit()
6763

6864
with uow:
69-
issue_to_update = await uow.issues.get_by_id(issue_number)
65+
issue_to_update = uow.get_by_id(issue_number)
7066
issue_to_update.issue_state = IssueState.CLOSED
71-
await uow.issues.update(issue_to_update)
67+
uow.update(issue_to_update)
7268
uow.commit()
7369

7470
with uow:
75-
updated_issue = await uow.issues.get_by_id(issue_number)
71+
updated_issue = uow.get_by_id(issue_number)
7672
assert updated_issue.issue_state == IssueState.CLOSED
7773

78-
@pytest.mark.anyio
79-
async def test_filter_issues(self, uow):
74+
def test_filter_issues(self, session: Session):
8075
"""Test filtering issues with a predicate."""
8176
issue_number = 1
8277
test_issue = Issue(issue_number=issue_number, issue_state=IssueState.CLOSED)
8378

79+
uow = SQLModelIssueRepository(session)
8480
with uow:
85-
await uow.issues.add(test_issue)
81+
uow.add(test_issue)
8682
uow.commit()
8783

8884
with uow:
89-
closed_issues = await uow.issues.list_with_predicate(
85+
closed_issues = uow.list_with_predicate(
9086
lambda i: i.issue_state == IssueState.CLOSED
9187
)
9288
assert len(closed_issues) == 1
9389

94-
@pytest.mark.anyio
95-
async def test_remove_issue(self, uow):
90+
def test_remove_issue(self, session: Session):
9691
"""Test removing an issue."""
9792
issue_number = 1
9893
test_issue = Issue(issue_number=issue_number, issue_state=IssueState.OPEN)
9994

95+
uow = SQLModelIssueRepository(session)
10096
with uow:
101-
await uow.issues.add(test_issue)
97+
uow.add(test_issue)
10298
uow.commit()
10399

104100
with uow:
105-
issue_to_remove = await uow.issues.get_by_id(issue_number)
106-
await uow.issues.remove(issue_to_remove)
101+
issue_to_remove = uow.get_by_id(issue_number)
102+
uow.remove(issue_to_remove)
107103
uow.commit()
108104

109105
with uow:
110-
remaining_issues = await uow.issues.list()
106+
remaining_issues = uow.list()
111107
assert len(remaining_issues) == 0

0 commit comments

Comments
 (0)