55import asyncio
66from functools import partial
77from typing import cast
8+ from uuid import UUID
89
910import pytest
1011from faker import Faker
1112from fastapi import FastAPI , status
1213from httpx import AsyncClient , BasicAuth
14+ from models_library .api_schemas_resource_usage_tracker .licensed_items_checkouts import (
15+ LicensedItemCheckoutGet ,
16+ )
1317from models_library .api_schemas_webserver .licensed_items import (
1418 LicensedItemGet as _LicensedItemGet ,
1519)
2024 LicensedItemCheckoutRpcGet ,
2125)
2226from models_library .licensed_items import LicensedItemID
27+ from models_library .resource_tracker_licensed_items_checkouts import (
28+ LicensedItemCheckoutID ,
29+ )
2330from models_library .services_types import ServiceRunID
2431from models_library .users import UserID
2532from models_library .wallets import WalletID
3340 NotEnoughAvailableSeatsError ,
3441)
3542from simcore_service_api_server ._meta import API_VTAG
43+ from simcore_service_api_server .api .dependencies .resource_usage_tracker_rpc import (
44+ get_resource_usage_tracker_client ,
45+ )
3646from simcore_service_api_server .api .dependencies .webserver_rpc import (
3747 get_wb_api_rpc_client ,
3848)
4151 LicensedItemCheckoutData ,
4252)
4353from simcore_service_api_server .models .schemas .model_adapter import LicensedItemGet
54+ from simcore_service_api_server .services_rpc .resource_usage_tracker import (
55+ ResourceUsageTrackerClient ,
56+ )
4457from simcore_service_api_server .services_rpc .wb_api_server import WbApiRpcClient
4558
4659
@@ -63,17 +76,27 @@ async def _get_backend_licensed_items(
6376 )
6477
6578
79+ class DummyRpcClient :
80+ pass
81+
82+
6683@pytest .fixture
6784async def mock_wb_api_server_rcp (app : FastAPI , mocker : MockerFixture ) -> MockerFixture :
68- class DummyRpcClient :
69- pass
7085
7186 app .dependency_overrides [get_wb_api_rpc_client ] = lambda : WbApiRpcClient (
7287 _client = DummyRpcClient ()
7388 )
7489 return mocker
7590
7691
92+ @pytest .fixture
93+ async def mock_rut_rpc (app : FastAPI , mocker : MockerFixture ) -> MockerFixture :
94+ app .dependency_overrides [
95+ get_resource_usage_tracker_client
96+ ] = lambda : ResourceUsageTrackerClient (_client = DummyRpcClient ())
97+ return mocker
98+
99+
77100async def test_get_licensed_items (
78101 mock_wb_api_server_rcp : MockerFixture , client : AsyncClient , auth : BasicAuth
79102):
@@ -173,7 +196,7 @@ async def side_effect(
173196 ),
174197 ],
175198)
176- async def test_get_licensed_items_checkout (
199+ async def test_checkout_licensed_item (
177200 mock_wb_api_server_rcp : MockerFixture ,
178201 client : AsyncClient ,
179202 auth : BasicAuth ,
@@ -218,3 +241,74 @@ async def side_effect(
218241 content = body .model_dump_json (),
219242 )
220243 assert resp .status_code == expected_api_server_status_code
244+
245+
246+ @pytest .mark .parametrize (
247+ "backend_exception_to_raise,expected_api_server_status_code,valid_license_checkout_id" ,
248+ [
249+ (None , status .HTTP_200_OK , True ),
250+ ],
251+ )
252+ async def test_release_checked_out_licensed_item (
253+ mock_wb_api_server_rcp : MockerFixture ,
254+ mock_rut_rpc : MockerFixture ,
255+ client : AsyncClient ,
256+ auth : BasicAuth ,
257+ backend_exception_to_raise : Exception | None ,
258+ expected_api_server_status_code : int ,
259+ valid_license_checkout_id : bool ,
260+ faker : Faker ,
261+ ):
262+ _licensed_item_id = cast (UUID , faker .uuid4 ())
263+ _licensed_item_checkout_id = cast (UUID , faker .uuid4 ())
264+
265+ async def get_licensed_item_checkout (
266+ rabbitmq_rpc_client : RabbitMQRPCClient ,
267+ product_name : str ,
268+ licensed_item_checkout_id : LicensedItemCheckoutID ,
269+ ) -> LicensedItemCheckoutGet :
270+ if backend_exception_to_raise is not None :
271+ raise backend_exception_to_raise
272+ extra = LicensedItemCheckoutGet .model_config .get ("json_schema_extra" )
273+ assert isinstance (extra , dict )
274+ examples = extra .get ("examples" )
275+ assert isinstance (examples , list )
276+ assert len (examples ) > 0
277+ example = examples [0 ]
278+ assert isinstance (example , dict )
279+ licensed_item_checkout_get = LicensedItemCheckoutGet .model_validate (example )
280+ if valid_license_checkout_id :
281+ licensed_item_checkout_get .licensed_item_id = _licensed_item_id
282+ return licensed_item_checkout_get
283+
284+ async def release_licensed_item_for_wallet (
285+ rabbitmq_rpc_client : RabbitMQRPCClient ,
286+ product_name : str ,
287+ user_id : int ,
288+ licensed_item_checkout_id : LicensedItemCheckoutID ,
289+ ) -> LicensedItemCheckoutRpcGet :
290+ if backend_exception_to_raise is not None :
291+ raise backend_exception_to_raise
292+ extra = LicensedItemCheckoutRpcGet .model_config .get ("json_schema_extra" )
293+ assert isinstance (extra , dict )
294+ examples = extra .get ("examples" )
295+ assert isinstance (examples , list )
296+ assert len (examples ) > 0
297+ example = examples [0 ]
298+ assert isinstance (example , dict )
299+ return LicensedItemCheckoutRpcGet .model_validate (example )
300+
301+ mock_rut_rpc .patch (
302+ "simcore_service_api_server.services_rpc.resource_usage_tracker._get_licensed_item_checkout" ,
303+ get_licensed_item_checkout ,
304+ )
305+ mock_wb_api_server_rcp .patch (
306+ "simcore_service_api_server.services_rpc.wb_api_server._release_licensed_item_for_wallet" ,
307+ release_licensed_item_for_wallet ,
308+ )
309+
310+ resp = await client .post (
311+ f"{ API_VTAG } /licensed-items/{ _licensed_item_id } /checked-out-items/{ _licensed_item_checkout_id } /release" ,
312+ auth = auth ,
313+ )
314+ assert resp .status_code == expected_api_server_status_code
0 commit comments