Skip to content

Commit 0590d81

Browse files
committed
Add loading of Lifetime from protobuf
Signed-off-by: Leandro Lucarella <[email protected]>
1 parent 1c8a132 commit 0590d81

File tree

2 files changed

+93
-1
lines changed

2 files changed

+93
-1
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# License: MIT
2+
# Copyright © 2024 Frequenz Energy-as-a-Service GmbH
3+
4+
"""Loading of Lifetime objects from protobuf messages."""
5+
6+
from frequenz.api.common.v1.microgrid import lifetime_pb2
7+
from frequenz.client.base.conversion import to_datetime
8+
9+
from ._lifetime import Lifetime
10+
11+
12+
def lifetime_from_proto(
13+
message: lifetime_pb2.Lifetime,
14+
) -> Lifetime:
15+
"""Create a [`Lifetime`][frequenz.client.microgrid.Lifetime] from a protobuf message."""
16+
start = (
17+
to_datetime(message.start_timestamp)
18+
if message.HasField("start_timestamp")
19+
else None
20+
)
21+
end = (
22+
to_datetime(message.end_timestamp)
23+
if message.HasField("end_timestamp")
24+
else None
25+
)
26+
return Lifetime(start=start, end=end)

tests/test_lifetime.py

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
# License: MIT
22
# Copyright © 2025 Frequenz Energy-as-a-Service GmbH
33

4-
"""Tests for the Lifetime class."""
4+
"""Tests for the Lifetime class and its protobuf conversion."""
55

66
from dataclasses import dataclass
77
from datetime import datetime, timezone
88
from enum import Enum, auto
9+
from typing import Any
910

1011
import pytest
12+
from frequenz.api.common.v1.microgrid import lifetime_pb2
13+
from google.protobuf import timestamp_pb2
1114

1215
from frequenz.client.microgrid import Lifetime
16+
from frequenz.client.microgrid._lifetime_proto import lifetime_from_proto
1317

1418

1519
class _Time(Enum):
@@ -65,6 +69,20 @@ class _ActivityTestCase:
6569
"""The expected operational state."""
6670

6771

72+
@dataclass(frozen=True, kw_only=True)
73+
class _ProtoConversionTestCase:
74+
"""Test case for protobuf conversion."""
75+
76+
name: str
77+
"""The description of the test case."""
78+
79+
include_start: bool
80+
"""Whether to include start timestamp."""
81+
82+
include_end: bool
83+
"""Whether to include end timestamp."""
84+
85+
6886
@dataclass(frozen=True, kw_only=True)
6987
class _FixedLifetimeTestCase:
7088
"""Test case for fixed lifetime activity testing."""
@@ -303,3 +321,51 @@ def test_active_at_with_fixed_lifetime(
303321
}[case.test_time]
304322

305323
assert lifetime.is_operational_at(test_time) == case.expected_operational
324+
325+
326+
@pytest.mark.parametrize(
327+
"case",
328+
[
329+
_ProtoConversionTestCase(
330+
name="both timestamps", include_start=True, include_end=True
331+
),
332+
_ProtoConversionTestCase(
333+
name="only start timestamp", include_start=True, include_end=False
334+
),
335+
_ProtoConversionTestCase(
336+
name="only end timestamp", include_start=False, include_end=True
337+
),
338+
_ProtoConversionTestCase(
339+
name="no timestamps", include_start=False, include_end=False
340+
),
341+
],
342+
ids=lambda case: case.name,
343+
)
344+
def test_from_proto(
345+
now: datetime, future: datetime, case: _ProtoConversionTestCase
346+
) -> None:
347+
"""Test conversion from protobuf message to Lifetime."""
348+
now_ts = timestamp_pb2.Timestamp()
349+
now_ts.FromDatetime(now)
350+
351+
future_ts = timestamp_pb2.Timestamp()
352+
future_ts.FromDatetime(future)
353+
354+
proto_kwargs: dict[str, Any] = {}
355+
if case.include_start:
356+
proto_kwargs["start_timestamp"] = now_ts
357+
if case.include_end:
358+
proto_kwargs["end_timestamp"] = future_ts
359+
360+
proto = lifetime_pb2.Lifetime(**proto_kwargs)
361+
lifetime = lifetime_from_proto(proto)
362+
363+
if case.include_start:
364+
assert lifetime.start == now
365+
else:
366+
assert lifetime.start is None
367+
368+
if case.include_end:
369+
assert lifetime.end == future
370+
else:
371+
assert lifetime.end is None

0 commit comments

Comments
 (0)