#!/usr/bin/env python3
"""
Automated test suite for smart-home-controller MCP server
Generated by KEN-MCP

INSTRUCTIONS FOR CLAUDE:
1. Run this test file immediately: python test.py
2. Fix any failing tests in server.py
3. Add domain-specific test cases based on the requirements
4. Ensure all tests pass before considering the MCP complete

Original Requirements:
Create an MCP for controlling smart home devices including lights, thermostats, and security cameras. It should be able to list all devices, control individual devices, set up automation rules, and monitor device status.
"""

import asyncio
import json
import sys
from pathlib import Path

# Add the parent directory to the path so we can import the server
sys.path.insert(0, str(Path(__file__).parent))

# Import the generated server
try:
    from server import mcp
except ImportError as e:
    print(f"❌ Failed to import server: {e}")
    print("Make sure server.py exists and has no syntax errors")
    sys.exit(1)

# Test utilities
class MockContext:
    """Mock context for testing MCP tools"""
    def __init__(self):
        self.logs = []
        self.progress = []
    
    async def info(self, msg):
        self.logs.append(msg)
    
    async def report_progress(self, current, total, msg):
        self.progress.append((current, total, msg))
    
    async def read_resource(self, uri):
        return {"uri": uri, "content": "mock resource content"}


# Test functions
async def test_server_initialization():
    """Test that the MCP server can be initialized"""
    print("Testing server initialization...")
    try:
        assert mcp.name == "smart-home-controller"
        assert hasattr(mcp, 'run')
        print("  ✅ Server initialization test passed")
        return True
    except Exception as e:
        print(f"  ❌ Server initialization failed: {e}")
        return False


async def test_create_device():
    """Test create_device: Create operation for device.

Domain: smart_home
Related operations: listing

TODO: Claude, implemen..."""
    print(f"\nTesting create_device...")
    
    # Get the tool function directly
    try:
        from server import create_device
        tool_func = create_device
    except ImportError as e:
        print(f"  ❌ Could not import create_device: {e}")
        return False
    except Exception as e:
        print(f"  ❌ Could not access create_device: {e}")
        return False
    
    ctx = MockContext()
    passed = 0
    failed = 0
    
    # Test 1: Valid inputs
    try:
        result = await tool_func(
            ctx=ctx,
            device_data={"type": "test", "name": "Test Device"},
            validate=True
        )
        # Check result structure
        assert isinstance(result, dict), "Result should be a dictionary"
        assert any(key in result for key in ["success", "status", "data", "result"]), \
            "Result should contain success, status, data, or result key"
        print("  ✅ Valid input test passed")
        passed += 1
    except Exception as e:
        print(f"  ❌ Valid input test failed: {e}")
        failed += 1

    
    # Test 2: Missing required parameters
    try:
        # Call without required parameter: device_data
        result = await tool_func(ctx=ctx)
        print(f"  ❌ Should have failed with missing required parameter")
        failed += 1
    except TypeError as e:
        if "device_data" in str(e):
            print(f"  ✅ Missing parameter validation passed")
            passed += 1
        else:
            print(f"  ❌ Wrong error for missing parameter: {e}")
            failed += 1
    except Exception as e:
        print(f"  ❌ Unexpected error for missing parameter: {e}")
        failed += 1

    
    # Test 3: Edge cases
    # TODO: Claude, add more specific edge case tests based on the requirements:
    # - Test with empty strings for string parameters
    # - Test with None for optional parameters  
    # - Test with boundary values for numeric parameters
    # - Test with special characters if applicable
    # - Test error conditions specific to this tool's purpose
    
    print(f"  📊 create_device tests: {passed} passed, {failed} failed")
    return failed == 0


