|
24 | 24 | from frequenz.sdk.actor.power_distributing.result import ( |
25 | 25 | Error, |
26 | 26 | OutOfBounds, |
| 27 | + PartialFailure, |
27 | 28 | PowerBounds, |
28 | 29 | Result, |
29 | 30 | Success, |
@@ -1249,3 +1250,65 @@ async def test_not_all_batteries_are_working(self, mocker: MockerFixture) -> Non |
1249 | 1250 | assert result.request == request |
1250 | 1251 |
|
1251 | 1252 | await mockgrid.cleanup() |
| 1253 | + |
| 1254 | + async def test_partial_failure_result(self, mocker: MockerFixture) -> None: |
| 1255 | + """Test power results when the microgrid failed to set power for one of the batteries.""" |
| 1256 | + mockgrid = MockMicrogrid(grid_meter=False) |
| 1257 | + mockgrid.add_batteries(3) |
| 1258 | + await mockgrid.start(mocker) |
| 1259 | + await self.init_component_data(mockgrid) |
| 1260 | + |
| 1261 | + mocker.patch("asyncio.sleep", new_callable=AsyncMock) |
| 1262 | + |
| 1263 | + batteries = {9, 19, 29} |
| 1264 | + failed_batteries = {9} |
| 1265 | + failed_power = 500.0 |
| 1266 | + |
| 1267 | + attrs = {"get_working_batteries.return_value": batteries} |
| 1268 | + mocker.patch( |
| 1269 | + "frequenz.sdk.actor.power_distributing.power_distributing.BatteryPoolStatus", |
| 1270 | + return_value=MagicMock( |
| 1271 | + spec=BatteryPoolStatus, |
| 1272 | + **attrs, |
| 1273 | + ), |
| 1274 | + ) |
| 1275 | + |
| 1276 | + mocker.patch( |
| 1277 | + "frequenz.sdk.actor.power_distributing.PowerDistributingActor._parse_result", |
| 1278 | + return_value=(failed_power, failed_batteries), |
| 1279 | + ) |
| 1280 | + |
| 1281 | + requests_channel = Broadcast[Request]("power_distributor requests") |
| 1282 | + results_channel = Broadcast[Result]("power_distributor results") |
| 1283 | + |
| 1284 | + battery_status_channel = Broadcast[BatteryStatus]("battery_status") |
| 1285 | + async with PowerDistributingActor( |
| 1286 | + requests_receiver=requests_channel.new_receiver(), |
| 1287 | + results_sender=results_channel.new_sender(), |
| 1288 | + battery_status_sender=battery_status_channel.new_sender(), |
| 1289 | + ): |
| 1290 | + request = Request( |
| 1291 | + power=Power.from_kilowatts(1.70), |
| 1292 | + batteries=batteries, |
| 1293 | + request_timeout=SAFETY_TIMEOUT, |
| 1294 | + ) |
| 1295 | + |
| 1296 | + await requests_channel.new_sender().send(request) |
| 1297 | + result_rx = results_channel.new_receiver() |
| 1298 | + |
| 1299 | + done, pending = await asyncio.wait( |
| 1300 | + [asyncio.create_task(result_rx.receive())], |
| 1301 | + timeout=SAFETY_TIMEOUT.total_seconds(), |
| 1302 | + ) |
| 1303 | + assert len(pending) == 0 |
| 1304 | + assert len(done) == 1 |
| 1305 | + result = done.pop().result() |
| 1306 | + assert isinstance(result, PartialFailure) |
| 1307 | + assert result.succeeded_batteries == batteries - failed_batteries |
| 1308 | + assert result.failed_batteries == failed_batteries |
| 1309 | + assert result.succeeded_power.isclose(Power.from_watts(1000.0)) |
| 1310 | + assert result.failed_power.isclose(Power.from_watts(failed_power)) |
| 1311 | + assert result.excess_power.isclose(Power.from_watts(200.0)) |
| 1312 | + assert result.request == request |
| 1313 | + |
| 1314 | + await mockgrid.cleanup() |
0 commit comments