""" Tests for Behavior Policy v1: Silent-by-default + Short-first + Media-no-comment """ import pytest import sys from pathlib import Path # Add gateway-bot to path sys.path.insert(0, str(Path(__file__).parent.parent / "gateway-bot")) from behavior_policy import ( detect_agent_mention, detect_any_agent_mention, detect_command, detect_question, detect_imperative, detect_broadcast_intent, detect_short_note, detect_media_question, analyze_message, should_respond, is_no_output_response, NO_OUTPUT, TRAINING_GROUP_IDS, ) # ======================================== # Unit Tests: detect_agent_mention # ======================================== class TestDetectAgentMention: def test_helion_mention_exact(self): assert detect_agent_mention("Helion, що ти думаєш?", "helion") is True def test_helion_mention_lowercase(self): assert detect_agent_mention("helion допоможи", "helion") is True def test_helion_mention_ukrainian(self): assert detect_agent_mention("Хеліон, як справи?", "helion") is True def test_helion_mention_at(self): assert detect_agent_mention("@energyunionBot глянь", "helion") is True def test_helion_no_mention(self): assert detect_agent_mention("Привіт всім", "helion") is False def test_daarwizz_mention(self): assert detect_agent_mention("@DAARWIZZBot поясни", "daarwizz") is True def test_daarwizz_no_mention(self): assert detect_agent_mention("Helion допоможи", "daarwizz") is False class TestDetectAnyAgentMention: def test_helion_detected(self): assert detect_any_agent_mention("Helion, скажи") == "helion" def test_daarwizz_detected(self): assert detect_any_agent_mention("@DAARWIZZBot допоможи") == "daarwizz" def test_no_agent(self): assert detect_any_agent_mention("Привіт всім") is None # ======================================== # Unit Tests: detect_command # ======================================== class TestDetectCommand: def test_ask_command(self): assert detect_command("/ask що таке DAO?") is True def test_helion_command(self): assert detect_command("/helion покажи") is True def test_brand_command(self): assert detect_command("/бренд_інтейк https://example.com") is True def test_no_command(self): assert detect_command("Привіт, як справи?") is False def test_slash_in_middle(self): assert detect_command("Дивись https://example.com/path") is False # ======================================== # Unit Tests: detect_question # ======================================== class TestDetectQuestion: def test_question_mark(self): assert detect_question("Що це таке?") is True def test_question_word_start(self): assert detect_question("Як це працює") is True def test_question_word_чому(self): assert detect_question("Чому так") is True def test_english_question(self): assert detect_question("What is this?") is True def test_no_question(self): assert detect_question("Добре") is False def test_statement(self): assert detect_question("Я згоден з цим") is False # ======================================== # Unit Tests: detect_imperative # ======================================== class TestDetectImperative: def test_поясни(self): assert detect_imperative("Поясни мені це") is True def test_зроби(self): assert detect_imperative("Зроби аналіз") is True def test_допоможи(self): assert detect_imperative("Допоможи з цим") is True def test_after_mention(self): assert detect_imperative("@Helion поясни") is True def test_no_imperative(self): assert detect_imperative("Привіт") is False # ======================================== # Unit Tests: detect_broadcast_intent # ======================================== class TestDetectBroadcastIntent: def test_time_pattern(self): assert detect_broadcast_intent("20:00 Вебінар") is True def test_time_date_pattern(self): assert detect_broadcast_intent("14.30 10.02 Зустріч") is True def test_emoji_start(self): assert detect_broadcast_intent("✅ Завершено") is True def test_url_only(self): assert detect_broadcast_intent("https://example.com") is True def test_announcement_word(self): assert detect_broadcast_intent("Анонс: новий реліз") is True def test_normal_message(self): assert detect_broadcast_intent("Привіт, як справи?") is False # ======================================== # Unit Tests: detect_short_note # ======================================== class TestDetectShortNote: def test_checkmark_only(self): assert detect_short_note("✅") is True def test_time_checkmark(self): assert detect_short_note("20:00 ✅") is True def test_ok(self): assert detect_short_note("ok") is True def test_plus(self): assert detect_short_note("+") is True def test_normal_message(self): assert detect_short_note("Привіт, як справи?") is False def test_empty(self): assert detect_short_note("") is True # ======================================== # Unit Tests: detect_media_question # ======================================== class TestDetectMediaQuestion: def test_question_in_caption(self): assert detect_media_question("Що на цьому фото?") is True def test_imperative_in_caption(self): assert detect_media_question("Опиши це зображення") is True def test_no_question(self): assert detect_media_question("") is False def test_just_hashtag(self): assert detect_media_question("#photo") is False # ======================================== # Unit Tests: is_no_output_response # ======================================== class TestIsNoOutputResponse: def test_empty_string(self): assert is_no_output_response("") is True def test_whitespace(self): assert is_no_output_response(" ") is True def test_no_output_marker(self): assert is_no_output_response("__NO_OUTPUT__") is True def test_no_output_lowercase(self): assert is_no_output_response("no_output") is True def test_normal_response(self): assert is_no_output_response("Ось моя відповідь") is False def test_dots_only(self): assert is_no_output_response("...") is True # ======================================== # Integration Tests: analyze_message / should_respond # ======================================== class TestAnalyzeMessage: """Test main decision logic""" def test_training_group_always_respond(self): decision = analyze_message( text="Привіт всім", agent_id="helion", chat_id="-1003556680911", # Training group ) assert decision.should_respond is True assert decision.reason == "training_group" def test_private_chat_always_respond(self): decision = analyze_message( text="Привіт", agent_id="helion", chat_id="123456", is_private_chat=True, ) assert decision.should_respond is True assert decision.reason == "private_chat" def test_direct_mention_respond(self): decision = analyze_message( text="Helion, що думаєш?", agent_id="helion", chat_id="group123", ) assert decision.should_respond is True assert decision.reason == "direct_mention" def test_command_respond(self): decision = analyze_message( text="/helion допоможи", agent_id="helion", chat_id="group123", ) assert decision.should_respond is True assert decision.reason == "command" def test_broadcast_no_mention_silent(self): decision = analyze_message( text="20:00 Вебінар Energy Union", agent_id="helion", chat_id="group123", ) assert decision.should_respond is False assert decision.reason == "broadcast_no_mention" def test_short_note_silent(self): decision = analyze_message( text="20:00 10.02 ✅", agent_id="helion", chat_id="group123", ) assert decision.should_respond is False assert "short_note" in decision.reason or "broadcast" in decision.reason def test_media_no_question_silent(self): decision = analyze_message( text="", agent_id="helion", chat_id="group123", has_media=True, media_caption="", ) assert decision.should_respond is False assert decision.reason == "media_no_question" def test_media_with_question_respond(self): decision = analyze_message( text="", agent_id="helion", chat_id="group123", has_media=True, media_caption="Що на цьому фото?", ) assert decision.should_respond is True assert decision.reason == "media_with_question" def test_question_no_mention_silent(self): """General question without mention = don't respond in groups""" decision = analyze_message( text="Як це працює?", agent_id="helion", chat_id="group123", ) assert decision.should_respond is False assert decision.reason == "question_no_mention" def test_addressed_to_other_agent(self): decision = analyze_message( text="@DAARWIZZBot поясни DAO", agent_id="helion", chat_id="group123", ) assert decision.should_respond is False assert "other_agent" in decision.reason # ======================================== # E2E Test Cases (from requirements) # ======================================== class TestE2ECases: """ Test cases from the requirements document. """ def test_case_1_poster_no_question(self): """Case 1: Постер у каналі без питання → (нічого)""" respond, reason = should_respond( text="", agent_id="helion", chat_id="channel123", has_media=True, media_caption="", ) assert respond is False assert reason == "media_no_question" def test_case_2_timing_no_question(self): """Case 2: Таймінг без питання → (нічого)""" respond, reason = should_respond( text="20:00 10.02 ✅", agent_id="helion", chat_id="group123", ) assert respond is False # Either short_note or broadcast pattern matches def test_case_3_direct_request_with_photo(self): """Case 3: @Helion що на цьому постері? коротко + image → respond""" respond, reason = should_respond( text="", agent_id="helion", chat_id="group123", has_media=True, media_caption="@Helion що на цьому постері? коротко", ) # Should respond because there's a question in caption assert respond is True assert reason == "media_with_question" def test_case_4_link_no_question(self): """Case 4: Посилання без питання → (нічого)""" respond, reason = should_respond( text="https://t.me/energyunionofficial/123", agent_id="helion", chat_id="group123", has_media=True, # Link treated as media media_caption="https://t.me/energyunionofficial/123", ) assert respond is False assert reason == "media_no_question" def test_case_5_link_with_question(self): """Case 5: @DAARWIZZ глянь посилання і скажи 3 тези + link → respond""" respond, reason = should_respond( text="@DAARWIZZBot глянь посилання і скажи 3 тези https://example.com", agent_id="daarwizz", chat_id="group123", has_media=True, media_caption="@DAARWIZZBot глянь посилання і скажи 3 тези https://example.com", ) # Should respond because there's a direct mention + question assert respond is True assert reason == "media_with_question" # ======================================== # Edge Cases # ======================================== class TestEdgeCases: def test_empty_text(self): respond, reason = should_respond( text="", agent_id="helion", chat_id="group123", ) assert respond is False def test_none_text(self): respond, reason = should_respond( text=None, agent_id="helion", chat_id="group123", ) assert respond is False def test_mixed_agents_mention(self): """When multiple agents mentioned, each should handle their own""" # Helion should respond to Helion mention respond_helion, _ = should_respond( text="Helion та DAARWIZZ, допоможіть", agent_id="helion", chat_id="group123", ) assert respond_helion is True # DAARWIZZ should also respond respond_daarwizz, _ = should_respond( text="Helion та DAARWIZZ, допоможіть", agent_id="daarwizz", chat_id="group123", ) assert respond_daarwizz is True def test_question_to_specific_agent(self): """Question directed to another agent""" respond, reason = should_respond( text="@greenfoodliveBot як справи?", agent_id="helion", chat_id="group123", ) assert respond is False assert "other_agent" in reason if __name__ == "__main__": pytest.main([__file__, "-v"])