async def test_get_device():
    """Test get_device: Read operation for device.

Domain: smart_home
Related operations: listing

TODO: Claude, implement..."""
    print(f"\nTesting get_device...")
    
    # Get the tool function directly
    try:
        from server import get_device
        tool_func = get_device
    except ImportError as e:
        print(f"  ❌ Could not import get_device: {e}")
        return False
    except Exception as e:
        print(f"  ❌ Could not access get_device: {e}")
        return False
    
    ctx = MockContext()
    passed = 0
    failed = 0
    
    # Test 1: Valid inputs
    try:
        result = await tool_func(
            ctx=ctx,
        device_id="test_id_123"
        )
        # Check result structure
        assert isinstance(result, dict), "Result should be a dictionary"
        assert any(key in result for key in ["success", "status", "data", "result"]), \
            "Result should contain success, status, data, or result key"
        print("  ✅ Valid input test passed")
        passed += 1
    except Exception as e:
        print(f"  ❌ Valid input test failed: {e}")
        failed += 1

    
    # Test 2: Missing required parameters
    try:
        # Call without required parameter: device_id
        result = await tool_func(ctx=ctx)
        print(f"  ❌ Should have failed with missing required parameter")
        failed += 1
    except TypeError as e:
        if "device_id" in str(e):
            print(f"  ✅ Missing parameter validation passed")
            passed += 1
        else:
            print(f"  ❌ Wrong error for missing parameter: {e}")
            failed += 1
    except Exception as e:
        print(f"  ❌ Unexpected error for missing parameter: {e}")
        failed += 1

    
    # Test 3: Edge cases
    # TODO: Claude, add more specific edge case tests based on the requirements:
    # - Test with empty strings for string parameters
    # - Test with None for optional parameters  
    # - Test with boundary values for numeric parameters
    # - Test with special characters if applicable
    # - Test error conditions specific to this tool's purpose
    
    print(f"  📊 get_device tests: {passed} passed, {failed} failed")
    return failed == 0


async def test_list_devices():
    """Test list_devices: List operation for device.

Domain: smart_home
Related operations: listing

TODO: Claude, implement..."""
    print(f"\nTesting list_devices...")
    
    # Get the tool function directly
    try:
        from server import list_devices
        tool_func = list_devices
    except ImportError as e:
        print(f"  ❌ Could not import list_devices: {e}")
        return False
    except Exception as e:
        print(f"  ❌ Could not access list_devices: {e}")
        return False
    
    ctx = MockContext()
    passed = 0
    failed = 0
    
    # Test 1: Valid inputs
    try:
        result = await tool_func(
            ctx=ctx,
            filter={"type": "test"},
            limit=42,
            offset=42
        )
        # Check result structure
        assert isinstance(result, dict), "Result should be a dictionary"
        assert any(key in result for key in ["success", "status", "data", "result"]), \
            "Result should contain success, status, data, or result key"
        print("  ✅ Valid input test passed")
        passed += 1
    except Exception as e:
        print(f"  ❌ Valid input test failed: {e}")
        failed += 1

    
    # Test 3: Edge cases
    # TODO: Claude, add more specific edge case tests based on the requirements:
    # - Test with empty strings for string parameters
    # - Test with None for optional parameters  
    # - Test with boundary values for numeric parameters
    # - Test with special characters if applicable
    # - Test error conditions specific to this tool's purpose
    
    print(f"  📊 list_devices tests: {passed} passed, {failed} failed")
    return failed == 0


async def test_update_device():
    """Test update_device: Update operation for device.

Domain: smart_home
Related operations: listing

TODO: Claude, implemen..."""
    print(f"\nTesting update_device...")
    
    # Get the tool function directly
    try:
        from server import update_device
        tool_func = update_device
    except ImportError as e:
        print(f"  ❌ Could not import update_device: {e}")
        return False
    except Exception as e:
        print(f"  ❌ Could not access update_device: {e}")
        return False
    
    ctx = MockContext()
    passed = 0
    failed = 0
    
    # Test 1: Valid inputs
    try:
        result = await tool_func(
            ctx=ctx,
            device_id="test_id_123",
            updates={"status": "updated"},
            partial=True
        )
        # Check result structure
        assert isinstance(result, dict), "Result should be a dictionary"
        assert any(key in result for key in ["success", "status", "data", "result"]), \
            "Result should contain success, status, data, or result key"
        print("  ✅ Valid input test passed")
        passed += 1
    except Exception as e:
        print(f"  ❌ Valid input test failed: {e}")
        failed += 1

    
    # Test 2: Missing required parameters
    try:
        # Call without required parameter: device_id
        result = await tool_func(ctx=ctx)
        print(f"  ❌ Should have failed with missing required parameter")
        failed += 1
    except TypeError as e:
        if "device_id" in str(e):
            print(f"  ✅ Missing parameter validation passed")
            passed += 1
        else:
            print(f"  ❌ Wrong error for missing parameter: {e}")
            failed += 1
    except Exception as e:
        print(f"  ❌ Unexpected error for missing parameter: {e}")
        failed += 1

    
    # Test 3: Edge cases
    # TODO: Claude, add more specific edge case tests based on the requirements:
    # - Test with empty strings for string parameters
    # - Test with None for optional parameters  
    # - Test with boundary values for numeric parameters
    # - Test with special characters if applicable
    # - Test error conditions specific to this tool's purpose
    
    print(f"  📊 update_device tests: {passed} passed, {failed} failed")
    return failed == 0


