@@ -80,56 +80,8 @@ async def _mock_get_current_user() -> UserResponse:
8080# ---------------------------------------------------------------------------
8181
8282
83- # ---------------------------------------------------------------------------
84- # Shared event loop for test session
85- # ---------------------------------------------------------------------------
86-
87- _test_loop = None
88-
89-
90- def _get_test_loop () -> asyncio .AbstractEventLoop :
91- """Return a shared event loop for synchronous async execution."""
92- global _test_loop
93- if _test_loop is None or _test_loop .is_closed ():
94- _test_loop = asyncio .new_event_loop ()
95- asyncio .set_event_loop (_test_loop )
96- return _test_loop
97-
98-
99- # ---------------------------------------------------------------------------
100- # Application assembly
101- # ---------------------------------------------------------------------------
102-
103-
10483def _create_test_app () -> FastAPI :
10584 """Create a minimal FastAPI app with all routers for E2E testing."""
106- from app .database import engine , Base
107-
108- # Models... (keeping the imports I added)
109- from app .models .notification import NotificationDB # noqa: F401
110- from app .models .user import User # noqa: F401
111- from app .models .bounty_table import BountyTable # noqa: F401
112- from app .models .agent import Agent # noqa: F401
113- from app .models .dispute import DisputeDB , DisputeHistoryDB # noqa: F401
114- from app .models .contributor import ContributorTable # noqa: F401
115- from app .models .submission import SubmissionDB # noqa: F401
116- from app .models .tables import ( # noqa: F401
117- PayoutTable ,
118- BuybackTable ,
119- ReputationHistoryTable ,
120- BountySubmissionTable ,
121- )
122- from app .models .review import AIReviewScoreDB # noqa: F401
123- from app .models .lifecycle import BountyLifecycleLogDB # noqa: F401
124- from app .models .escrow import EscrowTable , EscrowLedgerTable # noqa: F401
125- from app .models .boost import BountyBoostTable # noqa: F401
126-
127- async def _async_init ():
128- async with engine .begin () as conn :
129- await conn .run_sync (Base .metadata .create_all )
130-
131- _get_test_loop ().run_until_complete (_async_init ())
132-
13385 # get_current_user is already imported at module level
13486 from app .api .auth import router as auth_router
13587 from app .api .bounties import router as bounties_router
@@ -181,33 +133,59 @@ async def global_exception_handler(request: Request, exc: Exception):
181133app = _create_test_app ()
182134
183135
184- @pytest .fixture (autouse = True )
185- def clear_stores ():
186- """Reset all in-memory stores, database tables, and factory counters between tests."""
187- from sqlalchemy import delete , text
136+ @pytest_asyncio .fixture (scope = "session" , autouse = True )
137+ async def init_db ():
138+ """Initialise the test database once per session on the shared event loop."""
139+ from app .database import engine , Base
140+ import app .models .notification # noqa: F401
141+ import app .models .user # noqa: F401
142+ import app .models .bounty_table # noqa: F401
143+ import app .models .agent # noqa: F401
144+ import app .models .dispute # noqa: F401
145+ import app .models .contributor # noqa: F401
146+ import app .models .submission # noqa: F401
147+ import app .models .tables # noqa: F401
148+ import app .models .review # noqa: F401
149+ import app .models .lifecycle # noqa: F401
150+ import app .models .escrow # noqa: F401
151+ import app .models .boost # noqa: F401
152+
153+ if os .path .exists (TEST_DB_PATH ):
154+ os .remove (TEST_DB_PATH )
155+
156+ async with engine .begin () as conn :
157+ await conn .run_sync (Base .metadata .create_all )
158+ yield
159+ if os .path .exists (TEST_DB_PATH ):
160+ try :
161+ os .remove (TEST_DB_PATH )
162+ except PermissionError :
163+ pass
164+
165+
166+ @pytest_asyncio .fixture (autouse = True )
167+ async def truncate_db_tables ():
168+ """Truncate all tables between tests to ensure strict isolation."""
169+ from sqlalchemy import text
188170 from app .database import engine , Base
189171 from app .services import bounty_service , contributor_service
190172 from app .services .payout_service import reset_stores as reset_payout_stores
191173 from tests .e2e .factories import reset_counters
192174
193- async def _db_cleanup ():
194- async with engine .begin () as conn :
195- await conn .execute (text ("PRAGMA foreign_keys = OFF" ))
196- for table in reversed (Base .metadata .sorted_tables ):
197- await conn .execute (delete (table ))
198- await conn .execute (text ("PRAGMA foreign_keys = ON" ))
199-
200- _get_test_loop ().run_until_complete (_db_cleanup ())
175+ async with engine .begin () as conn :
176+ await conn .execute (text ("PRAGMA foreign_keys = OFF" ))
177+ for table in reversed (Base .metadata .sorted_tables ):
178+ await conn .execute (text (f"DELETE FROM { table .name } " ))
179+ await conn .execute (text ("PRAGMA foreign_keys = ON" ))
201180
202- # Reset internal service caches if any
181+ # Reset internal service caches
203182 if hasattr (bounty_service , "_bounty_store" ):
204183 bounty_service ._bounty_store .clear ()
205184 if hasattr (contributor_service , "_store" ):
206185 contributor_service ._store .clear ()
207186
208187 reset_payout_stores ()
209188 reset_counters ()
210- yield
211189
212190
213191# ---------------------------------------------------------------------------
0 commit comments