"""
Unit tests for plugin_config module in the configuration system.
Tests the PluginConfig class for managing Commitizen plugins.
"""
import pytest
from unittest.mock import patch, MagicMock
from commit_helper_mcp.config.plugin_config import PluginConfig
class TestPluginConfig:
"""Test suite for PluginConfig class."""
@patch("commitizen.factory.committer_factory")
def test_plugin_config_initialization(self, mock_get_by_name):
"""Test PluginConfig initialization with valid plugin."""
# Mock plugin
mock_plugin = MagicMock()
mock_plugin.__class__ = MagicMock()
mock_plugin.__class__.__name__ = "MockCommitizen"
mock_plugin.name = "cz_conventional_commits"
mock_plugin.schema = {"properties": {"type": {"type": "string"}}}
mock_plugin.example = "feat: example message"
mock_plugin.message_pattern = (
r"^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?: .+"
)
mock_plugin.bump_pattern = (
r"^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?: .+"
)
mock_plugin.bump_map = {"feat": "minor", "fix": "patch"}
mock_plugin.questions = [{"name": "type", "type": "list"}]
mock_get_by_name.return_value = mock_plugin
# Initialize with plugin name
config = PluginConfig("cz_conventional_commits")
# Should initialize correctly
assert config.plugin_name == "cz_conventional_commits"
assert config.plugin_class is mock_plugin.__class__
assert config.is_valid()
# Should detect capabilities
assert config.capabilities["has_questions"] is True
assert config.capabilities["has_message"] is True
assert config.capabilities["has_pattern"] is True
assert config.capabilities["has_example"] is True
assert config.capabilities["has_schema"] is True
assert config.capabilities["has_bump_pattern"] is True
assert config.capabilities["has_bump_map"] is True
@patch("commitizen.factory.committer_factory")
def test_plugin_config_invalid_plugin(self, mock_get_by_name):
"""Test PluginConfig with invalid plugin name."""
# Mock plugin not found
mock_get_by_name.side_effect = ImportError("Plugin not found")
# Initialize with invalid plugin name
config = PluginConfig("nonexistent_plugin")
# Should handle invalid plugin gracefully
assert config.plugin_name == "nonexistent_plugin"
assert config.plugin_class is None
assert not config.is_valid()
# Capabilities should all be False
for capability in config.capabilities.values():
assert capability is False
@patch("commitizen.factory.committer_factory")
def test_plugin_config_partial_capabilities(self, mock_get_by_name):
"""Test PluginConfig with plugin that has partial capabilities."""
# Mock plugin with partial capabilities
mock_plugin = MagicMock()
mock_plugin.__class__ = MagicMock()
mock_plugin.__class__.__name__ = "MockCommitizen"
# Configure __class__ to not have certain attributes
delattr(mock_plugin.__class__, "pattern")
delattr(mock_plugin.__class__, "schema")
delattr(mock_plugin.__class__, "schema_pattern")
delattr(mock_plugin.__class__, "bump_pattern")
delattr(mock_plugin.__class__, "bump_map")
mock_plugin.name = "partial_plugin"
mock_plugin.example = "example message"
# No schema, pattern, bump_pattern, or bump_map attributes
mock_plugin.questions = [{"name": "message", "type": "input"}]
mock_get_by_name.return_value = mock_plugin
# Initialize with plugin name
config = PluginConfig("partial_plugin")
# Should initialize correctly
assert config.plugin_name == "partial_plugin"
assert config.plugin_class is mock_plugin.__class__
assert config.is_valid()
# Should detect partial capabilities
assert config.capabilities["has_questions"] is True
assert config.capabilities["has_message"] is True # Inferred from questions
assert config.capabilities["has_pattern"] is False
assert config.capabilities["has_example"] is True
assert config.capabilities["has_schema"] is False
assert config.capabilities["has_bump_pattern"] is False
assert config.capabilities["has_bump_map"] is False
@patch("commitizen.factory.committer_factory")
def test_get_schema(self, mock_get_by_name):
"""Test get_schema method."""
# Mock plugin with schema
mock_plugin = MagicMock()
mock_plugin.__class__ = MagicMock()
mock_plugin.__class__.__name__ = "MockCommitizen"
mock_plugin.schema = {"properties": {"type": {"type": "string"}}}
mock_get_by_name.return_value = mock_plugin
# Initialize with plugin name
config = PluginConfig("cz_conventional_commits")
# Get schema through to_dict method
plugin_dict = config.to_dict()
# Should include schema in the dictionary
assert "plugin_class_name" in plugin_dict
assert plugin_dict["plugin_class_name"] is not None
# Test with no schema
mock_plugin.schema = None
plugin_dict = config.to_dict()
# Should still have plugin_class_name
assert "plugin_class_name" in plugin_dict
@patch("commitizen.factory.committer_factory")
def test_get_example(self, mock_get_by_name):
"""Test get_example method."""
# Mock plugin with example and required attributes for validity
mock_plugin = MagicMock()
mock_plugin.__class__ = MagicMock()
mock_plugin.__class__.__name__ = "MockCommitizen"
mock_plugin.name = "cz_conventional_commits"
mock_plugin.example = "feat: example message"
mock_plugin.questions = [{"name": "type", "type": "list"}]
mock_plugin.pattern = r"^(feat|fix)(\(.+\))?: .+"
mock_get_by_name.return_value = mock_plugin
# Mock adapter class
with patch(
"commit_helper_mcp.plugin_adapters.PluginAdapterFactory.create_adapter"
) as mock_adapter:
mock_adapter.return_value = MagicMock()
# Initialize with plugin name
config = PluginConfig("cz_conventional_commits")
# Get example through to_dict method
plugin_dict = config.to_dict()
# Should include example in the dictionary
assert plugin_dict["is_valid"] is True
# Test with no example
mock_plugin.example = None
plugin_dict = config.to_dict()
# Should still be valid
assert plugin_dict["is_valid"] is True
@patch("commitizen.factory.committer_factory")
def test_get_questions(self, mock_get_by_name):
"""Test get_questions method."""
# Mock plugin with questions and required attributes for validity
mock_plugin = MagicMock()
mock_plugin.__class__ = MagicMock()
mock_plugin.__class__.__name__ = "MockCommitizen"
mock_plugin.name = "cz_conventional_commits"
mock_plugin.pattern = r"^(feat|fix)(\(.+\))?: .+"
mock_plugin.questions = [
{"name": "type", "type": "list", "choices": ["feat", "fix"]},
{"name": "subject", "type": "input"},
]
mock_get_by_name.return_value = mock_plugin
# Mock adapter class
with patch(
"commit_helper_mcp.plugin_adapters.PluginAdapterFactory.create_adapter"
) as mock_adapter:
mock_adapter.return_value = MagicMock()
# Initialize with plugin name
config = PluginConfig("cz_conventional_commits")
# Get questions through to_dict method
plugin_dict = config.to_dict()
# Should be valid with questions
assert plugin_dict["is_valid"] is True
# Test with no questions
mock_plugin.questions = None
plugin_dict = config.to_dict()
# Should still be valid
assert plugin_dict["is_valid"] is True
@patch("commitizen.factory.committer_factory")
def test_validate_message(self, mock_get_by_name):
"""Test validate_message method."""
# Mock plugin with pattern and required attributes for validity
mock_plugin = MagicMock()
mock_plugin.__class__ = MagicMock()
mock_plugin.__class__.__name__ = "MockCommitizen"
mock_plugin.name = "cz_conventional_commits"
mock_plugin.pattern = r"^(feat|fix)(\(.+\))?: .+"
mock_plugin.questions = [{"name": "type", "type": "list"}]
mock_get_by_name.return_value = mock_plugin
# Mock adapter class
with patch(
"commit_helper_mcp.plugin_adapters.PluginAdapterFactory.create_adapter"
) as mock_adapter:
mock_adapter.return_value = MagicMock()
# Initialize with plugin name
config = PluginConfig("cz_conventional_commits")
# Get validation pattern
plugin_dict = config.to_dict()
# Should be valid
assert plugin_dict["is_valid"] is True
# Should have validation pattern
assert "validation_pattern" in plugin_dict
assert plugin_dict["validation_pattern"] is not None
# Test with no pattern
mock_plugin.pattern = None
# Should still be valid
plugin_dict = config.to_dict()
assert plugin_dict["is_valid"] is True