From 99f0ae7c9aa3a292762179c8c5efcd6c12fff78f Mon Sep 17 00:00:00 2001 From: qin-ctx Date: Thu, 5 Feb 2026 15:15:07 +0800 Subject: [PATCH] refactor: extract ObserverService from DebugService for cleaner status access API - Create ObserverService class with queue/vikingdb/vlm/system properties - DebugService holds ObserverService as a member - Add client.observer property, enabling print(client.observer.vikingdb) syntax - Add __str__ method to ComponentStatus and SystemStatus for direct printing - Create dedicated debug API documentation (07-debug.md) - Update architecture docs with new DebugService description - Update unit tests --- docs/design/server_client/prompt.md | 2 +- docs/en/api/01-client.md | 75 +------- docs/en/api/07-debug.md | 254 ++++++++++++++++++++++++++++ docs/en/concepts/01-architecture.md | 2 +- docs/zh/api/01-client.md | 75 +------- docs/zh/api/07-debug.md | 254 ++++++++++++++++++++++++++++ docs/zh/concepts/01-architecture.md | 2 +- openviking/async_client.py | 9 +- openviking/service/debug_service.py | 78 +++++++-- openviking/sync_client.py | 5 + tests/misc/test_debug_service.py | 249 ++++++++++++++++++++------- 11 files changed, 789 insertions(+), 216 deletions(-) create mode 100644 docs/en/api/07-debug.md create mode 100644 docs/zh/api/07-debug.md diff --git a/docs/design/server_client/prompt.md b/docs/design/server_client/prompt.md index dfd2425b..d21e7d31 100644 --- a/docs/design/server_client/prompt.md +++ b/docs/design/server_client/prompt.md @@ -35,7 +35,7 @@ openviking/service/ - `SearchService`: find, search - `SessionService`: session, sessions, add_message, compress, extract - `RelationService`: link, unlink, relations -- `DebugService`: get_queue_status, get_vikingdb_status, get_vlm_status, get_system_status, is_healthy +- `DebugService`: observer (ObserverService) - `PackService`: export_ovpack, import_ovpack 3. **创建 OpenVikingService 主类**: diff --git a/docs/en/api/01-client.md b/docs/en/api/01-client.md index f57233c0..eeb66cba 100644 --- a/docs/en/api/01-client.md +++ b/docs/en/api/01-client.md @@ -252,78 +252,21 @@ ov.OpenViking.reset() --- -### get_status() +## Debug Methods -Get system status including health status of all components. +For system health monitoring and component status, see [Debug API](./07-debug.md). -**Signature** - -```python -def get_status(self) -> SystemStatus -``` - -**Parameters** - -None. - -**Returns** - -| Type | Description | -|------|-------------| -| SystemStatus | System status object | - -**Return Structure** - -```python -SystemStatus( - is_healthy=True, # Overall system health - components={ # Component statuses - "queue": ComponentStatus(...), - "vikingdb": ComponentStatus(...), - "vlm": ComponentStatus(...) - }, - errors=[] # Error list -) -``` - -**Example** - -```python -status = client.get_status() -print(f"System healthy: {status.is_healthy}") -print(f"Queue status: {status.components['queue'].is_healthy}") -``` - ---- - -### is_healthy() - -Quick health check. - -**Signature** - -```python -def is_healthy(self) -> bool -``` - -**Parameters** - -None. - -**Returns** - -| Type | Description | -|------|-------------| -| bool | True if all components are healthy, False otherwise | - -**Example** +**Quick Reference** ```python +# Quick health check if client.is_healthy(): print("System OK") -else: - status = client.get_status() - print(f"Errors: {status.errors}") + +# Access component status via observer +print(client.observer.vikingdb) +print(client.observer.queue) +print(client.observer.system) ``` --- diff --git a/docs/en/api/07-debug.md b/docs/en/api/07-debug.md new file mode 100644 index 00000000..2f32f910 --- /dev/null +++ b/docs/en/api/07-debug.md @@ -0,0 +1,254 @@ +# Debug + +OpenViking provides debug and observability APIs for monitoring system health and component status. + +## API Reference + +### observer + +Property that provides convenient access to component status through `ObserverService`. + +**Signature** + +```python +@property +def observer(self) -> ObserverService +``` + +**Returns** + +| Type | Description | +|------|-------------| +| ObserverService | Service for accessing component status | + +**Example** + +```python +import openviking as ov + +client = ov.OpenViking(path="./data") +client.initialize() + +# Print component status directly +print(client.observer.vikingdb) +# Output: +# [vikingdb] (healthy) +# Collection Index Count Vector Count Status +# context 1 55 OK +# TOTAL 1 55 + +client.close() +``` + +--- + +## ObserverService + +`ObserverService` provides properties for accessing individual component status. + +### queue + +Get queue system status. + +**Signature** + +```python +@property +def queue(self) -> ComponentStatus +``` + +**Returns** + +| Type | Description | +|------|-------------| +| ComponentStatus | Queue system status | + +**Example** + +```python +print(client.observer.queue) +# Output: +# [queue] (healthy) +# Queue Pending In Progress Processed Errors Total +# Embedding 0 0 10 0 10 +# Semantic 0 0 10 0 10 +# TOTAL 0 0 20 0 20 +``` + +--- + +### vikingdb + +Get VikingDB status. + +**Signature** + +```python +@property +def vikingdb(self) -> ComponentStatus +``` + +**Returns** + +| Type | Description | +|------|-------------| +| ComponentStatus | VikingDB status | + +**Example** + +```python +print(client.observer.vikingdb) +# Output: +# [vikingdb] (healthy) +# Collection Index Count Vector Count Status +# context 1 55 OK +# TOTAL 1 55 + +# Access specific properties +print(client.observer.vikingdb.is_healthy) # True +print(client.observer.vikingdb.status) # Status table string +``` + +--- + +### vlm + +Get VLM (Vision Language Model) token usage status. + +**Signature** + +```python +@property +def vlm(self) -> ComponentStatus +``` + +**Returns** + +| Type | Description | +|------|-------------| +| ComponentStatus | VLM token usage status | + +**Example** + +```python +print(client.observer.vlm) +# Output: +# [vlm] (healthy) +# Model Provider Prompt Completion Total Last Updated +# doubao-1-5-vision-pro-32k volcengine 1000 500 1500 2024-01-01 12:00:00 +# TOTAL 1000 500 1500 +``` + +--- + +### system + +Get overall system status including all components. + +**Signature** + +```python +@property +def system(self) -> SystemStatus +``` + +**Returns** + +| Type | Description | +|------|-------------| +| SystemStatus | Overall system status | + +**Example** + +```python +print(client.observer.system) +# Output: +# [queue] (healthy) +# ... +# +# [vikingdb] (healthy) +# ... +# +# [vlm] (healthy) +# ... +# +# [system] (healthy) +``` + +--- + +### is_healthy() + +Quick health check for the entire system. + +**Signature** + +```python +def is_healthy(self) -> bool +``` + +**Returns** + +| Type | Description | +|------|-------------| +| bool | True if all components are healthy | + +**Example** + +```python +if client.observer.is_healthy(): + print("System OK") +else: + print(client.observer.system) +``` + +--- + +## Data Structures + +### ComponentStatus + +Status information for a single component. + +| Field | Type | Description | +|-------|------|-------------| +| name | str | Component name | +| is_healthy | bool | Whether the component is healthy | +| has_errors | bool | Whether the component has errors | +| status | str | Status table string | + +**String Representation** + +```python +print(component_status) +# Output: +# [component_name] (healthy) +# Status table content... +``` + +--- + +### SystemStatus + +Overall system status including all components. + +| Field | Type | Description | +|-------|------|-------------| +| is_healthy | bool | Whether the entire system is healthy | +| components | Dict[str, ComponentStatus] | Status of each component | +| errors | List[str] | List of error messages | + +**String Representation** + +```python +print(system_status) +# Output: +# [queue] (healthy) +# ... +# +# [vikingdb] (healthy) +# ... +# +# [system] (healthy) +# Errors: error1, error2 (if any) +``` diff --git a/docs/en/concepts/01-architecture.md b/docs/en/concepts/01-architecture.md index f92d0daf..e3442420 100644 --- a/docs/en/concepts/01-architecture.md +++ b/docs/en/concepts/01-architecture.md @@ -73,7 +73,7 @@ The Service layer decouples business logic from the transport layer, enabling re | **ResourceService** | Resource import | add_resource, add_skill, wait_processed | | **RelationService** | Relation management | relations, link, unlink | | **PackService** | Import/export | export_ovpack, import_ovpack | -| **DebugService** | Debug service | get_system_status, get_queue_status, get_vikingdb_status, get_vlm_status, is_healthy | +| **DebugService** | Debug service | observer (ObserverService) | ## Dual-Layer Storage diff --git a/docs/zh/api/01-client.md b/docs/zh/api/01-client.md index 6b1aae3e..c7d39bb1 100644 --- a/docs/zh/api/01-client.md +++ b/docs/zh/api/01-client.md @@ -252,78 +252,21 @@ ov.OpenViking.reset() --- -### get_status() +## 调试方法 -获取系统状态,包含所有组件的健康状态。 +系统健康监控和组件状态相关内容,请参阅 [调试 API](./07-debug.md)。 -**签名** - -```python -def get_status(self) -> SystemStatus -``` - -**参数** - -无。 - -**返回值** - -| 类型 | 说明 | -|------|------| -| SystemStatus | 系统状态对象 | - -**返回结构** - -```python -SystemStatus( - is_healthy=True, # 系统整体是否健康 - components={ # 各组件状态 - "queue": ComponentStatus(...), - "vikingdb": ComponentStatus(...), - "vlm": ComponentStatus(...) - }, - errors=[] # 错误列表 -) -``` - -**示例** - -```python -status = client.get_status() -print(f"系统健康: {status.is_healthy}") -print(f"队列状态: {status.components['queue'].is_healthy}") -``` - ---- - -### is_healthy() - -快速健康检查。 - -**签名** - -```python -def is_healthy(self) -> bool -``` - -**参数** - -无。 - -**返回值** - -| 类型 | 说明 | -|------|------| -| bool | 所有组件健康返回 True,否则返回 False | - -**示例** +**快速参考** ```python +# 快速健康检查 if client.is_healthy(): print("系统正常") -else: - status = client.get_status() - print(f"错误: {status.errors}") + +# 通过 observer 访问组件状态 +print(client.observer.vikingdb) +print(client.observer.queue) +print(client.observer.system) ``` --- diff --git a/docs/zh/api/07-debug.md b/docs/zh/api/07-debug.md new file mode 100644 index 00000000..43204e20 --- /dev/null +++ b/docs/zh/api/07-debug.md @@ -0,0 +1,254 @@ +# 调试 + +OpenViking 提供调试和可观测性 API,用于监控系统健康状态和组件状态。 + +## API 参考 + +### observer + +提供便捷访问组件状态的属性,返回 `ObserverService`。 + +**签名** + +```python +@property +def observer(self) -> ObserverService +``` + +**返回值** + +| 类型 | 说明 | +|------|------| +| ObserverService | 用于访问组件状态的服务 | + +**示例** + +```python +import openviking as ov + +client = ov.OpenViking(path="./data") +client.initialize() + +# 直接打印组件状态 +print(client.observer.vikingdb) +# 输出: +# [vikingdb] (healthy) +# Collection Index Count Vector Count Status +# context 1 55 OK +# TOTAL 1 55 + +client.close() +``` + +--- + +## ObserverService + +`ObserverService` 提供访问各个组件状态的属性。 + +### queue + +获取队列系统状态。 + +**签名** + +```python +@property +def queue(self) -> ComponentStatus +``` + +**返回值** + +| 类型 | 说明 | +|------|------| +| ComponentStatus | 队列系统状态 | + +**示例** + +```python +print(client.observer.queue) +# 输出: +# [queue] (healthy) +# Queue Pending In Progress Processed Errors Total +# Embedding 0 0 10 0 10 +# Semantic 0 0 10 0 10 +# TOTAL 0 0 20 0 20 +``` + +--- + +### vikingdb + +获取 VikingDB 状态。 + +**签名** + +```python +@property +def vikingdb(self) -> ComponentStatus +``` + +**返回值** + +| 类型 | 说明 | +|------|------| +| ComponentStatus | VikingDB 状态 | + +**示例** + +```python +print(client.observer.vikingdb) +# 输出: +# [vikingdb] (healthy) +# Collection Index Count Vector Count Status +# context 1 55 OK +# TOTAL 1 55 + +# 访问具体属性 +print(client.observer.vikingdb.is_healthy) # True +print(client.observer.vikingdb.status) # 状态表格字符串 +``` + +--- + +### vlm + +获取 VLM(视觉语言模型)token 使用状态。 + +**签名** + +```python +@property +def vlm(self) -> ComponentStatus +``` + +**返回值** + +| 类型 | 说明 | +|------|------| +| ComponentStatus | VLM token 使用状态 | + +**示例** + +```python +print(client.observer.vlm) +# 输出: +# [vlm] (healthy) +# Model Provider Prompt Completion Total Last Updated +# doubao-1-5-vision-pro-32k volcengine 1000 500 1500 2024-01-01 12:00:00 +# TOTAL 1000 500 1500 +``` + +--- + +### system + +获取系统整体状态,包含所有组件。 + +**签名** + +```python +@property +def system(self) -> SystemStatus +``` + +**返回值** + +| 类型 | 说明 | +|------|------| +| SystemStatus | 系统整体状态 | + +**示例** + +```python +print(client.observer.system) +# 输出: +# [queue] (healthy) +# ... +# +# [vikingdb] (healthy) +# ... +# +# [vlm] (healthy) +# ... +# +# [system] (healthy) +``` + +--- + +### is_healthy() + +快速健康检查。 + +**签名** + +```python +def is_healthy(self) -> bool +``` + +**返回值** + +| 类型 | 说明 | +|------|------| +| bool | 所有组件健康返回 True | + +**示例** + +```python +if client.observer.is_healthy(): + print("系统正常") +else: + print(client.observer.system) +``` + +--- + +## 数据结构 + +### ComponentStatus + +单个组件的状态信息。 + +| 字段 | 类型 | 说明 | +|------|------|------| +| name | str | 组件名称 | +| is_healthy | bool | 组件是否健康 | +| has_errors | bool | 组件是否有错误 | +| status | str | 状态表格字符串 | + +**字符串表示** + +```python +print(component_status) +# 输出: +# [component_name] (healthy) +# 状态表格内容... +``` + +--- + +### SystemStatus + +系统整体状态,包含所有组件。 + +| 字段 | 类型 | 说明 | +|------|------|------| +| is_healthy | bool | 整个系统是否健康 | +| components | Dict[str, ComponentStatus] | 各组件状态 | +| errors | List[str] | 错误信息列表 | + +**字符串表示** + +```python +print(system_status) +# 输出: +# [queue] (healthy) +# ... +# +# [vikingdb] (healthy) +# ... +# +# [system] (healthy) +# Errors: error1, error2 (如果有) +``` diff --git a/docs/zh/concepts/01-architecture.md b/docs/zh/concepts/01-architecture.md index 8db2f8bb..a1ad5b5c 100644 --- a/docs/zh/concepts/01-architecture.md +++ b/docs/zh/concepts/01-architecture.md @@ -72,7 +72,7 @@ Service 层将业务逻辑与传输层解耦,便于 HTTP Server 和 CLI 复用 | **ResourceService** | 资源导入 | add_resource, add_skill, wait_processed | | **RelationService** | 关联管理 | relations, link, unlink | | **PackService** | 导入导出 | export_ovpack, import_ovpack | -| **DebugService** | 调试服务 | get_system_status, get_queue_status, get_vikingdb_status, get_vlm_status, is_healthy | +| **DebugService** | 调试服务 | observer (ObserverService) | ## 双层存储 diff --git a/openviking/async_client.py b/openviking/async_client.py index d56c9637..3d8d5d85 100644 --- a/openviking/async_client.py +++ b/openviking/async_client.py @@ -433,7 +433,7 @@ def get_status(self) -> SystemStatus: Returns: SystemStatus containing health status of all components. """ - return self._service.debug.get_system_status() + return self._service.debug.observer.system def is_healthy(self) -> bool: """Quick health check. @@ -441,4 +441,9 @@ def is_healthy(self) -> bool: Returns: True if all components are healthy, False otherwise. """ - return self._service.debug.is_healthy() + return self._service.debug.observer.is_healthy() + + @property + def observer(self): + """Get observer service for component status.""" + return self._service.debug.observer diff --git a/openviking/service/debug_service.py b/openviking/service/debug_service.py index 3de83d82..faf8ce55 100644 --- a/openviking/service/debug_service.py +++ b/openviking/service/debug_service.py @@ -1,3 +1,4 @@ + # Copyright (c) 2026 Beijing Volcano Engine Technology Co., Ltd. # SPDX-License-Identifier: Apache-2.0 """ @@ -5,7 +6,7 @@ """ from dataclasses import dataclass -from typing import Any, Dict, List, Optional +from typing import Dict, List, Optional from openviking.storage import VikingDBManager from openviking.storage.observers import QueueObserver, VikingDBObserver, VLMObserver @@ -20,7 +21,11 @@ class ComponentStatus: name: str is_healthy: bool has_errors: bool - details: Dict[str, Any] + status: str + + def __str__(self) -> str: + health = "healthy" if self.is_healthy else "unhealthy" + return f"[{self.name}] ({health})\n{self.status}" @dataclass @@ -31,9 +36,20 @@ class SystemStatus: components: Dict[str, ComponentStatus] errors: List[str] + def __str__(self) -> str: + lines = [] + for component in self.components.values(): + lines.append(str(component)) + lines.append("") + health = "healthy" if self.is_healthy else "unhealthy" + lines.append(f"[system] ({health})") + if self.errors: + lines.append(f"Errors: {', '.join(self.errors)}") + return "\n".join(lines) -class DebugService: - """Debug service - provides system status query and health check.""" + +class ObserverService: + """Observer service - provides component status observation.""" def __init__( self, @@ -52,42 +68,46 @@ def set_dependencies( self._vikingdb = vikingdb self._config = config - def get_queue_status(self) -> ComponentStatus: + @property + def queue(self) -> ComponentStatus: """Get queue status.""" observer = QueueObserver(get_queue_manager()) return ComponentStatus( name="queue", is_healthy=observer.is_healthy(), has_errors=observer.has_errors(), - details={"status_table": observer.get_status_table()}, + status=observer.get_status_table(), ) - def get_vikingdb_status(self) -> ComponentStatus: + @property + def vikingdb(self) -> ComponentStatus: """Get VikingDB status.""" observer = VikingDBObserver(self._vikingdb) return ComponentStatus( name="vikingdb", is_healthy=observer.is_healthy(), has_errors=observer.has_errors(), - details={"status_table": observer.get_status_table()}, + status=observer.get_status_table(), ) - def get_vlm_status(self) -> ComponentStatus: + @property + def vlm(self) -> ComponentStatus: """Get VLM status.""" observer = VLMObserver(self._config.vlm.get_vlm_instance()) return ComponentStatus( name="vlm", is_healthy=observer.is_healthy(), has_errors=observer.has_errors(), - details={"status_table": observer.get_status_table()}, + status=observer.get_status_table(), ) - def get_system_status(self) -> SystemStatus: + @property + def system(self) -> SystemStatus: """Get system overall status.""" components = { - "queue": self.get_queue_status(), - "vikingdb": self.get_vikingdb_status(), - "vlm": self.get_vlm_status(), + "queue": self.queue, + "vikingdb": self.vikingdb, + "vlm": self.vlm, } errors = [f"{c.name} has errors" for c in components.values() if c.has_errors] return SystemStatus( @@ -98,4 +118,32 @@ def get_system_status(self) -> SystemStatus: def is_healthy(self) -> bool: """Quick health check.""" - return self.get_system_status().is_healthy + return self.system.is_healthy + + +class DebugService: + """Debug service - provides system status query and health check.""" + + def __init__( + self, + vikingdb: Optional[VikingDBManager] = None, + config: Optional[OpenVikingConfig] = None, + ): + self._observer = ObserverService(vikingdb, config) + + def set_dependencies( + self, + vikingdb: VikingDBManager, + config: OpenVikingConfig, + ) -> None: + """Set dependencies after initialization.""" + self._observer.set_dependencies(vikingdb, config) + + @property + def observer(self) -> ObserverService: + """Get observer service.""" + return self._observer + + def is_healthy(self) -> bool: + """Quick health check.""" + return self._observer.is_healthy() diff --git a/openviking/sync_client.py b/openviking/sync_client.py index 64ebdf60..0709e534 100644 --- a/openviking/sync_client.py +++ b/openviking/sync_client.py @@ -176,6 +176,11 @@ def is_healthy(self) -> bool: """ return self._async_client.is_healthy() + @property + def observer(self): + """Get observer service for component status.""" + return self._async_client.observer + @property def viking_fs(self): return self._async_client.viking_fs diff --git a/tests/misc/test_debug_service.py b/tests/misc/test_debug_service.py index 679f52d6..cbf80484 100644 --- a/tests/misc/test_debug_service.py +++ b/tests/misc/test_debug_service.py @@ -1,7 +1,7 @@ # Copyright (c) 2026 Beijing Volcano Engine Technology Co., Ltd. # SPDX-License-Identifier: Apache-2.0 """ -Tests for DebugService. +Tests for DebugService and ObserverService. """ from unittest.mock import MagicMock, patch @@ -11,6 +11,7 @@ from openviking.service.debug_service import ( ComponentStatus, DebugService, + ObserverService, SystemStatus, ) @@ -24,12 +25,12 @@ def test_component_status_creation(self): name="test_component", is_healthy=True, has_errors=False, - details={"key": "value"}, + status="Status Table", ) assert status.name == "test_component" assert status.is_healthy is True assert status.has_errors is False - assert status.details == {"key": "value"} + assert status.status == "Status Table" def test_component_status_unhealthy(self): """Test ComponentStatus with unhealthy state.""" @@ -37,11 +38,34 @@ def test_component_status_unhealthy(self): name="unhealthy_component", is_healthy=False, has_errors=True, - details={"error": "connection failed"}, + status="Error Status", ) assert status.is_healthy is False assert status.has_errors is True + def test_component_status_str_healthy(self): + """Test ComponentStatus __str__ for healthy component.""" + status = ComponentStatus( + name="vikingdb", + is_healthy=True, + has_errors=False, + status="Collection Count\ntest 10", + ) + result = str(status) + assert "[vikingdb] (healthy)" in result + assert "Collection Count" in result + + def test_component_status_str_unhealthy(self): + """Test ComponentStatus __str__ for unhealthy component.""" + status = ComponentStatus( + name="queue", + is_healthy=False, + has_errors=True, + status="Queue Error", + ) + result = str(status) + assert "[queue] (unhealthy)" in result + class TestSystemStatus: """Tests for SystemStatus dataclass.""" @@ -49,8 +73,8 @@ class TestSystemStatus: def test_system_status_healthy(self): """Test SystemStatus with all healthy components.""" components = { - "queue": ComponentStatus("queue", True, False, {}), - "vikingdb": ComponentStatus("vikingdb", True, False, {}), + "queue": ComponentStatus("queue", True, False, "OK"), + "vikingdb": ComponentStatus("vikingdb", True, False, "OK"), } status = SystemStatus(is_healthy=True, components=components, errors=[]) assert status.is_healthy is True @@ -60,8 +84,8 @@ def test_system_status_healthy(self): def test_system_status_with_errors(self): """Test SystemStatus with errors.""" components = { - "queue": ComponentStatus("queue", False, True, {}), - "vikingdb": ComponentStatus("vikingdb", True, False, {}), + "queue": ComponentStatus("queue", False, True, "Error"), + "vikingdb": ComponentStatus("vikingdb", True, False, "OK"), } status = SystemStatus( is_healthy=False, @@ -71,27 +95,39 @@ def test_system_status_with_errors(self): assert status.is_healthy is False assert len(status.errors) == 1 + def test_system_status_str(self): + """Test SystemStatus __str__ method.""" + components = { + "queue": ComponentStatus("queue", True, False, "Queue OK"), + "vikingdb": ComponentStatus("vikingdb", True, False, "VikingDB OK"), + } + status = SystemStatus(is_healthy=True, components=components, errors=[]) + result = str(status) + assert "[system] (healthy)" in result + assert "[queue] (healthy)" in result + assert "[vikingdb] (healthy)" in result -class TestDebugService: - """Tests for DebugService class.""" + +class TestObserverService: + """Tests for ObserverService class.""" def test_init_without_dependencies(self): - """Test DebugService can be created without dependencies.""" - service = DebugService() + """Test ObserverService can be created without dependencies.""" + service = ObserverService() assert service._vikingdb is None assert service._config is None def test_init_with_dependencies(self): - """Test DebugService can be created with dependencies.""" + """Test ObserverService can be created with dependencies.""" mock_vikingdb = MagicMock() mock_config = MagicMock() - service = DebugService(vikingdb=mock_vikingdb, config=mock_config) + service = ObserverService(vikingdb=mock_vikingdb, config=mock_config) assert service._vikingdb is mock_vikingdb assert service._config is mock_config def test_set_dependencies(self): """Test set_dependencies method.""" - service = DebugService() + service = ObserverService() mock_vikingdb = MagicMock() mock_config = MagicMock() service.set_dependencies(vikingdb=mock_vikingdb, config=mock_config) @@ -100,8 +136,8 @@ def test_set_dependencies(self): @patch("openviking.service.debug_service.get_queue_manager") @patch("openviking.service.debug_service.QueueObserver") - def test_get_queue_status(self, mock_observer_cls, mock_get_queue_manager): - """Test get_queue_status returns ComponentStatus.""" + def test_queue_property(self, mock_observer_cls, mock_get_queue_manager): + """Test queue property returns ComponentStatus.""" mock_queue_manager = MagicMock() mock_get_queue_manager.return_value = mock_queue_manager @@ -111,19 +147,19 @@ def test_get_queue_status(self, mock_observer_cls, mock_get_queue_manager): mock_observer.get_status_table.return_value = "Queue Status Table" mock_observer_cls.return_value = mock_observer - service = DebugService() - status = service.get_queue_status() + service = ObserverService() + status = service.queue assert isinstance(status, ComponentStatus) assert status.name == "queue" assert status.is_healthy is True assert status.has_errors is False - assert status.details["status_table"] == "Queue Status Table" + assert status.status == "Queue Status Table" mock_observer_cls.assert_called_once_with(mock_queue_manager) @patch("openviking.service.debug_service.VikingDBObserver") - def test_get_vikingdb_status(self, mock_observer_cls): - """Test get_vikingdb_status returns ComponentStatus.""" + def test_vikingdb_property(self, mock_observer_cls): + """Test vikingdb property returns ComponentStatus.""" mock_vikingdb = MagicMock() mock_observer = MagicMock() mock_observer.is_healthy.return_value = True @@ -131,19 +167,19 @@ def test_get_vikingdb_status(self, mock_observer_cls): mock_observer.get_status_table.return_value = "VikingDB Status Table" mock_observer_cls.return_value = mock_observer - service = DebugService(vikingdb=mock_vikingdb) - status = service.get_vikingdb_status() + service = ObserverService(vikingdb=mock_vikingdb) + status = service.vikingdb assert isinstance(status, ComponentStatus) assert status.name == "vikingdb" assert status.is_healthy is True assert status.has_errors is False - assert status.details["status_table"] == "VikingDB Status Table" + assert status.status == "VikingDB Status Table" mock_observer_cls.assert_called_once_with(mock_vikingdb) @patch("openviking.service.debug_service.VLMObserver") - def test_get_vlm_status(self, mock_observer_cls): - """Test get_vlm_status returns ComponentStatus.""" + def test_vlm_property(self, mock_observer_cls): + """Test vlm property returns ComponentStatus.""" mock_config = MagicMock() mock_vlm_instance = MagicMock() mock_config.vlm.get_vlm_instance.return_value = mock_vlm_instance @@ -154,48 +190,73 @@ def test_get_vlm_status(self, mock_observer_cls): mock_observer.get_status_table.return_value = "VLM Status Table" mock_observer_cls.return_value = mock_observer - service = DebugService(config=mock_config) - status = service.get_vlm_status() + service = ObserverService(config=mock_config) + status = service.vlm assert isinstance(status, ComponentStatus) assert status.name == "vlm" assert status.is_healthy is True assert status.has_errors is False - assert status.details["status_table"] == "VLM Status Table" + assert status.status == "VLM Status Table" mock_observer_cls.assert_called_once_with(mock_vlm_instance) - @patch.object(DebugService, "get_queue_status") - @patch.object(DebugService, "get_vikingdb_status") - @patch.object(DebugService, "get_vlm_status") - def test_get_system_status_all_healthy( - self, mock_vlm, mock_vikingdb, mock_queue + @patch("openviking.service.debug_service.get_queue_manager") + @patch("openviking.service.debug_service.QueueObserver") + @patch("openviking.service.debug_service.VikingDBObserver") + @patch("openviking.service.debug_service.VLMObserver") + def test_system_property_all_healthy( + self, mock_vlm_cls, mock_vikingdb_cls, mock_queue_cls, mock_get_queue_manager ): - """Test get_system_status when all components are healthy.""" - mock_queue.return_value = ComponentStatus("queue", True, False, {}) - mock_vikingdb.return_value = ComponentStatus("vikingdb", True, False, {}) - mock_vlm.return_value = ComponentStatus("vlm", True, False, {}) + """Test system property when all components are healthy.""" + # Setup mocks + for mock_cls in [mock_queue_cls, mock_vikingdb_cls, mock_vlm_cls]: + mock_observer = MagicMock() + mock_observer.is_healthy.return_value = True + mock_observer.has_errors.return_value = False + mock_observer.get_status_table.return_value = "OK" + mock_cls.return_value = mock_observer - service = DebugService() - status = service.get_system_status() + mock_config = MagicMock() + service = ObserverService(vikingdb=MagicMock(), config=mock_config) + status = service.system assert isinstance(status, SystemStatus) assert status.is_healthy is True assert len(status.components) == 3 assert status.errors == [] - @patch.object(DebugService, "get_queue_status") - @patch.object(DebugService, "get_vikingdb_status") - @patch.object(DebugService, "get_vlm_status") - def test_get_system_status_with_errors( - self, mock_vlm, mock_vikingdb, mock_queue + @patch("openviking.service.debug_service.get_queue_manager") + @patch("openviking.service.debug_service.QueueObserver") + @patch("openviking.service.debug_service.VikingDBObserver") + @patch("openviking.service.debug_service.VLMObserver") + def test_system_property_with_errors( + self, mock_vlm_cls, mock_vikingdb_cls, mock_queue_cls, mock_get_queue_manager ): - """Test get_system_status when some components have errors.""" - mock_queue.return_value = ComponentStatus("queue", False, True, {}) - mock_vikingdb.return_value = ComponentStatus("vikingdb", True, False, {}) - mock_vlm.return_value = ComponentStatus("vlm", False, True, {}) + """Test system property when some components have errors.""" + # Queue has errors + mock_queue = MagicMock() + mock_queue.is_healthy.return_value = False + mock_queue.has_errors.return_value = True + mock_queue.get_status_table.return_value = "Error" + mock_queue_cls.return_value = mock_queue + + # VikingDB is healthy + mock_vikingdb = MagicMock() + mock_vikingdb.is_healthy.return_value = True + mock_vikingdb.has_errors.return_value = False + mock_vikingdb.get_status_table.return_value = "OK" + mock_vikingdb_cls.return_value = mock_vikingdb + + # VLM has errors + mock_vlm = MagicMock() + mock_vlm.is_healthy.return_value = False + mock_vlm.has_errors.return_value = True + mock_vlm.get_status_table.return_value = "Error" + mock_vlm_cls.return_value = mock_vlm - service = DebugService() - status = service.get_system_status() + mock_config = MagicMock() + service = ObserverService(vikingdb=MagicMock(), config=mock_config) + status = service.system assert isinstance(status, SystemStatus) assert status.is_healthy is False @@ -203,20 +264,80 @@ def test_get_system_status_with_errors( assert "queue has errors" in status.errors assert "vlm has errors" in status.errors - @patch.object(DebugService, "get_system_status") - def test_is_healthy_returns_true(self, mock_get_system_status): + @patch("openviking.service.debug_service.get_queue_manager") + @patch("openviking.service.debug_service.QueueObserver") + @patch("openviking.service.debug_service.VikingDBObserver") + @patch("openviking.service.debug_service.VLMObserver") + def test_is_healthy_returns_true( + self, mock_vlm_cls, mock_vikingdb_cls, mock_queue_cls, mock_get_queue_manager + ): """Test is_healthy returns True when system is healthy.""" - mock_get_system_status.return_value = SystemStatus( - is_healthy=True, components={}, errors=[] - ) - service = DebugService() + for mock_cls in [mock_queue_cls, mock_vikingdb_cls, mock_vlm_cls]: + mock_observer = MagicMock() + mock_observer.is_healthy.return_value = True + mock_observer.has_errors.return_value = False + mock_observer.get_status_table.return_value = "OK" + mock_cls.return_value = mock_observer + + mock_config = MagicMock() + service = ObserverService(vikingdb=MagicMock(), config=mock_config) assert service.is_healthy() is True - @patch.object(DebugService, "get_system_status") - def test_is_healthy_returns_false(self, mock_get_system_status): + @patch("openviking.service.debug_service.get_queue_manager") + @patch("openviking.service.debug_service.QueueObserver") + @patch("openviking.service.debug_service.VikingDBObserver") + @patch("openviking.service.debug_service.VLMObserver") + def test_is_healthy_returns_false( + self, mock_vlm_cls, mock_vikingdb_cls, mock_queue_cls, mock_get_queue_manager + ): """Test is_healthy returns False when system is unhealthy.""" - mock_get_system_status.return_value = SystemStatus( - is_healthy=False, components={}, errors=["error"] - ) - service = DebugService() + # Queue has errors + mock_queue = MagicMock() + mock_queue.is_healthy.return_value = False + mock_queue.has_errors.return_value = True + mock_queue.get_status_table.return_value = "Error" + mock_queue_cls.return_value = mock_queue + + # Others are healthy + for mock_cls in [mock_vikingdb_cls, mock_vlm_cls]: + mock_observer = MagicMock() + mock_observer.is_healthy.return_value = True + mock_observer.has_errors.return_value = False + mock_observer.get_status_table.return_value = "OK" + mock_cls.return_value = mock_observer + + mock_config = MagicMock() + service = ObserverService(vikingdb=MagicMock(), config=mock_config) assert service.is_healthy() is False + + +class TestDebugService: + """Tests for DebugService class.""" + + def test_init_creates_observer(self): + """Test DebugService creates ObserverService on init.""" + service = DebugService() + assert isinstance(service._observer, ObserverService) + + def test_init_with_dependencies(self): + """Test DebugService passes dependencies to ObserverService.""" + mock_vikingdb = MagicMock() + mock_config = MagicMock() + service = DebugService(vikingdb=mock_vikingdb, config=mock_config) + assert service._observer._vikingdb is mock_vikingdb + assert service._observer._config is mock_config + + def test_set_dependencies(self): + """Test set_dependencies passes to ObserverService.""" + service = DebugService() + mock_vikingdb = MagicMock() + mock_config = MagicMock() + service.set_dependencies(vikingdb=mock_vikingdb, config=mock_config) + assert service._observer._vikingdb is mock_vikingdb + assert service._observer._config is mock_config + + def test_observer_property(self): + """Test observer property returns ObserverService.""" + service = DebugService() + assert service.observer is service._observer + assert isinstance(service.observer, ObserverService)