"""
Unit tests for MCP tool schema definitions.
Tests ensure all tool schemas are properly defined with correct parameters,
types, and required fields.
"""
import pytest
from hue_mcp_server.tools import TOOLS
class TestToolsRegistry:
"""Tests for the TOOLS registry structure."""
def test_tools_registry_exists(self):
"""Test that TOOLS registry is defined."""
assert TOOLS is not None
assert isinstance(TOOLS, dict)
def test_tools_registry_not_empty(self):
"""Test that TOOLS registry contains tools."""
assert len(TOOLS) > 0
def test_expected_tools_are_registered(self):
"""Test that all expected tools are registered."""
expected_tools = [
"list_lights",
"get_light_state",
"turn_light_on",
"turn_light_off",
"set_brightness",
"set_color_temp",
"set_color",
"list_groups",
"control_group",
"list_scenes",
"activate_scene",
]
for tool_name in expected_tools:
assert tool_name in TOOLS, f"Tool '{tool_name}' not found in TOOLS registry"
def test_all_tools_have_required_keys(self):
"""Test that all tools have function, description, and parameters."""
for tool_name, tool_def in TOOLS.items():
assert "function" in tool_def, f"Tool '{tool_name}' missing 'function' key"
assert (
"description" in tool_def
), f"Tool '{tool_name}' missing 'description' key"
assert (
"parameters" in tool_def
), f"Tool '{tool_name}' missing 'parameters' key"
def test_all_tool_functions_are_callable(self):
"""Test that all tool functions are callable."""
for tool_name, tool_def in TOOLS.items():
assert callable(
tool_def["function"]
), f"Tool '{tool_name}' function is not callable"
def test_all_tool_descriptions_are_strings(self):
"""Test that all tool descriptions are non-empty strings."""
for tool_name, tool_def in TOOLS.items():
desc = tool_def["description"]
assert isinstance(
desc, str
), f"Tool '{tool_name}' description is not a string"
assert len(desc) > 0, f"Tool '{tool_name}' has empty description"
class TestToolParameterSchemas:
"""Tests for tool parameter schemas."""
def test_all_tools_have_object_type_parameters(self):
"""Test that all tool parameters are defined as type 'object'."""
for tool_name, tool_def in TOOLS.items():
params = tool_def["parameters"]
assert params["type"] == "object", f"Tool '{tool_name}' parameters not type 'object'"
def test_all_tools_have_properties_key(self):
"""Test that all tool parameters have 'properties' key."""
for tool_name, tool_def in TOOLS.items():
params = tool_def["parameters"]
assert (
"properties" in params
), f"Tool '{tool_name}' parameters missing 'properties'"
assert isinstance(params["properties"], dict)
def test_all_tools_have_required_key(self):
"""Test that all tool parameters have 'required' key."""
for tool_name, tool_def in TOOLS.items():
params = tool_def["parameters"]
assert (
"required" in params
), f"Tool '{tool_name}' parameters missing 'required'"
assert isinstance(params["required"], list)
class TestLightControlToolsSchemas:
"""Tests for light control tool schemas."""
def test_list_lights_has_no_required_parameters(self):
"""Test that list_lights requires no parameters."""
schema = TOOLS["list_lights"]["parameters"]
assert schema["required"] == []
assert schema["properties"] == {}
def test_get_light_state_requires_light_id(self):
"""Test that get_light_state requires light_id."""
schema = TOOLS["get_light_state"]["parameters"]
assert "light_id" in schema["required"]
assert "light_id" in schema["properties"]
assert schema["properties"]["light_id"]["type"] == "string"
def test_turn_light_on_requires_light_id(self):
"""Test that turn_light_on requires light_id."""
schema = TOOLS["turn_light_on"]["parameters"]
assert "light_id" in schema["required"]
assert "light_id" in schema["properties"]
def test_turn_light_on_has_optional_brightness(self):
"""Test that turn_light_on has optional brightness parameter."""
schema = TOOLS["turn_light_on"]["parameters"]
assert "brightness" in schema["properties"]
assert "brightness" not in schema["required"]
assert schema["properties"]["brightness"]["type"] == "number"
def test_turn_light_off_requires_light_id(self):
"""Test that turn_light_off requires light_id."""
schema = TOOLS["turn_light_off"]["parameters"]
assert "light_id" in schema["required"]
assert len(schema["required"]) == 1
def test_set_brightness_requires_light_id_and_brightness(self):
"""Test that set_brightness requires both light_id and brightness."""
schema = TOOLS["set_brightness"]["parameters"]
assert "light_id" in schema["required"]
assert "brightness" in schema["required"]
assert len(schema["required"]) == 2
def test_set_brightness_has_correct_range(self):
"""Test that set_brightness has correct min/max for brightness."""
schema = TOOLS["set_brightness"]["parameters"]
brightness_schema = schema["properties"]["brightness"]
assert brightness_schema["minimum"] == 0
assert brightness_schema["maximum"] == 254
def test_set_color_temp_requires_light_id_and_color_temp(self):
"""Test that set_color_temp requires both parameters."""
schema = TOOLS["set_color_temp"]["parameters"]
assert "light_id" in schema["required"]
assert "color_temp" in schema["required"]
def test_set_color_temp_has_correct_range(self):
"""Test that set_color_temp has correct min/max for color_temp."""
schema = TOOLS["set_color_temp"]["parameters"]
color_temp_schema = schema["properties"]["color_temp"]
assert color_temp_schema["minimum"] == 153
assert color_temp_schema["maximum"] == 500
def test_set_color_requires_light_id_and_xy(self):
"""Test that set_color requires both parameters."""
schema = TOOLS["set_color"]["parameters"]
assert "light_id" in schema["required"]
assert "xy" in schema["required"]
def test_set_color_xy_is_array_type(self):
"""Test that set_color xy parameter is defined as array."""
schema = TOOLS["set_color"]["parameters"]
xy_schema = schema["properties"]["xy"]
assert xy_schema["type"] == "array"
assert xy_schema["minItems"] == 2
assert xy_schema["maxItems"] == 2
def test_set_color_xy_items_have_correct_range(self):
"""Test that set_color xy items have correct min/max."""
schema = TOOLS["set_color"]["parameters"]
xy_schema = schema["properties"]["xy"]
items_schema = xy_schema["items"]
assert items_schema["type"] == "number"
assert items_schema["minimum"] == 0
assert items_schema["maximum"] == 1
class TestGroupControlToolsSchemas:
"""Tests for group control tool schemas."""
def test_list_groups_has_no_required_parameters(self):
"""Test that list_groups requires no parameters."""
schema = TOOLS["list_groups"]["parameters"]
assert schema["required"] == []
def test_control_group_requires_group_id(self):
"""Test that control_group requires group_id."""
schema = TOOLS["control_group"]["parameters"]
assert "group_id" in schema["required"]
assert len(schema["required"]) == 1
def test_control_group_has_optional_on_parameter(self):
"""Test that control_group has optional 'on' parameter."""
schema = TOOLS["control_group"]["parameters"]
assert "on" in schema["properties"]
assert "on" not in schema["required"]
assert schema["properties"]["on"]["type"] == "boolean"
def test_control_group_has_optional_brightness(self):
"""Test that control_group has optional brightness parameter."""
schema = TOOLS["control_group"]["parameters"]
assert "brightness" in schema["properties"]
assert "brightness" not in schema["required"]
def test_control_group_has_optional_color_temp(self):
"""Test that control_group has optional color_temp parameter."""
schema = TOOLS["control_group"]["parameters"]
assert "color_temp" in schema["properties"]
assert "color_temp" not in schema["required"]
def test_control_group_has_optional_xy(self):
"""Test that control_group has optional xy parameter."""
schema = TOOLS["control_group"]["parameters"]
assert "xy" in schema["properties"]
assert "xy" not in schema["required"]
class TestSceneControlToolsSchemas:
"""Tests for scene control tool schemas."""
def test_list_scenes_has_no_required_parameters(self):
"""Test that list_scenes requires no parameters."""
schema = TOOLS["list_scenes"]["parameters"]
assert schema["required"] == []
def test_activate_scene_requires_scene_id(self):
"""Test that activate_scene requires scene_id."""
schema = TOOLS["activate_scene"]["parameters"]
assert "scene_id" in schema["required"]
assert len(schema["required"]) == 1
assert "scene_id" in schema["properties"]
class TestToolDescriptions:
"""Tests for tool descriptions quality."""
def test_all_descriptions_are_descriptive(self):
"""Test that all tool descriptions are reasonably descriptive."""
for tool_name, tool_def in TOOLS.items():
desc = tool_def["description"]
# Descriptions should be at least 15 characters (some are naturally brief)
assert (
len(desc) >= 15
), f"Tool '{tool_name}' has very short description: '{desc}'"
def test_parameter_descriptions_exist(self):
"""Test that all parameters have descriptions."""
for tool_name, tool_def in TOOLS.items():
params = tool_def["parameters"]
for param_name, param_def in params.get("properties", {}).items():
assert (
"description" in param_def
), f"Parameter '{param_name}' in tool '{tool_name}' missing description"
assert (
len(param_def["description"]) > 0
), f"Parameter '{param_name}' in tool '{tool_name}' has empty description"
class TestSchemaConsistency:
"""Tests for consistency across tool schemas."""
def test_all_light_id_parameters_are_consistent(self):
"""Test that all light_id parameters have consistent definition."""
tools_with_light_id = [
"get_light_state",
"turn_light_on",
"turn_light_off",
"set_brightness",
"set_color_temp",
"set_color",
]
for tool_name in tools_with_light_id:
schema = TOOLS[tool_name]["parameters"]
light_id_schema = schema["properties"]["light_id"]
assert light_id_schema["type"] == "string"
assert "description" in light_id_schema
def test_all_brightness_parameters_have_same_range(self):
"""Test that all brightness parameters have consistent range."""
tools_with_brightness = ["turn_light_on", "set_brightness", "control_group"]
for tool_name in tools_with_brightness:
schema = TOOLS[tool_name]["parameters"]
brightness_schema = schema["properties"]["brightness"]
assert brightness_schema["type"] == "number"
assert brightness_schema["minimum"] == 0
assert brightness_schema["maximum"] == 254
def test_all_color_temp_parameters_have_same_range(self):
"""Test that all color_temp parameters have consistent range."""
tools_with_color_temp = ["set_color_temp", "control_group"]
for tool_name in tools_with_color_temp:
schema = TOOLS[tool_name]["parameters"]
color_temp_schema = schema["properties"]["color_temp"]
assert color_temp_schema["type"] == "number"
assert color_temp_schema["minimum"] == 153
assert color_temp_schema["maximum"] == 500
def test_all_xy_parameters_have_same_structure(self):
"""Test that all xy parameters have consistent structure."""
tools_with_xy = ["set_color", "control_group"]
for tool_name in tools_with_xy:
schema = TOOLS[tool_name]["parameters"]
xy_schema = schema["properties"]["xy"]
assert xy_schema["type"] == "array"
assert xy_schema["minItems"] == 2
assert xy_schema["maxItems"] == 2
assert xy_schema["items"]["type"] == "number"
assert xy_schema["items"]["minimum"] == 0
assert xy_schema["items"]["maximum"] == 1