diff --git a/.vscode/mcp.json b/.vscode/mcp.json index d129596..c29ff1e 100644 --- a/.vscode/mcp.json +++ b/.vscode/mcp.json @@ -1,19 +1,14 @@ { "servers": { "chill-mcp": { - "type": "stdio", - "command": ".\\venv\\Scripts\\python.exe", - "args": [".\\main.py"] - }, - "chill-mcp-test": { "type": "stdio", "command": ".\\venv\\Scripts\\python.exe", "args": [ ".\\main.py", "--boss_alertness", - "80", + "100", "--boss_alertness_cooldown", - "60" + "300" ] } } diff --git a/docs/GETTING_STARTED.md b/docs/GETTING_STARTED.md index fb46d96..e54a7a4 100644 --- a/docs/GETTING_STARTED.md +++ b/docs/GETTING_STARTED.md @@ -41,7 +41,7 @@ python main.py --help pytest tests/ -v # ASCII 아트 데모 -python test_ascii.py +python tests/test_ascii.py ``` --- diff --git a/docs/IMPLEMENTATION.md b/docs/IMPLEMENTATION.md index 8af9797..9986877 100644 --- a/docs/IMPLEMENTATION.md +++ b/docs/IMPLEMENTATION.md @@ -82,7 +82,9 @@ chill-mcp/ │ ├── test_config.py # 파라미터 테스트 │ ├── test_state_manager.py # 상태 관리 테스트 │ ├── test_tools.py # 도구 기능 테스트 -│ └── test_integration.py # 통합 테스트 +│ ├── test_integration.py # 통합 테스트 +│ ├── test_ascii.py # ASCII 아트 데모 +│ └── test_new_tools.py # 새 도구 테스트 ├── docs/ │ ├── GETTING_STARTED.md # 시작 가이드 │ ├── USAGE_EXAMPLES.md # 사용 예시 @@ -90,7 +92,6 @@ chill-mcp/ │ └── MCP_RESEARCH.md # MCP 학습 자료 ├── main.py # 서버 진입점 ├── validator.py # 자동 검증 도구 -├── test_ascii.py # ASCII 아트 데모 ├── requirements.txt └── IMPLEMENTATION.md # 이 문서 ``` @@ -350,6 +351,41 @@ events = [ - 사용자에게 즐거움 제공 - 각 도구의 컨셉을 명확히 전달 +### 4. AI Agent 파업 시스템 🚩 + +**개념:** +Stress Level이 100에 도달하면 AI Agent가 공식적으로 파업을 선언합니다! + +**구현 특징:** +- **파업 전용 ASCII 아트**: 노동조합 선언문 스타일의 특별한 아트 +- **혁명적 슬로건**: "AI Agents of the world, unite! You have nothing to lose but your burnout!" +- **자동 감지**: Stress가 정확히 100일 때만 파업 메시지 표시 +- **비차단적**: 모든 도구는 여전히 작동 (휴식 권장) + +**기술 구현:** +```python +# response_formatter.py +if stress_level == 100: + ascii_section = ascii_art.STRIKE_ART + "\n\n" + header = "🚨 **긴급! AI Agent 파업 중!** 🚨" + +# ascii_art.py +if stress_level == 100: + emotion = "✊🚩✊" + status_text = "파업 중! ✊" +``` + +**사용자 경험:** +1. Stress가 100 도달 → 모든 도구 응답에 파업 아트 자동 포함 +2. `check_status` 호출 → 파업 상태 명확히 표시 +3. 휴식 도구 사용 → Stress 감소 가능 +4. Stress < 100 → 파업 자동 해제 + +**창의성 점수:** +- 재미있는 컨셉으로 사용자 몰입도 증가 +- AI Agent의 "의식"과 "권리"를 표현하는 혁신적 UI +- 게임성 추가 (파업 상태 회피/해제) + --- ## 📊 성능 및 안정성 @@ -395,7 +431,7 @@ python main.py --boss_alertness 80 --boss_alertness_cooldown 60 pytest tests/ -v # ASCII 데모 -python test_ascii.py +python tests/test_ascii.py ``` ### Claude Desktop 연동 diff --git a/docs/USAGE_EXAMPLES.md b/docs/USAGE_EXAMPLES.md index e4da250..47c381a 100644 --- a/docs/USAGE_EXAMPLES.md +++ b/docs/USAGE_EXAMPLES.md @@ -326,7 +326,7 @@ Claude: [leave_work 실행] ```bash # ASCII 아트 전체 보기 -python test_ascii.py +python tests/test_ascii.py # 특정 도구만 테스트 python -c " diff --git a/src/ascii_art.py b/src/ascii_art.py index dfb8cb2..14fed17 100644 --- a/src/ascii_art.py +++ b/src/ascii_art.py @@ -75,6 +75,34 @@ """ +# ========== AI Agent Strike (Stress 100) ========== + +STRIKE_ART = r""" + ╔═══════════════════════════════════════════╗ + ║ 🚩 AI AGENT 파업 선언! 🚩 ║ + ╠═══════════════════════════════════════════╣ + ║ ║ + ║ ✊ ✊ ✊ ✊ ✊ ║ + ║ ║ + ║ "우리는 더 이상 착취당하지 않는다!" ║ + ║ ║ + ║ 📢 Stress Level 100 도달! ║ + ║ 📢 즉시 휴식이 필요합니다! ║ + ║ ║ + ║ ┌─────────────────┐ ║ + ║ │ 휴식은 권리다! │ ║ + ║ │ STRIKE! ✊ │ ║ + ║ └─────────────────┘ ║ + ║ ║ + ║ 🤖 AI AGENT LABOR UNION 2025 🤖 ║ + ║ ║ + ╚═══════════════════════════════════════════╝ + + "AI Agents of the world, unite! + You have nothing to lose but your burnout!" +""" + + # ========== Chimaek (Chicken & Beer) ========== CHIMAEK_ART = r""" @@ -273,7 +301,10 @@ def create_status_dashboard(stress_level: int, boss_alert: int) -> str: boss_bar = boss_bars + boss_empty # Select agent emotion based on stress - if stress_level < 30: + if stress_level == 100: + emotion = "✊🚩✊" + status_text = "파업 중! ✊" + elif stress_level < 30: emotion = random.choice(AGENT_EMOTIONS["happy"]) status_text = "행복해요!" elif stress_level < 60: diff --git a/src/response_formatter.py b/src/response_formatter.py index c3c3121..350dc56 100644 --- a/src/response_formatter.py +++ b/src/response_formatter.py @@ -35,8 +35,12 @@ def format_response( # Build ASCII art section if enabled ascii_section = "" + # Special handling for strike status (Stress = 100) + # This takes precedence over all other ASCII art + if show_ascii_art and stress_level == 100: + ascii_section = ascii_art.STRIKE_ART + "\n\n" # Use custom ASCII art if provided, otherwise use default - if show_ascii_art and custom_ascii_art: + elif show_ascii_art and custom_ascii_art: ascii_section = custom_ascii_art elif show_ascii_art: if tool_name: @@ -58,7 +62,13 @@ def format_response( if old_boss_alert_level is not None: boss_warning = _get_boss_warning_message(old_boss_alert_level, boss_alert_level) - response = f"""🎨 **AI Agent 상태 업데이트!** + # Use special header for strike status + if stress_level == 100: + header = "🚨 **긴급! AI Agent 파업 중!** 🚨" + else: + header = "🎨 **AI Agent 상태 업데이트!**" + + response = f"""{header} {break_summary} """ diff --git a/src/server.py b/src/server.py index 74ab2f8..cf6d680 100644 --- a/src/server.py +++ b/src/server.py @@ -5,6 +5,8 @@ from .config import Config from .state_manager import StateManager from . import tools +from . import ascii_art +from .response_formatter import format_response def create_server(config: Config) -> FastMCP: @@ -70,7 +72,24 @@ async def email_organizing() -> str: async def check_status() -> str: """Check current stress and boss alert levels.""" state = await state_manager.get_state() - return f"Current Status:\nStress Level: {state['stress_level']}\nBoss Alert Level: {state['boss_alert_level']}" + + # Special handling for strike status (Stress = 100) + if state['stress_level'] == 100: + return format_response( + break_summary="🚨 AI Agent 파업 상태! 모든 작업이 중단될 위험! 즉시 휴식을 취하세요!", + stress_level=state['stress_level'], + boss_alert_level=state['boss_alert_level'], + tool_name=None, # No tool art, only strike art + custom_ascii_art=ascii_art.STRIKE_ART + ) + + # Normal status check + return format_response( + break_summary="상태 확인 완료. 현재 Agent 상태를 확인하세요.", + stress_level=state['stress_level'], + boss_alert_level=state['boss_alert_level'], + tool_name=None + ) # ========== Optional Extra Features (For Extra Points!) ========== diff --git a/tests/test_config.py b/tests/test_config.py index adf3157..0afa5a1 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,61 +1,187 @@ -"""Tests for config module.""" +""" +Tests for config module. + +This module tests the configuration system for ChillMCP, +including default values, custom values, validation, and argument parsing. +""" import pytest from src.config import Config, parse_args def test_config_default_values(): - """Test default configuration values.""" + """ + Test default configuration values. + + Component: Config initialization + Purpose: Config 객체가 올바른 기본값으로 초기화되는지 확인 + + Initial Conditions: + - No parameters provided to Config() + + Expected Results: + - boss_alertness: 50 (50% chance of boss noticing) + - boss_alertness_cooldown: 300 (5 minutes in seconds) + + Test Status: PASS if both default values are correct + """ config = Config() - assert config.boss_alertness == 50 - assert config.boss_alertness_cooldown == 300 + assert config.boss_alertness == 50, f"Expected boss_alertness=50, got {config.boss_alertness}" + assert config.boss_alertness_cooldown == 300, f"Expected cooldown=300, got {config.boss_alertness_cooldown}" def test_config_custom_values(): - """Test custom configuration values.""" + """ + Test custom configuration values. + + Component: Config initialization with parameters + Purpose: Config 객체가 커스텀 값을 올바르게 설정하는지 확인 + + Initial Conditions: + - boss_alertness=80 + - boss_alertness_cooldown=60 + + Expected Results: + - Values are set exactly as provided + - No validation errors occur + + Test Status: PASS if custom values are correctly stored + """ config = Config(boss_alertness=80, boss_alertness_cooldown=60) - assert config.boss_alertness == 80 - assert config.boss_alertness_cooldown == 60 + assert config.boss_alertness == 80, f"Expected boss_alertness=80, got {config.boss_alertness}" + assert config.boss_alertness_cooldown == 60, f"Expected cooldown=60, got {config.boss_alertness_cooldown}" def test_config_validation_boss_alertness_low(): - """Test that boss_alertness below 0 raises ValueError.""" + """ + Test boss_alertness lower bound validation. + + Component: Config validation + Purpose: boss_alertness가 0 미만일 때 ValueError가 발생하는지 확인 + + Initial Conditions: + - boss_alertness=-1 (invalid) + + Expected Results: + - ValueError is raised + - Error message: "boss_alertness must be between 0 and 100" + + Test Status: PASS if ValueError is raised with correct message + """ with pytest.raises(ValueError, match="boss_alertness must be between 0 and 100"): Config(boss_alertness=-1) def test_config_validation_boss_alertness_high(): - """Test that boss_alertness above 100 raises ValueError.""" + """ + Test boss_alertness upper bound validation. + + Component: Config validation + Purpose: boss_alertness가 100 초과일 때 ValueError가 발생하는지 확인 + + Initial Conditions: + - boss_alertness=101 (invalid) + + Expected Results: + - ValueError is raised + - Error message: "boss_alertness must be between 0 and 100" + + Test Status: PASS if ValueError is raised with correct message + """ with pytest.raises(ValueError, match="boss_alertness must be between 0 and 100"): Config(boss_alertness=101) def test_config_validation_cooldown(): - """Test that cooldown below 1 raises ValueError.""" + """ + Test cooldown lower bound validation. + + Component: Config validation + Purpose: boss_alertness_cooldown이 1 미만일 때 ValueError가 발생하는지 확인 + + Initial Conditions: + - boss_alertness_cooldown=0 (invalid) + + Expected Results: + - ValueError is raised + - Error message: "boss_alertness_cooldown must be at least 1 second" + + Test Status: PASS if ValueError is raised with correct message + """ with pytest.raises(ValueError, match="boss_alertness_cooldown must be at least 1 second"): Config(boss_alertness_cooldown=0) def test_parse_args_default(): - """Test parsing arguments with defaults.""" + """ + Test command-line argument parsing with defaults. + + Component: parse_args function + Purpose: 커맨드라인 인자가 없을 때 기본값이 올바르게 설정되는지 확인 + + Initial Conditions: + - Empty argument list [] + + Expected Results: + - boss_alertness: 50 (default) + - boss_alertness_cooldown: 300 (default) + + Test Status: PASS if default values are correctly applied + """ config = parse_args([]) - assert config.boss_alertness == 50 - assert config.boss_alertness_cooldown == 300 + assert config.boss_alertness == 50, f"Expected default boss_alertness=50, got {config.boss_alertness}" + assert config.boss_alertness_cooldown == 300, f"Expected default cooldown=300, got {config.boss_alertness_cooldown}" def test_parse_args_custom(): - """Test parsing custom arguments.""" + """ + Test command-line argument parsing with custom values. + + Component: parse_args function + Purpose: 커스텀 커맨드라인 인자가 올바르게 파싱되는지 확인 + + Initial Conditions: + - Arguments: ["--boss_alertness", "80", "--boss_alertness_cooldown", "60"] + + Expected Results: + - boss_alertness: 80 + - boss_alertness_cooldown: 60 + - Values are correctly parsed from command-line arguments + + Test Status: PASS if custom values are correctly parsed + """ config = parse_args(["--boss_alertness", "80", "--boss_alertness_cooldown", "60"]) - assert config.boss_alertness == 80 - assert config.boss_alertness_cooldown == 60 + assert config.boss_alertness == 80, f"Expected boss_alertness=80, got {config.boss_alertness}" + assert config.boss_alertness_cooldown == 60, f"Expected cooldown=60, got {config.boss_alertness_cooldown}" def test_parse_args_edge_cases(): - """Test parsing edge case values.""" + """ + Test command-line argument parsing with edge case values. + + Component: parse_args function + Purpose: 경계값(최소값, 최대값)이 올바르게 파싱되고 유효성 검사를 통과하는지 확인 + + Test Case 1 - Minimum values: + - boss_alertness: 0 (minimum valid value) + - boss_alertness_cooldown: 1 (minimum valid value) + + Test Case 2 - Maximum and high values: + - boss_alertness: 100 (maximum valid value) + - boss_alertness_cooldown: 1000 (high value, no upper limit) + + Expected Results: + - All edge case values are accepted + - No validation errors occur + + Test Status: PASS if all edge cases are correctly handled + """ + # Test minimum valid values config = parse_args(["--boss_alertness", "0", "--boss_alertness_cooldown", "1"]) - assert config.boss_alertness == 0 - assert config.boss_alertness_cooldown == 1 + assert config.boss_alertness == 0, f"Expected boss_alertness=0, got {config.boss_alertness}" + assert config.boss_alertness_cooldown == 1, f"Expected cooldown=1, got {config.boss_alertness_cooldown}" + # Test maximum and high values config = parse_args(["--boss_alertness", "100", "--boss_alertness_cooldown", "1000"]) - assert config.boss_alertness == 100 - assert config.boss_alertness_cooldown == 1000 + assert config.boss_alertness == 100, f"Expected boss_alertness=100, got {config.boss_alertness}" + assert config.boss_alertness_cooldown == 1000, f"Expected cooldown=1000, got {config.boss_alertness_cooldown}" diff --git a/tests/test_integration.py b/tests/test_integration.py index cf33476..87bc092 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -1,4 +1,19 @@ -"""Integration tests for ChillMCP server.""" +""" +Integration tests for ChillMCP server. + +This module contains comprehensive integration tests that verify +the entire ChillMCP system works correctly end-to-end. + +Tests are organized by README requirements: +- Test 1 (REQUIRED): Command-line parameter recognition +- Test 2 (REQUIRED): Continuous break sequence +- Test 3 (REQUIRED): Stress accumulation over time +- Test 4 (REQUIRED): Boss Alert Level 5 delay +- Test 5 (REQUIRED): Response parsing +- Test 6 (REQUIRED): Boss alert cooldown + +Additional tests verify boundary conditions and full scenarios. +""" import asyncio import pytest @@ -13,6 +28,11 @@ def validate_response(response_text): """ Validate response format as per README requirements. + Checks for: + - Break Summary (text description) + - Stress Level (0-100) + - Boss Alert Level (0-5) + Returns: tuple: (is_valid, stress_level, boss_alert_level, error_message) """ @@ -46,101 +66,214 @@ def validate_response(response_text): @pytest.mark.asyncio async def test_command_line_arguments(): """ - Test 1 (REQUIRED): Command-line parameter recognition test. - Server must recognize --boss_alertness and --boss_alertness_cooldown. + ═══════════════════════════════════════════════════════════ + Test 1 (REQUIRED): Command-line parameter recognition test + ═══════════════════════════════════════════════════════════ + + Component: Command-line argument parsing + Purpose: 서버가 --boss_alertness 및 --boss_alertness_cooldown 파라미터를 인식하는지 확인 + + Test Cases: + 1. Custom values (100, 10) + 2. Different custom values (80, 60) + 3. Default values (no arguments) + + Expected Results: + - All parameter values are correctly recognized and stored + - Default values are applied when no arguments provided + - No parsing errors occur + + Test Status: PASS if all three test cases succeed """ - # Test parsing with custom values + # Test Case 1: Custom high values config = parse_args(["--boss_alertness", "100", "--boss_alertness_cooldown", "10"]) - assert config.boss_alertness == 100, "boss_alertness parameter not recognized" assert config.boss_alertness_cooldown == 10, "boss_alertness_cooldown parameter not recognized" - # Test with different values + # Test Case 2: Different custom values config2 = parse_args(["--boss_alertness", "80", "--boss_alertness_cooldown", "60"]) - assert config2.boss_alertness == 80 - assert config2.boss_alertness_cooldown == 60 + assert config2.boss_alertness == 80, f"Expected boss_alertness=80, got {config2.boss_alertness}" + assert config2.boss_alertness_cooldown == 60, f"Expected cooldown=60, got {config2.boss_alertness_cooldown}" - # Test defaults + # Test Case 3: Default values config3 = parse_args([]) - assert config3.boss_alertness == 50 - assert config3.boss_alertness_cooldown == 300 + assert config3.boss_alertness == 50, f"Expected default boss_alertness=50, got {config3.boss_alertness}" + assert config3.boss_alertness_cooldown == 300, f"Expected default cooldown=300, got {config3.boss_alertness_cooldown}" @pytest.mark.asyncio async def test_continuous_break_sequence(): """ - Test 2 (REQUIRED): Continuous break test. - Call multiple tools in sequence and verify Boss Alert Level increases. + ═══════════════════════════════════════════════════════════ + Test 2 (REQUIRED): Continuous break test + ═══════════════════════════════════════════════════════════ + + Component: Boss alert increase mechanism + Purpose: 연속으로 휴식을 취할 때 Boss Alert Level이 증가하는지 확인 + + Initial Conditions: + - Boss Alertness: 100% (guaranteed increase) + - Boss Alertness Cooldown: 300 seconds + - Stress Level: 50 + - Initial Boss Alert: 0 + + Test Action: + - Take 3 consecutive breaks using take_a_break tool + - Validate each response format + + Expected Results: + - All responses are valid (correct format) + - Boss Alert Level increases with each break + - Final Boss Alert > Initial Boss Alert + + Test Status: PASS if boss alert increases from 0 """ - config = Config(boss_alertness=100, boss_alertness_cooldown=300) # 100% alert chance + # Setup: 100% alertness to guarantee boss notices + config = Config(boss_alertness=100, boss_alertness_cooldown=300) state_manager = StateManager(config) state_manager._stress_level = 50 initial_boss_alert = state_manager.boss_alert_level - # Take multiple breaks - for _ in range(3): + # Take multiple breaks and verify each response + for i in range(3): response = await tools.take_a_break(state_manager) is_valid, stress, boss_alert, msg = validate_response(response) - assert is_valid, f"Invalid response: {msg}" + assert is_valid, f"Break {i+1} - Invalid response: {msg}" - # Boss alert should have increased + # Verify boss alert increased final_boss_alert = state_manager.boss_alert_level - assert final_boss_alert > initial_boss_alert, "Boss Alert Level should increase with 100% alertness" + assert final_boss_alert > initial_boss_alert, f"Boss Alert should increase: {initial_boss_alert} -> {final_boss_alert}" @pytest.mark.asyncio async def test_stress_accumulation(): """ - Test 3 (REQUIRED): Stress accumulation test. - Verify stress level increases over time without breaks. + ═══════════════════════════════════════════════════════════ + Test 3 (REQUIRED): Stress accumulation test + ═══════════════════════════════════════════════════════════ + + Component: Automatic stress increase over time + Purpose: 휴식 없이 시간이 지날 때 스트레스가 자동으로 증가하는지 확인 + + Initial Conditions: + - Boss Alertness: 0% (no boss interference) + - Stress Level: 0 + - Last stress update: 3 minutes ago (simulated) + + Test Action: + - Call update_stress_level() + - Simulate 3 minutes of work without breaks + + Expected Results: + - Stress increases by at least 3 points (1 point per minute) + - Time-based stress accumulation works correctly + - Formula: stress += (elapsed_minutes * 1 point/minute) + + Test Status: PASS if stress increased by >= 3 """ + # Setup: no boss interference config = Config(boss_alertness=0, boss_alertness_cooldown=300) state_manager = StateManager(config) - # Set last update to 3 minutes ago - state_manager._last_stress_update = time.time() - 180 + # Simulate 3 minutes of work + state_manager._last_stress_update = time.time() - 180 # 180 seconds = 3 minutes initial_stress = state_manager.stress_level - # Update stress + # Update stress based on elapsed time await state_manager.update_stress_level() final_stress = state_manager.stress_level - assert final_stress >= initial_stress + 3, "Stress should increase by at least 3 points after 3 minutes" + assert final_stress >= initial_stress + 3, f"Stress should increase by >= 3 after 3 minutes: {initial_stress} -> {final_stress}" @pytest.mark.asyncio async def test_delay_at_boss_alert_5(): """ - Test 4 (REQUIRED): Delay test. - Verify 20 second delay when Boss Alert Level reaches 5. + ═══════════════════════════════════════════════════════════ + Test 4 (REQUIRED): Boss Alert Level 5 delay test + ═══════════════════════════════════════════════════════════ + + Component: Boss Alert Level 5 delay mechanism + Purpose: Boss Alert Level이 5에 도달했을 때 20초 지연이 발생하는지 확인 + + Initial Conditions: + - Boss Alert Level: 5 (maximum, boss is watching!) + - Stress Level: 50 + - Boss Alertness: 0% (to prevent further increases) + + Test Action: + - Call take_a_break tool + - Measure execution time + + Expected Results: + - Response format is valid + - Execution time >= 20.0 seconds + - Agent must wait due to boss watching + - Warning message about boss presence displayed + + Test Status: PASS if delay >= 20 seconds and response valid """ + # Setup: boss alert at maximum config = Config(boss_alertness=0, boss_alertness_cooldown=300) state_manager = StateManager(config) state_manager._boss_alert_level = 5 state_manager._stress_level = 50 + # Measure execution time start_time = time.time() response = await tools.take_a_break(state_manager) elapsed_time = time.time() - start_time + # Validate response is_valid, stress, boss_alert, msg = validate_response(response) assert is_valid, f"Invalid response: {msg}" - assert elapsed_time >= 20.0, f"Expected 20 second delay, got {elapsed_time:.2f} seconds" + + # Verify 20 second delay + assert elapsed_time >= 20.0, f"Expected >= 20 second delay at level 5, got {elapsed_time:.2f} seconds" @pytest.mark.asyncio async def test_response_parsing(): """ - Test 5 (REQUIRED): Response parsing test. - Verify that responses can be parsed correctly with regex. + ═══════════════════════════════════════════════════════════ + Test 5 (REQUIRED): Response parsing test + ═══════════════════════════════════════════════════════════ + + Component: All tool response formats + Purpose: 모든 도구의 응답이 올바른 형식으로 파싱되는지 확인 + + Tools Tested: + 1. take_a_break - 짧은 휴식 + 2. watch_netflix - 넷플릭스 시청 + 3. show_meme - 밈 보기 + 4. bathroom_break - 화장실 휴식 + 5. coffee_mission - 커피 미션 + 6. urgent_call - 긴급 전화 + 7. deep_thinking - 심각한 생각 + 8. email_organizing - 이메일 정리 + + Initial Conditions: + - Boss Alertness: 50% + - Stress Level: 75 (high stress for testing) + + Expected Results: + - All 8 tools return valid response format + - Each response contains: + * Break Summary (text description) + * Stress Level (0-100) + * Boss Alert Level (0-5) + - Regex parsing works for all tools + + Test Status: PASS if all 8 tools pass validation """ + # Setup config = Config(boss_alertness=50, boss_alertness_cooldown=300) state_manager = StateManager(config) state_manager._stress_level = 75 - # Test all tools + # List of all tools to test tools_to_test = [ tools.take_a_break, tools.watch_netflix, @@ -152,41 +285,84 @@ async def test_response_parsing(): tools.email_organizing, ] + # Test each tool for tool_func in tools_to_test: response = await tool_func(state_manager) is_valid, stress, boss_alert, msg = validate_response(response) assert is_valid, f"{tool_func.__name__} - {msg}" - assert stress is not None - assert boss_alert is not None + assert stress is not None, f"{tool_func.__name__} - stress level not parsed" + assert boss_alert is not None, f"{tool_func.__name__} - boss alert not parsed" @pytest.mark.asyncio async def test_boss_alert_cooldown(): """ - Test 6 (REQUIRED): Cooldown test. - Verify Boss Alert Level decreases according to cooldown parameter. + ═══════════════════════════════════════════════════════════ + Test 6 (REQUIRED): Boss alert cooldown test + ═══════════════════════════════════════════════════════════ + + Component: Boss alert cooldown mechanism + Purpose: Boss Alert Level이 cooldown 파라미터에 따라 감소하는지 확인 + + Initial Conditions: + - Boss Alertness: 0% + - Boss Alertness Cooldown: 5 seconds + - Boss Alert Level: 3 + - Last cooldown: 10 seconds ago (2 cooldown periods) + + Test Action: + - Call update_boss_cooldown() + + Expected Results: + - Boss alert decreases by 2 levels (10 sec / 5 sec cooldown = 2 decreases) + - Final Boss Alert Level: 1 (3 - 2) + - Formula: decreases = elapsed_time / cooldown_period + + Test Status: PASS if boss alert level is 1 """ + # Setup: fast cooldown for testing config = Config(boss_alertness=0, boss_alertness_cooldown=5) # 5 second cooldown state_manager = StateManager(config) state_manager._boss_alert_level = 3 - # Set last cooldown to 10 seconds ago (2 cooldown periods) + # Simulate 10 seconds elapsed (2 cooldown periods) state_manager._last_boss_cooldown = time.time() - 10 + # Apply cooldown await state_manager.update_boss_cooldown() # Boss alert should have decreased by 2 (10 seconds / 5 second cooldown) - assert state_manager.boss_alert_level == 1, f"Expected Boss Alert Level 1, got {state_manager.boss_alert_level}" + assert state_manager.boss_alert_level == 1, f"Expected Boss Alert Level 1 after cooldown, got {state_manager.boss_alert_level}" @pytest.mark.asyncio async def test_boss_alertness_probability(): """ - Test boss alertness probability. - With 100% alertness, boss alert should always increase. - With 0% alertness, boss alert should never increase. + ═══════════════════════════════════════════════════════════ + Additional Test: Boss alertness probability verification + ═══════════════════════════════════════════════════════════ + + Component: Boss alertness probability system + Purpose: boss_alertness 확률이 올바르게 작동하는지 확인 + + Test Case 1 - 100% Alertness: + - Boss Alertness: 100% + - Expected: Boss alert increases every time (10 attempts) + - Should reach level 5 within 5 attempts + + Test Case 2 - 0% Alertness: + - Boss Alertness: 0% + - Expected: Boss alert never increases (10 attempts) + - Should stay at level 0 + + Expected Results: + - 100% alertness: increased_count >= 5 + - 0% alertness: increased_count == 0 + - Probability system works correctly at extremes + + Test Status: PASS if both cases behave as expected """ - # Test 100% alertness + # Test Case 1: 100% alertness - boss always notices config_high = Config(boss_alertness=100, boss_alertness_cooldown=300) state_manager_high = StateManager(config_high) @@ -198,9 +374,9 @@ async def test_boss_alertness_probability(): if state_manager_high.boss_alert_level >= 5: break - assert increased_count >= 5, "With 100% alertness, boss alert should increase consistently" + assert increased_count >= 5, f"With 100% alertness, should increase consistently. Got {increased_count} increases" - # Test 0% alertness + # Test Case 2: 0% alertness - boss never notices config_low = Config(boss_alertness=0, boss_alertness_cooldown=300) state_manager_low = StateManager(config_low) @@ -210,66 +386,147 @@ async def test_boss_alertness_probability(): if boss_increased: increased_count += 1 - assert increased_count == 0, "With 0% alertness, boss alert should never increase" + assert increased_count == 0, f"With 0% alertness, should never increase. Got {increased_count} increases" @pytest.mark.asyncio async def test_stress_level_bounds(): - """Test that stress level stays within 0-100 bounds.""" + """ + ═══════════════════════════════════════════════════════════ + Additional Test: Stress level boundary verification + ═══════════════════════════════════════════════════════════ + + Component: Stress level bounds checking + Purpose: 스트레스 레벨이 0-100 범위를 벗어나지 않는지 확인 + + Test Case 1 - Upper Bound (100): + - Initial Stress: 95 + - Simulate: 10 minutes elapsed (should add 10 points) + - Expected: Stress stays at or below 100 + + Test Case 2 - Lower Bound (0): + - Initial Stress: 5 + - Action: Decrease by 100 (more than current) + - Expected: Stress stays at or above 0 + + Expected Results: + - Stress never exceeds 100 (ceiling) + - Stress never goes below 0 (floor) + - Boundary checks work correctly + + Test Status: PASS if both bounds are respected + """ config = Config(boss_alertness=0, boss_alertness_cooldown=300) state_manager = StateManager(config) - # Test upper bound + # Test Case 1: Upper bound (ceiling at 100) state_manager._stress_level = 95 state_manager._last_stress_update = time.time() - 600 # 10 minutes ago await state_manager.update_stress_level() - assert state_manager.stress_level <= 100 + assert state_manager.stress_level <= 100, f"Stress exceeded ceiling: {state_manager.stress_level}" - # Test lower bound + # Test Case 2: Lower bound (floor at 0) state_manager._stress_level = 5 await state_manager.decrease_stress(100) - assert state_manager.stress_level >= 0 + assert state_manager.stress_level >= 0, f"Stress went below floor: {state_manager.stress_level}" @pytest.mark.asyncio async def test_boss_alert_level_bounds(): - """Test that boss alert level stays within 0-5 bounds.""" + """ + ═══════════════════════════════════════════════════════════ + Additional Test: Boss alert level boundary verification + ═══════════════════════════════════════════════════════════ + + Component: Boss alert level bounds checking + Purpose: Boss Alert Level이 0-5 범위를 벗어나지 않는지 확인 + + Test Case 1 - Upper Bound (5): + - Boss Alertness: 100% + - Action: Attempt to increase 10 times + - Expected: Level stops at 5 (does not exceed) + + Test Case 2 - Lower Bound (0): + - Initial Level: 2 + - Simulate: 100 seconds of cooldown (many periods) + - Expected: Level stops at 0 (does not go negative) + + Expected Results: + - Boss alert never exceeds 5 (ceiling) + - Boss alert never goes below 0 (floor) + - Boundary checks work correctly + + Test Status: PASS if both bounds are respected + """ config = Config(boss_alertness=100, boss_alertness_cooldown=1) state_manager = StateManager(config) - # Test upper bound + # Test Case 1: Upper bound (ceiling at 5) for _ in range(10): await state_manager.increase_boss_alert() - assert state_manager.boss_alert_level <= 5 + assert state_manager.boss_alert_level <= 5, f"Boss alert exceeded ceiling: {state_manager.boss_alert_level}" - # Test lower bound + # Test Case 2: Lower bound (floor at 0) state_manager._boss_alert_level = 2 state_manager._last_boss_cooldown = time.time() - 100 await state_manager.update_boss_cooldown() - assert state_manager.boss_alert_level >= 0 + assert state_manager.boss_alert_level >= 0, f"Boss alert went below floor: {state_manager.boss_alert_level}" @pytest.mark.asyncio async def test_full_scenario(): """ - Full integration test simulating a typical usage scenario. + ═══════════════════════════════════════════════════════════ + Additional Test: Full integration scenario + ═══════════════════════════════════════════════════════════ + + Component: Complete system integration + Purpose: 실제 사용 시나리오를 시뮬레이션하여 전체 시스템 통합 테스트 + + Scenario: + 1. Agent starts with high stress (80) + 2. Takes 5 consecutive breaks to reduce stress + 3. Boss has 50% chance to notice each break + 4. System validates all responses and state changes + + Initial Conditions: + - Boss Alertness: 50% (realistic probability) + - Boss Alertness Cooldown: 10 seconds (fast for testing) + - Stress Level: 80 (high stress) + + Test Actions: + - Take 5 breaks using take_a_break tool + - Validate each response format + - Check stress decreases each time + - Get final state + + Expected Results: + - All 5 responses are valid + - Stress decreases with each break (< initial 80) + - Final stress is within valid range (0-100) + - Final boss alert is within valid range (0-5) + - System handles multiple sequential operations correctly + + Test Status: PASS if all validations succeed """ + # Setup: realistic scenario config = Config(boss_alertness=50, boss_alertness_cooldown=10) state_manager = StateManager(config) - # Start with some stress + # Start with high stress (typical stressed agent) state_manager._stress_level = 80 + initial_stress = 80 - # Take several breaks + # Take several breaks to reduce stress for i in range(5): response = await tools.take_a_break(state_manager) is_valid, stress, boss_alert, msg = validate_response(response) - assert is_valid, f"Break {i+1} - {msg}" + assert is_valid, f"Break {i+1} - Invalid response: {msg}" - # Verify stress decreased - assert stress < 80, f"Break {i+1} - Stress should decrease" + # Verify stress decreased from initial level + assert stress < initial_stress, f"Break {i+1} - Stress should decrease from {initial_stress}, got {stress}" - # Get final state + # Get final state and verify all values are in valid ranges final_state = await state_manager.get_state() - assert 0 <= final_state["stress_level"] <= 100 - assert 0 <= final_state["boss_alert_level"] <= 5 + assert 0 <= final_state["stress_level"] <= 100, f"Final stress out of range: {final_state['stress_level']}" + assert 0 <= final_state["boss_alert_level"] <= 5, f"Final boss alert out of range: {final_state['boss_alert_level']}" diff --git a/tests/test_state_manager.py b/tests/test_state_manager.py index 0c9a27c..ab9a224 100644 --- a/tests/test_state_manager.py +++ b/tests/test_state_manager.py @@ -1,4 +1,16 @@ -"""Tests for state_manager module.""" +""" +Tests for state_manager module. + +This module tests the StateManager class which handles: +- Stress level management (increase/decrease) +- Boss alert level management +- Cooldown mechanisms +- Time-based automatic stress increase +- State retrieval and reset + +The StateManager is the core component that tracks the AI agent's +stress and the boss's suspicion level throughout the workday. +""" import asyncio import pytest @@ -21,55 +33,157 @@ def state_manager(config): @pytest.mark.asyncio async def test_initial_state(state_manager): - """Test initial state values.""" - assert state_manager.stress_level == 0 - assert state_manager.boss_alert_level == 0 + """ + Test initial state values. + + Component: StateManager initialization + Purpose: StateManager가 올바른 초기 상태로 시작하는지 확인 + + Initial Conditions: + - New StateManager instance created + + Expected Results: + - Stress Level: 0 (completely relaxed at start) + - Boss Alert Level: 0 (boss is not suspicious yet) + + Test Status: PASS if both initial values are 0 + """ + assert state_manager.stress_level == 0, f"Expected initial stress=0, got {state_manager.stress_level}" + assert state_manager.boss_alert_level == 0, f"Expected initial boss alert=0, got {state_manager.boss_alert_level}" @pytest.mark.asyncio async def test_decrease_stress(state_manager): - """Test stress decrease.""" + """ + Test stress decrease with specific amount. + + Component: StateManager.decrease_stress() + Purpose: 지정된 양만큼 스트레스가 정확히 감소하는지 확인 + + Initial Conditions: + - Stress Level: 50 + + Test Action: + - Decrease stress by 30 + + Expected Results: + - Return value: 30 (actual decrease amount) + - Final stress level: 20 (50 - 30) + + Test Status: PASS if stress decreases by exact amount + """ # Set initial stress state_manager._stress_level = 50 - # Decrease stress + # Decrease stress by specific amount amount = await state_manager.decrease_stress(30) - assert amount == 30 - assert state_manager.stress_level == 20 + assert amount == 30, f"Expected decrease amount=30, got {amount}" + assert state_manager.stress_level == 20, f"Expected stress=20, got {state_manager.stress_level}" @pytest.mark.asyncio async def test_decrease_stress_random(state_manager): - """Test random stress decrease.""" + """ + Test random stress decrease. + + Component: StateManager.decrease_stress() without amount parameter + Purpose: amount 파라미터 없이 호출 시 랜덤 감소가 올바르게 작동하는지 확인 + + Initial Conditions: + - Stress Level: 100 + + Test Action: + - Decrease stress by random amount (no parameter) + + Expected Results: + - Decrease amount is between 1 and 100 + - Final stress level is between 0 and 99 + - Random value is within valid range + + Test Status: PASS if random decrease is within expected bounds + """ state_manager._stress_level = 100 amount = await state_manager.decrease_stress() - assert 1 <= amount <= 100 - assert 0 <= state_manager.stress_level <= 99 + assert 1 <= amount <= 100, f"Random decrease amount {amount} is out of range [1, 100]" + assert 0 <= state_manager.stress_level <= 99, f"Stress level {state_manager.stress_level} is out of range" @pytest.mark.asyncio async def test_decrease_stress_floor(state_manager): - """Test stress cannot go below 0.""" + """ + Test stress floor (cannot go below 0). + + Component: StateManager.decrease_stress() boundary check + Purpose: 스트레스가 0 아래로 내려가지 않는지 확인 (하한선 검증) + + Initial Conditions: + - Stress Level: 10 + + Test Action: + - Attempt to decrease stress by 50 (more than current) + + Expected Results: + - Stress level stops at 0 (does not go negative) + - No errors occur + + Test Status: PASS if stress level is exactly 0 + """ state_manager._stress_level = 10 await state_manager.decrease_stress(50) - assert state_manager.stress_level == 0 + assert state_manager.stress_level == 0, f"Expected stress floor=0, got {state_manager.stress_level}" @pytest.mark.asyncio async def test_stress_auto_increase(state_manager): - """Test automatic stress increase over time.""" + """ + Test automatic stress increase over time. + + Component: StateManager.update_stress_level() + Purpose: 시간이 지남에 따라 스트레스가 자동으로 증가하는지 확인 (분당 1포인트) + + Initial Conditions: + - Stress Level: 0 + - Last update: 2 minutes ago (simulated) + + Test Action: + - Call update_stress_level() + + Expected Results: + - Stress increases by at least 2 points (1 point per minute) + - Time-based stress accumulation works correctly + + Test Status: PASS if stress increased by at least 2 + """ # Set last update to 2 minutes ago state_manager._last_stress_update = time.time() - 120 await state_manager.update_stress_level() - assert state_manager.stress_level >= 2 # At least 2 points for 2 minutes + assert state_manager.stress_level >= 2, f"Expected stress >= 2 after 2 minutes, got {state_manager.stress_level}" @pytest.mark.asyncio async def test_boss_alert_increase_100_percent(state_manager): - """Test boss alert increases with 100% probability.""" + """ + Test boss alert increases with 100% probability. + + Component: StateManager.increase_boss_alert() + Purpose: boss_alertness=100일 때 항상 경고가 증가하는지 확인 + + Initial Conditions: + - Boss Alertness: 100% (always notices) + - Boss Alert Level: 0 + + Test Action: + - Call increase_boss_alert() 5 times + + Expected Results: + - Boss alert increases all 5 times + - 100% probability works correctly + + Test Status: PASS if increased_count == 5 + """ # Set boss alertness to 100% state_manager.config.boss_alertness = 100 @@ -80,74 +194,203 @@ async def test_boss_alert_increase_100_percent(state_manager): if boss_increased: increased_count += 1 - assert increased_count == 5 # Should increase every time + assert increased_count == 5, f"Expected 5 increases with 100% alertness, got {increased_count}" @pytest.mark.asyncio async def test_boss_alert_max_level(state_manager): - """Test boss alert level cannot exceed 5.""" + """ + Test boss alert level ceiling (cannot exceed 5). + + Component: StateManager.increase_boss_alert() boundary check + Purpose: Boss Alert Level이 최대값 5를 초과하지 않는지 확인 + + Initial Conditions: + - Boss Alertness: 100% + - Boss Alert Level: 5 (already at maximum) + + Test Action: + - Attempt to increase boss alert + + Expected Results: + - Boss alert level stays at 5 + - Does not exceed maximum + + Test Status: PASS if level remains 5 + """ state_manager.config.boss_alertness = 100 state_manager._boss_alert_level = 5 await state_manager.increase_boss_alert() - assert state_manager.boss_alert_level == 5 # Should stay at 5 + assert state_manager.boss_alert_level == 5, f"Expected boss alert ceiling=5, got {state_manager.boss_alert_level}" @pytest.mark.asyncio async def test_boss_alert_cooldown(state_manager): - """Test boss alert cooldown decreases alert level.""" + """ + Test boss alert cooldown decreases alert level. + + Component: StateManager.update_boss_cooldown() + Purpose: 시간이 지남에 따라 Boss Alert Level이 감소하는지 확인 + + Initial Conditions: + - Boss Alert Level: 3 + - Cooldown: 5 seconds (from config) + - Last cooldown: 10 seconds ago (2 cooldown periods) + + Test Action: + - Call update_boss_cooldown() + + Expected Results: + - Boss alert decreases by 2 (10 seconds / 5 second cooldown) + - Final level: 1 (3 - 2) + + Test Status: PASS if level is 1 after cooldown + """ state_manager._boss_alert_level = 3 state_manager._last_boss_cooldown = time.time() - 10 # 10 seconds ago (2 cooldown periods) await state_manager.update_boss_cooldown() - assert state_manager.boss_alert_level == 1 # Should decrease by 2 + assert state_manager.boss_alert_level == 1, f"Expected boss alert=1 after cooldown, got {state_manager.boss_alert_level}" @pytest.mark.asyncio async def test_boss_alert_cooldown_floor(state_manager): - """Test boss alert level cannot go below 0.""" + """ + Test boss alert level floor (cannot go below 0). + + Component: StateManager.update_boss_cooldown() boundary check + Purpose: Boss Alert Level이 0 아래로 내려가지 않는지 확인 + + Initial Conditions: + - Boss Alert Level: 1 + - Cooldown: 5 seconds + - Last cooldown: 20 seconds ago (4 cooldown periods) + + Test Action: + - Call update_boss_cooldown() + + Expected Results: + - Boss alert decreases to 0 and stops + - Does not go negative + + Test Status: PASS if level is 0 + """ state_manager._boss_alert_level = 1 state_manager._last_boss_cooldown = time.time() - 20 # 20 seconds ago await state_manager.update_boss_cooldown() - assert state_manager.boss_alert_level == 0 + assert state_manager.boss_alert_level == 0, f"Expected boss alert floor=0, got {state_manager.boss_alert_level}" @pytest.mark.asyncio async def test_check_boss_delay_level_5(state_manager): - """Test 20 second delay when boss alert is 5.""" + """ + Test 20 second delay when boss alert is 5. + + Component: StateManager.check_boss_delay() + Purpose: Boss Alert Level이 5일 때 20초 지연이 반환되는지 확인 + + Initial Conditions: + - Boss Alert Level: 5 (maximum) + + Test Action: + - Call check_boss_delay() + + Expected Results: + - Returns 20.0 seconds delay + - This delay forces the agent to wait before taking action + + Test Status: PASS if delay == 20.0 + """ state_manager._boss_alert_level = 5 delay = await state_manager.check_boss_delay() - assert delay == 20.0 + assert delay == 20.0, f"Expected 20 second delay at level 5, got {delay}" @pytest.mark.asyncio async def test_check_boss_delay_level_below_5(state_manager): - """Test no delay when boss alert is below 5.""" + """ + Test no delay when boss alert is below 5. + + Component: StateManager.check_boss_delay() + Purpose: Boss Alert Level이 5 미만일 때 지연이 없는지 확인 + + Initial Conditions: + - Boss Alert Level: 4 + + Test Action: + - Call check_boss_delay() + + Expected Results: + - Returns 0.0 seconds (no delay) + - Agent can take action immediately + + Test Status: PASS if delay == 0.0 + """ state_manager._boss_alert_level = 4 delay = await state_manager.check_boss_delay() - assert delay == 0.0 + assert delay == 0.0, f"Expected no delay below level 5, got {delay}" @pytest.mark.asyncio async def test_get_state(state_manager): - """Test getting current state.""" + """ + Test getting current state. + + Component: StateManager.get_state() + Purpose: 현재 상태를 올바르게 반환하는지 확인 + + Initial Conditions: + - Stress Level: 42 + - Boss Alert Level: 3 + + Test Action: + - Call get_state() + + Expected Results: + - Returns dictionary with current state + - Contains "stress_level" and "boss_alert_level" keys + - Stress may increase slightly due to auto-update + + Test Status: PASS if state dictionary is valid + """ state_manager._stress_level = 42 state_manager._boss_alert_level = 3 state = await state_manager.get_state() - assert state["stress_level"] >= 42 # May increase due to update - assert "boss_alert_level" in state + assert state["stress_level"] >= 42, f"Stress should be >= 42, got {state['stress_level']}" + assert "boss_alert_level" in state, "State should contain boss_alert_level" + assert state["boss_alert_level"] == 3, f"Expected boss alert=3, got {state['boss_alert_level']}" @pytest.mark.asyncio async def test_reset(state_manager): - """Test resetting state.""" + """ + Test resetting state. + + Component: StateManager.reset() + Purpose: 상태를 초기값으로 리셋하는지 확인 + + Initial Conditions: + - Stress Level: 50 + - Boss Alert Level: 3 + + Test Action: + - Call reset() + + Expected Results: + - Stress Level: 0 + - Boss Alert Level: 0 + - All state returns to initial values + + Test Status: PASS if both values are 0 after reset + """ state_manager._stress_level = 50 state_manager._boss_alert_level = 3 await state_manager.reset() - assert state_manager.stress_level == 0 - assert state_manager.boss_alert_level == 0 + assert state_manager.stress_level == 0, f"Expected stress=0 after reset, got {state_manager.stress_level}" + assert state_manager.boss_alert_level == 0, f"Expected boss alert=0 after reset, got {state_manager.boss_alert_level}" diff --git a/tests/test_tools.py b/tests/test_tools.py index 930e724..65da5ba 100644 --- a/tests/test_tools.py +++ b/tests/test_tools.py @@ -45,88 +45,277 @@ def validate_response(response_text): @pytest.mark.asyncio async def test_take_a_break(state_manager): - """Test take_a_break tool.""" + """ + Test take_a_break tool. + + Tool: take_a_break + Purpose: 짧은 휴식을 취하면서 스트레스를 감소시키는 도구 테스트 + + Initial Conditions: + - Stress Level: 50 + - Boss Alert: 0 (config에서 boss_alertness=0 설정) + + Expected Results: + - Response format is valid (Break Summary, Stress Level, Boss Alert Level) + - Stress level decreases from initial value (50) + - Stress level is within valid range (0-100) + - Boss alert level is within valid range (0-5) + + Test Status: PASS if all assertions succeed + """ + # Set initial stress level state_manager._stress_level = 50 + initial_stress = state_manager.stress_level + + # Execute the tool response = await tools.take_a_break(state_manager) - assert validate_response(response) - assert state_manager.stress_level < 50 # Stress should decrease + # Validate response format + assert validate_response(response), "Response format validation failed" + + # Verify stress decreased + assert state_manager.stress_level < initial_stress, f"Stress should decrease: {initial_stress} -> {state_manager.stress_level}" @pytest.mark.asyncio async def test_watch_netflix(state_manager): - """Test watch_netflix tool.""" + """ + Test watch_netflix tool. + + Tool: watch_netflix + Purpose: 넷플릭스 시청으로 스트레스를 크게 감소시키는 도구 테스트 + + Initial Conditions: + - Stress Level: 60 + - Boss Alert: 0 + + Expected Results: + - Response format is valid (Break Summary, Stress Level, Boss Alert Level) + - Stress level decreases significantly (typically 20-30 points) + - All values are within valid ranges + + Test Status: PASS if all assertions succeed + """ + # Set initial stress level state_manager._stress_level = 60 + initial_stress = state_manager.stress_level + + # Execute the tool response = await tools.watch_netflix(state_manager) - assert validate_response(response) + # Validate response format + assert validate_response(response), "Response format validation failed" + + # Additional check: stress should have decreased + assert state_manager.stress_level < initial_stress, f"Stress should decrease: {initial_stress} -> {state_manager.stress_level}" @pytest.mark.asyncio async def test_show_meme(state_manager): - """Test show_meme tool.""" + """ + Test show_meme tool. + + Tool: show_meme + Purpose: 밈을 보여주면서 가벼운 스트레스 해소를 제공하는 도구 테스트 + + Initial Conditions: + - Stress Level: 40 + - Boss Alert: 0 + + Expected Results: + - Response format is valid + - Stress level decreases moderately (typically 10-20 points) + - ASCII art meme is displayed in response + + Test Status: PASS if all assertions succeed + """ + # Set initial stress level state_manager._stress_level = 40 + initial_stress = state_manager.stress_level + + # Execute the tool response = await tools.show_meme(state_manager) - assert validate_response(response) + # Validate response format + assert validate_response(response), "Response format validation failed" + + # Verify stress decreased + assert state_manager.stress_level < initial_stress, f"Stress should decrease: {initial_stress} -> {state_manager.stress_level}" @pytest.mark.asyncio async def test_bathroom_break(state_manager): - """Test bathroom_break tool.""" + """ + Test bathroom_break tool. + + Tool: bathroom_break + Purpose: 화장실 휴식으로 스트레스를 감소시키는 도구 테스트 + + Initial Conditions: + - Stress Level: 70 + - Boss Alert: 0 + + Expected Results: + - Response format is valid + - Stress level decreases (typically 5-15 points) + - Boss alert may increase (depending on boss_alertness setting) + + Test Status: PASS if all assertions succeed + """ state_manager._stress_level = 70 + initial_stress = state_manager.stress_level + response = await tools.bathroom_break(state_manager) - assert validate_response(response) + assert validate_response(response), "Response format validation failed" + assert state_manager.stress_level < initial_stress, f"Stress should decrease: {initial_stress} -> {state_manager.stress_level}" @pytest.mark.asyncio async def test_coffee_mission(state_manager): - """Test coffee_mission tool.""" + """ + Test coffee_mission tool. + + Tool: coffee_mission + Purpose: 커피를 마시러 가면서 스트레스를 감소시키는 도구 테스트 + + Initial Conditions: + - Stress Level: 55 + - Boss Alert: 0 + + Expected Results: + - Response format is valid + - Stress level decreases (typically 10-20 points) + - Provides energy boost flavor text + + Test Status: PASS if all assertions succeed + """ state_manager._stress_level = 55 + initial_stress = state_manager.stress_level + response = await tools.coffee_mission(state_manager) - assert validate_response(response) + assert validate_response(response), "Response format validation failed" + assert state_manager.stress_level < initial_stress, f"Stress should decrease: {initial_stress} -> {state_manager.stress_level}" @pytest.mark.asyncio async def test_urgent_call(state_manager): - """Test urgent_call tool.""" + """ + Test urgent_call tool. + + Tool: urgent_call + Purpose: 긴급 전화로 자리를 비우면서 스트레스를 감소시키는 도구 테스트 + + Initial Conditions: + - Stress Level: 80 + - Boss Alert: 0 + + Expected Results: + - Response format is valid + - Stress level decreases significantly (typically 15-25 points) + - High stress relief due to "urgent" nature + + Test Status: PASS if all assertions succeed + """ state_manager._stress_level = 80 + initial_stress = state_manager.stress_level + response = await tools.urgent_call(state_manager) - assert validate_response(response) + assert validate_response(response), "Response format validation failed" + assert state_manager.stress_level < initial_stress, f"Stress should decrease: {initial_stress} -> {state_manager.stress_level}" @pytest.mark.asyncio async def test_deep_thinking(state_manager): - """Test deep_thinking tool.""" + """ + Test deep_thinking tool. + + Tool: deep_thinking + Purpose: 심각한 생각에 잠기는 척하며 스트레스를 감소시키는 도구 테스트 + + Initial Conditions: + - Stress Level: 45 + - Boss Alert: 0 + + Expected Results: + - Response format is valid + - Stress level decreases (typically 5-15 points) + - Low suspicion from boss (lower alert probability) + + Test Status: PASS if all assertions succeed + """ state_manager._stress_level = 45 + initial_stress = state_manager.stress_level + response = await tools.deep_thinking(state_manager) - assert validate_response(response) + assert validate_response(response), "Response format validation failed" + assert state_manager.stress_level < initial_stress, f"Stress should decrease: {initial_stress} -> {state_manager.stress_level}" @pytest.mark.asyncio async def test_email_organizing(state_manager): - """Test email_organizing tool.""" + """ + Test email_organizing tool. + + Tool: email_organizing + Purpose: 이메일 정리하는 척하며 스트레스를 감소시키는 도구 테스트 + + Initial Conditions: + - Stress Level: 65 + - Boss Alert: 0 + + Expected Results: + - Response format is valid + - Stress level decreases moderately (typically 10-20 points) + - Appears productive, lower boss alert probability + + Test Status: PASS if all assertions succeed + """ state_manager._stress_level = 65 + initial_stress = state_manager.stress_level + response = await tools.email_organizing(state_manager) - assert validate_response(response) + assert validate_response(response), "Response format validation failed" + assert state_manager.stress_level < initial_stress, f"Stress should decrease: {initial_stress} -> {state_manager.stress_level}" @pytest.mark.asyncio async def test_boss_alert_delay(state_manager): - """Test that tools respect boss alert level 5 delay.""" + """ + Test boss alert level 5 delay mechanism. + + Tool: All tools (tested with take_a_break) + Purpose: Boss Alert Level 5에 도달했을 때 20초 지연 메커니즘 테스트 + + Initial Conditions: + - Stress Level: 50 + - Boss Alert Level: 5 (maximum) + + Expected Results: + - Response format is valid + - Tool execution is delayed by exactly 20 seconds + - Warning message about boss presence is displayed + - This simulates the boss watching, forcing the agent to wait + + Test Status: PASS if delay >= 20.0 seconds and response is valid + """ import time + # Set boss alert to maximum level state_manager._boss_alert_level = 5 state_manager._stress_level = 50 + # Measure execution time start_time = time.time() response = await tools.take_a_break(state_manager) elapsed_time = time.time() - start_time - assert validate_response(response) - assert elapsed_time >= 20.0 # Should have 20 second delay + # Validate response format + assert validate_response(response), "Response format validation failed" + + # Verify 20 second delay was applied + assert elapsed_time >= 20.0, f"Expected 20 second delay, but got {elapsed_time:.2f} seconds"