Files
Octop/tests/integration/test_bridge_api.py
0394e28fdc fix(bridge): keep team member streams alive through refresh (#2159)
* fix(bridge): keep team member streams alive through refresh

Cloud rooms dropped member tokens after the host turn ended, and a refresh mid-answer had nothing in history to restore. Hold the room subscriber and replay the in-progress member text when a client subscribes again.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: note cloud team stream refresh in the changelog

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(bridge): keep local login when peer credentials are rejected

Peer auth failure used HTTP 401, so probing cloud collab cleared the local session and showed Unauthorized.

Fixes #2166

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(dashboard): split chat nav into experts, teams, and cloud

Empty team and cloud lists can jump to create the first one, the host wrap-up lines up with the host avatar, and the cloud collab page explains how to connect.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(dashboard): add knowledge-base folder tree and unify list-panel rail toggles

- Knowledge bases: add a resizable folder navigation tree beside the document
  list; move the breadcrumb into the docs toolbar and surface base id plus
  creator on list rows.
- Knowledge bases / skill packages: move the collapse control onto the split
  divider as a hover pill and drop the separate detail header.
- RailEdgeControl: pill button with center/before/after placement so two rails
  can share one divider.

---------

Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-18 20:05:30 +08:00

107 lines
3.5 KiB
Python

"""Integration smoke for Bridge connection management API."""
from __future__ import annotations
from typing import Any
from unittest.mock import AsyncMock, MagicMock, patch
from octop.infra.errors import ErrorCode, OctopError
async def test_bridge_list_empty(env: Any) -> None:
client, _srv, auth = env
r = await client.get("/api/bridge/connections", headers=auth)
assert r.status_code == 200
assert r.json() == []
async def test_bridge_probe_returns_expert_list(env: Any) -> None:
client, _srv, auth = env
peer_agents = [
{
"agent_id": "01REMOTE",
"id": 3,
"name": "Remote Expert",
"description": "Cloud helper",
"icon_url": "/experts/avatars/scene-healthcare.svg",
"color": "#112233",
"state": "idle",
"kind": "expert",
}
]
fake_resp = MagicMock()
fake_resp.status_code = 200
fake_resp.json.return_value = peer_agents
fake_resp.text = "[]"
mock_client = MagicMock()
mock_client.get = AsyncMock(return_value=fake_resp)
mock_client.__aenter__ = AsyncMock(return_value=mock_client)
mock_client.__aexit__ = AsyncMock(return_value=None)
with (
patch(
"octop.infra.bridge.manager.login_peer",
new=AsyncMock(
return_value={
"access_token": "peer-token",
"user": {"username": "peer", "display_name": "Peer User"},
}
),
),
patch("octop.infra.bridge.manager.httpx.AsyncClient", return_value=mock_client),
):
r = await client.post(
"/api/bridge/probe",
headers=auth,
json={
"peer_base_url": "https://demo.octop.chat",
"peer_username": "peer",
"password": "secret",
},
)
assert r.status_code == 200, r.text
body = r.json()
assert body["peer_display_name"] == "Peer User"
assert body["agent_count"] == 1
assert body["agents"][0]["agent_id"] == "01REMOTE"
assert body["agents"][0]["name"] == "Remote Expert"
assert body["agents"][0]["description"] == "Cloud helper"
assert body["agents"][0]["icon_url"] == "/experts/avatars/scene-healthcare.svg"
# Probe must not create a persisted connection.
listed = await client.get("/api/bridge/connections", headers=auth)
assert listed.json() == []
async def test_bridge_probe_peer_rejection_stays_logged_in(env: Any) -> None:
"""Peer credential failure is 400, so the dashboard does not clear the local session."""
client, _srv, auth = env
with patch(
"octop.infra.bridge.manager.login_peer",
new=AsyncMock(
side_effect=OctopError(ErrorCode.BRIDGE_AUTH_FAILED, "peer rejected credentials")
),
):
r = await client.post(
"/api/bridge/probe",
headers=auth,
json={
"peer_base_url": "https://demo.octop.chat",
"peer_username": "peer",
"password": "secret",
},
)
assert r.status_code == 400, r.text
assert r.json()["error"]["code"] == "BRIDGE_AUTH_FAILED"
async def test_bridge_probe_validation(env: Any) -> None:
client, _srv, auth = env
r = await client.post(
"/api/bridge/probe",
headers=auth,
json={"peer_base_url": "https://demo.octop.chat", "peer_username": "", "password": "x"},
)
assert r.status_code == 422