Files
memory-gateway/tests/test_memory_gateway_skill.py

205 lines
5.6 KiB
Python

from __future__ import annotations
import importlib.util
import json
from pathlib import Path
from urllib.error import HTTPError
import pytest
SCRIPT_PATH = (
Path(__file__).parents[1]
/ "skill"
/ "memory-gateway-agent"
/ "scripts"
/ "memory_gateway.py"
)
def load_cli():
spec = importlib.util.spec_from_file_location("memory_gateway_skill_cli", SCRIPT_PATH)
assert spec is not None and spec.loader is not None
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
class FakeResponse:
def __init__(self, body: dict[str, object], status: int = 200) -> None:
self.body = json.dumps(body).encode()
self.status = status
def __enter__(self):
return self
def __exit__(self, *args: object) -> None:
return None
def read(self) -> bytes:
return self.body
def close(self) -> None:
return None
def test_settings_read_gateway_credentials_from_environment(
monkeypatch: pytest.MonkeyPatch,
) -> None:
cli = load_cli()
monkeypatch.setenv("MEMORY_GATEWAY_BASE_URL", "http://gateway.test/")
monkeypatch.setenv("MEMORY_GATEWAY_USER_ID", "u_agent")
monkeypatch.setenv("MEMORY_GATEWAY_USER_KEY", "uk_secret")
settings = cli.Settings.from_env()
assert settings.base_url == "http://gateway.test"
assert settings.user_id == "u_agent"
assert settings.user_key == "uk_secret"
def test_json_request_sends_authenticated_payload(
monkeypatch: pytest.MonkeyPatch,
) -> None:
cli = load_cli()
captured: dict[str, object] = {}
def fake_urlopen(request, timeout):
captured["url"] = request.full_url
captured["method"] = request.method
captured["body"] = json.loads(request.data)
captured["content_type"] = request.headers["Content-type"]
captured["timeout"] = timeout
return FakeResponse({"results": []})
monkeypatch.setattr(cli, "urlopen", fake_urlopen)
client = cli.MemoryGatewayClient(
"http://gateway.test",
user_id="u_agent",
user_key="uk_secret",
timeout=9,
)
result = client.search("contract", scopes=["resources"], top_k=5)
assert result == {"results": []}
assert captured == {
"url": "http://gateway.test/memories/search",
"method": "POST",
"body": {
"user_id": "u_agent",
"user_key": "uk_secret",
"query": "contract",
"scope": ["resources"],
"top_k": 5,
"app_id": "default",
"project_id": "default",
},
"content_type": "application/json",
"timeout": 9,
}
def test_upload_builds_multipart_request_without_exposing_file_uri(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
cli = load_cli()
upload = tmp_path / "note.txt"
upload.write_text("remember this", encoding="utf-8")
captured: dict[str, object] = {}
def fake_urlopen(request, timeout):
captured["url"] = request.full_url
captured["method"] = request.method
captured["body"] = request.data
captured["content_type"] = request.headers["Content-type"]
return FakeResponse(
{
"resource_id": "r_1",
"uri": "resource://u_agent/r_1",
"status": "extracted",
}
)
monkeypatch.setattr(cli, "urlopen", fake_urlopen)
client = cli.MemoryGatewayClient(
"http://gateway.test",
user_id="u_agent",
user_key="uk_secret",
)
result = client.upload_resource(upload, title="Agent note")
body = captured["body"]
assert isinstance(body, bytes)
assert captured["url"] == "http://gateway.test/resources"
assert captured["method"] == "POST"
assert str(captured["content_type"]).startswith("multipart/form-data; boundary=")
assert b'name="user_id"' in body
assert b"u_agent" in body
assert b'name="file"; filename="note.txt"' in body
assert b"remember this" in body
assert b"file://" not in body
assert result["uri"] == "resource://u_agent/r_1"
def test_http_error_raises_gateway_error_without_leaking_user_key(
monkeypatch: pytest.MonkeyPatch,
) -> None:
cli = load_cli()
def fake_urlopen(request, timeout):
raise HTTPError(
request.full_url,
401,
"Unauthorized",
hdrs=None,
fp=FakeResponse({"detail": "invalid user credentials"}),
)
monkeypatch.setattr(cli, "urlopen", fake_urlopen)
client = cli.MemoryGatewayClient(
"http://gateway.test",
user_id="u_agent",
user_key="uk_super_secret",
)
with pytest.raises(cli.GatewayError) as exc_info:
client.list_resources()
message = str(exc_info.value)
assert "401" in message
assert "invalid user credentials" in message
assert "uk_super_secret" not in message
def test_load_messages_accepts_large_inline_json() -> None:
cli = load_cli()
value = json.dumps(
[
{
"sender_id": "u_agent",
"role": "user",
"timestamp": 1234567890123,
"content": "x" * 5000,
}
]
)
messages = cli._load_json_array(value)
assert messages[0]["content"] == "x" * 5000
def test_search_requires_conversation_id_for_current_chat_scope() -> None:
cli = load_cli()
client = cli.MemoryGatewayClient(
"http://gateway.test",
user_id="u_agent",
user_key="uk_secret",
)
with pytest.raises(cli.GatewayError, match="conversation_id"):
client.search("what did we discuss", scopes=["current_chat"])