Skip to content

Commit 481bbe1

Browse files
committed
Fix migration tooling
1 parent de5011b commit 481bbe1

6 files changed

Lines changed: 54 additions & 26 deletions

File tree

Makefile

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ PROJECT_NAME ?= $(notdir $(abspath .))
1616
INFRA_SERVICES ?= db_pg
1717
INFRA_INIT_SERVICES ?=
1818
MIGRATION_DB_SERVICE ?= db_pg
19-
STAIRWAY_TEST ?= tests/integration/with_infra/test_stairway.py
19+
STAIRWAY_TEST ?= tests/integration/migrations/test_stairway.py
2020

2121
# -----------------------------
2222
# Internal vars / aliases
@@ -44,10 +44,12 @@ PYTEST_PATHS_LIGHT := \
4444
tests/sanity \
4545
tests/unit \
4646
tests/integration/no_infra
47-
PYTEST_PATHS_ALL := \
47+
PYTEST_PATHS_APP_INFRA := \
4848
$(PYTEST_PATHS_LIGHT) \
4949
tests/smoke \
5050
tests/integration/with_infra
51+
PYTEST_PATHS_MIGRATIONS := \
52+
tests/integration/migrations
5153

5254
# Pytest args
5355
PYTEST_ARGS_VERBOSE := -s -vv
@@ -130,14 +132,14 @@ stop-all:
130132
# Migrations
131133
.PHONY: migration
132134
migration: local-env
133-
PROJECT_NAME=$(PROJECT_NAME) \
134135
MIGRATION_DB_SERVICE=$(MIGRATION_DB_SERVICE) \
136+
INFRA_INIT_SERVICES="$(INFRA_INIT_SERVICES)" \
135137
STAIRWAY_TEST=$(STAIRWAY_TEST) \
136138
$(MIGRATION) "$(msg)"
137139

138140
# Tests (with infra)
139-
.PHONY: test-docker
140-
test-docker: docker-env
141+
.PHONY: test-docker test-docker-app test-docker-migrations
142+
test-docker-app: docker-env
141143
rc=0; \
142144
$(DC_TEST_DOCKER) down -v --remove-orphans >/dev/null 2>&1 || true; \
143145
if [ -n "$(strip $(INFRA_SERVICES))" ]; then \
@@ -150,16 +152,36 @@ test-docker: docker-env
150152
fi; \
151153
$(DC_TEST_DOCKER) run --build --name $(TEST_RUNNER) app \
152154
pytest $(PYTEST_ARGS_VERBOSE) \
153-
$(PYTEST_PATHS_ALL) \
155+
$(PYTEST_PATHS_APP_INFRA) \
154156
$(PYTEST_ARGS_COV_DOCKER) \
155157
|| rc=$$?; \
156158
docker cp $(TEST_RUNNER):/tmp/.coverage ./.coverage.docker 2>/dev/null || true; \
157159
docker rm $(TEST_RUNNER) >/dev/null 2>&1 || true; \
158160
$(DC_TEST_DOCKER) down -v --remove-orphans; \
159-
coverage html --data-file=.coverage.docker -d htmlcov-docker && \
160-
echo "Coverage HTML report: htmlcov-docker/index.html" || true; \
161161
exit $$rc
162162

163+
test-docker-migrations: docker-env
164+
if [ -z "$(strip $(PYTEST_PATHS_MIGRATIONS))" ] || [ -z "$(strip $(MIGRATION_DB_SERVICE))" ]; then \
165+
echo "PYTEST_PATHS_MIGRATIONS or MIGRATION_DB_SERVICE is empty, skipping migrations tests"; \
166+
exit 0; \
167+
fi; \
168+
rc=0; \
169+
$(DC_TEST_DOCKER) down -v --remove-orphans >/dev/null 2>&1 || true; \
170+
$(DC_TEST_DOCKER) up -d --build --wait --wait-timeout 180 $(MIGRATION_DB_SERVICE); \
171+
$(DC_TEST_DOCKER) run --build --no-deps --name $(TEST_RUNNER) app \
172+
pytest $(PYTEST_ARGS_VERBOSE) \
173+
$(PYTEST_PATHS_MIGRATIONS) \
174+
|| rc=$$?; \
175+
docker rm $(TEST_RUNNER) >/dev/null 2>&1 || true; \
176+
$(DC_TEST_DOCKER) down -v --remove-orphans; \
177+
exit $$rc
178+
179+
test-docker:
180+
$(MAKE) test-docker-app
181+
$(MAKE) test-docker-migrations
182+
coverage html --data-file=.coverage.docker -d htmlcov-docker && \
183+
echo "Coverage HTML report: htmlcov-docker/index.html" || true
184+
163185
.PHONY: prune
164186
prune:
165187
$(DOCKER_PRUNE)

