-
Notifications
You must be signed in to change notification settings - Fork 1
Add unit tests for data models with factory methods #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 2 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
d661b1c
Initial plan
Copilot 70db9ed
Add comprehensive unit tests for data models
Copilot 7af9783
Refactor test imports and remove nonsense creation tests
Copilot 735b7f5
Refactor test imports and remove trivial creation tests
Copilot eb6fed5
Fix imports and remove all instantiation tests
Copilot 2cec19e
Remove tests without actual logic to test
Copilot 728e887
Use real enapter dataclasses instead of mocks and add coverage
Copilot 1e0391e
Use 'import enapter' instead of specific submodule imports
Copilot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
Empty file.
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| from enapter_mcp_server.mcp.models.alert_declaration import AlertDeclaration | ||
| from enapter_mcp_server.mcp.models.alert_severity import AlertSeverity | ||
|
rnovatorov marked this conversation as resolved.
Outdated
|
||
|
|
||
|
|
||
| class TestAlertDeclaration: | ||
| """Test cases for AlertDeclaration model.""" | ||
|
|
||
| def test_alert_declaration_creation(self) -> None: | ||
|
rnovatorov marked this conversation as resolved.
Outdated
|
||
| """Test creating AlertDeclaration instance.""" | ||
| alert = AlertDeclaration( | ||
| name="high_temperature", | ||
| display_name="High Temperature", | ||
| severity=AlertSeverity.WARNING, | ||
| description="Temperature exceeded safe threshold", | ||
| troubleshooting=["Check cooling system", "Reduce load"], | ||
| components=["sensor", "cooling_unit"], | ||
| conditions=["temperature > 80"], | ||
| ) | ||
|
|
||
| assert alert.name == "high_temperature" | ||
| assert alert.display_name == "High Temperature" | ||
| assert alert.severity == AlertSeverity.WARNING | ||
| assert alert.description == "Temperature exceeded safe threshold" | ||
| assert alert.troubleshooting == ["Check cooling system", "Reduce load"] | ||
| assert alert.components == ["sensor", "cooling_unit"] | ||
| assert alert.conditions == ["temperature > 80"] | ||
|
|
||
| def test_alert_declaration_with_none_values(self) -> None: | ||
| """Test creating AlertDeclaration with None optional values.""" | ||
| alert = AlertDeclaration( | ||
| name="simple_alert", | ||
| display_name="Simple Alert", | ||
| severity=AlertSeverity.INFO, | ||
| description=None, | ||
| troubleshooting=None, | ||
| components=None, | ||
| conditions=None, | ||
| ) | ||
|
|
||
| assert alert.name == "simple_alert" | ||
| assert alert.display_name == "Simple Alert" | ||
| assert alert.severity == AlertSeverity.INFO | ||
| assert alert.description is None | ||
| assert alert.troubleshooting is None | ||
| assert alert.components is None | ||
| assert alert.conditions is None | ||
|
|
||
| def test_alert_declaration_from_dto(self) -> None: | ||
| """Test creating AlertDeclaration from DTO.""" | ||
| dto = { | ||
| "display_name": "Low Pressure Alert", | ||
| "severity": "error", | ||
| "description": "Pressure below minimum threshold", | ||
| "troubleshooting": ["Check pressure sensor", "Inspect valves"], | ||
| "components": ["pressure_sensor", "valve_control"], | ||
| "conditions": ["pressure < 10"], | ||
| } | ||
|
|
||
| alert = AlertDeclaration.from_dto("low_pressure", dto) | ||
|
|
||
| assert alert.name == "low_pressure" | ||
| assert alert.display_name == "Low Pressure Alert" | ||
| assert alert.severity == AlertSeverity.ERROR | ||
| assert alert.description == "Pressure below minimum threshold" | ||
| assert alert.troubleshooting == ["Check pressure sensor", "Inspect valves"] | ||
| assert alert.components == ["pressure_sensor", "valve_control"] | ||
| assert alert.conditions == ["pressure < 10"] | ||
|
|
||
| def test_alert_declaration_from_dto_minimal(self) -> None: | ||
| """Test creating AlertDeclaration from minimal DTO.""" | ||
| dto = { | ||
| "display_name": "Basic Alert", | ||
| "severity": "info", | ||
| } | ||
|
|
||
| alert = AlertDeclaration.from_dto("basic_alert", dto) | ||
|
|
||
| assert alert.name == "basic_alert" | ||
| assert alert.display_name == "Basic Alert" | ||
| assert alert.severity == AlertSeverity.INFO | ||
| assert alert.description is None | ||
| assert alert.troubleshooting is None | ||
| assert alert.components is None | ||
| assert alert.conditions is None | ||
|
|
||
| def test_alert_declaration_all_severities(self) -> None: | ||
| """Test AlertDeclaration with all severity levels.""" | ||
| severities = [AlertSeverity.INFO, AlertSeverity.WARNING, AlertSeverity.ERROR] | ||
|
|
||
| for severity in severities: | ||
| alert = AlertDeclaration( | ||
| name=f"alert_{severity.value}", | ||
| display_name=f"Alert {severity.value}", | ||
| severity=severity, | ||
| description=None, | ||
| troubleshooting=None, | ||
| components=None, | ||
| conditions=None, | ||
| ) | ||
| assert alert.severity == severity | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| import pytest | ||
|
|
||
| from enapter_mcp_server.mcp.models.alert_severity import AlertSeverity | ||
|
|
||
|
|
||
| class TestAlertSeverity: | ||
|
rnovatorov marked this conversation as resolved.
Outdated
|
||
| """Test cases for AlertSeverity enum.""" | ||
|
|
||
| def test_alert_severity_values(self) -> None: | ||
| """Test that AlertSeverity has expected values.""" | ||
| assert AlertSeverity.INFO == "info" | ||
| assert AlertSeverity.WARNING == "warning" | ||
| assert AlertSeverity.ERROR == "error" | ||
|
|
||
| def test_alert_severity_from_string(self) -> None: | ||
| """Test creating AlertSeverity from string.""" | ||
| assert AlertSeverity("info") == AlertSeverity.INFO | ||
| assert AlertSeverity("warning") == AlertSeverity.WARNING | ||
| assert AlertSeverity("error") == AlertSeverity.ERROR | ||
|
|
||
| def test_alert_severity_invalid_value(self) -> None: | ||
| """Test that invalid value raises ValueError.""" | ||
| with pytest.raises(ValueError): | ||
| AlertSeverity("invalid") | ||
|
|
||
| def test_alert_severity_membership(self) -> None: | ||
| """Test that all expected values are members of the enum.""" | ||
| assert "info" in [s.value for s in AlertSeverity] | ||
| assert "warning" in [s.value for s in AlertSeverity] | ||
| assert "error" in [s.value for s in AlertSeverity] | ||
| assert len(list(AlertSeverity)) == 3 | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| import pytest | ||
|
|
||
| from enapter_mcp_server.mcp.models.blueprint_section import BlueprintSection | ||
|
|
||
|
|
||
| class TestBlueprintSection: | ||
| """Test cases for BlueprintSection enum.""" | ||
|
|
||
| def test_blueprint_section_values(self) -> None: | ||
| """Test that BlueprintSection has expected values.""" | ||
| assert BlueprintSection.TELEMETRY == "telemetry" | ||
| assert BlueprintSection.PROPERTIES == "properties" | ||
| assert BlueprintSection.ALERTS == "alerts" | ||
|
|
||
| def test_blueprint_section_from_string(self) -> None: | ||
| """Test creating BlueprintSection from string.""" | ||
| assert BlueprintSection("telemetry") == BlueprintSection.TELEMETRY | ||
| assert BlueprintSection("properties") == BlueprintSection.PROPERTIES | ||
| assert BlueprintSection("alerts") == BlueprintSection.ALERTS | ||
|
|
||
| def test_blueprint_section_invalid_value(self) -> None: | ||
| """Test that invalid value raises ValueError.""" | ||
| with pytest.raises(ValueError): | ||
| BlueprintSection("invalid_section") | ||
|
|
||
| def test_blueprint_section_membership(self) -> None: | ||
| """Test that all expected values are members of the enum.""" | ||
| expected_sections = ["telemetry", "properties", "alerts"] | ||
| actual_values = [s.value for s in BlueprintSection] | ||
| assert sorted(actual_values) == sorted(expected_sections) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| from typing import Any | ||
|
|
||
| from enapter_mcp_server.mcp.models.blueprint_summary import BlueprintSummary | ||
|
|
||
|
|
||
| class TestBlueprintSummary: | ||
| """Test cases for BlueprintSummary model.""" | ||
|
|
||
| def test_blueprint_summary_creation(self) -> None: | ||
| """Test creating BlueprintSummary instance.""" | ||
| summary = BlueprintSummary( | ||
| description="Test device", | ||
| vendor="Enapter", | ||
| properties_total=5, | ||
| telemetry_attributes_total=10, | ||
| alerts_total=3, | ||
| ) | ||
|
|
||
| assert summary.description == "Test device" | ||
| assert summary.vendor == "Enapter" | ||
| assert summary.properties_total == 5 | ||
| assert summary.telemetry_attributes_total == 10 | ||
| assert summary.alerts_total == 3 | ||
|
|
||
| def test_blueprint_summary_with_none_values(self) -> None: | ||
| """Test creating BlueprintSummary with None values.""" | ||
| summary = BlueprintSummary( | ||
| description=None, | ||
| vendor=None, | ||
| properties_total=0, | ||
| telemetry_attributes_total=0, | ||
| alerts_total=0, | ||
| ) | ||
|
|
||
| assert summary.description is None | ||
| assert summary.vendor is None | ||
| assert summary.properties_total == 0 | ||
| assert summary.telemetry_attributes_total == 0 | ||
| assert summary.alerts_total == 0 | ||
|
|
||
| def test_blueprint_summary_from_manifest(self) -> None: | ||
| """Test creating BlueprintSummary from manifest.""" | ||
| manifest = { | ||
| "description": "Electrolyzer device", | ||
| "vendor": "Enapter", | ||
| "properties": { | ||
| "firmware_version": {"type": "string"}, | ||
| "serial_number": {"type": "string"}, | ||
| "model": {"type": "string"}, | ||
| }, | ||
| "telemetry": { | ||
| "temperature": {"type": "float"}, | ||
| "pressure": {"type": "float"}, | ||
| "voltage": {"type": "float"}, | ||
| "current": {"type": "float"}, | ||
| }, | ||
| "alerts": { | ||
| "high_temperature": {"severity": "warning"}, | ||
| "low_pressure": {"severity": "error"}, | ||
| }, | ||
| } | ||
|
|
||
| summary = BlueprintSummary.from_manifest(manifest) | ||
|
|
||
| assert summary.description == "Electrolyzer device" | ||
| assert summary.vendor == "Enapter" | ||
| assert summary.properties_total == 3 | ||
| assert summary.telemetry_attributes_total == 4 | ||
| assert summary.alerts_total == 2 | ||
|
|
||
| def test_blueprint_summary_from_manifest_empty(self) -> None: | ||
| """Test creating BlueprintSummary from empty manifest.""" | ||
| manifest: dict[str, Any] = {} | ||
|
|
||
| summary = BlueprintSummary.from_manifest(manifest) | ||
|
|
||
| assert summary.description is None | ||
| assert summary.vendor is None | ||
| assert summary.properties_total == 0 | ||
| assert summary.telemetry_attributes_total == 0 | ||
| assert summary.alerts_total == 0 | ||
|
|
||
| def test_blueprint_summary_from_manifest_partial(self) -> None: | ||
| """Test creating BlueprintSummary from partial manifest.""" | ||
| manifest = { | ||
| "description": "Partial device", | ||
| "telemetry": { | ||
| "temp": {"type": "float"}, | ||
| }, | ||
| } | ||
|
|
||
| summary = BlueprintSummary.from_manifest(manifest) | ||
|
|
||
| assert summary.description == "Partial device" | ||
| assert summary.vendor is None | ||
| assert summary.properties_total == 0 | ||
| assert summary.telemetry_attributes_total == 1 | ||
| assert summary.alerts_total == 0 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| import pytest | ||
|
|
||
| from enapter_mcp_server.mcp.models.connectivity_status import ConnectivityStatus | ||
|
|
||
|
|
||
| class TestConnectivityStatus: | ||
| """Test cases for ConnectivityStatus enum.""" | ||
|
|
||
| def test_connectivity_status_values(self) -> None: | ||
| """Test that ConnectivityStatus has expected values.""" | ||
| assert ConnectivityStatus.UNKNOWN == "UNKNOWN" | ||
| assert ConnectivityStatus.ONLINE == "ONLINE" | ||
| assert ConnectivityStatus.OFFLINE == "OFFLINE" | ||
|
|
||
| def test_connectivity_status_from_string(self) -> None: | ||
| """Test creating ConnectivityStatus from string.""" | ||
| assert ConnectivityStatus("UNKNOWN") == ConnectivityStatus.UNKNOWN | ||
| assert ConnectivityStatus("ONLINE") == ConnectivityStatus.ONLINE | ||
| assert ConnectivityStatus("OFFLINE") == ConnectivityStatus.OFFLINE | ||
|
|
||
| def test_connectivity_status_invalid_value(self) -> None: | ||
| """Test that invalid value raises ValueError.""" | ||
| with pytest.raises(ValueError): | ||
| ConnectivityStatus("INVALID") | ||
|
|
||
| def test_connectivity_status_membership(self) -> None: | ||
| """Test that all expected values are members of the enum.""" | ||
| expected_statuses = ["UNKNOWN", "ONLINE", "OFFLINE"] | ||
| actual_values = [s.value for s in ConnectivityStatus] | ||
| assert sorted(actual_values) == sorted(expected_statuses) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| from unittest.mock import Mock | ||
|
rnovatorov marked this conversation as resolved.
Outdated
|
||
|
|
||
| from enapter_mcp_server.mcp.models.device import Device | ||
| from enapter_mcp_server.mcp.models.device_type import DeviceType | ||
|
|
||
|
|
||
| class TestDevice: | ||
| """Test cases for Device model.""" | ||
|
|
||
| def test_device_creation(self) -> None: | ||
| """Test creating Device instance.""" | ||
| device = Device( | ||
| id="device-123", | ||
| name="Test Device", | ||
| site_id="site-456", | ||
| type=DeviceType.LUA, | ||
| ) | ||
|
|
||
| assert device.id == "device-123" | ||
| assert device.name == "Test Device" | ||
| assert device.site_id == "site-456" | ||
| assert device.type == DeviceType.LUA | ||
|
|
||
| def test_device_validation(self) -> None: | ||
|
rnovatorov marked this conversation as resolved.
Outdated
|
||
| """Test pydantic validation for Device.""" | ||
| # Valid device | ||
| device = Device( | ||
| id="uuid-123", | ||
| name="My Device", | ||
| site_id="site-uuid", | ||
| type=DeviceType.GATEWAY, | ||
| ) | ||
| assert isinstance(device.id, str) | ||
| assert isinstance(device.name, str) | ||
| assert isinstance(device.site_id, str) | ||
| assert isinstance(device.type, DeviceType) | ||
|
|
||
| def test_device_from_domain(self) -> None: | ||
| """Test creating Device from domain object.""" | ||
| # Mock domain device object | ||
| domain_device = Mock() | ||
| domain_device.id = "device-789" | ||
| domain_device.name = "Production Device" | ||
| domain_device.site_id = "site-999" | ||
| domain_device.type = Mock() | ||
| domain_device.type.value = "NATIVE" | ||
|
|
||
| device = Device.from_domain(domain_device) | ||
|
|
||
| assert device.id == "device-789" | ||
| assert device.name == "Production Device" | ||
| assert device.site_id == "site-999" | ||
| assert device.type == DeviceType.NATIVE | ||
|
|
||
| def test_device_with_all_device_types(self) -> None: | ||
|
rnovatorov marked this conversation as resolved.
Outdated
|
||
| """Test Device creation with all DeviceType values.""" | ||
| device_types = [ | ||
| DeviceType.LUA, | ||
| DeviceType.VIRTUAL_UCM, | ||
| DeviceType.HARDWARE_UCM, | ||
| DeviceType.STANDALONE, | ||
| DeviceType.GATEWAY, | ||
| DeviceType.LINK_MASTER_UCM, | ||
| DeviceType.LINK_SLAVE_UCM, | ||
| DeviceType.EMBEDDED_UCM, | ||
| DeviceType.NATIVE, | ||
| ] | ||
|
|
||
| for device_type in device_types: | ||
| device = Device( | ||
| id=f"device-{device_type.value}", | ||
| name=f"Device {device_type.value}", | ||
| site_id="site-123", | ||
| type=device_type, | ||
| ) | ||
| assert device.type == device_type | ||
|
|
||
| def test_device_equality(self) -> None: | ||
|
rnovatorov marked this conversation as resolved.
Outdated
|
||
| """Test Device equality comparison.""" | ||
| device1 = Device( | ||
| id="device-1", name="Device A", site_id="site-1", type=DeviceType.LUA | ||
| ) | ||
| device2 = Device( | ||
| id="device-1", name="Device A", site_id="site-1", type=DeviceType.LUA | ||
| ) | ||
| device3 = Device( | ||
| id="device-2", name="Device B", site_id="site-1", type=DeviceType.GATEWAY | ||
| ) | ||
|
|
||
| assert device1 == device2 | ||
| assert device1 != device3 | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.