@@ -57,6 +57,7 @@ async def set_up() -> dict[str, Any]:
5757 price = Price (amount = Decimal ("56" ), currency = Currency .EUR )
5858 quantity = Power (mw = Decimal ("0.1" ))
5959 order_type = OrderType .LIMIT
60+ valid_until = None
6061
6162 return {
6263 "client" : client ,
@@ -65,26 +66,36 @@ async def set_up() -> dict[str, Any]:
6566 "price" : price ,
6667 "quantity" : quantity ,
6768 "order_type" : order_type ,
69+ "valid_until" : valid_until ,
6870 }
6971
7072
7173async def create_test_order (
7274 set_up : dict [str , Any ],
7375 side : MarketSide = MarketSide .BUY ,
7476 price : Price | None = None ,
77+ quantity : Power | None = None ,
7578 delivery_period : DeliveryPeriod | None = None ,
79+ delivery_area : DeliveryArea | None = None ,
80+ order_type : OrderType | None = None ,
81+ valid_until : datetime | None = None ,
7682) -> OrderDetail :
7783 """Create a test order with customizable parameters."""
7884 order_price = price or set_up ["price" ]
85+ order_quantity = quantity or set_up ["quantity" ]
7986 order_delivery_period = delivery_period or set_up ["delivery_period" ]
87+ order_delivery_area = delivery_area or set_up ["delivery_area" ]
88+ order_type = order_type or set_up ["order_type" ]
89+ order_valid_until = valid_until or set_up ["valid_until" ]
8090 order = await set_up ["client" ].create_gridpool_order (
8191 gridpool_id = GRIDPOOL_ID ,
82- delivery_area = set_up [ "delivery_area" ] ,
92+ delivery_area = order_delivery_area ,
8393 delivery_period = order_delivery_period ,
84- order_type = set_up [ " order_type" ] ,
94+ order_type = order_type ,
8595 side = side ,
8696 price = order_price ,
87- quantity = set_up ["quantity" ],
97+ quantity = order_quantity ,
98+ valid_until = order_valid_until ,
8899 tag = "api-integration-test" ,
89100 )
90101 return order # type: ignore
@@ -192,6 +203,17 @@ async def test_create_order_invalid_delivery_start_15_minutes_ago(
192203 with pytest .raises (ValueError , match = "delivery_period must be in the future" ):
193204 await create_test_order (set_up , delivery_period = delivery_period )
194205
206+ @pytest .mark .asyncio
207+ async def test_create_order_invalid_valid_until_one_hour_ago (
208+ set_up : dict [str , Any ]
209+ ) -> None :
210+ """Test creating an order with a passed valid until (one hour ago)."""
211+ valid_until = (datetime .now (timezone .utc ) - timedelta (hours = 1 )).replace (
212+ minute = 0 , second = 0 , microsecond = 0
213+ )
214+ with pytest .raises (ValueError , match = "valid_until must be in the future" ):
215+ await create_test_order (set_up , valid_until = valid_until )
216+
195217
196218@pytest .mark .asyncio
197219async def test_list_gridpool_orders (set_up : dict [str , Any ]) -> None :
@@ -376,6 +398,120 @@ async def test_stream_gridpool_trades(set_up: dict[str, Any]) -> None:
376398 except asyncio .TimeoutError :
377399 pytest .fail ("Streaming timed out, no trade received in 15 seconds" )
378400
401+ @pytest .mark .asyncio
402+ async def test_create_order_zero_quantity (set_up : dict [str , Any ]) -> None :
403+ """Test creating an order with zero quantity."""
404+ zero_quantity = Power (mw = Decimal ("0" ))
405+ with pytest .raises (ValueError , match = "Quantity must be positive" ):
406+ await create_test_order (set_up , quantity = zero_quantity )
407+
408+
409+ @pytest .mark .asyncio
410+ async def test_create_order_negative_quantity (set_up : dict [str , Any ]) -> None :
411+ """Test creating an order with a negative quantity."""
412+ negative_quantity = Power (mw = Decimal ("-0.1" ))
413+ with pytest .raises (ValueError , match = "Quantity must be positive" ):
414+ await create_test_order (set_up , quantity = negative_quantity )
415+
416+
417+ @pytest .mark .asyncio
418+ async def test_create_order_maximum_price_precision_exceeded (set_up : dict [str , Any ]) -> None :
419+ """Test creating an order with excessive decimal precision in price."""
420+ excessive_precision_price = Price (amount = Decimal ("56.123" ), currency = Currency .EUR )
421+ with pytest .raises (ValueError , match = "cannot have more than 2 decimal places" ):
422+ await create_test_order (set_up , price = excessive_precision_price )
423+
424+ @pytest .mark .asyncio
425+ async def test_create_order_maximum_quantity_precision_exceeded (set_up : dict [str , Any ]) -> None :
426+ """Test creating an order with excessive decimal precision in quantity."""
427+ excessive_precision_quantity = Power (mw = Decimal ("0.0001" ))
428+ with pytest .raises (ValueError , match = "The quantity cannot have more than 1 decimal." ):
429+ await create_test_order (set_up , quantity = excessive_precision_quantity )
430+
431+ @pytest .mark .asyncio
432+ async def test_cancel_non_existent_order (set_up : dict [str , Any ]) -> None :
433+ """Test canceling a non-existent order and expecting an error."""
434+ non_existent_order_id = 999999
435+ with pytest .raises (grpc .aio .AioRpcError ) as excinfo :
436+ await set_up ["client" ].cancel_gridpool_order (GRIDPOOL_ID , non_existent_order_id )
437+ assert excinfo .value .code () == grpc .StatusCode .UNAVAILABLE , "Cancelling non-existent order should return an error"
438+
439+
440+ @pytest .mark .asyncio
441+ async def test_cancel_already_cancelled_order (set_up : dict [str , Any ]) -> None :
442+ """Test cancelling an order twice to ensure idempotent behavior."""
443+ order = await create_test_order (set_up )
444+ await set_up ["client" ].cancel_gridpool_order (GRIDPOOL_ID , order .order_id )
445+ with pytest .raises (grpc .aio .AioRpcError ) as excinfo :
446+ cancelled_order = await set_up ["client" ].cancel_gridpool_order (GRIDPOOL_ID , order .order_id )
447+ assert excinfo .value .code () == grpc .StatusCode .INVALID_ARGUMENT , "Order is already cancelled"
448+
449+
450+ @pytest .mark .asyncio
451+ async def test_create_order_with_invalid_delivery_area (set_up : dict [str , Any ]) -> None :
452+ """Test creating an order with an invalid delivery area code."""
453+ invalid_delivery_area = DeliveryArea (code = "INVALID_CODE" , code_type = EnergyMarketCodeType .EUROPE_EIC )
454+ with pytest .raises (grpc .aio .AioRpcError ) as excinfo :
455+ await set_up ["client" ].create_gridpool_order (
456+ gridpool_id = GRIDPOOL_ID ,
457+ delivery_area = invalid_delivery_area ,
458+ delivery_period = set_up ["delivery_period" ],
459+ order_type = set_up ["order_type" ],
460+ side = MarketSide .BUY ,
461+ price = set_up ["price" ],
462+ quantity = set_up ["quantity" ],
463+ tag = "invalid-delivery-area" ,
464+ )
465+ assert excinfo .value .code () == grpc .StatusCode .UNAVAILABLE , "Delivery area not found"
466+
467+
468+ @pytest .mark .asyncio
469+ async def test_create_order_extremely_large_price (set_up : dict [str , Any ]) -> None :
470+ """Test creating an order with an extremely large price."""
471+ large_price = Price (amount = Decimal ("1000000000.00" ), currency = Currency .EUR )
472+ order = await create_test_order (set_up , price = large_price )
473+ assert order .order .price .amount == large_price .amount , "Extremely large price not handled correctly"
474+
475+
476+
477+ @pytest .mark .asyncio
478+ async def test_concurrent_cancel_and_update_order (set_up : dict [str , Any ]) -> None :
479+ """Test concurrent cancellation and update of the same order."""
480+ order = await create_test_order (set_up )
481+ new_price = Price (amount = Decimal ("50" ), currency = Currency .EUR )
482+
483+ cancelled_order = await set_up ["client" ].cancel_gridpool_order (GRIDPOOL_ID , order .order_id )
484+
485+ with pytest .raises (grpc .aio .AioRpcError ) as excinfo :
486+ await set_up ["client" ].update_gridpool_order (
487+ gridpool_id = GRIDPOOL_ID , order_id = order .order_id , price = new_price
488+ )
489+ assert excinfo .value .code () == grpc .StatusCode .INVALID_ARGUMENT , "Order is already cancelled"
490+
491+
492+ @pytest .mark .asyncio
493+ async def test_multiple_streams_different_filters (set_up : dict [str , Any ]) -> None :
494+ """Test creating multiple streams with different filters and ensure independent operation."""
495+ area_1 = DeliveryArea (code = "10YDE-EON------1" , code_type = EnergyMarketCodeType .EUROPE_EIC )
496+ area_2 = DeliveryArea (code = "10YDE-RWENET---I" , code_type = EnergyMarketCodeType .EUROPE_EIC )
497+
498+ stream_1 = await set_up ["client" ].stream_gridpool_orders (GRIDPOOL_ID , delivery_area = area_1 )
499+ stream_2 = await set_up ["client" ].stream_gridpool_orders (GRIDPOOL_ID , delivery_area = area_2 )
500+
501+ # Create orders in each area to see if they appear on correct streams
502+ order_1 = await create_test_order (set_up , delivery_area = area_1 )
503+ order_2 = await create_test_order (set_up , delivery_area = area_2 )
504+
505+ try :
506+ streamed_order_1 = await asyncio .wait_for (anext (stream_1 ), timeout = 15 )
507+ streamed_order_2 = await asyncio .wait_for (anext (stream_2 ), timeout = 15 )
508+
509+ assert streamed_order_1 .order == order_1 .order , "Streamed order does not match area-specific order in stream 1"
510+ assert streamed_order_2 .order == order_2 .order , "Streamed order does not match area-specific order in stream 2"
511+ except asyncio .TimeoutError :
512+ pytest .fail ("Failed to receive streamed orders within timeout" )
513+
514+
379515
380516@pytest .fixture (scope = "session" )
381517def event_loop () -> Generator [asyncio .AbstractEventLoop , None , None ]:
0 commit comments