- 添加MinIO用户文件系统配置选项(BEAVER_MINIO_ROOT_USER等) - 更新外部连接器配置结构,包括BASE_URL和认证令牌设置 - 改进connector provider支持更多类型(official, feishu_bot等) - 实现Mistral模型推理模式支持reasoning_effort参数 - 增强外部连接器策略配置和运行时配置管理 - 添加connector bridge事件验证和安全保护机制 - 优化任务路由逻辑,区分simple_chat和new_task场景 - 更新初始技能工具提示配置,分离authoring admin功能
147 lines
4.9 KiB
Python
147 lines
4.9 KiB
Python
from __future__ import annotations
|
|
|
|
import base64
|
|
|
|
from fastapi.testclient import TestClient
|
|
|
|
from external_connector.app import create_app
|
|
from external_connector.providers.fake import FakeProvider
|
|
from external_connector.state import SidecarStateStore
|
|
|
|
|
|
def test_fake_provider_lists_weixin_and_feishu(tmp_path) -> None:
|
|
provider = FakeProvider(SidecarStateStore(tmp_path / "state.json"))
|
|
|
|
connectors = provider.connectors()
|
|
|
|
assert [item["kind"] for item in connectors] == ["weixin", "feishu"]
|
|
assert connectors[0]["authType"] == "qr"
|
|
|
|
|
|
def test_fake_provider_session_flow(tmp_path) -> None:
|
|
provider = FakeProvider(SidecarStateStore(tmp_path / "state.json"))
|
|
|
|
session = provider.start_session(
|
|
{
|
|
"kind": "weixin",
|
|
"connectionId": "conn_1",
|
|
"channelId": "weixin-main",
|
|
"displayName": "Weixin Main",
|
|
"callbackBaseUrl": "http://beaver:8080",
|
|
"options": {},
|
|
}
|
|
)
|
|
loaded = provider.get_session(session["sessionId"])
|
|
|
|
assert session["status"] == "qr_ready"
|
|
prefix = "data:image/svg+xml;base64,"
|
|
assert session["qrImage"].startswith(prefix)
|
|
svg = base64.b64decode(session["qrImage"][len(prefix) :]).decode("utf-8")
|
|
assert svg.startswith("<svg")
|
|
assert "FAKE QR" in svg
|
|
assert loaded["sessionId"] == session["sessionId"]
|
|
|
|
|
|
def test_fake_provider_send_returns_idempotent_result(tmp_path) -> None:
|
|
provider = FakeProvider(SidecarStateStore(tmp_path / "state.json"))
|
|
payload = {
|
|
"requestId": "out_1",
|
|
"connectionId": "conn_1",
|
|
"channelId": "weixin-main",
|
|
"kind": "weixin",
|
|
"target": {"peerId": "peer-1", "peerType": "dm", "threadId": None},
|
|
"content": "hello",
|
|
"metadata": {},
|
|
}
|
|
|
|
first = provider.send(payload)
|
|
second = provider.send(payload)
|
|
|
|
assert first == second
|
|
assert first["ok"] is True
|
|
|
|
|
|
def test_sidecar_http_api_requires_bearer_token(tmp_path) -> None:
|
|
app = create_app(provider=FakeProvider(SidecarStateStore(tmp_path / "state.json")), api_token="sidecar-token")
|
|
|
|
with TestClient(app) as client:
|
|
response = client.get("/connectors")
|
|
|
|
assert response.status_code == 401
|
|
|
|
|
|
def test_sidecar_http_api_fails_closed_without_configured_token(tmp_path) -> None:
|
|
app = create_app(provider=FakeProvider(SidecarStateStore(tmp_path / "state.json")), api_token="")
|
|
|
|
with TestClient(app) as client:
|
|
response = client.get("/connectors")
|
|
health = client.get("/health")
|
|
|
|
assert response.status_code == 503
|
|
assert health.status_code == 200
|
|
|
|
|
|
def test_sidecar_http_api_session_and_send(tmp_path) -> None:
|
|
app = create_app(provider=FakeProvider(SidecarStateStore(tmp_path / "state.json")), api_token="sidecar-token")
|
|
headers = {"Authorization": "Bearer sidecar-token"}
|
|
|
|
with TestClient(app) as client:
|
|
connectors = client.get("/connectors", headers=headers)
|
|
session = client.post(
|
|
"/connector-sessions",
|
|
headers=headers,
|
|
json={
|
|
"kind": "weixin",
|
|
"connectionId": "conn_1",
|
|
"channelId": "weixin-main",
|
|
"displayName": "Weixin Main",
|
|
"callbackBaseUrl": "http://beaver:8080",
|
|
"options": {},
|
|
},
|
|
)
|
|
session_id = session.json()["sessionId"]
|
|
loaded = client.get(f"/connector-sessions/{session_id}", headers=headers)
|
|
sent = client.post(
|
|
"/send",
|
|
headers=headers,
|
|
json={
|
|
"requestId": "out_1",
|
|
"connectionId": "conn_1",
|
|
"channelId": "weixin-main",
|
|
"kind": "weixin",
|
|
"target": {"peerId": "peer-1", "peerType": "dm", "threadId": None},
|
|
"content": "hello",
|
|
"metadata": {},
|
|
},
|
|
)
|
|
|
|
assert connectors.status_code == 200
|
|
assert session.status_code == 200
|
|
assert loaded.json()["sessionId"] == session_id
|
|
assert sent.json()["ok"] is True
|
|
|
|
|
|
def test_sidecar_http_api_returns_conflict_for_processing_send(tmp_path) -> None:
|
|
store = SidecarStateStore(tmp_path / "state.json", send_processing_ttl_seconds=60)
|
|
store.begin_send(connection_id="conn_1", request_id="out_1")
|
|
app = create_app(provider=FakeProvider(store), api_token="sidecar-token")
|
|
headers = {"Authorization": "Bearer sidecar-token"}
|
|
|
|
with TestClient(app) as client:
|
|
response = client.post(
|
|
"/send",
|
|
headers=headers,
|
|
json={
|
|
"requestId": "out_1",
|
|
"connectionId": "conn_1",
|
|
"channelId": "weixin-main",
|
|
"kind": "weixin",
|
|
"target": {"peerId": "peer-1", "peerType": "dm", "threadId": None},
|
|
"content": "hello",
|
|
"metadata": {},
|
|
},
|
|
)
|
|
|
|
assert response.status_code == 409
|
|
assert response.json()["retryAfterSeconds"] == 5
|