|
3 | 3 |
|
4 | 4 | """Tests for the Market Metering client.""" |
5 | 5 |
|
6 | | -from datetime import timedelta |
| 6 | +from collections.abc import AsyncIterator |
| 7 | +from datetime import datetime, timedelta, timezone |
| 8 | +from typing import Any, cast |
7 | 9 |
|
| 10 | +import pytest |
| 11 | + |
| 12 | +from frequenz.client.marketmetering import MarketMeteringApiClient |
8 | 13 | from frequenz.client.marketmetering.types import ( |
9 | 14 | DataQuality, |
10 | 15 | DownsamplingMethod, |
|
13 | 18 | MarketLocationId, |
14 | 19 | MarketLocationIdType, |
15 | 20 | MarketLocationRef, |
| 21 | + MarketLocationSample, |
| 22 | + MarketLocationSeries, |
16 | 23 | MetricType, |
17 | 24 | MetricUnit, |
18 | 25 | ResamplingMethod, |
@@ -117,3 +124,96 @@ def test_resampling_options_with_resolution(self) -> None: |
117 | 124 | """Test ResamplingOptions with resolution.""" |
118 | 125 | options = ResamplingOptions(resolution=TimeResolution.MIN_15) |
119 | 126 | assert options.resolution == TimeResolution.MIN_15 |
| 127 | + |
| 128 | + |
| 129 | +class _UpsertStub: |
| 130 | + """A fake stub capturing upsert stream call arguments.""" |
| 131 | + |
| 132 | + def __init__(self) -> None: |
| 133 | + self.metadata: tuple[tuple[str, str | bytes], ...] | None = None |
| 134 | + self.requests: list[object] = [] |
| 135 | + |
| 136 | + # gRPC-generated method names use UpperCamelCase and this stub mirrors that. |
| 137 | + # pylint: disable=invalid-name,missing-function-docstring |
| 138 | + async def UpsertMarketLocationSamplesStream( # noqa: N802 |
| 139 | + self, |
| 140 | + request_iterator: AsyncIterator[object], |
| 141 | + *, |
| 142 | + metadata: tuple[tuple[str, str | bytes], ...] | None = None, |
| 143 | + timeout: float | None = None, |
| 144 | + ) -> AsyncIterator[object]: |
| 145 | + del timeout |
| 146 | + self.metadata = metadata |
| 147 | + async for request in request_iterator: |
| 148 | + self.requests.append(request) |
| 149 | + items: tuple[object, ...] = () |
| 150 | + for item in items: |
| 151 | + yield item |
| 152 | + |
| 153 | + |
| 154 | +class TestClientMethods: |
| 155 | + """Tests for client RPC helpers.""" |
| 156 | + |
| 157 | + @pytest.mark.asyncio |
| 158 | + async def test_upsert_samples_adds_stream_metadata(self) -> None: |
| 159 | + """Test that bidi upsert includes auth and signing metadata.""" |
| 160 | + client = MarketMeteringApiClient( |
| 161 | + server_url="grpc://example.com", |
| 162 | + auth_key="test-key", |
| 163 | + sign_secret="test-secret", |
| 164 | + connect=False, |
| 165 | + ) |
| 166 | + stub = _UpsertStub() |
| 167 | + setattr(client, "_stub", cast(Any, stub)) |
| 168 | + setattr(client, "_channel", cast(Any, object())) |
| 169 | + |
| 170 | + market_location_ref = MarketLocationRef( |
| 171 | + enterprise_id=42, |
| 172 | + market_area=MarketArea.EU_DE, |
| 173 | + market_location_id=MarketLocationId( |
| 174 | + value="DE01234567890", |
| 175 | + type=MarketLocationIdType.MALO_ID, |
| 176 | + ), |
| 177 | + ) |
| 178 | + series = MarketLocationSeries( |
| 179 | + market_location_ref=market_location_ref, |
| 180 | + direction=EnergyFlowDirection.IMPORT, |
| 181 | + metric_type=MetricType.ACTIVE_ENERGY, |
| 182 | + metric_unit=MetricUnit.KWH, |
| 183 | + resolution=TimeResolution.MIN_15, |
| 184 | + samples=[], |
| 185 | + ) |
| 186 | + sample = MarketLocationSample( |
| 187 | + sample_time=datetime.now(timezone.utc), |
| 188 | + value=1.0, |
| 189 | + quality=DataQuality.MEASURED, |
| 190 | + revision=1, |
| 191 | + update_time=None, |
| 192 | + resampling_method=ResamplingMethod.UNSPECIFIED, |
| 193 | + ) |
| 194 | + |
| 195 | + async def sample_generator() -> ( |
| 196 | + AsyncIterator[tuple[MarketLocationRef, MarketLocationSeries]] |
| 197 | + ): |
| 198 | + yield ( |
| 199 | + market_location_ref, |
| 200 | + MarketLocationSeries( |
| 201 | + market_location_ref=series.market_location_ref, |
| 202 | + direction=series.direction, |
| 203 | + metric_type=series.metric_type, |
| 204 | + metric_unit=series.metric_unit, |
| 205 | + resolution=series.resolution, |
| 206 | + samples=[sample], |
| 207 | + ), |
| 208 | + ) |
| 209 | + |
| 210 | + assert [ |
| 211 | + result async for result in client.upsert_samples(sample_generator()) |
| 212 | + ] == [] |
| 213 | + assert stub.metadata is not None |
| 214 | + metadata = dict(stub.metadata) |
| 215 | + assert metadata["key"] == "test-key" |
| 216 | + assert metadata["ts"] |
| 217 | + assert metadata["nonce"] |
| 218 | + assert metadata["sig"] |
| 219 | + assert len(stub.requests) == 1 |
0 commit comments