33
44"""System tests for Electricity Trading API."""
55import asyncio
6+ import os
67from datetime import datetime , timedelta , timezone
78from decimal import Decimal
89from typing import Any , Generator
910
10- import os
1111import grpc
1212import pytest
1313
3838MIN_PRICE = Decimal (- 9999.0 )
3939MAX_PRICE = Decimal (9999.0 )
4040
41+
4142@pytest .fixture
4243async def set_up () -> dict [str , Any ]:
4344 """Set up the test suite."""
@@ -206,6 +207,7 @@ async def test_create_order_invalid_delivery_start_15_minutes_ago(
206207 with pytest .raises (ValueError , match = "delivery_period must be in the future" ):
207208 await create_test_order (set_up , delivery_period = delivery_period )
208209
210+
209211@pytest .mark .asyncio
210212async def test_create_order_invalid_valid_until_one_hour_ago (
211213 set_up : dict [str , Any ]
@@ -227,9 +229,10 @@ async def test_list_gridpool_orders(set_up: dict[str, Any]) -> None:
227229 # List the orders and check they are present
228230 # filter by delivery period to avoid fetching too many orders
229231 orders = [
230- order async for order in set_up ["client" ].list_gridpool_orders (
231- gridpool_id = GRIDPOOL_ID ,
232- delivery_period = set_up ["delivery_period" ])
232+ order
233+ async for order in set_up ["client" ].list_gridpool_orders (
234+ gridpool_id = GRIDPOOL_ID , delivery_period = set_up ["delivery_period" ]
235+ )
233236 ]
234237 listed_orders_id = [order .order_id for order in orders ]
235238 for order_id in created_orders_id :
@@ -330,7 +333,8 @@ async def test_cancel_all_orders(set_up: dict[str, Any]) -> None:
330333 await set_up ["client" ].cancel_all_gridpool_orders (GRIDPOOL_ID )
331334
332335 orders = [
333- order async for order in set_up ["client" ].list_gridpool_orders (
336+ order
337+ async for order in set_up ["client" ].list_gridpool_orders (
334338 gridpool_id = GRIDPOOL_ID ,
335339 )
336340 ]
@@ -346,10 +350,11 @@ async def test_list_gridpool_trades(set_up: dict[str, Any]) -> None:
346350 """Test listing gridpool trades."""
347351 buy_order , sell_order = await create_test_trade (set_up )
348352 trades = [
349- trade async for trade in set_up ["client" ].list_gridpool_trades (
350- GRIDPOOL_ID ,
351- delivery_period = buy_order .order .delivery_period ,
352- )
353+ trade
354+ async for trade in set_up ["client" ].list_gridpool_trades (
355+ GRIDPOOL_ID ,
356+ delivery_period = buy_order .order .delivery_period ,
357+ )
353358 ]
354359 assert len (trades ) >= 1
355360
@@ -358,20 +363,23 @@ async def test_list_gridpool_trades(set_up: dict[str, Any]) -> None:
358363async def test_list_public_trades (set_up : dict [str , Any ]) -> None :
359364 """Test listing public trades."""
360365 delivery_period = DeliveryPeriod (
361- start = datetime .fromisoformat ("2024-06-10T10:00:00+00:00" ),
362- duration = timedelta (minutes = 15 )
363- )
366+ start = datetime .fromisoformat ("2024-06-10T10:00:00+00:00" ),
367+ duration = timedelta (minutes = 15 ),
368+ )
364369
365370 public_trades = []
366371 counter = 0
367- async for trade in set_up ["client" ].list_public_trades (delivery_period = delivery_period ):
372+ async for trade in set_up ["client" ].list_public_trades (
373+ delivery_period = delivery_period
374+ ):
368375 public_trades .append (trade )
369376 counter += 1
370377 if counter == 10 :
371378 break
372379
373380 assert len (public_trades ) == 10 , "Failed to retrieve 10 public trades"
374381
382+
375383@pytest .mark .asyncio
376384async def test_stream_gridpool_orders (set_up : dict [str , Any ]) -> None :
377385 """Test streaming gridpool orders."""
@@ -417,6 +425,7 @@ async def test_stream_gridpool_trades(set_up: dict[str, Any]) -> None:
417425 except asyncio .TimeoutError :
418426 pytest .fail ("Streaming timed out, no trade received in 15 seconds" )
419427
428+
420429@pytest .mark .asyncio
421430async def test_create_order_zero_quantity (set_up : dict [str , Any ]) -> None :
422431 """Test creating an order with zero quantity."""
@@ -434,26 +443,36 @@ async def test_create_order_negative_quantity(set_up: dict[str, Any]) -> None:
434443
435444
436445@pytest .mark .asyncio
437- async def test_create_order_maximum_price_precision_exceeded (set_up : dict [str , Any ]) -> None :
446+ async def test_create_order_maximum_price_precision_exceeded (
447+ set_up : dict [str , Any ]
448+ ) -> None :
438449 """Test creating an order with excessive decimal precision in price."""
439450 excessive_precision_price = Price (amount = Decimal ("56.123" ), currency = Currency .EUR )
440451 with pytest .raises (ValueError , match = "cannot have more than 2 decimal places" ):
441452 await create_test_order (set_up , price = excessive_precision_price )
442453
454+
443455@pytest .mark .asyncio
444- async def test_create_order_maximum_quantity_precision_exceeded (set_up : dict [str , Any ]) -> None :
456+ async def test_create_order_maximum_quantity_precision_exceeded (
457+ set_up : dict [str , Any ]
458+ ) -> None :
445459 """Test creating an order with excessive decimal precision in quantity."""
446460 excessive_precision_quantity = Power (mw = Decimal ("0.5001" ))
447- with pytest .raises (ValueError , match = "The quantity cannot have more than 1 decimal." ):
461+ with pytest .raises (
462+ ValueError , match = "The quantity cannot have more than 1 decimal."
463+ ):
448464 await create_test_order (set_up , quantity = excessive_precision_quantity )
449465
466+
450467@pytest .mark .asyncio
451468async def test_cancel_non_existent_order (set_up : dict [str , Any ]) -> None :
452469 """Test canceling a non-existent order and expecting an error."""
453470 non_existent_order_id = 999999
454471 with pytest .raises (grpc .aio .AioRpcError ) as excinfo :
455472 await set_up ["client" ].cancel_gridpool_order (GRIDPOOL_ID , non_existent_order_id )
456- assert excinfo .value .code () == grpc .StatusCode .UNAVAILABLE , "Cancelling non-existent order should return an error"
473+ assert (
474+ excinfo .value .code () == grpc .StatusCode .UNAVAILABLE
475+ ), "Cancelling non-existent order should return an error"
457476
458477
459478@pytest .mark .asyncio
@@ -462,14 +481,20 @@ async def test_cancel_already_cancelled_order(set_up: dict[str, Any]) -> None:
462481 order = await create_test_order (set_up )
463482 await set_up ["client" ].cancel_gridpool_order (GRIDPOOL_ID , order .order_id )
464483 with pytest .raises (grpc .aio .AioRpcError ) as excinfo :
465- cancelled_order = await set_up ["client" ].cancel_gridpool_order (GRIDPOOL_ID , order .order_id )
466- assert excinfo .value .code () == grpc .StatusCode .INVALID_ARGUMENT , "Order is already cancelled"
484+ cancelled_order = await set_up ["client" ].cancel_gridpool_order (
485+ GRIDPOOL_ID , order .order_id
486+ )
487+ assert (
488+ excinfo .value .code () == grpc .StatusCode .INVALID_ARGUMENT
489+ ), "Order is already cancelled"
467490
468491
469492@pytest .mark .asyncio
470493async def test_create_order_with_invalid_delivery_area (set_up : dict [str , Any ]) -> None :
471494 """Test creating an order with an invalid delivery area code."""
472- invalid_delivery_area = DeliveryArea (code = "INVALID_CODE" , code_type = EnergyMarketCodeType .EUROPE_EIC )
495+ invalid_delivery_area = DeliveryArea (
496+ code = "INVALID_CODE" , code_type = EnergyMarketCodeType .EUROPE_EIC
497+ )
473498 with pytest .raises (grpc .aio .AioRpcError ) as excinfo :
474499 await set_up ["client" ].create_gridpool_order (
475500 gridpool_id = GRIDPOOL_ID ,
@@ -481,22 +506,28 @@ async def test_create_order_with_invalid_delivery_area(set_up: dict[str, Any]) -
481506 quantity = set_up ["quantity" ],
482507 tag = "invalid-delivery-area" ,
483508 )
484- assert excinfo .value .code () == grpc .StatusCode .UNAVAILABLE , "Delivery area not found"
509+ assert (
510+ excinfo .value .code () == grpc .StatusCode .UNAVAILABLE
511+ ), "Delivery area not found"
485512
486513
487514@pytest .mark .asyncio
488515async def test_create_order_below_minimum_quantity (set_up : dict [str , Any ]) -> None :
489516 """Test creating an order with a quantity below the minimum allowed."""
490517 below_min_quantity = Power (mw = MIN_QUANTITY_MW - Decimal ("0.01" ))
491- with pytest .raises (ValueError , match = f"Quantity must be at least { MIN_QUANTITY_MW } MW." ):
518+ with pytest .raises (
519+ ValueError , match = f"Quantity must be at least { MIN_QUANTITY_MW } MW."
520+ ):
492521 await create_test_order (set_up , quantity = below_min_quantity )
493522
494523
495524@pytest .mark .asyncio
496525async def test_create_order_above_maximum_price (set_up : dict [str , Any ]) -> None :
497526 """Test creating an order with a price above the maximum allowed."""
498527 above_max_price = Price (amount = MAX_PRICE + Decimal ("0.01" ), currency = Currency .EUR )
499- with pytest .raises (ValueError , match = f"Price must be between { MIN_PRICE } and { MAX_PRICE } ." ):
528+ with pytest .raises (
529+ ValueError , match = f"Price must be between { MIN_PRICE } and { MAX_PRICE } ."
530+ ):
500531 await create_test_order (set_up , price = above_max_price )
501532
502533
@@ -505,56 +536,75 @@ async def test_create_order_at_maximum_price(set_up: dict[str, Any]) -> None:
505536 """Test creating an order with the exact maximum allowed price."""
506537 max_price = Price (amount = MAX_PRICE , currency = Currency .EUR )
507538 order = await create_test_order (set_up , price = max_price )
508- assert order .order .price .amount == max_price .amount , "Order with maximum price was not created correctly"
539+ assert (
540+ order .order .price .amount == max_price .amount
541+ ), "Order with maximum price was not created correctly"
542+
509543
510544@pytest .mark .asyncio
511- async def test_create_order_at_minimum_quantity_and_price (set_up : dict [str , Any ]) -> None :
545+ async def test_create_order_at_minimum_quantity_and_price (
546+ set_up : dict [str , Any ]
547+ ) -> None :
512548 """Test creating an order with the exact minimum allowed quantity and price."""
513549 min_quantity = Power (mw = MIN_QUANTITY_MW )
514550 min_price = Price (amount = MIN_PRICE , currency = Currency .EUR )
515551 order = await create_test_order (set_up , quantity = min_quantity , price = min_price )
516- assert order .order .quantity .mw == min_quantity .mw , \
517- "Order with minimum quantity was not created correctly"
518- assert order .order .price .amount == min_price .amount , \
519- "Order with minimum price was not created correctly"
552+ assert (
553+ order .order .quantity .mw == min_quantity .mw
554+ ), "Order with minimum quantity was not created correctly"
555+ assert (
556+ order .order .price .amount == min_price .amount
557+ ), "Order with minimum price was not created correctly"
520558
521559
522560@pytest .mark .asyncio
523561async def test_update_order_to_invalid_price (set_up : dict [str , Any ]) -> None :
524562 """Test updating an order to have a price outside the valid range."""
525563 order = await create_test_order (set_up )
526564 invalid_price = Price (amount = MAX_PRICE + Decimal ("0.01" ), currency = Currency .EUR )
527- with pytest .raises (ValueError , match = f"Price must be between { MIN_PRICE } and { MAX_PRICE } ." ):
565+ with pytest .raises (
566+ ValueError , match = f"Price must be between { MIN_PRICE } and { MAX_PRICE } ."
567+ ):
528568 await set_up ["client" ].update_gridpool_order (
529569 gridpool_id = GRIDPOOL_ID , order_id = order .order_id , price = invalid_price
530570 )
531571
532572
533-
534-
535573@pytest .mark .asyncio
536574async def test_concurrent_cancel_and_update_order (set_up : dict [str , Any ]) -> None :
537575 """Test concurrent cancellation and update of the same order."""
538576 order = await create_test_order (set_up )
539577 new_price = Price (amount = Decimal ("50" ), currency = Currency .EUR )
540578
541- cancelled_order = await set_up ["client" ].cancel_gridpool_order (GRIDPOOL_ID , order .order_id )
579+ cancelled_order = await set_up ["client" ].cancel_gridpool_order (
580+ GRIDPOOL_ID , order .order_id
581+ )
542582
543583 with pytest .raises (grpc .aio .AioRpcError ) as excinfo :
544584 await set_up ["client" ].update_gridpool_order (
545585 gridpool_id = GRIDPOOL_ID , order_id = order .order_id , price = new_price
546586 )
547- assert excinfo .value .code () == grpc .StatusCode .INVALID_ARGUMENT , "Order is already cancelled"
587+ assert (
588+ excinfo .value .code () == grpc .StatusCode .INVALID_ARGUMENT
589+ ), "Order is already cancelled"
548590
549591
550592@pytest .mark .asyncio
551593async def test_multiple_streams_different_filters (set_up : dict [str , Any ]) -> None :
552594 """Test creating multiple streams with different filters and ensure independent operation."""
553- area_1 = DeliveryArea (code = "10YDE-EON------1" , code_type = EnergyMarketCodeType .EUROPE_EIC )
554- area_2 = DeliveryArea (code = "10YDE-RWENET---I" , code_type = EnergyMarketCodeType .EUROPE_EIC )
595+ area_1 = DeliveryArea (
596+ code = "10YDE-EON------1" , code_type = EnergyMarketCodeType .EUROPE_EIC
597+ )
598+ area_2 = DeliveryArea (
599+ code = "10YDE-RWENET---I" , code_type = EnergyMarketCodeType .EUROPE_EIC
600+ )
555601
556- stream_1 = await set_up ["client" ].stream_gridpool_orders (GRIDPOOL_ID , delivery_area = area_1 )
557- stream_2 = await set_up ["client" ].stream_gridpool_orders (GRIDPOOL_ID , delivery_area = area_2 )
602+ stream_1 = await set_up ["client" ].stream_gridpool_orders (
603+ GRIDPOOL_ID , delivery_area = area_1
604+ )
605+ stream_2 = await set_up ["client" ].stream_gridpool_orders (
606+ GRIDPOOL_ID , delivery_area = area_2
607+ )
558608
559609 # Create orders in each area to see if they appear on correct streams
560610 order_1 = await create_test_order (set_up , delivery_area = area_1 )
@@ -564,13 +614,16 @@ async def test_multiple_streams_different_filters(set_up: dict[str, Any]) -> Non
564614 streamed_order_1 = await asyncio .wait_for (anext (stream_1 ), timeout = 15 )
565615 streamed_order_2 = await asyncio .wait_for (anext (stream_2 ), timeout = 15 )
566616
567- assert streamed_order_1 .order == order_1 .order , "Streamed order does not match area-specific order in stream 1"
568- assert streamed_order_2 .order == order_2 .order , "Streamed order does not match area-specific order in stream 2"
617+ assert (
618+ streamed_order_1 .order == order_1 .order
619+ ), "Streamed order does not match area-specific order in stream 1"
620+ assert (
621+ streamed_order_2 .order == order_2 .order
622+ ), "Streamed order does not match area-specific order in stream 2"
569623 except asyncio .TimeoutError :
570624 pytest .fail ("Failed to receive streamed orders within timeout" )
571625
572626
573-
574627@pytest .fixture (scope = "session" )
575628def event_loop () -> Generator [asyncio .AbstractEventLoop , None , None ]:
576629 """Create an event loop for the tests."""
0 commit comments