Full optimization: 38 improvements across frontend, backend, infrastructure, and docs
Frontend (17 items): - Virtualized message list with batch loading - CSS split with skeleton, drawer, search filter, message action styles - Code splitting via React.lazy + Suspense for Admin/Ethics/Settings pages - Skeleton loading components (Skeleton, SkeletonCard, SkeletonGrid) - Debounced search/filter component (SearchFilter) - Error boundary with fallback UI - Keyboard shortcuts (Ctrl+K search, Ctrl+Enter send, Escape dismiss) - Page transition animations (fade-in) - PWA support (manifest.json + service worker) - WebSocket auto-reconnect with exponential backoff (10 retries) - Chat history persistence to localStorage (500 msg limit) - Message edit/delete on hover - Copy-to-clipboard on code blocks - Mobile drawer (bottom-sheet for consensus panel) - File upload support - User preferences sync to backend Testing (8 items): - Component tests: Toast, Markdown, ChatMessage, Avatar, ErrorBoundary, Skeleton - Hook tests: useChatHistory - E2E smoke tests (5 tests) - Accessibility audit utility Backend (12 items): - Vector memory with cosine similarity search - TTS/STT adapter factory wiring - Geometry kernel with orphan detection - Tenant registry with CRUD operations - Response cache with TTL - Connection pool (async) - Background task queue - Health check endpoints (/health, /ready) - Request tracing middleware (X-Request-ID) - API key rotation mechanism - Environment-based config (settings.py) - API route documentation improvements Infrastructure (4 items): - Grafana dashboard template - Database migration system - Storybook configuration Documentation (3 items): - ADR-001: Advisory Governance Model - ADR-002: Twelve-Head Architecture - ADR-003: Consequence Engine 552 Python tests + 45 frontend tests passing, 0 ruff errors. Co-Authored-By: Nakamoto, S <defi@defi-oracle.io>
This commit is contained in:
64
tests/test_cache.py
Normal file
64
tests/test_cache.py
Normal file
@@ -0,0 +1,64 @@
|
||||
"""Tests for response cache."""
|
||||
|
||||
import time
|
||||
|
||||
from fusionagi.api.cache import ResponseCache
|
||||
|
||||
|
||||
def test_cache_set_and_get():
|
||||
cache = ResponseCache(max_size=10, ttl_seconds=60)
|
||||
cache.set("hello", "s1", {"answer": "world"})
|
||||
result = cache.get("hello", "s1")
|
||||
assert result == {"answer": "world"}
|
||||
|
||||
|
||||
def test_cache_miss():
|
||||
cache = ResponseCache()
|
||||
assert cache.get("nonexistent", "s1") is None
|
||||
|
||||
|
||||
def test_cache_ttl_expiry():
|
||||
cache = ResponseCache(ttl_seconds=0.01)
|
||||
cache.set("prompt", "s1", "cached")
|
||||
time.sleep(0.02)
|
||||
assert cache.get("prompt", "s1") is None
|
||||
|
||||
|
||||
def test_cache_invalidate():
|
||||
cache = ResponseCache()
|
||||
cache.set("p", "s", "val")
|
||||
assert cache.invalidate("p", "s") is True
|
||||
assert cache.get("p", "s") is None
|
||||
|
||||
|
||||
def test_cache_clear():
|
||||
cache = ResponseCache()
|
||||
cache.set("a", "s", 1)
|
||||
cache.set("b", "s", 2)
|
||||
count = cache.clear()
|
||||
assert count == 2
|
||||
assert cache.get("a", "s") is None
|
||||
|
||||
|
||||
def test_cache_max_size():
|
||||
cache = ResponseCache(max_size=2)
|
||||
cache.set("a", "s", 1)
|
||||
cache.set("b", "s", 2)
|
||||
cache.set("c", "s", 3)
|
||||
assert cache.stats()["total"] == 2
|
||||
|
||||
|
||||
def test_cache_stats():
|
||||
cache = ResponseCache(max_size=100)
|
||||
cache.set("a", "s", 1)
|
||||
stats = cache.stats()
|
||||
assert stats["total"] == 1
|
||||
assert stats["max_size"] == 100
|
||||
|
||||
|
||||
def test_cache_tenant_isolation():
|
||||
cache = ResponseCache()
|
||||
cache.set("prompt", "s1", "tenant_a_result", tenant_id="a")
|
||||
cache.set("prompt", "s1", "tenant_b_result", tenant_id="b")
|
||||
assert cache.get("prompt", "s1", "a") == "tenant_a_result"
|
||||
assert cache.get("prompt", "s1", "b") == "tenant_b_result"
|
||||
30
tests/test_config.py
Normal file
30
tests/test_config.py
Normal file
@@ -0,0 +1,30 @@
|
||||
"""Tests for environment-based configuration."""
|
||||
|
||||
from fusionagi.settings import FusionAGIConfig, load_config
|
||||
|
||||
|
||||
def test_default_config():
|
||||
config = FusionAGIConfig()
|
||||
assert config.api.host == "0.0.0.0"
|
||||
assert config.api.port == 8000
|
||||
assert config.api.rate_limit == 120
|
||||
assert config.database.url == "sqlite:///fusionagi.db"
|
||||
assert config.cache.enabled is True
|
||||
assert config.governance.mode == "advisory"
|
||||
|
||||
|
||||
def test_load_config_from_env(monkeypatch):
|
||||
monkeypatch.setenv("FUSIONAGI_API_PORT", "9000")
|
||||
monkeypatch.setenv("FUSIONAGI_LOG_LEVEL", "DEBUG")
|
||||
config = load_config()
|
||||
assert config.api.port == 9000
|
||||
assert config.logging.level == "DEBUG"
|
||||
|
||||
|
||||
def test_config_sections():
|
||||
config = FusionAGIConfig()
|
||||
assert hasattr(config, 'api')
|
||||
assert hasattr(config, 'database')
|
||||
assert hasattr(config, 'cache')
|
||||
assert hasattr(config, 'logging')
|
||||
assert hasattr(config, 'governance')
|
||||
65
tests/test_connection_pool.py
Normal file
65
tests/test_connection_pool.py
Normal file
@@ -0,0 +1,65 @@
|
||||
"""Tests for connection pool."""
|
||||
|
||||
import pytest
|
||||
|
||||
from fusionagi.api.pool import ConnectionPool
|
||||
|
||||
|
||||
class MockConnection:
|
||||
"""Mock connection for testing."""
|
||||
def __init__(self):
|
||||
self.connected = False
|
||||
self.closed = False
|
||||
|
||||
async def connect(self):
|
||||
self.connected = True
|
||||
|
||||
async def close(self):
|
||||
self.closed = True
|
||||
|
||||
def is_alive(self):
|
||||
return self.connected and not self.closed
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pool():
|
||||
return ConnectionPool(factory=MockConnection, min_size=2, max_size=5)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_initialize(pool):
|
||||
await pool.initialize()
|
||||
stats = pool.stats()
|
||||
assert stats["available"] == 2
|
||||
assert stats["total_created"] == 2
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_acquire_and_release(pool):
|
||||
await pool.initialize()
|
||||
conn = await pool.acquire()
|
||||
assert isinstance(conn, MockConnection)
|
||||
stats = pool.stats()
|
||||
assert stats["in_use"] == 1
|
||||
await pool.release(conn)
|
||||
stats = pool.stats()
|
||||
assert stats["in_use"] == 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_close_all(pool):
|
||||
await pool.initialize()
|
||||
await pool.close_all()
|
||||
stats = pool.stats()
|
||||
assert stats["available"] == 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_max_size():
|
||||
pool = ConnectionPool(factory=MockConnection, min_size=1, max_size=2)
|
||||
await pool.initialize()
|
||||
c1 = await pool.acquire()
|
||||
c2 = await pool.acquire()
|
||||
assert pool.stats()["in_use"] == 2
|
||||
await pool.release(c1)
|
||||
await pool.release(c2)
|
||||
47
tests/test_migration.py
Normal file
47
tests/test_migration.py
Normal file
@@ -0,0 +1,47 @@
|
||||
"""Tests for migration system."""
|
||||
|
||||
import os
|
||||
import sqlite3
|
||||
import tempfile
|
||||
|
||||
from migrations.migrate import migrate_down, migrate_up
|
||||
|
||||
|
||||
def test_migrate_up():
|
||||
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
|
||||
db_path = f.name
|
||||
try:
|
||||
count = migrate_up(db_path)
|
||||
assert count >= 1
|
||||
conn = sqlite3.connect(db_path)
|
||||
tables = conn.execute("SELECT name FROM sqlite_master WHERE type='table'").fetchall()
|
||||
table_names = [t[0] for t in tables]
|
||||
assert "sessions" in table_names
|
||||
assert "ethical_lessons" in table_names
|
||||
assert "consequences" in table_names
|
||||
conn.close()
|
||||
finally:
|
||||
os.unlink(db_path)
|
||||
|
||||
|
||||
def test_migrate_down():
|
||||
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
|
||||
db_path = f.name
|
||||
try:
|
||||
migrate_up(db_path)
|
||||
result = migrate_down(db_path)
|
||||
assert result is True
|
||||
finally:
|
||||
os.unlink(db_path)
|
||||
|
||||
|
||||
def test_migrate_idempotent():
|
||||
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
|
||||
db_path = f.name
|
||||
try:
|
||||
count1 = migrate_up(db_path)
|
||||
count2 = migrate_up(db_path)
|
||||
assert count1 >= 1
|
||||
assert count2 == 0
|
||||
finally:
|
||||
os.unlink(db_path)
|
||||
65
tests/test_secret_rotation.py
Normal file
65
tests/test_secret_rotation.py
Normal file
@@ -0,0 +1,65 @@
|
||||
"""Tests for secret rotation mechanism."""
|
||||
|
||||
import time
|
||||
|
||||
from fusionagi.api.secret_rotation import SecretRotator
|
||||
|
||||
|
||||
def test_generate_and_validate():
|
||||
rotator = SecretRotator()
|
||||
key = rotator.generate_key()
|
||||
assert rotator.validate_key(key) is True
|
||||
|
||||
|
||||
def test_invalid_key():
|
||||
rotator = SecretRotator()
|
||||
assert rotator.validate_key("invalid") is False
|
||||
|
||||
|
||||
def test_key_expiry():
|
||||
rotator = SecretRotator()
|
||||
key = rotator.generate_key(ttl_seconds=0.01)
|
||||
assert rotator.validate_key(key) is True
|
||||
time.sleep(0.02)
|
||||
assert rotator.validate_key(key) is False
|
||||
|
||||
|
||||
def test_revoke():
|
||||
rotator = SecretRotator()
|
||||
key = rotator.generate_key()
|
||||
assert rotator.revoke(key) is True
|
||||
assert rotator.validate_key(key) is False
|
||||
|
||||
|
||||
def test_rotate():
|
||||
rotator = SecretRotator()
|
||||
key1 = rotator.generate_key()
|
||||
key2 = rotator.rotate()
|
||||
assert rotator.validate_key(key1) is True
|
||||
assert rotator.validate_key(key2) is True
|
||||
|
||||
|
||||
def test_max_active_keys():
|
||||
rotator = SecretRotator(max_active_keys=2)
|
||||
key1 = rotator.generate_key()
|
||||
rotator.generate_key()
|
||||
rotator.generate_key()
|
||||
assert rotator.validate_key(key1) is False
|
||||
|
||||
|
||||
def test_list_keys():
|
||||
rotator = SecretRotator()
|
||||
rotator.generate_key(label="test")
|
||||
keys = rotator.list_keys()
|
||||
assert len(keys) == 1
|
||||
assert keys[0]["label"] == "test"
|
||||
assert "key_hash" not in keys[0]
|
||||
|
||||
|
||||
def test_revoke_expired():
|
||||
rotator = SecretRotator()
|
||||
rotator.generate_key(ttl_seconds=0.01)
|
||||
rotator.generate_key(ttl_seconds=100)
|
||||
time.sleep(0.02)
|
||||
count = rotator.revoke_expired()
|
||||
assert count == 1
|
||||
68
tests/test_task_queue.py
Normal file
68
tests/test_task_queue.py
Normal file
@@ -0,0 +1,68 @@
|
||||
"""Tests for background task queue."""
|
||||
|
||||
import asyncio
|
||||
|
||||
import pytest
|
||||
|
||||
from fusionagi.api.task_queue import BackgroundTaskQueue, TaskStatus
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def queue():
|
||||
return BackgroundTaskQueue(max_concurrent=3)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_submit_and_complete(queue):
|
||||
async def work():
|
||||
await asyncio.sleep(0.01)
|
||||
return 42
|
||||
|
||||
tid = queue.submit(work)
|
||||
await asyncio.sleep(0.05)
|
||||
result = queue.get_status(tid)
|
||||
assert result is not None
|
||||
assert result.status == TaskStatus.COMPLETED
|
||||
assert result.result == 42
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_failed_task(queue):
|
||||
async def fail():
|
||||
raise ValueError("boom")
|
||||
|
||||
tid = queue.submit(fail)
|
||||
await asyncio.sleep(0.05)
|
||||
result = queue.get_status(tid)
|
||||
assert result is not None
|
||||
assert result.status == TaskStatus.FAILED
|
||||
assert "boom" in (result.error or "")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_tasks(queue):
|
||||
async def noop():
|
||||
pass
|
||||
|
||||
queue.submit(noop)
|
||||
queue.submit(noop)
|
||||
await asyncio.sleep(0.05)
|
||||
tasks = queue.list_tasks()
|
||||
assert len(tasks) == 2
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_tasks_filtered(queue):
|
||||
async def noop():
|
||||
pass
|
||||
|
||||
queue.submit(noop)
|
||||
await asyncio.sleep(0.05)
|
||||
completed = queue.list_tasks(status=TaskStatus.COMPLETED)
|
||||
assert len(completed) == 1
|
||||
pending = queue.list_tasks(status=TaskStatus.PENDING)
|
||||
assert len(pending) == 0
|
||||
|
||||
|
||||
def test_nonexistent_task(queue):
|
||||
assert queue.get_status("nonexistent") is None
|
||||
19
tests/test_tracing.py
Normal file
19
tests/test_tracing.py
Normal file
@@ -0,0 +1,19 @@
|
||||
"""Tests for request tracing."""
|
||||
|
||||
from fusionagi.api.tracing import generate_trace_id, get_trace_id, set_trace_id
|
||||
|
||||
|
||||
def test_generate_trace_id():
|
||||
tid = generate_trace_id()
|
||||
assert len(tid) == 8
|
||||
assert isinstance(tid, str)
|
||||
|
||||
|
||||
def test_set_and_get_trace_id():
|
||||
set_trace_id("abc123")
|
||||
assert get_trace_id() == "abc123"
|
||||
|
||||
|
||||
def test_default_trace_id():
|
||||
set_trace_id("")
|
||||
assert get_trace_id() == ""
|
||||
56
tests/test_vector_memory.py
Normal file
56
tests/test_vector_memory.py
Normal file
@@ -0,0 +1,56 @@
|
||||
"""Tests for vector memory with cosine similarity."""
|
||||
|
||||
from fusionagi.memory.service import VectorMemory
|
||||
|
||||
|
||||
def test_add_and_search():
|
||||
vm = VectorMemory()
|
||||
vm.add("doc1", [1.0, 0.0, 0.0], {"text": "hello"})
|
||||
vm.add("doc2", [0.0, 1.0, 0.0], {"text": "world"})
|
||||
results = vm.search([1.0, 0.0, 0.0], top_k=1)
|
||||
assert len(results) == 1
|
||||
assert results[0]["id"] == "doc1"
|
||||
assert results[0]["score"] > 0.99
|
||||
|
||||
|
||||
def test_cosine_similarity():
|
||||
assert abs(VectorMemory._cosine_similarity([1, 0], [1, 0]) - 1.0) < 0.001
|
||||
assert abs(VectorMemory._cosine_similarity([1, 0], [0, 1])) < 0.001
|
||||
assert abs(VectorMemory._cosine_similarity([1, 1], [1, 1]) - 1.0) < 0.001
|
||||
|
||||
|
||||
def test_zero_vector():
|
||||
assert VectorMemory._cosine_similarity([0, 0], [1, 0]) == 0.0
|
||||
|
||||
|
||||
def test_delete():
|
||||
vm = VectorMemory()
|
||||
vm.add("doc1", [1.0, 0.0])
|
||||
assert vm.count() == 1
|
||||
assert vm.delete("doc1") is True
|
||||
assert vm.count() == 0
|
||||
|
||||
|
||||
def test_max_entries():
|
||||
vm = VectorMemory(max_entries=2)
|
||||
vm.add("a", [1.0])
|
||||
vm.add("b", [2.0])
|
||||
vm.add("c", [3.0])
|
||||
assert vm.count() == 2
|
||||
|
||||
|
||||
def test_search_top_k():
|
||||
vm = VectorMemory()
|
||||
vm.add("a", [1.0, 0.0])
|
||||
vm.add("b", [0.9, 0.1])
|
||||
vm.add("c", [0.0, 1.0])
|
||||
results = vm.search([1.0, 0.0], top_k=2)
|
||||
assert len(results) == 2
|
||||
assert results[0]["id"] == "a"
|
||||
|
||||
|
||||
def test_search_with_metadata():
|
||||
vm = VectorMemory()
|
||||
vm.add("doc", [1.0], {"key": "value"})
|
||||
results = vm.search([1.0])
|
||||
assert results[0]["metadata"]["key"] == "value"
|
||||
Reference in New Issue
Block a user