Skip to content

Commit 98696ee

Browse files
committed
Implement GetMicrogridMetadata
We call the `MicrogridApiClient` method `get_microgrid_info()` to be more forward-compatible with upcoming changes. Signed-off-by: Leandro Lucarella <[email protected]>
1 parent e66fce5 commit 98696ee

File tree

5 files changed

+200
-1
lines changed

5 files changed

+200
-1
lines changed

src/frequenz/client/microgrid/_client.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,13 @@
1212

1313
from frequenz.api.microgrid.v1 import microgrid_pb2_grpc
1414
from frequenz.client.base import channel, client, retry, streaming
15+
from google.protobuf.empty_pb2 import Empty
1516
from typing_extensions import override
1617

1718
from ._exception import ClientNotConnected
1819
from ._id import ComponentId
20+
from ._microgrid_info import MicrogridInfo
21+
from ._microgrid_info_proto import microgrid_info_from_proto
1922

2023
DEFAULT_GRPC_CALL_TIMEOUT = 60.0
2124
"""The default timeout for gRPC calls made by this client (in seconds)."""
@@ -122,3 +125,31 @@ async def __aexit__(
122125
"Error while disconnecting from the microgrid API", exceptions
123126
)
124127
return result
128+
129+
async def get_microgrid_info( # noqa: DOC502 (raises ApiClientError indirectly)
130+
self,
131+
) -> MicrogridInfo:
132+
"""Fetch the information about the local microgrid.
133+
134+
This consists of information that describes the overall microgrid, as opposed to
135+
its electrical components or sensors, e.g., the microgrid ID, location.
136+
137+
Returns:
138+
The information about the local microgrid.
139+
140+
Raises:
141+
ApiClientError: If the are any errors communicating with the Microgrid API,
142+
most likely a subclass of
143+
[GrpcError][frequenz.client.microgrid.GrpcError].
144+
"""
145+
print(f"CALLED get_microgrid_info(): {self.stub.GetMicrogridMetadata=}")
146+
microgrid = await client.call_stub_method(
147+
self,
148+
lambda: self.stub.GetMicrogridMetadata(
149+
Empty(),
150+
timeout=DEFAULT_GRPC_CALL_TIMEOUT,
151+
),
152+
method_name="GetMicrogridMetadata",
153+
)
154+
155+
return microgrid_info_from_proto(microgrid.microgrid)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"""Test data for successful microgrid info retrieval."""
2+
3+
from datetime import datetime, timezone
4+
from typing import Any
5+
from unittest.mock import AsyncMock
6+
7+
from frequenz.api.common.v1.microgrid import microgrid_pb2 as microgrid_common_pb2
8+
from frequenz.api.microgrid.v1 import microgrid_pb2
9+
from google.protobuf.empty_pb2 import Empty
10+
11+
from frequenz.client.microgrid import (
12+
EnterpriseId,
13+
MicrogridId,
14+
MicrogridInfo,
15+
MicrogridStatus,
16+
)
17+
18+
# No client_args or client_kwargs needed for this call
19+
20+
21+
def assert_stub_method_call(stub_method: AsyncMock) -> None:
22+
"""Assert that the gRPC request matches the expected request."""
23+
stub_method.assert_called_once_with(Empty(), timeout=60.0)
24+
25+
26+
create_timestamp = datetime(2023, 1, 1, tzinfo=timezone.utc)
27+
grpc_response = microgrid_pb2.GetMicrogridMetadataResponse(
28+
microgrid=microgrid_common_pb2.Microgrid()
29+
)
30+
31+
32+
def assert_client_result(result: Any) -> None:
33+
"""Assert that the client result matches the expected MicrogridInfo."""
34+
assert result == MicrogridInfo(
35+
id=MicrogridId(0),
36+
enterprise_id=EnterpriseId(0),
37+
name=None,
38+
status=MicrogridStatus.UNSPECIFIED,
39+
location=None,
40+
delivery_area=None,
41+
create_timestamp=datetime(1970, 1, 1, 0, 0, tzinfo=timezone.utc),
42+
)
43+
assert result.is_active
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"""Test data for microgrid info retrieval with error."""
2+
3+
from typing import Any
4+
5+
from google.protobuf.empty_pb2 import Empty
6+
from grpc import StatusCode
7+
8+
from frequenz.client.microgrid import PermissionDenied
9+
from tests.util import make_grpc_error
10+
11+
# No client_args or client_kwargs needed for this call
12+
13+
14+
def assert_stub_method_call(stub_method: Any) -> None:
15+
"""Assert that the gRPC request matches the expected request."""
16+
stub_method.assert_called_once_with(Empty(), timeout=60.0)
17+
18+
19+
grpc_response = make_grpc_error(StatusCode.PERMISSION_DENIED)
20+
21+
22+
def assert_client_exception(exception: Exception) -> None:
23+
"""Assert that the client exception matches the expected error."""
24+
assert isinstance(exception, PermissionDenied)
25+
assert exception.grpc_error == grpc_response
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
"""Test data for successful microgrid info retrieval."""
2+
3+
from datetime import datetime, timezone
4+
from typing import Any
5+
from unittest.mock import AsyncMock
6+
7+
import pytest
8+
from frequenz.api.common.v1 import location_pb2
9+
from frequenz.api.common.v1.grid import delivery_area_pb2
10+
from frequenz.api.common.v1.microgrid import microgrid_pb2 as microgrid_common_pb2
11+
from frequenz.api.microgrid.v1 import microgrid_pb2
12+
from frequenz.client.base.conversion import to_timestamp
13+
from google.protobuf.empty_pb2 import Empty
14+
15+
from frequenz.client.microgrid import (
16+
EnterpriseId,
17+
MicrogridId,
18+
MicrogridInfo,
19+
MicrogridStatus,
20+
)
21+
from frequenz.client.microgrid._delivery_area import DeliveryArea, EnergyMarketCodeType
22+
from frequenz.client.microgrid._location import Location
23+
24+
# No client_args or client_kwargs needed for this call
25+
26+
27+
def assert_stub_method_call(stub_method: AsyncMock) -> None:
28+
"""Assert that the gRPC request matches the expected request."""
29+
stub_method.assert_called_once_with(Empty(), timeout=60.0)
30+
31+
32+
create_timestamp = datetime(2023, 1, 1, tzinfo=timezone.utc)
33+
grpc_response = microgrid_pb2.GetMicrogridMetadataResponse(
34+
microgrid=microgrid_common_pb2.Microgrid(
35+
id=1234,
36+
enterprise_id=5678,
37+
name="Test Microgrid",
38+
delivery_area=delivery_area_pb2.DeliveryArea(
39+
code="Test Delivery Area",
40+
code_type=delivery_area_pb2.EnergyMarketCodeType.ENERGY_MARKET_CODE_TYPE_EUROPE_EIC,
41+
),
42+
location=location_pb2.Location(
43+
latitude=37.7749, longitude=-122.4194, country_code="DE"
44+
),
45+
status=microgrid_common_pb2.MICROGRID_STATUS_INACTIVE,
46+
create_timestamp=to_timestamp(create_timestamp),
47+
)
48+
)
49+
50+
51+
def assert_client_result(result: Any) -> None:
52+
"""Assert that the client result matches the expected MicrogridInfo."""
53+
assert result == MicrogridInfo(
54+
id=MicrogridId(1234),
55+
enterprise_id=EnterpriseId(5678),
56+
name="Test Microgrid",
57+
delivery_area=DeliveryArea(
58+
code="Test Delivery Area", code_type=EnergyMarketCodeType.EUROPE_EIC
59+
),
60+
location=Location(
61+
latitude=pytest.approx(37.7749), # type: ignore[arg-type]
62+
longitude=pytest.approx(-122.4194), # type: ignore[arg-type]
63+
country_code="DE",
64+
),
65+
status=MicrogridStatus.INACTIVE,
66+
create_timestamp=datetime(2023, 1, 1, tzinfo=timezone.utc),
67+
)
68+
assert not result.is_active

