Files
Octop/tests/unit/bridge/test_create_rollback.py
e9deb58206 fix: harden Docker bootstrap, Bridge sessions, and desktop/provider UX (#1825)
Avoid silent random admin passwords, keep Bridge links alive under load,
and stop custom providers from sending stream_options that MindIE rejects.

Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-09 07:04:56 +08:00

215 lines
6.5 KiB
Python

"""BridgeManager create_connection rollback behavior."""
from __future__ import annotations
from typing import Any
from unittest.mock import AsyncMock, MagicMock
import pytest
from octop.infra.bridge.manager import BridgeManager
from octop.infra.db.repos.bridge_connections import BridgeConnectionRow
from octop.infra.errors import ErrorCode, OctopError
def _row(**overrides: Any) -> BridgeConnectionRow:
base = {
"pk": 1,
"connection_id": "01CONN",
"owner_user_id": 1,
"peer_base_url": "https://peer.example",
"peer_username": "bob",
"display_name": "Cloud",
"notes": None,
"icon_name": None,
"credential_blob": b"x",
"access_token_blob": b"y",
"token_expires_at": None,
"status": "connecting",
"last_error": None,
"last_seen_at": None,
"auto_reconnect": True,
"created_at": 1,
"updated_at": 1,
}
base.update(overrides)
return BridgeConnectionRow(**base)
@pytest.mark.asyncio
async def test_create_connection_rolls_back_when_connect_fails(
monkeypatch: pytest.MonkeyPatch,
) -> None:
repo = MagicMock()
repo.find_by_display_name.return_value = None
repo.list_for_owner.return_value = []
created = _row()
repo.create.return_value = created
repo.get.return_value = None
repo.get_for_owner.return_value = created
mgr = BridgeManager(
bridge_repo=repo,
secret_repo=MagicMock(),
user_repo=MagicMock(),
advertise_base_url="http://local.test",
)
monkeypatch.setattr(
"octop.infra.bridge.manager.normalize_peer_base_url",
lambda url: url.rstrip("/"),
)
monkeypatch.setattr(
"octop.infra.bridge.manager.new_short_id",
lambda: "01CONN",
)
monkeypatch.setattr(
"octop.infra.bridge.manager.login_peer",
AsyncMock(return_value={"access_token": "tok", "expires_in": 3600}),
)
monkeypatch.setattr(
"octop.infra.bridge.manager.encrypt_payload",
lambda _s, _p: b"enc",
)
mgr.connect = AsyncMock( # type: ignore[method-assign]
side_effect=OctopError(ErrorCode.BRIDGE_PEER_UNREACHABLE, "ws failed")
)
mgr.disconnect = AsyncMock() # type: ignore[method-assign]
with pytest.raises(OctopError) as ei:
await mgr.create_connection(
owner_user_id=1,
peer_base_url="https://peer.example",
peer_username="bob",
password="secret",
display_name="Cloud",
connect=True,
)
assert ei.value.code == ErrorCode.BRIDGE_PEER_UNREACHABLE
repo.delete.assert_called_once_with("01CONN")
mgr.disconnect.assert_awaited()
@pytest.mark.asyncio
async def test_create_connection_rejects_taken_display_name(
monkeypatch: pytest.MonkeyPatch,
) -> None:
repo = MagicMock()
repo.find_by_display_name.return_value = _row()
repo.list_for_owner.return_value = []
mgr = BridgeManager(
bridge_repo=repo,
secret_repo=MagicMock(),
user_repo=MagicMock(),
advertise_base_url="http://local.test",
)
monkeypatch.setattr(
"octop.infra.bridge.manager.normalize_peer_base_url",
lambda url: url.rstrip("/"),
)
with pytest.raises(OctopError) as ei:
await mgr.create_connection(
owner_user_id=1,
peer_base_url="https://peer.example",
peer_username="bob",
password="secret",
display_name="Cloud",
connect=True,
)
assert ei.value.code == ErrorCode.BRIDGE_DISPLAY_NAME_TAKEN
repo.create.assert_not_called()
@pytest.mark.asyncio
async def test_create_connection_rejects_duplicate_peer_account(
monkeypatch: pytest.MonkeyPatch,
) -> None:
repo = MagicMock()
repo.find_by_display_name.return_value = None
repo.list_for_owner.return_value = [
_row(peer_base_url="https://peer.example", peer_username="Bob")
]
mgr = BridgeManager(
bridge_repo=repo,
secret_repo=MagicMock(),
user_repo=MagicMock(),
advertise_base_url="http://127.0.0.1:9",
)
monkeypatch.setattr(
"octop.infra.bridge.manager.normalize_peer_base_url",
lambda url: url.rstrip("/"),
)
with pytest.raises(OctopError) as ei:
await mgr.create_connection(
owner_user_id=1,
peer_base_url="https://peer.example/",
peer_username="bob",
password="secret",
display_name="Other",
connect=True,
)
assert ei.value.code == ErrorCode.BRIDGE_PEER_ALREADY_LINKED
assert ei.value.details["name"] == "Cloud"
repo.create.assert_not_called()
@pytest.mark.asyncio
async def test_create_connection_rejects_at_user_limit(
monkeypatch: pytest.MonkeyPatch,
) -> None:
repo = MagicMock()
repo.find_by_display_name.return_value = None
repo.list_for_owner.return_value = [
_row(
connection_id=f"c{i}",
display_name=f"n{i}",
peer_base_url=f"https://n{i}.example",
peer_username="bob",
)
for i in range(10)
]
mgr = BridgeManager(
bridge_repo=repo,
secret_repo=MagicMock(),
user_repo=MagicMock(),
advertise_base_url="http://127.0.0.1:9",
)
monkeypatch.setattr(
"octop.infra.bridge.manager.normalize_peer_base_url",
lambda url: url.rstrip("/"),
)
with pytest.raises(OctopError) as ei:
await mgr.create_connection(
owner_user_id=1,
peer_base_url="https://fresh.example",
peer_username="bob",
password="secret",
display_name="Fresh",
connect=True,
)
assert ei.value.code == ErrorCode.BRIDGE_CONNECTION_LIMIT
assert ei.value.details["limit"] == 10
repo.create.assert_not_called()
@pytest.mark.asyncio
async def test_create_connection_rejects_self_url() -> None:
repo = MagicMock()
repo.find_by_display_name.return_value = None
repo.list_for_owner.return_value = []
mgr = BridgeManager(
bridge_repo=repo,
secret_repo=MagicMock(),
user_repo=MagicMock(),
advertise_base_url="http://127.0.0.1:8787",
)
with pytest.raises(OctopError) as ei:
await mgr.create_connection(
owner_user_id=1,
peer_base_url="http://localhost:8787",
peer_username="alice",
password="secret",
display_name="Me",
connect=True,
)
assert ei.value.code == ErrorCode.BRIDGE_SELF_CONNECT
repo.create.assert_not_called()