diff --git a/integration_tests/test_api.py b/integration_tests/test_api.py index d6f7078..2522f8d 100644 --- a/integration_tests/test_api.py +++ b/integration_tests/test_api.py @@ -3,11 +3,11 @@ """System tests for Electricity Trading API.""" import asyncio +import os from datetime import datetime, timedelta, timezone from decimal import Decimal from typing import Any, Generator -import os import grpc import pytest @@ -38,6 +38,7 @@ MIN_PRICE = Decimal(-9999.0) MAX_PRICE = Decimal(9999.0) + @pytest.fixture async def set_up() -> dict[str, Any]: """Set up the test suite.""" @@ -206,6 +207,7 @@ async def test_create_order_invalid_delivery_start_15_minutes_ago( with pytest.raises(ValueError, match="delivery_period must be in the future"): await create_test_order(set_up, delivery_period=delivery_period) + @pytest.mark.asyncio async def test_create_order_invalid_valid_until_one_hour_ago( set_up: dict[str, Any] @@ -227,9 +229,10 @@ async def test_list_gridpool_orders(set_up: dict[str, Any]) -> None: # List the orders and check they are present # filter by delivery period to avoid fetching too many orders orders = [ - order async for order in set_up["client"].list_gridpool_orders( - gridpool_id=GRIDPOOL_ID, - delivery_period=set_up["delivery_period"]) + order + async for order in set_up["client"].list_gridpool_orders( + gridpool_id=GRIDPOOL_ID, delivery_period=set_up["delivery_period"] + ) ] listed_orders_id = [order.order_id for order in orders] for order_id in created_orders_id: @@ -330,7 +333,8 @@ async def test_cancel_all_orders(set_up: dict[str, Any]) -> None: await set_up["client"].cancel_all_gridpool_orders(GRIDPOOL_ID) orders = [ - order async for order in set_up["client"].list_gridpool_orders( + order + async for order in set_up["client"].list_gridpool_orders( gridpool_id=GRIDPOOL_ID, ) ] @@ -346,10 +350,11 @@ async def test_list_gridpool_trades(set_up: dict[str, Any]) -> None: """Test listing gridpool trades.""" buy_order, sell_order = await create_test_trade(set_up) trades = [ - trade async for trade in set_up["client"].list_gridpool_trades( - GRIDPOOL_ID, - delivery_period=buy_order.order.delivery_period, - ) + trade + async for trade in set_up["client"].list_gridpool_trades( + GRIDPOOL_ID, + delivery_period=buy_order.order.delivery_period, + ) ] assert len(trades) >= 1 @@ -358,13 +363,15 @@ async def test_list_gridpool_trades(set_up: dict[str, Any]) -> None: async def test_list_public_trades(set_up: dict[str, Any]) -> None: """Test listing public trades.""" delivery_period = DeliveryPeriod( - start=datetime.fromisoformat("2024-06-10T10:00:00+00:00"), - duration=timedelta(minutes=15) - ) + start=datetime.fromisoformat("2024-06-10T10:00:00+00:00"), + duration=timedelta(minutes=15), + ) public_trades = [] counter = 0 - async for trade in set_up["client"].list_public_trades(delivery_period=delivery_period): + async for trade in set_up["client"].list_public_trades( + delivery_period=delivery_period + ): public_trades.append(trade) counter += 1 if counter == 10: @@ -372,6 +379,7 @@ async def test_list_public_trades(set_up: dict[str, Any]) -> None: assert len(public_trades) == 10, "Failed to retrieve 10 public trades" + @pytest.mark.asyncio async def test_stream_gridpool_orders(set_up: dict[str, Any]) -> None: """Test streaming gridpool orders.""" @@ -417,6 +425,7 @@ async def test_stream_gridpool_trades(set_up: dict[str, Any]) -> None: except asyncio.TimeoutError: pytest.fail("Streaming timed out, no trade received in 15 seconds") + @pytest.mark.asyncio async def test_create_order_zero_quantity(set_up: dict[str, Any]) -> None: """Test creating an order with zero quantity.""" @@ -434,26 +443,36 @@ async def test_create_order_negative_quantity(set_up: dict[str, Any]) -> None: @pytest.mark.asyncio -async def test_create_order_maximum_price_precision_exceeded(set_up: dict[str, Any]) -> None: +async def test_create_order_maximum_price_precision_exceeded( + set_up: dict[str, Any] +) -> None: """Test creating an order with excessive decimal precision in price.""" excessive_precision_price = Price(amount=Decimal("56.123"), currency=Currency.EUR) with pytest.raises(ValueError, match="cannot have more than 2 decimal places"): await create_test_order(set_up, price=excessive_precision_price) + @pytest.mark.asyncio -async def test_create_order_maximum_quantity_precision_exceeded(set_up: dict[str, Any]) -> None: +async def test_create_order_maximum_quantity_precision_exceeded( + set_up: dict[str, Any] +) -> None: """Test creating an order with excessive decimal precision in quantity.""" excessive_precision_quantity = Power(mw=Decimal("0.5001")) - with pytest.raises(ValueError, match="The quantity cannot have more than 1 decimal."): + with pytest.raises( + ValueError, match="The quantity cannot have more than 1 decimal." + ): await create_test_order(set_up, quantity=excessive_precision_quantity) + @pytest.mark.asyncio async def test_cancel_non_existent_order(set_up: dict[str, Any]) -> None: """Test canceling a non-existent order and expecting an error.""" non_existent_order_id = 999999 with pytest.raises(grpc.aio.AioRpcError) as excinfo: await set_up["client"].cancel_gridpool_order(GRIDPOOL_ID, non_existent_order_id) - assert excinfo.value.code() == grpc.StatusCode.UNAVAILABLE, "Cancelling non-existent order should return an error" + assert ( + excinfo.value.code() == grpc.StatusCode.UNAVAILABLE + ), "Cancelling non-existent order should return an error" @pytest.mark.asyncio @@ -462,14 +481,20 @@ async def test_cancel_already_cancelled_order(set_up: dict[str, Any]) -> None: order = await create_test_order(set_up) await set_up["client"].cancel_gridpool_order(GRIDPOOL_ID, order.order_id) with pytest.raises(grpc.aio.AioRpcError) as excinfo: - cancelled_order = await set_up["client"].cancel_gridpool_order(GRIDPOOL_ID, order.order_id) - assert excinfo.value.code() == grpc.StatusCode.INVALID_ARGUMENT, "Order is already cancelled" + cancelled_order = await set_up["client"].cancel_gridpool_order( + GRIDPOOL_ID, order.order_id + ) + assert ( + excinfo.value.code() == grpc.StatusCode.INVALID_ARGUMENT + ), "Order is already cancelled" @pytest.mark.asyncio async def test_create_order_with_invalid_delivery_area(set_up: dict[str, Any]) -> None: """Test creating an order with an invalid delivery area code.""" - invalid_delivery_area = DeliveryArea(code="INVALID_CODE", code_type=EnergyMarketCodeType.EUROPE_EIC) + invalid_delivery_area = DeliveryArea( + code="INVALID_CODE", code_type=EnergyMarketCodeType.EUROPE_EIC + ) with pytest.raises(grpc.aio.AioRpcError) as excinfo: await set_up["client"].create_gridpool_order( gridpool_id=GRIDPOOL_ID, @@ -481,14 +506,18 @@ async def test_create_order_with_invalid_delivery_area(set_up: dict[str, Any]) - quantity=set_up["quantity"], tag="invalid-delivery-area", ) - assert excinfo.value.code() == grpc.StatusCode.UNAVAILABLE, "Delivery area not found" + assert ( + excinfo.value.code() == grpc.StatusCode.UNAVAILABLE + ), "Delivery area not found" @pytest.mark.asyncio async def test_create_order_below_minimum_quantity(set_up: dict[str, Any]) -> None: """Test creating an order with a quantity below the minimum allowed.""" below_min_quantity = Power(mw=MIN_QUANTITY_MW - Decimal("0.01")) - with pytest.raises(ValueError, match=f"Quantity must be at least {MIN_QUANTITY_MW} MW."): + with pytest.raises( + ValueError, match=f"Quantity must be at least {MIN_QUANTITY_MW} MW." + ): await create_test_order(set_up, quantity=below_min_quantity) @@ -496,7 +525,9 @@ async def test_create_order_below_minimum_quantity(set_up: dict[str, Any]) -> No async def test_create_order_above_maximum_price(set_up: dict[str, Any]) -> None: """Test creating an order with a price above the maximum allowed.""" above_max_price = Price(amount=MAX_PRICE + Decimal("0.01"), currency=Currency.EUR) - with pytest.raises(ValueError, match=f"Price must be between {MIN_PRICE} and {MAX_PRICE}."): + with pytest.raises( + ValueError, match=f"Price must be between {MIN_PRICE} and {MAX_PRICE}." + ): await create_test_order(set_up, price=above_max_price) @@ -505,18 +536,25 @@ async def test_create_order_at_maximum_price(set_up: dict[str, Any]) -> None: """Test creating an order with the exact maximum allowed price.""" max_price = Price(amount=MAX_PRICE, currency=Currency.EUR) order = await create_test_order(set_up, price=max_price) - assert order.order.price.amount == max_price.amount, "Order with maximum price was not created correctly" + assert ( + order.order.price.amount == max_price.amount + ), "Order with maximum price was not created correctly" + @pytest.mark.asyncio -async def test_create_order_at_minimum_quantity_and_price(set_up: dict[str, Any]) -> None: +async def test_create_order_at_minimum_quantity_and_price( + set_up: dict[str, Any] +) -> None: """Test creating an order with the exact minimum allowed quantity and price.""" min_quantity = Power(mw=MIN_QUANTITY_MW) min_price = Price(amount=MIN_PRICE, currency=Currency.EUR) order = await create_test_order(set_up, quantity=min_quantity, price=min_price) - assert order.order.quantity.mw == min_quantity.mw, \ - "Order with minimum quantity was not created correctly" - assert order.order.price.amount == min_price.amount, \ - "Order with minimum price was not created correctly" + assert ( + order.order.quantity.mw == min_quantity.mw + ), "Order with minimum quantity was not created correctly" + assert ( + order.order.price.amount == min_price.amount + ), "Order with minimum price was not created correctly" @pytest.mark.asyncio @@ -524,37 +562,49 @@ async def test_update_order_to_invalid_price(set_up: dict[str, Any]) -> None: """Test updating an order to have a price outside the valid range.""" order = await create_test_order(set_up) invalid_price = Price(amount=MAX_PRICE + Decimal("0.01"), currency=Currency.EUR) - with pytest.raises(ValueError, match=f"Price must be between {MIN_PRICE} and {MAX_PRICE}."): + with pytest.raises( + ValueError, match=f"Price must be between {MIN_PRICE} and {MAX_PRICE}." + ): await set_up["client"].update_gridpool_order( gridpool_id=GRIDPOOL_ID, order_id=order.order_id, price=invalid_price ) - - @pytest.mark.asyncio async def test_concurrent_cancel_and_update_order(set_up: dict[str, Any]) -> None: """Test concurrent cancellation and update of the same order.""" order = await create_test_order(set_up) new_price = Price(amount=Decimal("50"), currency=Currency.EUR) - cancelled_order = await set_up["client"].cancel_gridpool_order(GRIDPOOL_ID, order.order_id) + cancelled_order = await set_up["client"].cancel_gridpool_order( + GRIDPOOL_ID, order.order_id + ) with pytest.raises(grpc.aio.AioRpcError) as excinfo: await set_up["client"].update_gridpool_order( gridpool_id=GRIDPOOL_ID, order_id=order.order_id, price=new_price ) - assert excinfo.value.code() == grpc.StatusCode.INVALID_ARGUMENT, "Order is already cancelled" + assert ( + excinfo.value.code() == grpc.StatusCode.INVALID_ARGUMENT + ), "Order is already cancelled" @pytest.mark.asyncio async def test_multiple_streams_different_filters(set_up: dict[str, Any]) -> None: """Test creating multiple streams with different filters and ensure independent operation.""" - area_1 = DeliveryArea(code="10YDE-EON------1", code_type=EnergyMarketCodeType.EUROPE_EIC) - area_2 = DeliveryArea(code="10YDE-RWENET---I", code_type=EnergyMarketCodeType.EUROPE_EIC) + area_1 = DeliveryArea( + code="10YDE-EON------1", code_type=EnergyMarketCodeType.EUROPE_EIC + ) + area_2 = DeliveryArea( + code="10YDE-RWENET---I", code_type=EnergyMarketCodeType.EUROPE_EIC + ) - stream_1 = await set_up["client"].stream_gridpool_orders(GRIDPOOL_ID, delivery_area=area_1) - stream_2 = await set_up["client"].stream_gridpool_orders(GRIDPOOL_ID, delivery_area=area_2) + stream_1 = await set_up["client"].stream_gridpool_orders( + GRIDPOOL_ID, delivery_area=area_1 + ) + stream_2 = await set_up["client"].stream_gridpool_orders( + GRIDPOOL_ID, delivery_area=area_2 + ) # Create orders in each area to see if they appear on correct streams 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 streamed_order_1 = await asyncio.wait_for(anext(stream_1), timeout=15) streamed_order_2 = await asyncio.wait_for(anext(stream_2), timeout=15) - assert streamed_order_1.order == order_1.order, "Streamed order does not match area-specific order in stream 1" - assert streamed_order_2.order == order_2.order, "Streamed order does not match area-specific order in stream 2" + assert ( + streamed_order_1.order == order_1.order + ), "Streamed order does not match area-specific order in stream 1" + assert ( + streamed_order_2.order == order_2.order + ), "Streamed order does not match area-specific order in stream 2" except asyncio.TimeoutError: pytest.fail("Failed to receive streamed orders within timeout") - @pytest.fixture(scope="session") def event_loop() -> Generator[asyncio.AbstractEventLoop, None, None]: """Create an event loop for the tests."""