mirror of
https://github.com/MervinPraison/PraisonAIUI.git
synced 2026-11-07 05:56:17 -10:00
* feat: standardize bootstrap entrypoint to 'aiui run app.py' across examples Standardizes platform bootstrap patterns across examples per issue #71: - Update examples README table to use 'aiui run app.py' consistently - Update docstrings in examples 10, 12, 19-21 to show 'aiui run' instead of 'python app.py' - Remove fragmented bootstrap approaches (uvicorn.run, asyncio patterns) - Enforce single bootstrap SSOT as designed in CLI Co-authored-by: Mervin Praison <[email protected]> * feat: consolidate bootstrap entrypoints - auto-detect gateway apps (fixes #71) - Add bootstrap detection in CLI run command - Auto-detect gateway pattern (async main + AIUIGateway imports) - Auto-switch to praisonai backend for gateway apps - Delegate to user-defined main() when present - Remove sys.path hacks from example 14 - Update examples README to show unified aiui run entrypoint - Add BOOTSTRAP.md documentation for migration patterns Co-authored-by: Mervin Praison <[email protected]> * fix: resolve critical bootstrap entrypoint issues - Fix AST parsing bug in gateway auto-detection (cli.py:1419) - Revert background bots (email-bot, agentmail-bot) to python app.py - Revert custom UI apps (agent-dashboard) to python app.py - Update README.md to reflect correct run commands - Fix import formatting and remove unused imports These changes ensure that: 1. Gateway auto-detection works correctly by properly checking ast.alias.name 2. Background bots execute their __main__ blocks and don't start unnecessary web servers 3. Custom UI apps register their custom routes in __main__ blocks 4. Documentation matches actual working run commands 🤖 Generated with [Claude Code](https://claude.ai/code) Co-authored-by: Mervin Praison <[email protected]> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Mervin Praison <[email protected]> Co-authored-by: Mervin Praison <[email protected]> Co-authored-by: Cursor <[email protected]>
149 lines
5.0 KiB
Python
149 lines
5.0 KiB
Python
"""Tests for the chat feature protocol (Gap 1–4).
|
||
|
||
TDD: These tests define the expected behaviour BEFORE the feature is built.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import pytest
|
||
|
||
# ── Unit tests for ChatFeature protocol ──────────────────────────────
|
||
|
||
class TestChatFeatureProtocol:
|
||
"""Verify the chat feature meets BaseFeatureProtocol."""
|
||
|
||
def test_feature_name(self):
|
||
from praisonaiui.features.chat import PraisonAIChat
|
||
f = PraisonAIChat()
|
||
assert f.name == "chat"
|
||
|
||
def test_feature_description(self):
|
||
from praisonaiui.features.chat import PraisonAIChat
|
||
f = PraisonAIChat()
|
||
assert "chat" in f.description.lower()
|
||
|
||
def test_routes_not_empty(self):
|
||
from praisonaiui.features.chat import PraisonAIChat
|
||
f = PraisonAIChat()
|
||
routes = f.routes()
|
||
assert len(routes) >= 2 # at minimum: WS + REST send
|
||
|
||
def test_route_paths(self):
|
||
from praisonaiui.features.chat import PraisonAIChat
|
||
f = PraisonAIChat()
|
||
paths = [r.path for r in f.routes()]
|
||
assert "/api/chat/ws" in paths or "/api/chat/send" in paths
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_health(self):
|
||
from praisonaiui.features.chat import PraisonAIChat
|
||
f = PraisonAIChat()
|
||
h = await f.health()
|
||
assert h["status"] == "ok"
|
||
assert h["feature"] == "chat"
|
||
|
||
|
||
# ── Unit tests for ChatMessage model ─────────────────────────────────
|
||
|
||
class TestChatMessage:
|
||
"""Verify the chat message dataclass."""
|
||
|
||
def test_chat_message_creation(self):
|
||
from praisonaiui.features.chat import ChatMessage
|
||
msg = ChatMessage(
|
||
role="user",
|
||
content="Hello",
|
||
session_id="s1",
|
||
)
|
||
assert msg.role == "user"
|
||
assert msg.content == "Hello"
|
||
assert msg.session_id == "s1"
|
||
assert msg.message_id # auto-generated
|
||
|
||
def test_chat_message_to_dict(self):
|
||
from praisonaiui.features.chat import ChatMessage
|
||
msg = ChatMessage(role="assistant", content="Hi!", session_id="s1")
|
||
d = msg.to_dict()
|
||
assert d["role"] == "assistant"
|
||
assert d["content"] == "Hi!"
|
||
assert "message_id" in d
|
||
assert "timestamp" in d
|
||
|
||
def test_chat_message_agent_name(self):
|
||
from praisonaiui.features.chat import ChatMessage
|
||
msg = ChatMessage(
|
||
role="assistant",
|
||
content="Result",
|
||
session_id="s1",
|
||
agent_name="Researcher",
|
||
)
|
||
assert msg.agent_name == "Researcher"
|
||
|
||
|
||
# ── Unit tests for ChatProtocol ──────────────────────────────────────
|
||
|
||
class TestChatProtocol:
|
||
"""Verify the chat protocol interface."""
|
||
|
||
def test_protocol_has_send(self):
|
||
from praisonaiui.features.chat import ChatProtocol
|
||
assert hasattr(ChatProtocol, 'send_message')
|
||
|
||
def test_protocol_has_history(self):
|
||
from praisonaiui.features.chat import ChatProtocol
|
||
assert hasattr(ChatProtocol, 'get_history')
|
||
|
||
def test_protocol_has_abort(self):
|
||
from praisonaiui.features.chat import ChatProtocol
|
||
assert hasattr(ChatProtocol, 'abort_run')
|
||
|
||
|
||
# ── Unit tests for ChatManager ───────────────────────────────────────
|
||
|
||
class TestChatManager:
|
||
"""Verify the chat manager (default implementation of ChatProtocol)."""
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_send_message_returns_message_id(self):
|
||
from praisonaiui.features.chat import ChatManager
|
||
mgr = ChatManager()
|
||
result = await mgr.send_message(
|
||
content="Hello",
|
||
session_id="test-session",
|
||
)
|
||
assert "message_id" in result
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_get_history_empty(self):
|
||
from praisonaiui.features.chat import ChatManager
|
||
mgr = ChatManager()
|
||
history = await mgr.get_history("nonexistent")
|
||
assert history == []
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_abort_run(self):
|
||
from praisonaiui.features.chat import ChatManager
|
||
mgr = ChatManager()
|
||
# Should not error on non-existent run
|
||
result = await mgr.abort_run("fake-run-id")
|
||
assert result["status"] in ("aborted", "no_active_run")
|
||
|
||
|
||
# ── Integration: Feature registered in auto_register_defaults ────────
|
||
|
||
class TestAutoRegistration:
|
||
"""Verify chat feature is auto-registered."""
|
||
|
||
def test_chat_in_features(self):
|
||
from praisonaiui.features import auto_register_defaults, get_features
|
||
auto_register_defaults()
|
||
features = get_features()
|
||
assert "chat" in features
|
||
|
||
def test_chat_routes_mounted(self):
|
||
from praisonaiui.features import auto_register_defaults, get_features
|
||
auto_register_defaults()
|
||
chat = get_features()["chat"]
|
||
routes = chat.routes()
|
||
assert len(routes) >= 2
|