forked from Rockachopa/Timmy-time-dashboard
fix: test DB isolation, Discord recovery, and over-mocked tests
Test data was bleeding into production tasks.db because swarm.task_queue.models.DB_PATH (relative path) was never patched in conftest.clean_database. Fixed by switching to absolute paths via settings.repo_root and adding the missing module to the patching list. Discord bot could leak orphaned clients on retry after ERROR state. Added _cleanup_stale() to close stale client/task before each start() attempt, with improved logging in the token watcher. Rewrote test_paperclip_client.py to use httpx.MockTransport instead of patching _get/_post/_delete — tests now exercise real HTTP status codes, error handling, and JSON parsing. Added end-to-end test for capture_error → create_task DB isolation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -235,10 +235,18 @@ async def _discord_token_watcher() -> None:
|
||||
|
||||
if token:
|
||||
try:
|
||||
logger.info(
|
||||
"Discord watcher: token found, attempting start (state=%s)",
|
||||
discord_bot.state.name,
|
||||
)
|
||||
success = await discord_bot.start(token=token)
|
||||
if success:
|
||||
logger.info("Discord bot auto-started (token detected)")
|
||||
return # Done — stop watching
|
||||
logger.warning(
|
||||
"Discord watcher: start() returned False (state=%s)",
|
||||
discord_bot.state.name,
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.warning("Discord auto-start failed: %s", exc)
|
||||
|
||||
|
||||
@@ -9,13 +9,14 @@ from pathlib import Path
|
||||
from fastapi import APIRouter, Form, HTTPException, Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
|
||||
from config import settings
|
||||
from dashboard.templating import templates
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter(tags=["work-orders"])
|
||||
|
||||
DB_PATH = Path("data/work_orders.db")
|
||||
DB_PATH = Path(settings.repo_root) / "data" / "work_orders.db"
|
||||
|
||||
PRIORITIES = ["low", "medium", "high", "critical"]
|
||||
CATEGORIES = ["bug", "feature", "suggestion", "maintenance", "security"]
|
||||
|
||||
26
src/integrations/chat_bridge/vendors/discord.py
vendored
26
src/integrations/chat_bridge/vendors/discord.py
vendored
@@ -134,6 +134,12 @@ class DiscordVendor(ChatPlatform):
|
||||
logger.warning("Discord bot: no token configured, skipping start.")
|
||||
return False
|
||||
|
||||
# Clean up any stale client/task from a previous failed attempt
|
||||
# so we don't leak background tasks or hold orphaned connections.
|
||||
# Must happen before the import check — the old client could exist
|
||||
# from when discord.py was available.
|
||||
await self._cleanup_stale()
|
||||
|
||||
try:
|
||||
import discord
|
||||
except ImportError:
|
||||
@@ -163,9 +169,10 @@ class DiscordVendor(ChatPlatform):
|
||||
logger.info("Discord bot connected (%d guilds).", self._guild_count)
|
||||
return True
|
||||
if self._state == PlatformState.ERROR:
|
||||
logger.warning("Discord bot: entered ERROR state during connection.")
|
||||
return False
|
||||
|
||||
logger.warning("Discord bot: connection timed out.")
|
||||
logger.warning("Discord bot: connection timed out after 15s.")
|
||||
self._state = PlatformState.ERROR
|
||||
return False
|
||||
|
||||
@@ -176,6 +183,23 @@ class DiscordVendor(ChatPlatform):
|
||||
self._client = None
|
||||
return False
|
||||
|
||||
async def _cleanup_stale(self) -> None:
|
||||
"""Close any orphaned client/task from a previous failed start."""
|
||||
if self._client and not self._client.is_closed():
|
||||
try:
|
||||
await self._client.close()
|
||||
except Exception:
|
||||
pass
|
||||
self._client = None
|
||||
|
||||
if self._task and not self._task.done():
|
||||
self._task.cancel()
|
||||
try:
|
||||
await self._task
|
||||
except (asyncio.CancelledError, Exception):
|
||||
pass
|
||||
self._task = None
|
||||
|
||||
async def stop(self) -> None:
|
||||
"""Gracefully disconnect the Discord bot."""
|
||||
if self._client and not self._client.is_closed():
|
||||
|
||||
@@ -13,7 +13,14 @@ from pathlib import Path
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
DB_PATH = Path("data/tasks.db")
|
||||
# Use absolute path via settings.repo_root so tests can reliably redirect it
|
||||
# and relative-path CWD differences don't cause DB leaks.
|
||||
try:
|
||||
from config import settings as _settings
|
||||
|
||||
DB_PATH = Path(_settings.repo_root) / "data" / "tasks.db"
|
||||
except Exception:
|
||||
DB_PATH = Path("data/tasks.db")
|
||||
|
||||
|
||||
@dataclass
|
||||
|
||||
Reference in New Issue
Block a user