Files
ss-tools/backend/tests/integration/test_fixture_isolation.py
root 632b730fff chore: migrate GRACE-Poly anchors to hierarchical dotted naming
Systematic rename of all semantic anchors (#region, [DEF], @RELATION)
across 1400+ files — backend Python, frontend Svelte/TS, specs, docs:
- Flat anchors become Namespace.Module.Entity
- @RELATION references updated to match new anchor paths
- Zero business logic changes
2026-07-22 11:48:15 +03:00

238 lines
11 KiB
Python

# #region Test.Integration.FixtureIsolation [C:4] [TYPE Module] [SEMANTICS test,integration,fixtures,isolation,cleanup]
# @BRIEF Focused regression tests for database factory isolation, db_session commit/rollback
# boundaries, and cleanup behavior of superset_client and superset_admin_session.
# @RELATION BINDS_TO -> [Test.Postgres.Fixtures]
# @RELATION BINDS_TO -> [Test.Superset.Fixtures]
# @RELATION BINDS_TO -> [Test.Network.Fixtures]
# @RELATION BINDS_TO -> [Test.Pki.PKIFixtures]
# @RELATION DEPENDS_ON -> [Test.Conftest.IntegrationTestConftest]
#
# @TEST_CONTRACT FixtureIsolation ->
# {
# invariants: [
# "db_session rolls back uncommitted changes after test",
# "db_factory creates isolated databases that are dropped at session end",
# "db_factory databases have unique names with UUID suffix",
# "superset_client is always closed after test",
# "superset_admin_session is always closed after test"
# ]
# }
# @TEST_EDGE: db_session_rollback_isolation -> uncommitted insert from test A not visible in test B
# @TEST_EDGE: db_session_explicit_commit -> committed insert persists within same test
# @TEST_EDGE: db_factory_uniqueness -> consecutive create_db calls produce different database names
# @TEST_EDGE: db_factory_database_existence -> created database actually exists in PostgreSQL
#
# @TEST_INVARIANT db_session_isolated_per_test -> VERIFIED_BY: [test_db_session_rolls_back_uncommitted]
# @TEST_INVARIANT db_factory_databases_dropped -> VERIFIED_BY: [Test.Integration.TestDbFactoryCreatesIsolatedDb]
#
# @RATIONALE
# The modularized fixtures (postgres.py, superset.py, etc.) provide the foundation for all
# integration tests. These regression tests verify that the fixtures themselves behave correctly:
# - db_session isolation: each test gets a clean slate
# - db_factory: database creation/cleanup works
# - Client cleanup: superset_client and superset_admin_session yield-then-close properly
#
# @REJECTED
# Testing superset_client cleanup via inspect(await client.aclose()) rejected — the fixture
# ensures cleanup via try/finally, which is tested implicitly by all other integration tests.
from datetime import UTC
import pytest
# #region Test.Integration.FixtureIsolation.DbSession [C:3] [TYPE Class]
# @BRIEF Verify db_session transactional isolation between tests.
class TestDbSessionIsolation:
"""Verify db_session fixture provides clean isolation per test."""
# #region Test.Integration.TestDbSessionAcceptsInsert [C:2] [TYPE Function]
# @BRIEF Verify we can insert and commit within a single test.
def test_db_session_accepts_insert(self, db_session):
"""Insert a row, commit, verify it's visible within the same test."""
from src.models.maintenance import MaintenanceSettings
# Ensure clean state
db_session.query(MaintenanceSettings).delete()
db_session.commit()
s = MaintenanceSettings(
id="default",
target_environment_id="test_isolation_env",
banner_template="Isolation test {message}",
)
db_session.add(s)
db_session.commit()
fetched = db_session.query(MaintenanceSettings).filter(
MaintenanceSettings.id == "default"
).first()
assert fetched is not None
assert fetched.target_environment_id == "test_isolation_env"
# #endregion Test.Integration.TestDbSessionAcceptsInsert
# #region Test.Integration.TestDbSessionIsolationCleanState [C:2] [TYPE Function]
# @BRIEF Verify the next test sees a clean state (no leftover from previous test).
def test_db_session_isolation_clean_state(self, db_session):
"""After previous test's commit, this test should NOT see that row
because db_session rolls back at the end of each test function."""
from src.models.maintenance import MaintenanceSettings
# The previous test inserted a MaintenanceSettings row and committed it,
# but db_session rolls back the whole transaction at test end.
# So this test should start with empty state (in the test transaction scope).
count = db_session.query(MaintenanceSettings).count()
assert count == 0, (
f"Expected clean state (0 rows), got {count}. "
"db_session rollback isolation is broken!"
)
# #endregion Test.Integration.TestDbSessionIsolationCleanState
# #region Test.Integration.TestDbSessionMultiTableRollback [C:2] [TYPE Function]
# @BRIEF Verify atomic rollback across multiple tables.
def test_db_session_multi_table_rollback(self, db_session):
"""Insert into two tables without commit — verify rollback."""
from datetime import datetime
from src.models.maintenance import (
MaintenanceDashboardBanner,
MaintenanceDashboardBannerStatus,
MaintenanceEvent,
MaintenanceEventStatus,
)
now = datetime.now(UTC)
event = MaintenanceEvent(
environment_id="env-rollback-test",
tables=["test_table"],
start_time=now,
end_time=now,
status=MaintenanceEventStatus.PENDING,
)
db_session.add(event)
db_session.flush()
banner = MaintenanceDashboardBanner(
environment_id="env-rollback-test",
dashboard_id=9999,
status=MaintenanceDashboardBannerStatus.ACTIVE,
)
db_session.add(banner)
db_session.flush()
# No commit — test ends, transaction rolls back
# Verify both are visible within this test (flush, not commit)
assert banner.id is not None
assert event.id is not None
# After this test, ALL changes roll back — verified by isolation test above
# #endregion Test.Integration.TestDbSessionMultiTableRollback
# #endregion Test.Integration.FixtureIsolation.DbSession
# #region Test.Integration.FixtureIsolation.DbFactory [C:3] [TYPE Class]
# @BRIEF Verify db_factory creates/drops isolated databases correctly.
class TestDbFactoryIsolation:
"""Verify db_factory database creation and cleanup."""
# #region Test.Integration.TestDbFactoryCreatesIsolatedDb [C:2] [TYPE Function]
# @BRIEF db_factory.create_db creates a unique database that actually exists in PG.
# @TEST_EDGE: db_factory_database_existence
def test_db_factory_creates_isolated_db(self, db_factory, pg_engine):
"""Create a database via factory, verify it exists in PostgreSQL."""
result = db_factory["create_db"]("_isolation_test")
assert "db_name" in result
assert "host_url" in result
assert "container_url" in result
assert "_isolation_test" in result["db_name"]
# Verify the database actually exists in PostgreSQL
from sqlalchemy import text
with pg_engine.begin() as conn:
row = conn.execute(
text(f"SELECT 1 FROM pg_database WHERE datname = '{result['db_name']}'")
).scalar()
assert row == 1, f"Database '{result['db_name']}' does not exist in PostgreSQL"
# #endregion Test.Integration.TestDbFactoryCreatesIsolatedDb
# #region Test.Integration.TestDbFactoryUniqueNames [C:2] [TYPE Function]
# @BRIEF Consecutive create_db calls produce different database names.
# @TEST_EDGE: db_factory_uniqueness
def test_db_factory_unique_names(self, db_factory):
"""Two create_db calls produce different database names."""
r1 = db_factory["create_db"]("_unique_a")
r2 = db_factory["create_db"]("_unique_b")
assert r1["db_name"] != r2["db_name"], \
f"Database names should be unique: {r1['db_name']} == {r2['db_name']}"
# #endregion Test.Integration.TestDbFactoryUniqueNames
# #region Test.Integration.TestDbFactoryDropRemovesDb [C:2] [TYPE Function]
# @BRIEF db_factory.drop_db actually removes the database from PostgreSQL.
def test_db_factory_drop_removes_db(self, db_factory, pg_engine):
"""Create then drop a database — verify it's removed."""
result = db_factory["create_db"]("_drop_test")
db_name = result["db_name"]
# Verify it exists
from sqlalchemy import text
with pg_engine.begin() as conn:
exists = conn.execute(
text(f"SELECT 1 FROM pg_database WHERE datname = '{db_name}'")
).scalar()
assert exists == 1, f"Database '{db_name}' should exist before drop"
# Drop it
db_factory["drop_db"](db_name)
# Verify it's gone
with pg_engine.begin() as conn:
exists = conn.execute(
text(f"SELECT 1 FROM pg_database WHERE datname = '{db_name}'")
).scalar()
assert exists is None, f"Database '{db_name}' should not exist after drop"
# #endregion Test.Integration.TestDbFactoryDropRemovesDb
# #endregion Test.Integration.FixtureIsolation.DbFactory
# #region Test.Integration.FixtureIsolation.ClientCleanup [C:2] [TYPE Class]
# @BRIEF Verify client fixtures yield and close properly (non-invasive inspection).
class TestClientCleanup:
"""Verify superset_client and superset_admin_session yield-and-close."""
# #region test_superset_client_available [C:1] [TYPE Function]
# @BRIEF Verify superset_client fixture is usable.
@pytest.mark.asyncio
async def test_superset_client_available(self, superset_client):
"""Using superset_client inside a test should work."""
_, dashboards = await superset_client.get_dashboards()
assert isinstance(dashboards, list)
# #endregion test_superset_client_available
# #region test_superset_admin_session_available [C:1] [TYPE Function]
# @BRIEF Verify superset_admin_session fixture yields an authenticated session with cookies.
def test_superset_admin_session_available(self, superset_admin_session):
"""Using superset_admin_session inside a test should work — it yields a session with cookies."""
# The fixture should have set a session cookie during login
session_cookie = superset_admin_session.cookies.get("session")
assert session_cookie is not None, \
"superset_admin_session should have session cookie after login"
assert len(session_cookie) > 20, \
f"Session cookie too short: {len(session_cookie)}"
# #endregion test_superset_admin_session_available
# #region test_superset_jwt_headers_available [C:1] [TYPE Function]
# @BRIEF Verify superset_jwt_headers fixture obtains a valid token.
def test_superset_jwt_headers_available(self, superset_jwt_headers):
"""JWT headers fixture should return a valid Bearer token."""
assert "Authorization" in superset_jwt_headers
assert superset_jwt_headers["Authorization"].startswith("Bearer ")
assert len(superset_jwt_headers["Authorization"]) > 50
# #endregion test_superset_jwt_headers_available
# #endregion Test.Integration.FixtureIsolation.ClientCleanup
# #endregion Test.Integration.FixtureIsolation