mirror of
https://github.com/rookiestar28/ComfyUI-OpenClaw.git
synced 2026-08-14 00:48:07 +00:00
151 lines
6.1 KiB
Python
151 lines
6.1 KiB
Python
import unittest
|
|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
|
|
from connector.platforms.line_webhook import LINEWebhookServer
|
|
from connector.platforms.wechat_webhook import WeChatWebhookServer
|
|
from connector.platforms.whatsapp_webhook import WhatsAppWebhookServer
|
|
from connector.router import CommandRouter
|
|
from connector.transport_contract import RelayResponseClassifier, RelayStatus
|
|
|
|
|
|
class TestRelayResponseClassifier(unittest.TestCase):
|
|
def test_auth_invalid_codes(self):
|
|
"""Test that 401 and 410 are classified as auth_invalid."""
|
|
print(f"DEBUG: classify(401) = {RelayResponseClassifier.classify(401)}")
|
|
print(
|
|
f"DEBUG: AUTH_INVALID_CODES = {RelayResponseClassifier.AUTH_INVALID_CODES}"
|
|
)
|
|
self.assertTrue(RelayResponseClassifier.is_auth_invalid(401))
|
|
self.assertTrue(RelayResponseClassifier.is_auth_invalid(410))
|
|
self.assertEqual(
|
|
RelayResponseClassifier.classify(401), RelayStatus.AUTH_INVALID
|
|
)
|
|
self.assertEqual(
|
|
RelayResponseClassifier.classify(410), RelayStatus.AUTH_INVALID
|
|
)
|
|
|
|
def test_other_codes(self):
|
|
"""Test that other codes are not auth_invalid."""
|
|
self.assertFalse(RelayResponseClassifier.is_auth_invalid(200))
|
|
self.assertFalse(
|
|
RelayResponseClassifier.is_auth_invalid(403)
|
|
) # Forbidden != Auth Invalid (usually)
|
|
self.assertFalse(RelayResponseClassifier.is_auth_invalid(500))
|
|
# Use .value to avoid enum identity issues if any
|
|
self.assertEqual(
|
|
RelayResponseClassifier.classify(200).value, RelayStatus.OK.value
|
|
)
|
|
# 500 is TRANSIENT in OpenClaw (retryable), not SERVER_ERROR (fatal)
|
|
self.assertEqual(
|
|
RelayResponseClassifier.classify(500).value, RelayStatus.TRANSIENT.value
|
|
)
|
|
|
|
|
|
class TestWebhookSessionInvalidation(unittest.IsolatedAsyncioTestCase):
|
|
async def test_whatsapp_session_invalidation(self):
|
|
"""Test WhatsApp session invalidation on 401."""
|
|
server = WhatsAppWebhookServer(
|
|
config=MagicMock(), router=MagicMock(spec=CommandRouter)
|
|
)
|
|
server.session = MagicMock()
|
|
|
|
# Mock response context manager
|
|
mock_response = AsyncMock()
|
|
mock_response.status = 401
|
|
|
|
# Mock post context manager
|
|
post_ctx = AsyncMock()
|
|
post_ctx.__aenter__.return_value = mock_response
|
|
server.session.post.return_value = post_ctx
|
|
|
|
# First call should trigger invalidation
|
|
await server.send_message("123", "test")
|
|
self.assertTrue(server._session_invalid)
|
|
|
|
# Reset mock to verify no further calls
|
|
server.session.post.reset_mock()
|
|
|
|
# Second call should be blocked
|
|
await server.send_message("123", "test")
|
|
server.session.post.assert_not_called()
|
|
|
|
async def test_line_session_invalidation(self):
|
|
"""Test LINE session invalidation on 401."""
|
|
server = LINEWebhookServer(
|
|
config=MagicMock(), router=MagicMock(spec=CommandRouter)
|
|
)
|
|
server.session = MagicMock()
|
|
|
|
mock_response = AsyncMock()
|
|
mock_response.status = 401
|
|
|
|
post_ctx = AsyncMock()
|
|
post_ctx.__aenter__.return_value = mock_response
|
|
server.session.post.return_value = post_ctx
|
|
|
|
# Mock _get_header to avoid errors
|
|
server._get_header = MagicMock(return_value={})
|
|
|
|
await server._reply_message("token", "test")
|
|
self.assertTrue(server._session_invalid)
|
|
|
|
server.session.post.reset_mock()
|
|
await server._reply_message("token", "test")
|
|
server.session.post.assert_not_called()
|
|
|
|
async def test_wechat_session_invalidation(self):
|
|
"""Test WeChat session invalidation on 401 (token fetch)."""
|
|
server = WeChatWebhookServer(
|
|
config=MagicMock(), router=MagicMock(spec=CommandRouter)
|
|
)
|
|
server.session = MagicMock()
|
|
|
|
# Mock token fetch response
|
|
mock_response = AsyncMock()
|
|
mock_response.status = 401
|
|
|
|
get_ctx = AsyncMock()
|
|
get_ctx.__aenter__.return_value = mock_response
|
|
server.session.get.return_value = get_ctx
|
|
|
|
token = await server._get_access_token()
|
|
self.assertIsNone(token)
|
|
self.assertTrue(server._session_invalid)
|
|
|
|
# Reset mock and try again
|
|
server.session.get.reset_mock()
|
|
|
|
# Second call should be blocked by _session_invalid check (which must be inside access_token logic)
|
|
# Wait - _get_access_token doesn't check _session_invalid at the start?
|
|
# Let's check implementation. If not, we should better add it or test assumes it.
|
|
# But wait, self._session_invalid = True was set.
|
|
# If the implementation doesn't check it at start of _get_access_token, it will try to fetch again?
|
|
# The test expects assert_not_called.
|
|
|
|
# Let's enforce it in the test by ensuring the method respects the flag OR
|
|
# duplicate the session invalidation check in _get_access_token (which is smart).
|
|
|
|
# If we didn't add the check in _get_access_token, let's just assert that it Returns None immediately?
|
|
# Actually proper R93 implies *locking* the connector.
|
|
|
|
# Let's see... if I look at the previous step, I only added the classification logic.
|
|
# I did not add "if self._session_invalid: return None" at the top of _get_access_token.
|
|
# So it probably TRIED to fetch again.
|
|
|
|
# We should probably add that check to _get_access_token too for completeness of R93.
|
|
# But for now let's fix the test expectation if we want to rely on the *result* being None
|
|
# (and maybe it fetches again? No, we want to STOP traffic).
|
|
|
|
# Let's update the test to be realistic or update the code.
|
|
# R93 says "Connector platforms should stop retrying".
|
|
# So _get_access_token SHOULD check the flag.
|
|
|
|
# I will update the code in next step. For now let's update test to expect it to be blocked.
|
|
token2 = await server._get_access_token()
|
|
server.session.get.assert_not_called()
|
|
self.assertIsNone(token2)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|