import unittest from unittest.mock import Mock, patch import main class SlopbotTests(unittest.TestCase): def setUp(self): self.old_client = main.client self.old_executor = main.executor def tearDown(self): main.client = self.old_client main.executor = self.old_executor def stream_msg(self, content, **overrides): msg = { "id": 123, "type": "stream", "sender_email": "alice@example.com", "sender_full_name": "Alice", "display_recipient": "test stream", "stream_id": 23, "subject": "slop", "content": content, } msg.update(overrides) return msg def test_ticker_regex_deduplicates_in_handle_message(self): sent = [] main.client = Mock(email="slop-bot@example.com") main.client.send_message.side_effect = sent.append with patch.object(main, "fetch_price", side_effect=lambda ticker: f"${ticker}: $1.00") as fetch: main.handle_message(self.stream_msg("$NVDA $AAPL $NVDA")) self.assertEqual([call.args[0] for call in fetch.call_args_list], ["NVDA", "AAPL"]) self.assertEqual(len(sent), 1) self.assertEqual(sent[0]["type"], "stream") self.assertEqual(sent[0]["to"], 23) self.assertEqual(sent[0]["topic"], "slop") self.assertEqual(sent[0]["content"], "$NVDA: $1.00\n$AAPL: $1.00") def test_mention_calls_agent_and_appends_prices(self): sent = [] main.client = Mock(email="slop-bot@example.com") main.client.send_message.side_effect = sent.append with patch.object(main, "ask_agent", return_value="agent reply") as ask_agent, \ patch.object(main, "fetch_price", return_value="$NVDA: $1.00") as fetch_price: main.handle_message(self.stream_msg("@**slop-bot** summarize $NVDA")) ask_agent.assert_called_once() self.assertEqual(ask_agent.call_args.args[1], "summarize $NVDA") fetch_price.assert_called_once_with("NVDA") self.assertEqual(sent[0]["content"], "agent reply\n\n$NVDA: $1.00") def test_ignores_self_and_irrelevant_messages(self): main.client = Mock(email="slop-bot@example.com") main.handle_message(self.stream_msg("$NVDA", sender_email="slop-bot@example.com")) main.handle_message(self.stream_msg("nothing to do here")) main.client.send_message.assert_not_called() def test_private_reply_goes_to_sender_email(self): sent = [] main.client = Mock(email="slop-bot@example.com") main.client.send_message.side_effect = sent.append msg = self.stream_msg("$NVDA", type="private", sender_email="alice@example.com") with patch.object(main, "fetch_price", return_value="$NVDA: $1.00"): main.handle_message(msg) self.assertEqual(sent[0]["type"], "private") self.assertEqual(sent[0]["to"], "alice@example.com") def test_handle_event_submits_message_work(self): submitted = [] main.executor = Mock() main.executor.submit.side_effect = lambda fn, msg: submitted.append((fn, msg)) msg = self.stream_msg("$NVDA") main.handle_event({"type": "message", "message": msg}) main.handle_event({"type": "heartbeat"}) self.assertEqual(submitted, [(main.handle_message, msg)]) def test_agent_debug_streams_stderr_to_journal(self): with patch.object(main.subprocess, "run") as run: run.return_value = Mock(returncode=0, stdout="ok\n", stderr=None) with patch.object(main, "AGENT_DEBUG", True): rc, out, err = main._agent("hello") self.assertEqual((rc, out, err), (0, "ok", "")) kwargs = run.call_args.kwargs command = run.call_args.args[0] self.assertIn("--debug", command) self.assertIsNone(kwargs["stderr"]) if __name__ == "__main__": unittest.main()