async def test_monitor_device():
    """Test monitor_device: Monitor operation for device.

Domain: smart_home
Related operations: listing

TODO: Claude, impleme..."""
    print(f"\nTesting monitor_device...")
    
    # Get the tool function directly
    try:
        from server import monitor_device
        tool_func = monitor_device
    except ImportError as e:
        print(f"  ❌ Could not import monitor_device: {e}")
        return False
    except Exception as e:
        print(f"  ❌ Could not access monitor_device: {e}")
        return False
    
    ctx = MockContext()
    passed = 0
    failed = 0
    
    # Test 1: Valid inputs
    try:
        result = await tool_func(
            ctx=ctx,
            target="test_device",
            threshold={"temperature": 75.0},
            interval=42
        )
        # Check result structure
        assert isinstance(result, dict), "Result should be a dictionary"
        assert any(key in result for key in ["success", "status", "data", "result"]), \
            "Result should contain success, status, data, or result key"
        print("  ✅ Valid input test passed")
        passed += 1
    except Exception as e:
        print(f"  ❌ Valid input test failed: {e}")
        failed += 1

    
    # Test 2: Missing required parameters
    try:
        # Call without required parameter: target
        result = await tool_func(ctx=ctx)
        print(f"  ❌ Should have failed with missing required parameter")
        failed += 1
    except TypeError as e:
        if "target" in str(e):
            print(f"  ✅ Missing parameter validation passed")
            passed += 1
        else:
            print(f"  ❌ Wrong error for missing parameter: {e}")
            failed += 1
    except Exception as e:
        print(f"  ❌ Unexpected error for missing parameter: {e}")
        failed += 1

    
    # Test 3: Edge cases
    # TODO: Claude, add more specific edge case tests based on the requirements:
    # - Test with empty strings for string parameters
    # - Test with None for optional parameters  
    # - Test with boundary values for numeric parameters
    # - Test with special characters if applicable
    # - Test error conditions specific to this tool's purpose
    
    print(f"  📊 monitor_device tests: {passed} passed, {failed} failed")
    return failed == 0


