Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/enapter_mcp_server/http/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from .enapter_api import EnapterAPI
from .enapter_data_mapper import EnapterDataMapper

__all__ = ["EnapterAPI", "EnapterDataMapper"]
__all__ = ["EnapterAPI"]
9 changes: 5 additions & 4 deletions tests/unit/http/test_enapter_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
import enapter
import pytest

from enapter_mcp_server import core, domain, http
from enapter_mcp_server import core, domain
from enapter_mcp_server.http.enapter_api import EnapterAPI


class FailingTelemetryClient:
Expand All @@ -24,7 +25,7 @@ def __init__(self) -> None:
self.telemetry = FailingTelemetryClient()


class StubEnapterAPI(http.EnapterAPI):
class StubEnapterAPI(EnapterAPI):
@contextlib.asynccontextmanager
async def _new_client(
self, auth: core.AuthConfig
Expand Down Expand Up @@ -100,7 +101,7 @@ def __init__(self, commands: _SpyCommandsClient) -> None:
self.commands = commands


class _CommandStubEnapterAPI(http.EnapterAPI):
class _CommandStubEnapterAPI(EnapterAPI):
"""Stub that injects a fake `commands` client for execute_command tests."""

def __init__(self, base_url: str, commands: _SpyCommandsClient) -> None:
Expand Down Expand Up @@ -164,7 +165,7 @@ def _make_execution(


class TestExecuteCommand:
"""Unit tests for http.EnapterAPI.execute_command."""
"""Unit tests for EnapterAPI.execute_command."""

# -- helpers -----------------------------------------------------------

Expand Down
45 changes: 23 additions & 22 deletions tests/unit/http/test_enapter_data_mapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

import enapter

from enapter_mcp_server import domain, http
from enapter_mcp_server import domain
from enapter_mcp_server.http.enapter_data_mapper import EnapterDataMapper


class TestEnapterDataMapper:
def test_parse_device_manifest(self) -> None:
manifest = http.EnapterDataMapper().to_device_manifest(
manifest = EnapterDataMapper().to_device_manifest(
{
"description": "Electrolyzer device",
"vendor": "Enapter",
Expand Down Expand Up @@ -59,7 +60,7 @@ def test_parse_device_manifest(self) -> None:
assert manifest.commands["c1"].access_level == domain.AccessRole.USER

def test_parse_device_manifest_implements_list(self) -> None:
manifest = http.EnapterDataMapper().to_device_manifest(
manifest = EnapterDataMapper().to_device_manifest(
{
"description": "Electrolyzer device",
"vendor": "Enapter",
Expand All @@ -71,7 +72,7 @@ def test_parse_device_manifest_implements_list(self) -> None:
assert manifest.implements == ["energy.battery", "energy.inverter"]

def test_parse_device_manifest_missing_sections(self) -> None:
manifest = http.EnapterDataMapper().to_device_manifest({})
manifest = EnapterDataMapper().to_device_manifest({})

assert manifest is not None
assert manifest.description is None
Expand All @@ -83,13 +84,13 @@ def test_parse_device_manifest_missing_sections(self) -> None:
assert manifest.commands == {}

def test_parse_device_manifest_implements_null(self) -> None:
manifest = http.EnapterDataMapper().to_device_manifest({"implements": None})
manifest = EnapterDataMapper().to_device_manifest({"implements": None})
assert manifest is not None
assert manifest.implements == []

def test_parse_device_manifest_raises_on_invalid_payload(self) -> None:
try:
http.EnapterDataMapper().to_device_manifest(
EnapterDataMapper().to_device_manifest(
{
"properties": {
"p1": {
Expand All @@ -104,7 +105,7 @@ def test_parse_device_manifest_raises_on_invalid_payload(self) -> None:
raise AssertionError("Expected manifest parsing to fail")

def test_parse_device_manifest_explicit_access_level(self) -> None:
manifest = http.EnapterDataMapper().to_device_manifest(
manifest = EnapterDataMapper().to_device_manifest(
{
"properties": {
"p1": {
Expand Down Expand Up @@ -136,7 +137,7 @@ def test_parse_device_manifest_explicit_access_level(self) -> None:

def test_parse_device_manifest_access_level_null(self) -> None:
"""Explicit null access_level should fall back to defaults."""
manifest = http.EnapterDataMapper().to_device_manifest(
manifest = EnapterDataMapper().to_device_manifest(
{
"properties": {
"p1": {
Expand Down Expand Up @@ -171,7 +172,7 @@ def test_parse_device_manifest_access_level_null(self) -> None:

def test_parse_device_manifest_maps_implements(self) -> None:
"""Per-declaration `implements` is mapped for telemetry, properties, commands."""
manifest = http.EnapterDataMapper().to_device_manifest(
manifest = EnapterDataMapper().to_device_manifest(
{
"properties": {
"p1": {
Expand Down Expand Up @@ -205,7 +206,7 @@ def test_parse_device_manifest_maps_implements(self) -> None:

def test_parse_device_manifest_implements_absent_is_empty(self) -> None:
"""When `implements` key is absent, the field is an empty list."""
manifest = http.EnapterDataMapper().to_device_manifest(
manifest = EnapterDataMapper().to_device_manifest(
{
"properties": {
"p1": {
Expand Down Expand Up @@ -234,7 +235,7 @@ def test_parse_device_manifest_implements_absent_is_empty(self) -> None:

def test_to_latest_telemetry(self) -> None:
timestamp = datetime.datetime.now()
telemetry = http.EnapterDataMapper().to_latest_telemetry(
telemetry = EnapterDataMapper().to_latest_telemetry(
{
"dev-1": {
"alerts": enapter.http.api.telemetry.LatestDatapoint(
Expand All @@ -258,7 +259,7 @@ def test_to_latest_telemetry(self) -> None:

def test_to_historical_telemetry(self) -> None:
timestamp = datetime.datetime.now()
telemetry = http.EnapterDataMapper().to_historical_telemetry(
telemetry = EnapterDataMapper().to_historical_telemetry(
enapter.http.api.telemetry.WideTimeseries(
timestamps=[timestamp],
columns=[
Expand Down Expand Up @@ -287,7 +288,7 @@ def test_to_site_dto(self) -> None:
authorized_role=enapter.http.api.AccessRole.OWNER,
)

dto = http.EnapterDataMapper().to_site_dto(site)
dto = EnapterDataMapper().to_site_dto(site)

assert dto.id == "site-1"
assert dto.name == "Site 1"
Expand All @@ -303,7 +304,7 @@ def test_to_site_dto_authorized_role(self) -> None:
authorized_role=enapter.http.api.AccessRole.READONLY,
)

dto = http.EnapterDataMapper().to_site_dto(site)
dto = EnapterDataMapper().to_site_dto(site)

assert dto.authorized_role == domain.AccessRole.READONLY

Expand All @@ -320,7 +321,7 @@ def test_to_device_dto_null_alerts_mapped_to_empty_list(self) -> None:
raised_alert_names=None,
)

dto = http.EnapterDataMapper().to_device_dto(device)
dto = EnapterDataMapper().to_device_dto(device)

assert dto.active_alerts == []
assert dto.authorized_role == domain.AccessRole.USER
Expand All @@ -337,7 +338,7 @@ def test_to_device_dto_authorized_role(self) -> None:
authorized_role=enapter.http.api.AccessRole.OWNER,
)

dto = http.EnapterDataMapper().to_device_dto(device)
dto = EnapterDataMapper().to_device_dto(device)

assert dto.authorized_role == domain.AccessRole.OWNER

Expand All @@ -353,7 +354,7 @@ def test_to_device_dto_blueprint_id(self) -> None:
authorized_role=enapter.http.api.AccessRole.USER,
)

dto = http.EnapterDataMapper().to_device_dto(device)
dto = EnapterDataMapper().to_device_dto(device)

assert dto.blueprint_id == "bp-3"

Expand All @@ -376,7 +377,7 @@ def test_to_command_execution(self) -> None:
log=None,
)

mapped = http.EnapterDataMapper().to_command_execution(execution)
mapped = EnapterDataMapper().to_command_execution(execution)

assert mapped.id == "exec-1"
assert mapped.device_id == "dev-1"
Expand All @@ -387,7 +388,7 @@ def test_to_command_execution(self) -> None:
assert mapped.response_payload == {"status": "ok"}

def test_command_declaration_maps_confirmation_when_present(self) -> None:
manifest = http.EnapterDataMapper().to_device_manifest(
manifest = EnapterDataMapper().to_device_manifest(
{
"commands": {
"reboot": {
Expand All @@ -410,7 +411,7 @@ def test_command_declaration_maps_confirmation_when_present(self) -> None:
assert confirmation.description == "Restarts the device."

def test_command_declaration_confirmation_absent_is_none(self) -> None:
manifest = http.EnapterDataMapper().to_device_manifest(
manifest = EnapterDataMapper().to_device_manifest(
{
"commands": {
"status": {
Expand All @@ -424,7 +425,7 @@ def test_command_declaration_confirmation_absent_is_none(self) -> None:
assert manifest.commands["status"].confirmation is None

def test_command_declaration_confirmation_null_value_is_none(self) -> None:
manifest = http.EnapterDataMapper().to_device_manifest(
manifest = EnapterDataMapper().to_device_manifest(
{
"commands": {
"reboot": {
Expand All @@ -439,7 +440,7 @@ def test_command_declaration_confirmation_null_value_is_none(self) -> None:
assert manifest.commands["reboot"].confirmation is None

def test_command_declaration_confirmation_partial_block_is_defensive(self) -> None:
manifest = http.EnapterDataMapper().to_device_manifest(
manifest = EnapterDataMapper().to_device_manifest(
{
"commands": {
"reboot": {
Expand Down
Loading