mirror of
https://github.com/open-jarvis/OpenJarvis.git
synced 2026-07-30 19:02:16 +00:00
- Rewrite .github/workflows/desktop.yml: 2-job pipeline (validate + build-and-release) with rolling desktop-latest pre-release on push to main and stable desktop-v* releases - Add UpdateChecker component: checks for updates on startup + every 30 min, background download with progress bar, one-click relaunch - Configure Tauri updater: endpoints pointing to desktop-latest release, pubkey placeholder - Add tauri-plugin-process for relaunch support (Cargo.toml, lib.rs, package.json) - Add macOS Entitlements.plist for notarization (network + file access, no sandbox) - Add scripts/bump-desktop-version.sh for atomic version bumps across 3 config files - Add desktop/README.md with dev setup, auto-update architecture, signing docs - Update .gitignore for desktop/node_modules, dist, target - Configure macOS minimumSystemVersion, Windows timestampUrl - Include all Phase 14-21 work: agent hardening, RBAC, taint tracking, workflows, skills, knowledge graph, sessions, A2A, MCP templates, WASM sandbox, TUI dashboard, production tools, CLI expansion, API expansion, learning productionization, Tauri desktop app, and 10 new channels Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
203 lines
6.1 KiB
Python
203 lines
6.1 KiB
Python
"""Tests for A2A protocol (Phase 16.1)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from openjarvis.a2a.protocol import (
|
|
A2ARequest,
|
|
A2AResponse,
|
|
A2ATask,
|
|
AgentCard,
|
|
TaskState,
|
|
)
|
|
from openjarvis.a2a.server import A2AServer
|
|
from openjarvis.core.events import EventBus, EventType
|
|
|
|
|
|
class TestAgentCard:
|
|
def test_create_card(self):
|
|
card = AgentCard(
|
|
name="TestAgent",
|
|
description="A test agent",
|
|
url="http://localhost:8000",
|
|
)
|
|
assert card.name == "TestAgent"
|
|
|
|
def test_to_dict(self):
|
|
card = AgentCard(name="TestAgent", capabilities=["text"])
|
|
d = card.to_dict()
|
|
assert d["name"] == "TestAgent"
|
|
assert "text" in d["capabilities"]
|
|
|
|
|
|
class TestA2ATask:
|
|
def test_initial_state(self):
|
|
task = A2ATask(input_text="Hello")
|
|
assert task.state == TaskState.SUBMITTED
|
|
assert task.input_text == "Hello"
|
|
|
|
def test_to_dict(self):
|
|
task = A2ATask(input_text="Hello")
|
|
d = task.to_dict()
|
|
assert d["state"] == "submitted"
|
|
assert d["input"] == "Hello"
|
|
assert "id" in d
|
|
|
|
|
|
class TestA2ARequest:
|
|
def test_create_request(self):
|
|
req = A2ARequest(method="tasks/send", params={"input": "hello"})
|
|
assert req.method == "tasks/send"
|
|
|
|
def test_to_dict(self):
|
|
req = A2ARequest(method="tasks/send")
|
|
d = req.to_dict()
|
|
assert d["jsonrpc"] == "2.0"
|
|
assert d["method"] == "tasks/send"
|
|
|
|
def test_to_json(self):
|
|
req = A2ARequest(method="tasks/get")
|
|
j = req.to_json()
|
|
assert "tasks/get" in j
|
|
|
|
|
|
class TestA2AResponse:
|
|
def test_success_response(self):
|
|
resp = A2AResponse(result={"output": "hello"}, request_id="1")
|
|
d = resp.to_dict()
|
|
assert d["result"]["output"] == "hello"
|
|
assert d["id"] == "1"
|
|
assert "error" not in d
|
|
|
|
def test_error_response(self):
|
|
resp = A2AResponse(
|
|
error={"code": -32601, "message": "Not found"},
|
|
request_id="2",
|
|
)
|
|
d = resp.to_dict()
|
|
assert d["error"]["code"] == -32601
|
|
|
|
def test_from_json(self):
|
|
import json
|
|
data = json.dumps({"jsonrpc": "2.0", "result": "ok", "id": "3"})
|
|
resp = A2AResponse.from_json(data)
|
|
assert resp.result == "ok"
|
|
assert resp.request_id == "3"
|
|
|
|
|
|
class TestA2AServer:
|
|
def test_task_send(self):
|
|
card = AgentCard(name="Test")
|
|
server = A2AServer(card, handler=lambda x: f"Echo: {x}")
|
|
response = server.handle_request({
|
|
"jsonrpc": "2.0",
|
|
"method": "tasks/send",
|
|
"params": {"input": "Hello"},
|
|
"id": "1",
|
|
})
|
|
assert response["result"]["state"] == "completed"
|
|
assert "Echo: Hello" in response["result"]["output"]
|
|
|
|
def test_task_send_with_message_format(self):
|
|
card = AgentCard(name="Test")
|
|
server = A2AServer(card, handler=lambda x: f"Got: {x}")
|
|
response = server.handle_request({
|
|
"jsonrpc": "2.0",
|
|
"method": "tasks/send",
|
|
"params": {"message": {"role": "user", "parts": [{"text": "Hi"}]}},
|
|
"id": "1",
|
|
})
|
|
assert response["result"]["state"] == "completed"
|
|
assert "Got: Hi" in response["result"]["output"]
|
|
|
|
def test_task_get(self):
|
|
card = AgentCard(name="Test")
|
|
server = A2AServer(card, handler=lambda x: x)
|
|
# First send a task
|
|
send_resp = server.handle_request({
|
|
"jsonrpc": "2.0",
|
|
"method": "tasks/send",
|
|
"params": {"input": "test"},
|
|
"id": "1",
|
|
})
|
|
task_id = send_resp["result"]["id"]
|
|
|
|
# Now get it
|
|
get_resp = server.handle_request({
|
|
"jsonrpc": "2.0",
|
|
"method": "tasks/get",
|
|
"params": {"id": task_id},
|
|
"id": "2",
|
|
})
|
|
assert get_resp["result"]["id"] == task_id
|
|
|
|
def test_task_get_not_found(self):
|
|
card = AgentCard(name="Test")
|
|
server = A2AServer(card)
|
|
response = server.handle_request({
|
|
"jsonrpc": "2.0",
|
|
"method": "tasks/get",
|
|
"params": {"id": "nonexistent"},
|
|
"id": "1",
|
|
})
|
|
assert "error" in response
|
|
|
|
def test_task_cancel(self):
|
|
card = AgentCard(name="Test")
|
|
server = A2AServer(card, handler=lambda x: x)
|
|
send_resp = server.handle_request({
|
|
"jsonrpc": "2.0",
|
|
"method": "tasks/send",
|
|
"params": {"input": "test"},
|
|
"id": "1",
|
|
})
|
|
task_id = send_resp["result"]["id"]
|
|
|
|
cancel_resp = server.handle_request({
|
|
"jsonrpc": "2.0",
|
|
"method": "tasks/cancel",
|
|
"params": {"id": task_id},
|
|
"id": "2",
|
|
})
|
|
assert cancel_resp["result"]["state"] == "canceled"
|
|
|
|
def test_unknown_method(self):
|
|
card = AgentCard(name="Test")
|
|
server = A2AServer(card)
|
|
response = server.handle_request({
|
|
"jsonrpc": "2.0",
|
|
"method": "unknown/method",
|
|
"params": {},
|
|
"id": "1",
|
|
})
|
|
assert "error" in response
|
|
assert response["error"]["code"] == -32601
|
|
|
|
def test_events_emitted(self):
|
|
bus = EventBus(record_history=True)
|
|
card = AgentCard(name="Test")
|
|
server = A2AServer(card, handler=lambda x: x, bus=bus)
|
|
server.handle_request({
|
|
"jsonrpc": "2.0",
|
|
"method": "tasks/send",
|
|
"params": {"input": "test"},
|
|
"id": "1",
|
|
})
|
|
event_types = {e.event_type for e in bus.history}
|
|
assert EventType.A2A_TASK_RECEIVED in event_types
|
|
assert EventType.A2A_TASK_COMPLETED in event_types
|
|
|
|
def test_handler_error(self):
|
|
card = AgentCard(name="Test")
|
|
|
|
def bad_handler(x):
|
|
raise ValueError("boom")
|
|
|
|
server = A2AServer(card, handler=bad_handler)
|
|
response = server.handle_request({
|
|
"jsonrpc": "2.0",
|
|
"method": "tasks/send",
|
|
"params": {"input": "test"},
|
|
"id": "1",
|
|
})
|
|
assert response["result"]["state"] == "failed"
|