cleanup: remove clawd/openclaw references, fix permissions, add architecture docs
- Replace all ~/clawd and ~/.clawdbot paths with ~/echo-core equivalents in tools (git_commit, ralph_prd_generator, backup_config, lead-gen) - Update personality files: TOOLS.md repo/paths, AGENTS.md security audit cmd - Migrate HANDOFF.md architectural decisions to docs/architecture.md - Tighten credentials/ dir to 700, add to .gitignore - Add .claude/ and *.pid to .gitignore - Various adapter, router, and session improvements from prior work Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,7 @@ from src.adapters.whatsapp import (
|
||||
split_message,
|
||||
poll_messages,
|
||||
send_whatsapp,
|
||||
react_whatsapp,
|
||||
get_bridge_status,
|
||||
handle_incoming,
|
||||
run_whatsapp,
|
||||
@@ -229,6 +230,41 @@ class TestGetBridgeStatus:
|
||||
assert result is None
|
||||
|
||||
|
||||
class TestReactWhatsapp:
|
||||
@pytest.mark.asyncio
|
||||
async def test_successful_react(self):
|
||||
client = _mock_client()
|
||||
client.post.return_value = _mock_httpx_response(json_data={"ok": True})
|
||||
result = await react_whatsapp(client, "123@s.whatsapp.net", "msg-id-1", "\U0001f440")
|
||||
assert result is True
|
||||
client.post.assert_called_once()
|
||||
sent_json = client.post.call_args[1]["json"]
|
||||
assert sent_json == {"to": "123@s.whatsapp.net", "id": "msg-id-1", "emoji": "\U0001f440", "fromMe": False}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_react_remove(self):
|
||||
client = _mock_client()
|
||||
client.post.return_value = _mock_httpx_response(json_data={"ok": True})
|
||||
result = await react_whatsapp(client, "123@s.whatsapp.net", "msg-id-1", "")
|
||||
assert result is True
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_react_bridge_error(self):
|
||||
client = _mock_client()
|
||||
client.post.side_effect = httpx.ConnectError("bridge down")
|
||||
result = await react_whatsapp(client, "123@s.whatsapp.net", "msg-id-1", "\U0001f440")
|
||||
assert result is False
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_react_500(self):
|
||||
client = _mock_client()
|
||||
client.post.return_value = _mock_httpx_response(
|
||||
status_code=500, json_data={"ok": False}
|
||||
)
|
||||
result = await react_whatsapp(client, "123@s.whatsapp.net", "msg-id-1", "\U0001f440")
|
||||
assert result is False
|
||||
|
||||
|
||||
# --- Message handler ---
|
||||
|
||||
|
||||
@@ -363,6 +399,78 @@ class TestHandleIncoming:
|
||||
sent_json = client.post.call_args[1]["json"]
|
||||
assert "Sorry" in sent_json["text"]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_reaction_flow(self, _set_owned):
|
||||
"""Eyes reaction added on receipt and removed after response."""
|
||||
client = _mock_client()
|
||||
client.post.return_value = _mock_httpx_response(json_data={"ok": True})
|
||||
msg = {
|
||||
"from": "5511999990000@s.whatsapp.net",
|
||||
"text": "Hello",
|
||||
"pushName": "Owner",
|
||||
"isGroup": False,
|
||||
"id": "msg-abc-123",
|
||||
}
|
||||
with patch("src.adapters.whatsapp.route_message", return_value=("Hi!", False)):
|
||||
await handle_incoming(msg, client)
|
||||
|
||||
# Should have 3 post calls: react 👀, send response, react "" (remove)
|
||||
assert client.post.call_count == 3
|
||||
calls = client.post.call_args_list
|
||||
|
||||
# First call: eyes reaction
|
||||
react_json = calls[0][1]["json"]
|
||||
assert react_json["emoji"] == "\U0001f440"
|
||||
assert react_json["id"] == "msg-abc-123"
|
||||
assert react_json["fromMe"] is False
|
||||
|
||||
# Second call: actual message
|
||||
send_json = calls[1][1]["json"]
|
||||
assert send_json["text"] == "Hi!"
|
||||
|
||||
# Third call: remove reaction
|
||||
unreact_json = calls[2][1]["json"]
|
||||
assert unreact_json["emoji"] == ""
|
||||
assert unreact_json["id"] == "msg-abc-123"
|
||||
assert unreact_json["fromMe"] is False
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_reaction_removed_on_error(self, _set_owned):
|
||||
"""Eyes reaction removed even when route_message raises."""
|
||||
client = _mock_client()
|
||||
client.post.return_value = _mock_httpx_response(json_data={"ok": True})
|
||||
msg = {
|
||||
"from": "5511999990000@s.whatsapp.net",
|
||||
"text": "Hello",
|
||||
"pushName": "Owner",
|
||||
"isGroup": False,
|
||||
"id": "msg-abc-456",
|
||||
}
|
||||
with patch("src.adapters.whatsapp.route_message", side_effect=Exception("boom")):
|
||||
await handle_incoming(msg, client)
|
||||
|
||||
# react 👀, send error, react "" (remove) — reaction still removed in finally
|
||||
calls = client.post.call_args_list
|
||||
unreact_call = calls[-1][1]["json"]
|
||||
assert unreact_call["emoji"] == ""
|
||||
assert unreact_call["id"] == "msg-abc-456"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_no_reaction_without_message_id(self, _set_owned):
|
||||
"""No reaction calls when message has no id."""
|
||||
client = _mock_client()
|
||||
client.post.return_value = _mock_httpx_response(json_data={"ok": True})
|
||||
msg = {
|
||||
"from": "5511999990000@s.whatsapp.net",
|
||||
"text": "Hello",
|
||||
"pushName": "Owner",
|
||||
"isGroup": False,
|
||||
}
|
||||
with patch("src.adapters.whatsapp.route_message", return_value=("Hi!", False)):
|
||||
await handle_incoming(msg, client)
|
||||
# Only 1 call: send response (no react calls)
|
||||
client.post.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_empty_text_ignored(self, _set_owned):
|
||||
client = _mock_client()
|
||||
|
||||
Reference in New Issue
Block a user