async def test_control_device():
    """Test control_device: Control operation for device.

Domain: smart_home
Related operations: listing

TODO: Claude, impleme..."""
    print(f"\nTesting control_device...")
    
    # Get the tool function directly
    try:
        from server import control_device
        tool_func = control_device
    except ImportError as e:
        print(f"  ❌ Could not import control_device: {e}")
        return False
    except Exception as e:
        print(f"  ❌ Could not access control_device: {e}")
        return False
    
    ctx = MockContext()
    passed = 0
    failed = 0
    
    # Test 1: Valid inputs
    try:
        result = await tool_func(
            ctx=ctx,
        device_id="test_id_123",
        action={"test": "data"},
        parameters={"test": "data"}
        )
        # Check result structure
        assert isinstance(result, dict), "Result should be a dictionary"
        assert any(key in result for key in ["success", "status", "data", "result"]), \
            "Result should contain success, status, data, or result key"
        print("  ✅ Valid input test passed")
        passed += 1
    except Exception as e:
        print(f"  ❌ Valid input test failed: {e}")
        failed += 1

    
    # Test 2: Missing required parameters
    try:
        # Call without required parameter: device_id
        result = await tool_func(ctx=ctx)
        print(f"  ❌ Should have failed with missing required parameter")
        failed += 1
    except TypeError as e:
        if "device_id" in str(e):
            print(f"  ✅ Missing parameter validation passed")
            passed += 1
        else:
            print(f"  ❌ Wrong error for missing parameter: {e}")
            failed += 1
    except Exception as e:
        print(f"  ❌ Unexpected error for missing parameter: {e}")
        failed += 1

    
    # Test 3: Edge cases
    # TODO: Claude, add more specific edge case tests based on the requirements:
    # - Test with empty strings for string parameters
    # - Test with None for optional parameters  
    # - Test with boundary values for numeric parameters
    # - Test with special characters if applicable
    # - Test error conditions specific to this tool's purpose
    
    print(f"  📊 control_device tests: {passed} passed, {failed} failed")
    return failed == 0


async def test_resources():
    """Test that MCP resources are accessible"""
    print(f"\nTesting resources...")
    
    try:
        # Test that resource functions exist
        from server import resource_devices, resource_device_id, resource_cameras, resource_camera_id
        
        # Test a simple resource
        result = await resource_devices()
        assert isinstance(result, list), "Resource should return a list"
        print(f"  ✅ Resource functions are accessible")
        
        # Test parameterized resource
        result = await resource_device_id("test-id")
        assert isinstance(result, list), "Parameterized resource should return a list"
        print(f"  ✅ Parameterized resources work")
        
        return True
    except Exception as e:
        print(f"  ❌ Resource test failed: {e}")
        return False


async def test_prompts():
    """Test that MCP prompts are defined"""
    print(f"\nTesting prompts...")
    
    try:
        # Test that prompt functions exist
        from server import help, assistant
        
        # Test help prompt
        result = help("test")
        assert isinstance(result, str), "Prompt should return a string"
        print(f"  ✅ Help prompt is accessible")
        
        # Test assistant prompt  
        result = assistant("test request")
        assert isinstance(result, str), "Prompt should return a string"
        print(f"  ✅ Assistant prompt is accessible")
        
        return True
    except Exception as e:
        print(f"  ❌ Prompt test failed: {e}")
        return False


async def run_all_tests():
    """Run all test cases"""
    print("=" * 50)
    print(f"🧪 Running MCP Server Tests for smart-home-controller")
    print("=" * 50)
    
    # List all tests to run
    tests = [
        ("Server Initialization", test_server_initialization),
        ("create_device", test_create_device),
        ("get_device", test_get_device),
        ("list_devices", test_list_devices),
        ("update_device", test_update_device),
        ("monitor_device", test_monitor_device),
        ("control_device", test_control_device),
        ("Resources", test_resources),
        ("Prompts", test_prompts),
    ]
    
    total_passed = 0
    total_failed = 0
    
    for test_name, test_func in tests:
        try:
            result = await test_func()
            if result:
                total_passed += 1
            else:
                total_failed += 1
        except Exception as e:
            print(f"\n❌ {test_name} crashed: {e}")
            total_failed += 1
    
    # Summary
    print("\n" + "=" * 50)
    print(f"📊 Test Summary: {total_passed}/{len(tests)} passed")
    print("=" * 50)
    
    if total_failed > 0:
        print(f"\n⚠️  {total_failed} test(s) failed!")
        print("\nNext steps:")
        print("1. Check the error messages above")
        print("2. Fix the implementation in server.py")
        print("3. Run the tests again: python test.py")
        print("4. All tests must pass before the MCP is ready")
        return 1
    else:
        print("\n✅ All tests passed! The MCP server is ready to use.")
        print("\nYou can now:")
        print("1. Add it to Claude Desktop (see help.md)")
        print("2. Add more specific test cases based on your use case")
        print("3. Test with real data")
        return 0

if __name__ == "__main__":
    exit_code = asyncio.run(run_all_tests())
    sys.exit(exit_code)
