6d658b4f4d
- 工具系统从 kilostar/plugin/tool_plugin/ 迁移到 data/toolset/(manifest.json 声明式) - 新增 plugin_runtime 模块:BaseOrganization / GlobalPluginManager / loader / tool_bridge - 新增 org_task + org_task_event 表及 DAO(alembic 0009) - 新增 /api/v1/plugin 路由(submit/status/stream/install/reload) - 新增 data/plugin/example_dept 示例重型插件 - regulatory_node 支持聊天历史上下文注入 - send_file 改为 artifact 存盘 + SSE 推送下载链接 - 前端 WorkflowFileCard 组件 + ToolSettings README 渲染 - utils 整理:合并 access/role_check、standalone_proxy→ray_compat、删除废弃模块 - 项目结构文档移至 docs/STRUCTURE.md 并详细展开 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
107 lines
3.0 KiB
Python
107 lines
3.0 KiB
Python
"""ray_compat 适配层单元测试。
|
|
|
|
验证 StandaloneProxy / _MethodProxy / actor_class / remote_task
|
|
在单机模式下的行为是否正确模拟了 Ray Actor Handle 的 .remote() 接口。
|
|
"""
|
|
|
|
import asyncio
|
|
import pytest
|
|
|
|
from kilostar.utils import ray_compat
|
|
from kilostar.utils.ray_compat import StandaloneProxy, _MethodProxy
|
|
|
|
|
|
class TestMethodProxy:
|
|
def test_sync_method(self):
|
|
def add(a, b):
|
|
return a + b
|
|
|
|
proxy = _MethodProxy(add)
|
|
result = asyncio.get_event_loop().run_until_complete(proxy.remote(2, 3))
|
|
assert result == 5
|
|
|
|
def test_async_method(self):
|
|
async def async_add(a, b):
|
|
return a + b
|
|
|
|
proxy = _MethodProxy(async_add)
|
|
result = asyncio.get_event_loop().run_until_complete(proxy.remote(4, 6))
|
|
assert result == 10
|
|
|
|
|
|
class TestStandaloneProxy:
|
|
def test_method_call(self):
|
|
class FakeActor:
|
|
def greet(self, name):
|
|
return f"hello {name}"
|
|
|
|
proxy = StandaloneProxy(FakeActor())
|
|
future = proxy.greet.remote("world")
|
|
result = asyncio.get_event_loop().run_until_complete(future)
|
|
assert result == "hello world"
|
|
|
|
def test_async_method_call(self):
|
|
class FakeActor:
|
|
async def compute(self, x):
|
|
return x * 2
|
|
|
|
proxy = StandaloneProxy(FakeActor())
|
|
future = proxy.compute.remote(7)
|
|
result = asyncio.get_event_loop().run_until_complete(future)
|
|
assert result == 14
|
|
|
|
def test_attribute_access(self):
|
|
class FakeActor:
|
|
def __init__(self):
|
|
self.name = "test"
|
|
|
|
proxy = StandaloneProxy(FakeActor())
|
|
assert proxy.name == "test"
|
|
|
|
|
|
class TestActorClass:
|
|
def test_standalone_returns_class_unchanged(self, monkeypatch):
|
|
monkeypatch.setattr(ray_compat, "_STANDALONE", True)
|
|
|
|
@ray_compat.actor_class
|
|
class MyActor:
|
|
def do_work(self):
|
|
return 42
|
|
|
|
instance = MyActor()
|
|
assert instance.do_work() == 42
|
|
|
|
def test_standalone_class_is_plain_python(self, monkeypatch):
|
|
monkeypatch.setattr(ray_compat, "_STANDALONE", True)
|
|
|
|
@ray_compat.actor_class
|
|
class MyActor:
|
|
pass
|
|
|
|
assert not hasattr(MyActor, "remote")
|
|
assert not hasattr(MyActor, "options")
|
|
|
|
|
|
class TestRemoteTask:
|
|
def test_sync_task(self, monkeypatch):
|
|
monkeypatch.setattr(ray_compat, "_STANDALONE", True)
|
|
|
|
@ray_compat.remote_task
|
|
def multiply(a, b):
|
|
return a * b
|
|
|
|
future = multiply.remote(3, 4)
|
|
result = asyncio.get_event_loop().run_until_complete(future)
|
|
assert result == 12
|
|
|
|
def test_async_task(self, monkeypatch):
|
|
monkeypatch.setattr(ray_compat, "_STANDALONE", True)
|
|
|
|
@ray_compat.remote_task
|
|
async def async_multiply(a, b):
|
|
return a * b
|
|
|
|
future = async_multiply.remote(5, 6)
|
|
result = asyncio.get_event_loop().run_until_complete(future)
|
|
assert result == 30
|