mirror of
https://gitcode.com/JianFeeeee/TrulyMEM-TrueHumanMEM.git
synced 2026-09-20 00:48:52 +00:00
complete: comprehensive tests and docs update
- 37 tests covering core, ui, and integration - TestPacketType, Packet, BackendServer, BackendClient tests - UI component imports and functionality tests - Integration workflow tests - Updated README.md with new architecture - Updated docs/架构.md with packet protocol
This commit is contained in:
140
README.md
140
README.md
@ -26,6 +26,7 @@ TrulyMEM (TrueHumanMEM) 是一个让 AI 拥有长期记忆能力的图记忆系
|
||||
- 💾 **内嵌数据库** - SQLite 实现,无需 Docker/Neo4j
|
||||
- 📦 **独立部署** - 支持打包为独立可执行文件
|
||||
- 🌍 **跨平台** - 支持 Windows/Linux/macOS
|
||||
- 🔌 **后端独立** - 后端可脱离 UI 独立运行
|
||||
|
||||
---
|
||||
|
||||
@ -60,6 +61,60 @@ python trulymem_entry.py
|
||||
|
||||
---
|
||||
|
||||
## 架构设计
|
||||
|
||||
### 核心原则
|
||||
|
||||
- **后端独立**: BackendServer 可脱离 UI 独立运行
|
||||
- **数据包通信**: 所有通信使用 Packet 格式 `{id, type, body}`
|
||||
- **多线程**: 后端在独立线程处理请求
|
||||
- **UI 简化**: UI 仅负责输入和显示
|
||||
|
||||
### 数据包协议
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class Packet:
|
||||
id: str # 包ID,用于识别
|
||||
type: PacketType # MESSAGE | CONFIG | TOOL | STATUS | HISTORY
|
||||
body: Dict # 包体
|
||||
```
|
||||
|
||||
### 项目结构
|
||||
|
||||
```
|
||||
TrulyMEM-TrueHumanMEM/
|
||||
├── trulymem_entry.py # 入口
|
||||
├── core/ # 后端(独立运行)
|
||||
│ ├── __init__.py # BackendServer, BackendClient, Packet
|
||||
│ ├── embedded_db.py # SQLite 图数据库
|
||||
│ ├── graph_client.py # LLM 客户端
|
||||
│ └── tools/ # 工具定义
|
||||
├── ui/ # TUI 显示层
|
||||
│ ├── app.py # 主应用
|
||||
│ ├── widgets/ # UI 组件
|
||||
│ └── models/ # 数据模型
|
||||
└── tests/ # 测试 (37 tests)
|
||||
```
|
||||
|
||||
### 启动流程
|
||||
|
||||
```python
|
||||
# 1. 创建后端
|
||||
server = BackendServer(db_path="graph_memory.db")
|
||||
server.start(api_key="your-key")
|
||||
|
||||
# 2. 创建UI(可选,后端可独立使用)
|
||||
app = GraphMemoryApp(backend_server=server)
|
||||
app.run()
|
||||
|
||||
# 或直接使用后端
|
||||
client = BackendClient(server)
|
||||
result = client.send_message("hello")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 功能说明
|
||||
|
||||
### 记忆管理工具
|
||||
@ -96,38 +151,25 @@ python trulymem_entry.py
|
||||
| F1 | 帮助 |
|
||||
| F2 | 切换侧边栏 |
|
||||
| F3 | 工具详情 |
|
||||
| F4 | 聚焦查询框 |
|
||||
| F5 | 清屏 |
|
||||
| F6 | 退出 |
|
||||
|
||||
---
|
||||
|
||||
## 项目结构
|
||||
## 测试
|
||||
|
||||
```bash
|
||||
pytest tests/
|
||||
```
|
||||
TrulyMEM-TrueHumanMEM/
|
||||
├── trulymem_entry.py # 入口:先启动 core → 再启动 ui
|
||||
├── core/ # 后端/业务逻辑
|
||||
│ ├── __init__.py
|
||||
│ ├── server.py # BackendServer (多线程)
|
||||
│ ├── client.py # BackendClient
|
||||
│ ├── embedded_db.py # SQLite 图数据库
|
||||
│ ├── graph_client.py
|
||||
│ ├── tool_executor.py
|
||||
│ ├── tool_limiter.py
|
||||
│ ├── memory_tools.py
|
||||
│ ├── prompts/
|
||||
│ └── tools/ # TOOLS 定义
|
||||
├── ui/ # TUI 显示层
|
||||
│ ├── __init__.py
|
||||
│ ├── app.py
|
||||
│ ├── widgets/
|
||||
│ ├── handlers/
|
||||
│ ├── models/
|
||||
│ ├── services/
|
||||
│ └── styles/
|
||||
└── tests/ # 测试 (38 tests)
|
||||
```
|
||||
|
||||
37 个测试用例覆盖:
|
||||
- 数据包协议
|
||||
- 后端初始化/启动/关闭
|
||||
- 客户端方法
|
||||
- 配置管理
|
||||
- 消息历史
|
||||
- UI 组件
|
||||
- 集成测试
|
||||
|
||||
---
|
||||
|
||||
@ -137,40 +179,8 @@ TrulyMEM-TrueHumanMEM/
|
||||
- **Textual** - TUI 框架
|
||||
- **SQLite** - 内嵌图数据库
|
||||
- **OpenAI SDK** - API 调用(兼容 DeepSeek)
|
||||
|
||||
---
|
||||
|
||||
## 架构说明
|
||||
|
||||
### TUI 与后端通信
|
||||
|
||||
```
|
||||
trulymem_entry.py
|
||||
│
|
||||
├─ 1. BackendServer.start() → 启动独立线程
|
||||
│
|
||||
├─ 2. GraphMemoryApp(backend_server=server)
|
||||
│
|
||||
└─ 3. BackendClient ← Queue → BackendServer
|
||||
```
|
||||
|
||||
- **core/** - 业务逻辑(数据库、API调用、工具执行)
|
||||
- **ui/** - 显示逻辑(Textual 组件)
|
||||
- 多线程 Queue 通信解耦
|
||||
|
||||
---
|
||||
|
||||
## 开发指南
|
||||
|
||||
```bash
|
||||
python -m venv venv
|
||||
source venv/bin/activate # Linux/macOS
|
||||
venv\Scripts\activate # Windows
|
||||
|
||||
pip install -r requirements.txt
|
||||
|
||||
pytest tests/
|
||||
```
|
||||
- **threading** - 多线程通信
|
||||
- **queue** - 线程安全队列
|
||||
|
||||
---
|
||||
|
||||
@ -196,14 +206,4 @@ pytest tests/
|
||||
2. 创建特性分支 (`git checkout -b feature/AmazingFeature`)
|
||||
3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
|
||||
4. 推送到分支 (`git push origin feature/AmazingFeature`)
|
||||
5. 创建 Pull Request
|
||||
|
||||
---
|
||||
|
||||
## 文档
|
||||
|
||||
更多文档见 [docs/](docs/) 目录:
|
||||
|
||||
- [架构设计](docs/架构.md) - 系统架构和技术设计
|
||||
- [快速开始](docs/一键启动指南.md) - 启动指南
|
||||
- [工作记忆链机制说明](docs/工作记忆链机制说明.md) - 连续性任务处理
|
||||
5. 创建 Pull Request
|
||||
@ -1,32 +0,0 @@
|
||||
import pytest
|
||||
|
||||
|
||||
def test_import_backend_server():
|
||||
from core import BackendServer
|
||||
assert BackendServer is not None
|
||||
|
||||
|
||||
def test_import_backend_client():
|
||||
from core import BackendClient
|
||||
assert BackendClient is not None
|
||||
|
||||
|
||||
def test_import_embedded_db():
|
||||
from core import EmbeddedGraphDB
|
||||
assert EmbeddedGraphDB is not None
|
||||
|
||||
|
||||
def test_import_graph_client():
|
||||
from core.graph_client import GraphMemoryClient
|
||||
assert GraphMemoryClient is not None
|
||||
|
||||
|
||||
def test_import_tool_limiter():
|
||||
from core.tool_limiter import ToolLimiter
|
||||
assert ToolLimiter is not None
|
||||
|
||||
|
||||
def test_import_tool_executor():
|
||||
from core.tool_executor import execute_tool
|
||||
assert execute_tool is not None
|
||||
assert callable(execute_tool)
|
||||
312
tests/test_core/test_packet.py
Normal file
312
tests/test_core/test_packet.py
Normal file
@ -0,0 +1,312 @@
|
||||
import pytest
|
||||
import os
|
||||
import tempfile
|
||||
import time
|
||||
import threading
|
||||
import queue
|
||||
|
||||
os.environ["DEEPSEEK_API_KEY"] = "fake-test-key"
|
||||
|
||||
|
||||
class TestPacketTypeEnum:
|
||||
def test_packet_type_message_exists(self):
|
||||
from core import PacketType
|
||||
assert PacketType.MESSAGE is not None
|
||||
assert PacketType.MESSAGE.value == "message"
|
||||
|
||||
def test_packet_type_config_exists(self):
|
||||
from core import PacketType
|
||||
assert PacketType.CONFIG is not None
|
||||
assert PacketType.CONFIG.value == "config"
|
||||
|
||||
def test_packet_type_tool_exists(self):
|
||||
from core import PacketType
|
||||
assert PacketType.TOOL is not None
|
||||
assert PacketType.TOOL.value == "tool"
|
||||
|
||||
def test_packet_type_status_exists(self):
|
||||
from core import PacketType
|
||||
assert PacketType.STATUS is not None
|
||||
assert PacketType.STATUS.value == "status"
|
||||
|
||||
def test_packet_type_history_exists(self):
|
||||
from core import PacketType
|
||||
assert PacketType.HISTORY is not None
|
||||
assert PacketType.HISTORY.value == "history"
|
||||
|
||||
def test_packet_type_all_values(self):
|
||||
from core import PacketType
|
||||
values = [pt.value for pt in PacketType]
|
||||
assert "message" in values
|
||||
assert "config" in values
|
||||
assert "tool" in values
|
||||
assert "status" in values
|
||||
assert "history" in values
|
||||
assert len(values) == 5
|
||||
|
||||
|
||||
class TestPacketCreation:
|
||||
def test_packet_with_id_and_type(self):
|
||||
from core import Packet, PacketType
|
||||
packet = Packet(id="test-1", type=PacketType.MESSAGE, body={"message": "hello"})
|
||||
assert packet.id == "test-1"
|
||||
assert packet.type == PacketType.MESSAGE
|
||||
|
||||
def test_packet_body(self):
|
||||
from core import Packet, PacketType
|
||||
body = {"message": "test", "extra": "data"}
|
||||
packet = Packet(id="test-2", type=PacketType.CONFIG, body=body)
|
||||
assert packet.body == body
|
||||
|
||||
def test_packet_created_at_default(self):
|
||||
from core import Packet, PacketType
|
||||
before = time.time()
|
||||
packet = Packet(id="test-3", type=PacketType.STATUS, body={})
|
||||
after = time.time()
|
||||
assert before <= packet.created_at <= after
|
||||
|
||||
def test_packet_with_custom_created_at(self):
|
||||
from core import Packet, PacketType
|
||||
custom_time = 1234567890.0
|
||||
packet = Packet(id="test-4", type=PacketType.HISTORY, body={}, created_at=custom_time)
|
||||
assert packet.created_at == custom_time
|
||||
|
||||
|
||||
class TestBackendServerInit:
|
||||
def test_create_server_defaults(self):
|
||||
from core import BackendServer
|
||||
server = BackendServer()
|
||||
assert server._db_path == "graph_memory.db"
|
||||
assert server._use_embedded_db is True
|
||||
assert server._graph is None
|
||||
assert server._client is None
|
||||
assert server._running is False
|
||||
|
||||
def test_create_server_custom_db(self):
|
||||
from core import BackendServer
|
||||
server = BackendServer(db_path="custom.db")
|
||||
assert server._db_path == "custom.db"
|
||||
|
||||
def test_create_server_no_embedded(self):
|
||||
from core import BackendServer
|
||||
server = BackendServer(use_embedded_db=False)
|
||||
assert server._use_embedded_db is False
|
||||
|
||||
|
||||
class TestBackendServerStart:
|
||||
def test_start_without_api_key(self):
|
||||
from core import BackendServer
|
||||
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
|
||||
db_path = f.name
|
||||
try:
|
||||
server = BackendServer(db_path=db_path, use_embedded_db=True)
|
||||
server.start(api_key="")
|
||||
assert server._running is True
|
||||
assert server._graph is not None
|
||||
assert server._client is None
|
||||
server.shutdown()
|
||||
finally:
|
||||
if os.path.exists(db_path):
|
||||
os.unlink(db_path)
|
||||
|
||||
def test_start_with_api_key(self):
|
||||
from core import BackendServer
|
||||
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
|
||||
db_path = f.name
|
||||
try:
|
||||
server = BackendServer(db_path=db_path, use_embedded_db=True)
|
||||
server.start(api_key="test-key", base_url="https://api.test.com")
|
||||
assert server._running is True
|
||||
assert server._config["api_key"] == "test-key"
|
||||
assert server._config["base_url"] == "https://api.test.com"
|
||||
server.shutdown()
|
||||
finally:
|
||||
if os.path.exists(db_path):
|
||||
os.unlink(db_path)
|
||||
|
||||
def test_start_twice_returns_early(self):
|
||||
from core import BackendServer
|
||||
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
|
||||
db_path = f.name
|
||||
try:
|
||||
server = BackendServer(db_path=db_path)
|
||||
server.start(api_key="")
|
||||
running_before = server._running
|
||||
server.start(api_key="")
|
||||
running_after = server._running
|
||||
assert running_before is True
|
||||
assert running_after is True
|
||||
server.shutdown()
|
||||
finally:
|
||||
if os.path.exists(db_path):
|
||||
os.unlink(db_path)
|
||||
|
||||
|
||||
class TestBackendServerShutdown:
|
||||
def test_shutdown_stops_server(self):
|
||||
from core import BackendServer
|
||||
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
|
||||
db_path = f.name
|
||||
try:
|
||||
server = BackendServer(db_path=db_path)
|
||||
server.start(api_key="")
|
||||
assert server._running is True
|
||||
server.shutdown()
|
||||
assert server._running is False
|
||||
finally:
|
||||
if os.path.exists(db_path):
|
||||
os.unlink(db_path)
|
||||
|
||||
def test_shutdown_closes_graph(self):
|
||||
from core import BackendServer
|
||||
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
|
||||
db_path = f.name
|
||||
try:
|
||||
server = BackendServer(db_path=db_path)
|
||||
server.start(api_key="")
|
||||
server.shutdown()
|
||||
assert server._graph is None
|
||||
finally:
|
||||
if os.path.exists(db_path):
|
||||
os.unlink(db_path)
|
||||
|
||||
|
||||
class TestBackendServerPacketHandling:
|
||||
def test_send_message_without_client(self):
|
||||
from core import BackendServer, Packet, PacketType
|
||||
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
|
||||
db_path = f.name
|
||||
try:
|
||||
server = BackendServer(db_path=db_path)
|
||||
server.start(api_key="")
|
||||
packet = Packet(id="1", type=PacketType.MESSAGE, body={"message": "hello"})
|
||||
response = server.send(packet)
|
||||
assert response.body["success"] is True
|
||||
assert "API Key not configured" in response.body["error"]
|
||||
server.shutdown()
|
||||
finally:
|
||||
if os.path.exists(db_path):
|
||||
os.unlink(db_path)
|
||||
|
||||
def test_send_status_packet(self):
|
||||
from core import BackendServer, Packet, PacketType
|
||||
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
|
||||
db_path = f.name
|
||||
try:
|
||||
server = BackendServer(db_path=db_path)
|
||||
server.start(api_key="")
|
||||
packet = Packet(id="2", type=PacketType.STATUS, body={})
|
||||
response = server.send(packet)
|
||||
assert response.body["success"] is True
|
||||
assert response.body["running"] is True
|
||||
server.shutdown()
|
||||
finally:
|
||||
if os.path.exists(db_path):
|
||||
os.unlink(db_path)
|
||||
|
||||
|
||||
class TestBackendClientInit:
|
||||
def test_create_client_with_server(self):
|
||||
from core import BackendServer, BackendClient
|
||||
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
|
||||
db_path = f.name
|
||||
try:
|
||||
server = BackendServer(db_path=db_path, use_embedded_db=True)
|
||||
server.start(api_key="")
|
||||
client = BackendClient(server)
|
||||
assert client._server is server
|
||||
assert client._counter == 0
|
||||
server.shutdown()
|
||||
finally:
|
||||
if os.path.exists(db_path):
|
||||
os.unlink(db_path)
|
||||
|
||||
|
||||
class TestBackendClientMethods:
|
||||
def test_get_status(self):
|
||||
from core import BackendServer, BackendClient
|
||||
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
|
||||
db_path = f.name
|
||||
try:
|
||||
server = BackendServer(db_path=db_path, use_embedded_db=True)
|
||||
server.start(api_key="")
|
||||
client = BackendClient(server)
|
||||
status = client.get_status()
|
||||
assert status["success"] is True
|
||||
assert status["running"] is True
|
||||
server.shutdown()
|
||||
finally:
|
||||
if os.path.exists(db_path):
|
||||
os.unlink(db_path)
|
||||
|
||||
def test_update_config(self):
|
||||
from core import BackendServer, BackendClient
|
||||
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
|
||||
db_path = f.name
|
||||
try:
|
||||
server = BackendServer(db_path=db_path, use_embedded_db=True)
|
||||
server.start(api_key="")
|
||||
client = BackendClient(server)
|
||||
result = client.update_config(api_key="new-key", base_url="https://new-api.test.com")
|
||||
assert result["success"] is True
|
||||
assert result["status"] == "config_updated"
|
||||
server.shutdown()
|
||||
finally:
|
||||
if os.path.exists(db_path):
|
||||
os.unlink(db_path)
|
||||
|
||||
def test_save_and_get_history(self):
|
||||
from core import BackendServer, BackendClient
|
||||
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
|
||||
db_path = f.name
|
||||
try:
|
||||
server = BackendServer(db_path=db_path, use_embedded_db=True)
|
||||
server.start(api_key="")
|
||||
client = BackendClient(server)
|
||||
|
||||
test_messages = [{"role": "user", "content": "hello"}]
|
||||
client.save_history(test_messages)
|
||||
|
||||
history = client.get_history()
|
||||
assert history == test_messages
|
||||
server.shutdown()
|
||||
finally:
|
||||
if os.path.exists(db_path):
|
||||
os.unlink(db_path)
|
||||
|
||||
|
||||
class TestMultipleClients:
|
||||
def test_multiple_clients_thread_safety(self):
|
||||
from core import BackendServer, BackendClient
|
||||
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
|
||||
db_path = f.name
|
||||
try:
|
||||
server = BackendServer(db_path=db_path, use_embedded_db=True)
|
||||
server.start(api_key="")
|
||||
client = BackendClient(server)
|
||||
|
||||
status1 = client.get_status()
|
||||
status2 = client.get_status()
|
||||
|
||||
assert status1["success"] is True
|
||||
assert status2["success"] is True
|
||||
server.shutdown()
|
||||
finally:
|
||||
if os.path.exists(db_path):
|
||||
os.unlink(db_path)
|
||||
|
||||
|
||||
class TestServerStateAfterShutdown:
|
||||
def test_server_state_not_running(self):
|
||||
from core import BackendServer
|
||||
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
|
||||
db_path = f.name
|
||||
try:
|
||||
server = BackendServer(db_path=db_path)
|
||||
server.start(api_key="")
|
||||
assert server._running is True
|
||||
server.shutdown()
|
||||
assert server._running is False
|
||||
finally:
|
||||
if os.path.exists(db_path):
|
||||
os.unlink(db_path)
|
||||
@ -1,57 +0,0 @@
|
||||
import pytest
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
os.environ["DEEPSEEK_API_KEY"] = "fake-test-key"
|
||||
|
||||
|
||||
class TestCoreImport:
|
||||
def test_import_backend_server(self):
|
||||
from core import BackendServer
|
||||
assert BackendServer is not None
|
||||
|
||||
def test_import_backend_client(self):
|
||||
from core import BackendClient
|
||||
assert BackendClient is not None
|
||||
|
||||
def test_import_embedded_db(self):
|
||||
from core import EmbeddedGraphDB
|
||||
assert EmbeddedGraphDB is not None
|
||||
|
||||
|
||||
class TestBackendServer:
|
||||
def test_create_server(self):
|
||||
from core import BackendServer
|
||||
server = BackendServer(db_path=":memory:", use_embedded_db=True)
|
||||
assert server is not None
|
||||
assert server._running is False
|
||||
|
||||
def test_start_server(self):
|
||||
from core import BackendServer
|
||||
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
|
||||
db_path = f.name
|
||||
|
||||
try:
|
||||
server = BackendServer(db_path=db_path, use_embedded_db=True)
|
||||
server.start(api_key="", base_url="https://api.test.com")
|
||||
|
||||
assert server._running is True
|
||||
assert server._graph is not None
|
||||
|
||||
server.shutdown()
|
||||
finally:
|
||||
if os.path.exists(db_path):
|
||||
os.unlink(db_path)
|
||||
|
||||
def test_shutdown(self):
|
||||
from core import BackendServer
|
||||
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
|
||||
db_path = f.name
|
||||
|
||||
server = BackendServer(db_path=db_path, use_embedded_db=True)
|
||||
server.start(api_key="", base_url="https://api.test.com")
|
||||
|
||||
assert server._running is True
|
||||
|
||||
server.shutdown()
|
||||
assert server._running is False
|
||||
278
tests/test_integration/__init__.py
Normal file
278
tests/test_integration/__init__.py
Normal file
@ -0,0 +1,278 @@
|
||||
import pytest
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
os.environ["DEEPSEEK_API_KEY"] = "fake-test-key"
|
||||
|
||||
|
||||
class TestIntegrationPacketFlow:
|
||||
def test_packet_round_trip_message(self):
|
||||
from core import BackendServer, BackendClient, Packet, PacketType
|
||||
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
|
||||
db_path = f.name
|
||||
try:
|
||||
server = BackendServer(db_path=db_path, use_embedded_db=True)
|
||||
server.start(api_key="")
|
||||
client = BackendClient(server)
|
||||
|
||||
result = client.send_message("test message")
|
||||
assert result["success"] is True
|
||||
assert "API Key not configured" in result.get("error", "")
|
||||
|
||||
server.shutdown()
|
||||
finally:
|
||||
if os.path.exists(db_path):
|
||||
os.unlink(db_path)
|
||||
|
||||
def test_packet_round_trip_config(self):
|
||||
from core import BackendServer, BackendClient
|
||||
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
|
||||
db_path = f.name
|
||||
try:
|
||||
server = BackendServer(db_path=db_path, use_embedded_db=True)
|
||||
server.start(api_key="")
|
||||
client = BackendClient(server)
|
||||
|
||||
result = client.update_config(api_key="test-api", base_url="https://test.com")
|
||||
assert result["success"] is True
|
||||
assert result["status"] == "config_updated"
|
||||
|
||||
status = client.get_status()
|
||||
assert status["config"]["api_key"] == "test-api"
|
||||
assert status["config"]["base_url"] == "https://test.com"
|
||||
|
||||
server.shutdown()
|
||||
finally:
|
||||
if os.path.exists(db_path):
|
||||
os.unlink(db_path)
|
||||
|
||||
def test_packet_round_trip_status(self):
|
||||
from core import BackendServer, BackendClient
|
||||
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
|
||||
db_path = f.name
|
||||
try:
|
||||
server = BackendServer(db_path=db_path, use_embedded_db=True)
|
||||
server.start(api_key="")
|
||||
client = BackendClient(server)
|
||||
|
||||
status = client.get_status()
|
||||
assert status["success"] is True
|
||||
assert status["running"] is True
|
||||
assert status["client_ready"] is False
|
||||
|
||||
server.shutdown()
|
||||
finally:
|
||||
if os.path.exists(db_path):
|
||||
os.unlink(db_path)
|
||||
|
||||
def test_packet_round_trip_history(self):
|
||||
from core import BackendServer, BackendClient
|
||||
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
|
||||
db_path = f.name
|
||||
try:
|
||||
server = BackendServer(db_path=db_path, use_embedded_db=True)
|
||||
server.start(api_key="")
|
||||
client = BackendClient(server)
|
||||
|
||||
messages = [
|
||||
{"role": "user", "content": "hello"},
|
||||
{"role": "assistant", "content": "hi there"}
|
||||
]
|
||||
client.save_history(messages)
|
||||
|
||||
retrieved = client.get_history()
|
||||
assert retrieved == messages
|
||||
|
||||
server.shutdown()
|
||||
finally:
|
||||
if os.path.exists(db_path):
|
||||
os.unlink(db_path)
|
||||
|
||||
|
||||
class TestIntegrationSequentialOperations:
|
||||
def test_sequential_config_updates(self):
|
||||
from core import BackendServer, BackendClient
|
||||
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
|
||||
db_path = f.name
|
||||
try:
|
||||
server = BackendServer(db_path=db_path, use_embedded_db=True)
|
||||
server.start(api_key="")
|
||||
client = BackendClient(server)
|
||||
|
||||
client.update_config(api_key="key1", base_url="https://api1.com")
|
||||
status1 = client.get_status()
|
||||
assert status1["config"]["api_key"] == "key1"
|
||||
|
||||
client.update_config(api_key="key2", base_url="https://api2.com")
|
||||
status2 = client.get_status()
|
||||
assert status2["config"]["api_key"] == "key2"
|
||||
|
||||
server.shutdown()
|
||||
finally:
|
||||
if os.path.exists(db_path):
|
||||
os.unlink(db_path)
|
||||
|
||||
def test_save_history_override(self):
|
||||
from core import BackendServer, BackendClient
|
||||
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
|
||||
db_path = f.name
|
||||
try:
|
||||
server = BackendServer(db_path=db_path, use_embedded_db=True)
|
||||
server.start(api_key="")
|
||||
client = BackendClient(server)
|
||||
|
||||
client.save_history([{"role": "user", "content": "first"}])
|
||||
history1 = client.get_history()
|
||||
assert len(history1) == 1
|
||||
|
||||
client.save_history([{"role": "user", "content": "second"}])
|
||||
history2 = client.get_history()
|
||||
assert len(history2) == 1
|
||||
assert history2[0]["content"] == "second"
|
||||
|
||||
server.shutdown()
|
||||
finally:
|
||||
if os.path.exists(db_path):
|
||||
os.unlink(db_path)
|
||||
|
||||
|
||||
class TestIntegrationMultipleClients:
|
||||
def test_two_clients_same_server(self):
|
||||
from core import BackendServer, BackendClient
|
||||
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
|
||||
db_path = f.name
|
||||
try:
|
||||
server = BackendServer(db_path=db_path, use_embedded_db=True)
|
||||
server.start(api_key="")
|
||||
|
||||
client1 = BackendClient(server)
|
||||
client2 = BackendClient(server)
|
||||
|
||||
status1 = client1.get_status()
|
||||
status2 = client2.get_status()
|
||||
|
||||
assert status1["success"] is True
|
||||
assert status2["success"] is True
|
||||
|
||||
server.shutdown()
|
||||
finally:
|
||||
if os.path.exists(db_path):
|
||||
os.unlink(db_path)
|
||||
|
||||
|
||||
class TestIntegrationDatabase:
|
||||
def test_server_creates_database(self):
|
||||
from core import BackendServer
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
db_path = os.path.join(tmpdir, "test.db")
|
||||
server = BackendServer(db_path=db_path, use_embedded_db=True)
|
||||
server.start(api_key="")
|
||||
|
||||
assert os.path.exists(db_path)
|
||||
|
||||
server.shutdown()
|
||||
assert os.path.exists(db_path)
|
||||
|
||||
def test_server_persists_across_restart(self):
|
||||
from core import BackendServer, BackendClient
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
db_path = os.path.join(tmpdir, "persist.db")
|
||||
|
||||
server1 = BackendServer(db_path=db_path, use_embedded_db=True)
|
||||
server1.start(api_key="")
|
||||
server1.shutdown()
|
||||
|
||||
server2 = BackendServer(db_path=db_path, use_embedded_db=True)
|
||||
server2.start(api_key="")
|
||||
assert os.path.exists(db_path)
|
||||
server2.shutdown()
|
||||
|
||||
|
||||
class TestIntegrationEntryPoint:
|
||||
def test_entry_import(self):
|
||||
import trulymem_entry
|
||||
assert trulymem_entry is not None
|
||||
|
||||
def test_entry_has_main(self):
|
||||
import trulymem_entry
|
||||
assert hasattr(trulymem_entry, 'main')
|
||||
assert callable(trulymem_entry.main)
|
||||
|
||||
|
||||
class TestIntegrationAllPacketTypes:
|
||||
def test_message_type_string(self):
|
||||
from core import PacketType
|
||||
assert PacketType.MESSAGE.value == "message"
|
||||
|
||||
def test_config_type_string(self):
|
||||
from core import PacketType
|
||||
assert PacketType.CONFIG.value == "config"
|
||||
|
||||
def test_tool_type_string(self):
|
||||
from core import PacketType
|
||||
assert PacketType.TOOL.value == "tool"
|
||||
|
||||
def test_status_type_string(self):
|
||||
from core import PacketType
|
||||
assert PacketType.STATUS.value == "status"
|
||||
|
||||
def test_history_type_string(self):
|
||||
from core import PacketType
|
||||
assert PacketType.HISTORY.value == "history"
|
||||
|
||||
|
||||
class TestIntegrationErrorHandling:
|
||||
def test_timeout_on_slow_response(self):
|
||||
from core import BackendServer, Packet, PacketType
|
||||
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
|
||||
db_path = f.name
|
||||
try:
|
||||
server = BackendServer(db_path=db_path)
|
||||
server.start(api_key="")
|
||||
server._running = False
|
||||
|
||||
packet = Packet(id="timeout-test", type=PacketType.MESSAGE, body={"message": "test"})
|
||||
|
||||
original_timeout = 30.0
|
||||
server.send = lambda p, timeout=original_timeout: (
|
||||
setattr(server, '_running', True),
|
||||
server.send(p)
|
||||
)[1]
|
||||
|
||||
server.shutdown()
|
||||
finally:
|
||||
if os.path.exists(db_path):
|
||||
os.unlink(db_path)
|
||||
|
||||
|
||||
class TestIntegrationFullWorkflow:
|
||||
def test_complete_workflow(self):
|
||||
from core import BackendServer, BackendClient
|
||||
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
|
||||
db_path = f.name
|
||||
try:
|
||||
server = BackendServer(db_path=db_path, use_embedded_db=True)
|
||||
server.start(api_key="")
|
||||
client = BackendClient(server)
|
||||
|
||||
status_before = client.get_status()
|
||||
assert status_before["success"] is True
|
||||
|
||||
client.update_config(api_key="workflow-key", base_url="https://workflow.com")
|
||||
status_after_config = client.get_status()
|
||||
assert status_after_config["config"]["api_key"] == "workflow-key"
|
||||
|
||||
history = [{"role": "user", "content": "test workflow"}]
|
||||
client.save_history(history)
|
||||
retrieved_history = client.get_history()
|
||||
assert retrieved_history == history
|
||||
|
||||
message_result = client.send_message("test")
|
||||
assert message_result["success"] is True
|
||||
|
||||
server.shutdown()
|
||||
status_after_shutdown = client.get_status()
|
||||
assert status_after_shutdown["running"] is False
|
||||
finally:
|
||||
if os.path.exists(db_path):
|
||||
os.unlink(db_path)
|
||||
@ -1,83 +0,0 @@
|
||||
"""数据模型测试"""
|
||||
|
||||
import pytest
|
||||
from datetime import datetime
|
||||
from ui.models.message import Message, ToolCall, ToolResult
|
||||
from ui.models.config import AppConfig
|
||||
from ui.models.log_entry import LogEntry
|
||||
|
||||
|
||||
def test_message_creation(sample_message):
|
||||
"""测试消息创建"""
|
||||
assert sample_message.role == "user"
|
||||
assert sample_message.content == "测试消息"
|
||||
assert isinstance(sample_message.timestamp, datetime)
|
||||
|
||||
|
||||
def test_message_with_tool_calls():
|
||||
"""测试带工具调用的消息"""
|
||||
tool_call = ToolCall(
|
||||
id="test-id",
|
||||
name="memory_recall",
|
||||
arguments={"query_intent": "测试"}
|
||||
)
|
||||
|
||||
message = Message(
|
||||
role="assistant",
|
||||
content="测试回复",
|
||||
tool_calls=[tool_call]
|
||||
)
|
||||
|
||||
assert message.role == "assistant"
|
||||
assert message.tool_calls is not None
|
||||
assert len(message.tool_calls) == 1
|
||||
assert message.tool_calls[0].name == "memory_recall"
|
||||
|
||||
|
||||
def test_tool_call_creation(sample_tool_call):
|
||||
"""测试工具调用创建"""
|
||||
assert sample_tool_call.id == "test-call-id"
|
||||
assert sample_tool_call.name == "memory_recall"
|
||||
assert sample_tool_call.arguments == {"query_intent": "测试查询"}
|
||||
|
||||
|
||||
def test_tool_result_creation(sample_tool_result):
|
||||
"""测试工具结果创建"""
|
||||
assert sample_tool_result.tool_call_id == "test-call-id"
|
||||
assert sample_tool_result.success is True
|
||||
assert sample_tool_result.result == "测试结果"
|
||||
|
||||
|
||||
def test_config_creation(sample_config):
|
||||
"""测试配置创建"""
|
||||
assert sample_config.api_key == "test-api-key"
|
||||
assert sample_config.model == "test-model"
|
||||
assert sample_config.base_url == "https://test.api.com"
|
||||
|
||||
|
||||
def test_config_from_env():
|
||||
"""测试从环境变量加载配置"""
|
||||
import os
|
||||
os.environ["DEEPSEEK_API_KEY"] = "env-api-key"
|
||||
os.environ["MODEL_NAME"] = "env-model"
|
||||
|
||||
config = AppConfig.from_env()
|
||||
assert config.api_key == "env-api-key"
|
||||
assert config.model == "env-model"
|
||||
|
||||
|
||||
def test_log_entry_creation(sample_log_entry):
|
||||
"""测试日志条目创建"""
|
||||
assert sample_log_entry.tool_name == "memory_recall"
|
||||
assert sample_log_entry.duration == 0.5
|
||||
|
||||
|
||||
def test_log_entry_summary(sample_log_entry):
|
||||
"""测试日志条目摘要"""
|
||||
args_summary = sample_log_entry.args_summary
|
||||
result_summary = sample_log_entry.result_summary
|
||||
|
||||
assert isinstance(args_summary, str)
|
||||
assert isinstance(result_summary, str)
|
||||
assert len(args_summary) <= 53 # 50 + "..."
|
||||
assert len(result_summary) <= 103 # 100 + "..."
|
||||
@ -1,69 +0,0 @@
|
||||
import pytest
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
os.environ["DEEPSEEK_API_KEY"] = "fake-test-key"
|
||||
|
||||
|
||||
class TestUIImport:
|
||||
def test_import_app(self):
|
||||
from ui import GraphMemoryApp
|
||||
assert GraphMemoryApp is not None
|
||||
|
||||
|
||||
class TestUIApp:
|
||||
def test_create_app_without_backend(self):
|
||||
from ui import GraphMemoryApp
|
||||
app = GraphMemoryApp()
|
||||
assert app is not None
|
||||
assert app._backend_server is None
|
||||
assert app._backend_client is None
|
||||
|
||||
def test_create_app_with_backend(self):
|
||||
from ui import GraphMemoryApp
|
||||
from core import BackendServer
|
||||
|
||||
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
|
||||
db_path = f.name
|
||||
|
||||
try:
|
||||
server = BackendServer(db_path=db_path, use_embedded_db=True)
|
||||
server.start(api_key="", base_url="https://api.test.com")
|
||||
|
||||
app = GraphMemoryApp(backend_server=server)
|
||||
|
||||
assert app._backend_server is server
|
||||
assert app._backend_client is not None
|
||||
|
||||
server.shutdown()
|
||||
finally:
|
||||
if os.path.exists(db_path):
|
||||
os.unlink(db_path)
|
||||
|
||||
|
||||
class TestAppConfig:
|
||||
def test_config_from_env(self):
|
||||
from ui import AppConfig
|
||||
config = AppConfig.from_env()
|
||||
assert config is not None
|
||||
|
||||
def test_config_default_values(self):
|
||||
from ui import AppConfig
|
||||
config = AppConfig()
|
||||
assert config.api_key == ""
|
||||
assert config.model == "deepseek-chat"
|
||||
assert config.base_url == "https://api.deepseek.com"
|
||||
|
||||
|
||||
class TestPacketProtocol:
|
||||
def test_import_packet(self):
|
||||
from core import Packet, PacketType
|
||||
assert Packet is not None
|
||||
assert PacketType is not None
|
||||
|
||||
def test_packet_creation(self):
|
||||
from core import Packet, PacketType
|
||||
packet = Packet(id="test-1", type=PacketType.MESSAGE, body={"message": "hello"})
|
||||
assert packet.id == "test-1"
|
||||
assert packet.type == PacketType.MESSAGE
|
||||
assert packet.body["message"] == "hello"
|
||||
322
tests/test_ui/__init__.py
Normal file
322
tests/test_ui/__init__.py
Normal file
@ -0,0 +1,322 @@
|
||||
import pytest
|
||||
import os
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
os.environ["DEEPSEEK_API_KEY"] = "fake-test-key"
|
||||
|
||||
|
||||
class TestUIImportFull:
|
||||
def test_import_graphmemoryapp(self):
|
||||
from ui import GraphMemoryApp
|
||||
assert GraphMemoryApp is not None
|
||||
|
||||
def test_import_appconfig(self):
|
||||
from ui import AppConfig
|
||||
assert AppConfig is not None
|
||||
|
||||
def test_import_from_models(self):
|
||||
from ui.models.message import Message, ToolCall, ToolResult
|
||||
assert Message is not None
|
||||
assert ToolCall is not None
|
||||
assert ToolResult is not None
|
||||
|
||||
def test_import_from_models_config(self):
|
||||
from ui.models.config import AppConfig
|
||||
assert AppConfig is not None
|
||||
|
||||
def test_import_from_models_log_entry(self):
|
||||
from ui.models.log_entry import LogEntry
|
||||
assert LogEntry is not None
|
||||
|
||||
def test_import_from_widgets(self):
|
||||
from ui.widgets.left_panel import LeftPanel
|
||||
from ui.widgets.right_panel import RightPanel
|
||||
from ui.widgets.input_box import InputBox
|
||||
from ui.widgets.message_history import MessageHistory
|
||||
from ui.widgets.status_bar import StatusBar
|
||||
assert LeftPanel is not None
|
||||
assert RightPanel is not None
|
||||
assert InputBox is not None
|
||||
assert MessageHistory is not None
|
||||
assert StatusBar is not None
|
||||
|
||||
def test_import_from_handlers(self):
|
||||
from ui.handlers.focus_handler import FocusHandler
|
||||
from ui.handlers.key_handler import KeyHandler
|
||||
from ui.handlers.message_handler import MessageHandler
|
||||
assert FocusHandler is not None
|
||||
assert KeyHandler is not None
|
||||
assert MessageHandler is not None
|
||||
|
||||
def test_import_from_services(self):
|
||||
from ui.services.config_manager import ConfigManager
|
||||
from ui.services.config_service import ConfigService
|
||||
from ui.services.chat_service import ChatService
|
||||
from ui.services.tool_service import ToolService
|
||||
assert ConfigManager is not None
|
||||
assert ConfigService is not None
|
||||
assert ChatService is not None
|
||||
assert ToolService is not None
|
||||
|
||||
|
||||
class TestAppConfigFull:
|
||||
def test_config_default_values(self):
|
||||
from ui.models.config import AppConfig
|
||||
config = AppConfig()
|
||||
assert config.api_key == ""
|
||||
assert config.model == "deepseek-chat"
|
||||
assert config.base_url == "https://api.deepseek.com"
|
||||
|
||||
def test_config_from_env_with_key(self):
|
||||
from ui.models.config import AppConfig
|
||||
config = AppConfig.from_env()
|
||||
assert "fake-test-key" in config.api_key
|
||||
|
||||
def test_config_from_env_custom(self):
|
||||
os.environ["DEEPSEEK_API_KEY"] = "my-key"
|
||||
os.environ["MODEL_NAME"] = "my-model"
|
||||
os.environ["DEEPSEEK_BASE_URL"] = "https://my-api.com"
|
||||
|
||||
from ui.models.config import AppConfig
|
||||
config = AppConfig.from_env()
|
||||
|
||||
assert config.api_key == "my-key"
|
||||
assert config.model == "my-model"
|
||||
assert config.base_url == "https://my-api.com"
|
||||
|
||||
|
||||
class TestMessageModelFull:
|
||||
def test_message_creation_user(self):
|
||||
from ui.models.message import Message
|
||||
from datetime import datetime
|
||||
msg = Message(role="user", content="test content")
|
||||
assert msg.role == "user"
|
||||
assert msg.content == "test content"
|
||||
assert isinstance(msg.timestamp, datetime)
|
||||
|
||||
def test_message_creation_assistant(self):
|
||||
from ui.models.message import Message
|
||||
msg = Message(role="assistant", content="assistant response")
|
||||
assert msg.role == "assistant"
|
||||
assert msg.content == "assistant response"
|
||||
|
||||
def test_message_with_tool_calls(self):
|
||||
from ui.models.message import Message, ToolCall
|
||||
tc = ToolCall(id="call-1", name="memory_recall", arguments={"query": "test"})
|
||||
msg = Message(role="assistant", content="response", tool_calls=[tc])
|
||||
assert msg.role == "assistant"
|
||||
assert msg.tool_calls is not None
|
||||
assert len(msg.tool_calls) == 1
|
||||
assert msg.tool_calls[0].name == "memory_recall"
|
||||
|
||||
def test_message_with_tool_results(self):
|
||||
from ui.models.message import Message, ToolResult
|
||||
tr = ToolResult(tool_call_id="call-1", name="memory_recall", arguments={}, result="result", success=True)
|
||||
msg = Message(role="assistant", content="response", tool_results=[tr])
|
||||
assert msg.tool_results is not None
|
||||
assert len(msg.tool_results) == 1
|
||||
assert msg.tool_results[0].success is True
|
||||
|
||||
|
||||
class TestToolCallModelFull:
|
||||
def test_toolcall_creation(self):
|
||||
from ui.models.message import ToolCall
|
||||
tc = ToolCall(id="call-1", name="memory_recall", arguments={"query": "test"})
|
||||
assert tc.id == "call-1"
|
||||
assert tc.name == "memory_recall"
|
||||
assert tc.arguments["query"] == "test"
|
||||
|
||||
|
||||
class TestToolResultModelFull:
|
||||
def test_toolresult_creation_success(self):
|
||||
from ui.models.message import ToolResult
|
||||
tr = ToolResult(tool_call_id="call-1", name="memory_recall", arguments={}, result="success result", success=True)
|
||||
assert tr.tool_call_id == "call-1"
|
||||
assert tr.name == "memory_recall"
|
||||
assert tr.success is True
|
||||
assert tr.result == "success result"
|
||||
|
||||
def test_toolresult_creation_failure(self):
|
||||
from ui.models.message import ToolResult
|
||||
tr = ToolResult(tool_call_id="call-1", name="memory_recall", arguments={}, result="error", success=False)
|
||||
assert tr.success is False
|
||||
assert tr.result == "error"
|
||||
|
||||
|
||||
class TestLogEntryModelFull:
|
||||
def test_logentry_creation(self):
|
||||
from ui.models.log_entry import LogEntry
|
||||
from datetime import datetime
|
||||
entry = LogEntry(
|
||||
timestamp=datetime.now(),
|
||||
tool_name="memory_recall",
|
||||
arguments={"query": "test"},
|
||||
result="result",
|
||||
duration=0.5
|
||||
)
|
||||
assert entry.tool_name == "memory_recall"
|
||||
assert entry.duration == 0.5
|
||||
|
||||
def test_logentry_args_summary(self):
|
||||
from ui.models.log_entry import LogEntry
|
||||
from datetime import datetime
|
||||
entry = LogEntry(
|
||||
timestamp=datetime.now(),
|
||||
tool_name="memory_recall",
|
||||
arguments={"query": "test query with many characters"},
|
||||
result="result",
|
||||
duration=0.5
|
||||
)
|
||||
summary = entry.args_summary
|
||||
assert isinstance(summary, str)
|
||||
assert "query" in summary
|
||||
|
||||
def test_logentry_result_summary(self):
|
||||
from ui.models.log_entry import LogEntry
|
||||
from datetime import datetime
|
||||
entry = LogEntry(
|
||||
timestamp=datetime.now(),
|
||||
tool_name="memory_recall",
|
||||
arguments={},
|
||||
result="a" * 200,
|
||||
duration=0.5
|
||||
)
|
||||
summary = entry.result_summary
|
||||
assert len(summary) <= 103
|
||||
|
||||
|
||||
class TestAppCSSPathFull:
|
||||
def test_app_has_css_path(self):
|
||||
from ui import GraphMemoryApp
|
||||
assert hasattr(GraphMemoryApp, 'CSS_PATH')
|
||||
assert len(GraphMemoryApp.CSS_PATH) > 0
|
||||
|
||||
def test_css_path_are_paths(self):
|
||||
from ui import GraphMemoryApp
|
||||
for path in GraphMemoryApp.CSS_PATH:
|
||||
assert isinstance(path, Path)
|
||||
|
||||
|
||||
class TestAppBindingsFull:
|
||||
def test_app_has_bindings(self):
|
||||
from ui import GraphMemoryApp
|
||||
assert hasattr(GraphMemoryApp, 'BINDINGS')
|
||||
assert len(GraphMemoryApp.BINDINGS) > 0
|
||||
|
||||
def test_bindings_have_required_keys(self):
|
||||
from ui import GraphMemoryApp
|
||||
for binding in GraphMemoryApp.BINDINGS:
|
||||
assert hasattr(binding, 'key')
|
||||
assert hasattr(binding, 'action')
|
||||
|
||||
|
||||
class TestWidgetImportsFull:
|
||||
def test_import_left_panel(self):
|
||||
from ui.widgets.left_panel import LeftPanel
|
||||
assert LeftPanel is not None
|
||||
|
||||
def test_import_right_panel(self):
|
||||
from ui.widgets.right_panel import RightPanel
|
||||
assert RightPanel is not None
|
||||
|
||||
def test_import_input_box(self):
|
||||
from ui.widgets.input_box import InputBox
|
||||
assert InputBox is not None
|
||||
|
||||
def test_import_message_history(self):
|
||||
from ui.widgets.message_history import MessageHistory
|
||||
assert MessageHistory is not None
|
||||
|
||||
def test_import_message_widget(self):
|
||||
from ui.widgets.message_widget import MessageWidget
|
||||
assert MessageWidget is not None
|
||||
|
||||
def test_import_status_bar(self):
|
||||
from ui.widgets.status_bar import StatusBar
|
||||
assert StatusBar is not None
|
||||
|
||||
def test_import_config_section(self):
|
||||
from ui.widgets.config_section import ConfigSection
|
||||
assert ConfigSection is not None
|
||||
|
||||
def test_import_operation_log(self):
|
||||
from ui.widgets.operation_log import OperationLog
|
||||
assert OperationLog is not None
|
||||
|
||||
def test_import_cypher_query_box(self):
|
||||
from ui.widgets.cypher_query_box import CypherQueryBox
|
||||
assert CypherQueryBox is not None
|
||||
|
||||
|
||||
class TestHandlerImportsFull:
|
||||
def test_import_focus_handler(self):
|
||||
from ui.handlers.focus_handler import FocusHandler
|
||||
assert FocusHandler is not None
|
||||
|
||||
def test_import_key_handler(self):
|
||||
from ui.handlers.key_handler import KeyHandler
|
||||
assert KeyHandler is not None
|
||||
|
||||
def test_import_message_handler(self):
|
||||
from ui.handlers.message_handler import MessageHandler
|
||||
assert MessageHandler is not None
|
||||
|
||||
|
||||
class TestServiceImportsFull:
|
||||
def test_import_config_manager(self):
|
||||
from ui.services.config_manager import ConfigManager
|
||||
assert ConfigManager is not None
|
||||
|
||||
def test_import_config_service(self):
|
||||
from ui.services.config_service import ConfigService
|
||||
assert ConfigService is not None
|
||||
|
||||
def test_import_chat_service(self):
|
||||
from ui.services.chat_service import ChatService
|
||||
assert ChatService is not None
|
||||
|
||||
def test_import_tool_service(self):
|
||||
from ui.services.tool_service import ToolService
|
||||
assert ToolService is not None
|
||||
|
||||
|
||||
class TestAppInitializationFull:
|
||||
def test_app_without_backend(self):
|
||||
from ui import GraphMemoryApp
|
||||
app = GraphMemoryApp()
|
||||
assert app._backend_server is None
|
||||
assert app._backend_client is None
|
||||
|
||||
def test_app_with_backend(self):
|
||||
from ui import GraphMemoryApp
|
||||
from core import BackendServer
|
||||
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
|
||||
db_path = f.name
|
||||
try:
|
||||
server = BackendServer(db_path=db_path)
|
||||
server.start(api_key="")
|
||||
app = GraphMemoryApp(backend_server=server)
|
||||
assert app._backend_server is server
|
||||
assert app._backend_client is not None
|
||||
server.shutdown()
|
||||
finally:
|
||||
if os.path.exists(db_path):
|
||||
os.unlink(db_path)
|
||||
|
||||
|
||||
class TestConfigManagerFull:
|
||||
def test_config_manager_creation(self):
|
||||
from ui.services.config_manager import ConfigManager
|
||||
cm = ConfigManager()
|
||||
assert cm is not None
|
||||
|
||||
def test_config_manager_config_path(self):
|
||||
from ui.services.config_manager import ConfigManager
|
||||
cm = ConfigManager()
|
||||
assert cm._config_path is not None
|
||||
|
||||
def test_config_manager_exists_false(self):
|
||||
from ui.services.config_manager import ConfigManager
|
||||
cm = ConfigManager()
|
||||
assert cm.exists() is False
|
||||
Reference in New Issue
Block a user