tests/test_client.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33

44
"""Tests for the MicrogridApiClient class."""
55

6+
from __future__ import annotations
7+
8+
from collections.abc import AsyncIterator
9+
from pathlib import Path
610

711
import pytest
812
from frequenz.api.microgrid.v1 import microgrid_pb2_grpc
@@ -15,10 +19,26 @@
1519
MicrogridApiClient,
1620
)
1721

18-
from .util import patch_client_class
22+
from .util import ApiClientTestCaseSpec, get_test_specs, patch_client_class
1923

2024
# pylint: disable=protected-access
2125

26+
TESTS_DIR = Path(__file__).parent / "client_test_cases"
27+
28+
29+
@pytest.fixture
30+
async def client() -> AsyncIterator[MicrogridApiClient]:
31+
"""Fixture that provides a MicrogridApiClient with a mock gRPC stub and channel."""
32+
with patch_client_class(MicrogridApiClient, microgrid_pb2_grpc.MicrogridStub):
33+
client = MicrogridApiClient(
34+
"grpc://localhost:1234",
35+
# Retry very fast to avoid long test times, and also not too many
36+
# times to avoid test hanging forever.
37+
retry_strategy=LinearBackoff(interval=0.0, jitter=0.0, limit=10),
38+
)
39+
async with client:
40+
yield client
41+
2242

2343
@patch_client_class(MicrogridApiClient, microgrid_pb2_grpc.MicrogridStub)
2444
def test_init_defaults() -> None:
@@ -64,3 +84,15 @@ def test_init_with_custom_retry_strategy() -> None:
6484
"grpc://localhost:1234", retry_strategy=retry_strategy, connect=False
6585
)
6686
client._retry_strategy = retry_strategy # pylint: disable=protected-access
87+
88+
89+
@pytest.mark.parametrize(
90+
"spec",
91+
get_test_specs("get_microgrid_info", tests_dir=TESTS_DIR),
92+
ids=str,
93+
)
94+
async def test_get_microgrid_info(
95+
client: MicrogridApiClient, spec: ApiClientTestCaseSpec
96+
) -> None:
97+
"""Test get_microgrid_info method."""
98+
await spec.test_unary_unary_call(client, "GetMicrogridMetadata")

0 commit comments

Comments
 (0)