1
0

[kimi] Add WebSocket authentication for Matrix connections (#682) (#744)

This commit is contained in:
2026-03-21 16:14:05 +00:00
parent d54493a87b
commit 815933953c
3 changed files with 196 additions and 1 deletions

View File

@@ -246,6 +246,131 @@ def test_world_ws_endpoint_accepts_connection(client):
pass # Connection accepted — just close it
# ---------------------------------------------------------------------------
# WebSocket Authentication Tests
# ---------------------------------------------------------------------------
class TestWebSocketAuth:
"""Tests for WebSocket token-based authentication."""
def test_ws_auth_disabled_when_token_unset(self, client):
"""When matrix_ws_token is empty, auth is disabled (dev mode)."""
with patch("dashboard.routes.world.settings") as mock_settings:
mock_settings.matrix_ws_token = ""
with client.websocket_connect("/api/world/ws") as ws:
# Should receive world_state without auth
msg = json.loads(ws.receive_text())
assert msg["type"] == "world_state"
def test_ws_valid_token_via_query_param(self, client):
"""Valid token via ?token= query param allows connection."""
with patch("dashboard.routes.world.settings") as mock_settings:
mock_settings.matrix_ws_token = "secret123"
with client.websocket_connect("/api/world/ws?token=secret123") as ws:
# Should receive connection_ack first
ack = json.loads(ws.receive_text())
assert ack["type"] == "connection_ack"
# Then world_state
msg = json.loads(ws.receive_text())
assert msg["type"] == "world_state"
def test_ws_valid_token_via_first_message(self, client):
"""Valid token via first auth message allows connection."""
with patch("dashboard.routes.world.settings") as mock_settings:
mock_settings.matrix_ws_token = "secret123"
with client.websocket_connect("/api/world/ws") as ws:
# Send auth message
ws.send_text(json.dumps({"type": "auth", "token": "secret123"}))
# Should receive connection_ack
ack = json.loads(ws.receive_text())
assert ack["type"] == "connection_ack"
# Then world_state
msg = json.loads(ws.receive_text())
assert msg["type"] == "world_state"
def test_ws_invalid_token_via_query_param(self, client):
"""Invalid token via ?token= closes connection with code 4001."""
from starlette.websockets import WebSocketDisconnect
with patch("dashboard.routes.world.settings") as mock_settings:
mock_settings.matrix_ws_token = "secret123"
# When auth fails with query param, accept() is called then close()
# The test client raises WebSocketDisconnect on close
with pytest.raises(WebSocketDisconnect) as exc_info:
with client.websocket_connect("/api/world/ws?token=wrongtoken") as ws:
# Try to receive - should trigger the close
ws.receive_text()
assert exc_info.value.code == 4001
def test_ws_invalid_token_via_first_message(self, client):
"""Invalid token via first message closes connection with code 4001."""
from starlette.websockets import WebSocketDisconnect
with patch("dashboard.routes.world.settings") as mock_settings:
mock_settings.matrix_ws_token = "secret123"
with client.websocket_connect("/api/world/ws") as ws:
# Send invalid auth message
ws.send_text(json.dumps({"type": "auth", "token": "wrongtoken"}))
# Connection should close with 4001
with pytest.raises(WebSocketDisconnect) as exc_info:
ws.receive_text()
assert exc_info.value.code == 4001
def test_ws_no_token_when_auth_required(self, client):
"""No token when auth is required closes connection with code 4001."""
from starlette.websockets import WebSocketDisconnect
with patch("dashboard.routes.world.settings") as mock_settings:
mock_settings.matrix_ws_token = "secret123"
with client.websocket_connect("/api/world/ws") as ws:
# Send non-auth message without token
ws.send_text(json.dumps({"type": "visitor_message", "text": "hello"}))
# Connection should close with 4001
with pytest.raises(WebSocketDisconnect) as exc_info:
ws.receive_text()
assert exc_info.value.code == 4001
def test_ws_non_json_first_message_when_auth_required(self, client):
"""Non-JSON first message when auth required closes with 4001."""
from starlette.websockets import WebSocketDisconnect
with patch("dashboard.routes.world.settings") as mock_settings:
mock_settings.matrix_ws_token = "secret123"
with client.websocket_connect("/api/world/ws") as ws:
# Send non-JSON message
ws.send_text("not json")
# Connection should close with 4001
with pytest.raises(WebSocketDisconnect) as exc_info:
ws.receive_text()
assert exc_info.value.code == 4001
def test_ws_existing_behavior_unchanged_when_token_not_configured(self, client, tmp_path):
"""Existing /api/world/ws behavior unchanged when token not configured."""
f = tmp_path / "presence.json"
f.write_text(
json.dumps(
{
"version": 1,
"liveness": "2026-03-19T02:00:00Z",
"mood": "exploring",
"current_focus": "testing",
"active_threads": [],
"recent_events": [],
"concerns": [],
}
)
)
with patch("dashboard.routes.world.settings") as mock_settings:
mock_settings.matrix_ws_token = "" # Not configured
with patch("dashboard.routes.world.PRESENCE_FILE", f):
with client.websocket_connect("/api/world/ws") as ws:
# Should receive world_state directly (no connection_ack)
msg = json.loads(ws.receive_text())
assert msg["type"] == "world_state"
assert msg["timmyState"]["mood"] == "exploring"
def test_world_ws_sends_snapshot_on_connect(client, tmp_path):
"""WebSocket sends a world_state snapshot immediately on connect."""
f = tmp_path / "presence.json"