4b6817381b
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
115 lines
4.5 KiB
Python
115 lines
4.5 KiB
Python
from __future__ import annotations
|
|
|
|
from types import SimpleNamespace
|
|
|
|
import pytest
|
|
|
|
from core.agent_harness.prompts import build_action_system_prompt
|
|
from core.agent_harness.session import InMemorySessionStorage, SessionCore, SessionManager
|
|
from core.agent_harness.turns.turn_snapshot import TurnSnapshot
|
|
from gateway.storage import SessionBindingStore, SessionResolver, connect_gateway_db
|
|
|
|
|
|
@pytest.fixture
|
|
def resolver(tmp_path, monkeypatch) -> SessionResolver:
|
|
# Keep integration bootstrap a no-op so tests don't resolve real integrations.
|
|
monkeypatch.setattr(SessionCore, "warm_resolved_integrations", lambda _self, **_k: None)
|
|
monkeypatch.setattr(SessionCore, "hydrate_configured_integrations", lambda _self: None)
|
|
|
|
conn = connect_gateway_db(tmp_path / "state.db")
|
|
store = SessionBindingStore(conn)
|
|
# A mutable fake repo whose load_session each test can override.
|
|
repo = SimpleNamespace(load_session=lambda _session_id: None)
|
|
manager = SessionManager(storage=InMemorySessionStorage(), repo=repo)
|
|
resolver = SessionResolver(store, manager=manager)
|
|
resolver._fake_repo = repo # test handle to swap load_session
|
|
yield resolver
|
|
conn.close()
|
|
|
|
|
|
def test_resolve_creates_and_injects_gateway_chat_context(resolver: SessionResolver) -> None:
|
|
resolved = resolver.resolve(user_id="42", chat_id="99")
|
|
|
|
# New session was created, bound, and tagged with the per-turn chat id.
|
|
assert resolved.resolved_integrations_cache["_gateway_chat_id"] == "99"
|
|
assert (
|
|
resolver._bindings.get_session_id(platform="telegram", chat_id="42") == resolved.session_id
|
|
)
|
|
|
|
|
|
def test_resolve_restores_persisted_conversation_context(resolver: SessionResolver) -> None:
|
|
resolver._bindings.bind(platform="telegram", chat_id="42", session_id="session-1")
|
|
resolver._fake_repo.load_session = lambda session_id: {
|
|
"session_id": session_id,
|
|
"cli_agent_messages": [
|
|
("user", "weather in Hawaii"),
|
|
("assistant", "Hawaii: +28C"),
|
|
("user", "send that to Slack"),
|
|
(
|
|
"assistant",
|
|
'slack_send_message input: {"message": "Hawaii: +28C"}\n'
|
|
'slack_send_message result: {"status": "sent"}',
|
|
),
|
|
],
|
|
"accumulated_context": {"service": "checkout"},
|
|
"history": [{"type": "shell", "text": "curl wttr.in/Hawaii", "ok": True}],
|
|
}
|
|
|
|
resolved = resolver.resolve(user_id="42", chat_id="99")
|
|
|
|
assert resolved.cli_agent_messages[-1] == (
|
|
"assistant",
|
|
'slack_send_message input: {"message": "Hawaii: +28C"}\n'
|
|
'slack_send_message result: {"status": "sent"}',
|
|
)
|
|
assert resolved.accumulated_context == {"service": "checkout"}
|
|
assert resolved.history == [{"type": "shell", "text": "curl wttr.in/Hawaii", "ok": True}]
|
|
assert resolved.resolved_integrations_cache["_gateway_chat_id"] == "99"
|
|
|
|
|
|
def test_resolved_telegram_context_is_visible_as_prior_action_facts(
|
|
resolver: SessionResolver,
|
|
) -> None:
|
|
resolver._bindings.bind(platform="telegram", chat_id="42", session_id="session-1")
|
|
resolver._fake_repo.load_session = lambda session_id: {
|
|
"session_id": session_id,
|
|
"cli_agent_messages": [
|
|
("user", "Can you send the weather of both hawaii and antartica to slack?"),
|
|
(
|
|
"assistant",
|
|
"Hawaii: +28C\n"
|
|
"Antarctica: -24C\n"
|
|
'slack_send_message input: {"message": "Hawaii: +28C\\nAntarctica: -24C"}\n'
|
|
'slack_send_message result: {"sent": true}',
|
|
),
|
|
("user", "Write it in a nicer message and compare to London"),
|
|
("assistant", "London: +22C"),
|
|
],
|
|
}
|
|
|
|
resolved = resolver.resolve(user_id="42", chat_id="99")
|
|
|
|
prompt = build_action_system_prompt(
|
|
TurnSnapshot.from_session(
|
|
"No, compute those temperatures and send the nice comparison to Slack",
|
|
resolved,
|
|
)
|
|
)
|
|
|
|
assert "PRIOR ACTION FACTS" in prompt
|
|
assert "Hawaii: +28C" in prompt
|
|
assert "Antarctica: -24C" in prompt
|
|
assert "London: +22C" in prompt
|
|
assert "slack_send_message input" in prompt
|
|
|
|
|
|
def test_rotate_flushes_old_and_binds_new(resolver: SessionResolver) -> None:
|
|
first = resolver.resolve(user_id="42", chat_id="99")
|
|
rotated = resolver.rotate(user_id="42", chat_id="99")
|
|
|
|
assert rotated.session_id != first.session_id
|
|
assert rotated.resolved_integrations_cache["_gateway_chat_id"] == "99"
|
|
assert (
|
|
resolver._bindings.get_session_id(platform="telegram", chat_id="42") == rotated.session_id
|
|
)
|