Ver Fonte

feat: add DB startup diagnostics + fix dashboard test isolation

init_db() now logs the resolved DB_PATH, whether the file exists,
its size, and row counts for all tables after bootstrap. This makes
volume-mount issues visible immediately at startup instead of
surfacing as cryptic 'account not found' errors later.

Dashboard test now uses temp_db (tmp_path + monkeypatch) instead of
relying on the production DB, fixing the pre-existing test failure.

All 21 tests pass.
Lukas Goldschmidt há 1 semana atrás
pai
commit
71ce544110
2 ficheiros alterados com 70 adições e 47 exclusões
  1. 19 0
      src/exec_mcp/storage.py
  2. 51 47
      tests/test_dashboard.py

+ 19 - 0
src/exec_mcp/storage.py

@@ -1,10 +1,13 @@
 from __future__ import annotations
 
+import logging
 import sqlite3
 from contextlib import contextmanager
 from pathlib import Path
 from typing import Iterator
 
+logger = logging.getLogger("exec_mcp")
+
 DB_PATH = Path(__file__).resolve().parents[2] / "data" / "exec_mcp.sqlite3"
 
 
@@ -81,6 +84,13 @@ def _migrate_order_records(conn: sqlite3.Connection) -> None:
 
 
 def init_db() -> None:
+    # Startup diagnostics — log DB path, file size, and row counts so volume
+    # mount problems are visible immediately instead of surfacing as
+    # "account not found" errors later.
+    db_exists = DB_PATH.exists()
+    db_size = DB_PATH.stat().st_size if db_exists else 0
+    logger.info("init_db: path=%s exists=%s size=%d", DB_PATH, db_exists, db_size)
+
     with get_connection() as conn:
         # Non-destructive schema bootstrap. Keep this stable from here on.
         conn.executescript(
@@ -166,3 +176,12 @@ def init_db() -> None:
         )
         _migrate_order_records(conn)
         conn.commit()
+
+        # Log table row counts after bootstrap so we can confirm the DB
+        # was found with data (or not) at startup.
+        for table in ("accounts", "account_secrets", "order_records", "api_cache", "account_balance_snapshots"):
+            try:
+                count = conn.execute(f"SELECT count(*) FROM {table}").fetchone()[0]
+                logger.info("init_db: table %s has %d rows", table, count)
+            except Exception:
+                logger.debug("init_db: could not count table %s", table)

+ 51 - 47
tests/test_dashboard.py

@@ -1,53 +1,57 @@
 from fastapi.testclient import TestClient
 
 from exec_mcp.server import app
-from exec_mcp.repo import list_accounts
-
-client = TestClient(app)
+from exec_mcp import repo, storage
 
 
 def test_health():
-    resp = client.get('/health')
-    assert resp.status_code == 200
-    assert resp.json() == {'ok': True, 'server': 'exec-mcp'}
-
-
-def test_dashboard_account_crud_roundtrip():
-    payload = {
-        'display_name': 'synthetic-test-account',
-        'venue': 'bitstamp',
-        'venue_account_ref': 'synthetic-ref-001',
-        'api_key': 'synthetic-api-key-001',
-        'api_secret': 'synthetic-api-secret-001',
-        'description': 'test row',
-        'enabled': 'on',
-    }
-
-    resp = client.post('/dashboard/accounts/create', data=payload, follow_redirects=True)
-    assert resp.status_code == 200, resp.text
-    assert 'synthetic-test-account' in resp.text
-    assert 'synthetic-ref-001' in resp.text
-
-    resp = client.get('/dashboard')
-    assert resp.status_code == 200
-    assert '/dashboard/accounts/' in resp.text
-
-    account_id = next(a['id'] for a in list_accounts(enabled_only=False) if a['venue_account_ref'] == 'synthetic-ref-001')
-
-    resp = client.get(f'/dashboard/accounts/{account_id}/edit')
-    assert resp.status_code == 200
-    assert 'Edit account' in resp.text
-
-    resp = client.post(
-        f'/dashboard/accounts/{account_id}/update',
-        data={'display_name': 'synthetic-test-account-updated', 'description': 'updated', 'enabled': 'on'},
-        follow_redirects=True,
-    )
-    assert resp.status_code == 200
-    assert 'synthetic-test-account-updated' in resp.text
-
-    resp = client.post(f'/dashboard/accounts/{account_id}/delete', follow_redirects=True)
-    assert resp.status_code == 200
-
-    remaining = list_accounts(enabled_only=False)
-    assert not any(a['id'] == account_id for a in remaining)
+    with TestClient(app) as client:
+        resp = client.get('/health')
+        assert resp.status_code == 200
+        assert resp.json() == {'ok': True, 'server': 'exec-mcp'}
+
+
+def test_dashboard_account_crud_roundtrip(tmp_path, monkeypatch):
+    db_path = tmp_path / "exec.sqlite3"
+    monkeypatch.setattr(storage, "DB_PATH", db_path)
+    storage.init_db()
+
+    with TestClient(app) as client:
+        payload = {
+            'display_name': 'synthetic-test-account',
+            'venue': 'bitstamp',
+            'venue_account_ref': 'synthetic-ref-001',
+            'api_key': 'synthetic-api-key-001',
+            'api_secret': 'synthetic-api-secret-001',
+            'description': 'test row',
+            'enabled': 'on',
+        }
+
+        resp = client.post('/dashboard/accounts/create', data=payload, follow_redirects=True)
+        assert resp.status_code == 200, resp.text
+        assert 'synthetic-test-account' in resp.text
+        assert 'synthetic-ref-001' in resp.text
+
+        resp = client.get('/dashboard')
+        assert resp.status_code == 200
+        assert '/dashboard/accounts/' in resp.text
+
+        account_id = next(a['id'] for a in repo.list_accounts(enabled_only=False) if a['venue_account_ref'] == 'synthetic-ref-001')
+
+        resp = client.get(f'/dashboard/accounts/{account_id}/edit')
+        assert resp.status_code == 200
+        assert 'Edit account' in resp.text
+
+        resp = client.post(
+            f'/dashboard/accounts/{account_id}/update',
+            data={'display_name': 'synthetic-test-account-updated', 'description': 'updated', 'enabled': 'on'},
+            follow_redirects=True,
+        )
+        assert resp.status_code == 200
+        assert 'synthetic-test-account-updated' in resp.text
+
+        resp = client.post(f'/dashboard/accounts/{account_id}/delete', follow_redirects=True)
+        assert resp.status_code == 200
+
+        remaining = repo.list_accounts(enabled_only=False)
+        assert not any(a['id'] == account_id for a in remaining)