scripts/makefile/migration.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ if [ "$#" -ne 1 ] || [ -z "$1" ]; then
88
fi
99
slug="$1"
1010

11-
: "${PROJECT_NAME:?must be set in Makefile}"
11+
MIGRATION_PROJECT="$(basename "$PWD")-migration"
1212
: "${MIGRATION_DB_SERVICE:?must be set in Makefile (transactional db service for alembic)}"
1313

14-
trap 'docker compose -p "$PROJECT_NAME" stop "$MIGRATION_DB_SERVICE" >/dev/null' EXIT
14+
trap 'docker compose -p "$MIGRATION_PROJECT" down -v --remove-orphans >/dev/null' EXIT
1515

16-
docker compose -p "$PROJECT_NAME" up -d --build --wait --wait-timeout 180 "$MIGRATION_DB_SERVICE"
16+
docker compose -p "$MIGRATION_PROJECT" up -d --build --wait --wait-timeout 180 "$MIGRATION_DB_SERVICE"
1717
alembic upgrade head
1818
alembic revision --autogenerate -m "$slug"
1919

tests/integration/conftest.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import os
2+
from typing import Final
3+
4+
import pytest
5+
6+
ALLOW_DESTRUCTIVE_TEST_CLEANUP: Final[str] = "ALLOW_DESTRUCTIVE_TEST_CLEANUP"
7+
ALLOW_DESTRUCTIVE_TEST_CLEANUP_EXPECTED_VALUE: Final[str] = "1"
8+
9+
10+
@pytest.fixture(scope="session")
11+
def allow_destructive() -> None:
12+
"""Use on fixtures that require potentially dangerous cleanup."""
13+
if os.getenv(ALLOW_DESTRUCTIVE_TEST_CLEANUP) != ALLOW_DESTRUCTIVE_TEST_CLEANUP_EXPECTED_VALUE:
14+
raise pytest.UsageError(
15+
"Destructive cleanup is disabled: "
16+
f"{ALLOW_DESTRUCTIVE_TEST_CLEANUP} must be set to {ALLOW_DESTRUCTIVE_TEST_CLEANUP_EXPECTED_VALUE}. "
17+
"This guard prevents accidental cleanup of non-test data."
18+
)

tests/integration/migrations/__init__.py

Whitespace-only changes.
File renamed without changes.

tests/integration/with_infra/conftest.py

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import os
21
from collections.abc import AsyncIterator, Sequence
32
from typing import Final, cast
43

@@ -16,19 +15,6 @@
1615
from app.outbound.persistence_sqla.registry import mapper_registry
1716

1817
LIFESPAN_MANAGER_STARTUP_TIMEOUT_S: Final[int] = 30
19-
ALLOW_DESTRUCTIVE_TEST_CLEANUP: Final[str] = "ALLOW_DESTRUCTIVE_TEST_CLEANUP"
20-
ALLOW_DESTRUCTIVE_TEST_CLEANUP_EXPECTED_VALUE: Final[str] = "1"
21-
22-
23-
@pytest.fixture(scope="session")
24-
def allow_destructive() -> None:
25-
"""Use on fixtures that require potentially dangerous cleanup."""
26-
if os.getenv(ALLOW_DESTRUCTIVE_TEST_CLEANUP) != ALLOW_DESTRUCTIVE_TEST_CLEANUP_EXPECTED_VALUE:
27-
raise pytest.UsageError(
28-
"Destructive cleanup is disabled: "
29-
f"{ALLOW_DESTRUCTIVE_TEST_CLEANUP} must be set to {ALLOW_DESTRUCTIVE_TEST_CLEANUP_EXPECTED_VALUE}. "
30-
"This guard prevents accidental cleanup of non-test data."
31-
)
3218

3319

3420
@pytest.fixture
@@ -79,7 +65,9 @@ async def it_db_clean(
7965
it_sessionmaker: async_sessionmaker[AsyncSession],
8066
) -> None:
8167
table_names = [table.name for table in mapper_registry.metadata.sorted_tables if table.name != "alembic_version"]
82-
assert table_names, "it_db_clean: no tables found in mapper_registry.metadata (fixture is a no-op)"
68+
if not table_names:
69+
return
70+
8371
sql = "TRUNCATE " + ", ".join(f'"{name}"' for name in table_names) + " RESTART IDENTITY CASCADE;"
8472

8573
async with it_sessionmaker() as session:

0 commit comments

Comments
 (0)