Skip to content

Commit 349e30b

Browse files
Add edge cases in the integration tests
Signed-off-by: camille-bouvy-frequenz <[email protected]>
1 parent 1a646d4 commit 349e30b

File tree

1 file changed

+139
-3
lines changed

1 file changed

+139
-3
lines changed

integration_tests/test_api.py

Lines changed: 139 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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

7173
async 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
197219
async 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")
381517
def event_loop() -> Generator[asyncio.AbstractEventLoop, None, None]:

0 commit comments

Comments
 (0)