44# pylint: disable=too-many-arguments
55
66
7+ from decimal import Decimal
8+
79import pytest
810from aiohttp import web
911from aiohttp .test_utils import TestServer
10- from models_library .groups import GroupID
11- from models_library . products import ProductName
12- from pydantic import ValidationError
12+ from models_library .products import ProductName , ProductStripeInfoGet
13+ from pydantic import TypeAdapter , ValidationError
14+ from pytest_mock import MockerFixture
1315from servicelib .exceptions import InvalidConfig
1416from simcore_postgres_database .utils_products_prices import ProductPriceInfo
1517from simcore_service_webserver .products import _service , products_service
1618from simcore_service_webserver .products ._repository import ProductRepository
1719from simcore_service_webserver .products .errors import (
20+ BelowMinimumPaymentError ,
1821 MissingStripeConfigError ,
22+ ProductNotFoundError ,
1923 ProductPriceNotDefinedError ,
2024 ProductTemplateNotFoundError ,
2125)
@@ -41,7 +45,11 @@ async def test_load_products_validation_error(app: web.Application, mocker):
4145 mock_repo = mocker .patch (
4246 "simcore_service_webserver.products._service.ProductRepository.create_from_app"
4347 )
44- mock_repo .return_value .list_products .side_effect = ValidationError ("Invalid data" )
48+
49+ try :
50+ TypeAdapter (int ).validate_python ("not-an-int" )
51+ except ValidationError as validation_error :
52+ mock_repo .return_value .list_products .side_effect = validation_error
4553
4654 with pytest .raises (InvalidConfig , match = "Invalid product configuration in db" ):
4755 await _service .load_products (app )
@@ -53,7 +61,6 @@ async def test_get_default_product_name(app: web.Application):
5361
5462
5563async def test_get_product (app : web .Application , default_product_name : ProductName ):
56-
5764 product = products_service .get_product (app , product_name = default_product_name )
5865 assert product .name == default_product_name
5966
@@ -62,51 +69,115 @@ async def test_get_product(app: web.Application, default_product_name: ProductNa
6269 assert products [0 ] == product
6370
6471
65- async def test_get_product_ui (app : web .Application , default_product_name : ProductName ):
66- # this feature is currently setup from adminer by an operator
72+ async def test_products_on_uninitialized_app (default_product_name : ProductName ):
73+ uninit_app = web .Application ()
74+ with pytest .raises (ProductNotFoundError ):
75+ _service .get_product (uninit_app , default_product_name )
76+
77+
78+ async def test_list_products_names (app : web .Application ):
79+ product_names = await products_service .list_products_names (app )
80+ assert isinstance (product_names , list )
81+ assert all (isinstance (name , ProductName ) for name in product_names )
82+
83+
84+ async def test_get_credit_price_info (
85+ app : web .Application , default_product_name : ProductName
86+ ):
87+ price_info = await _service .get_credit_price_info (
88+ app , product_name = default_product_name
89+ )
90+ assert price_info is None or isinstance (price_info , ProductPriceInfo )
91+
6792
93+ async def test_get_product_ui (app : web .Application , default_product_name : ProductName ):
6894 repo = ProductRepository .create_from_app (app )
6995 ui = await products_service .get_product_ui (repo , product_name = default_product_name )
7096 assert ui == {}, "Expected empty by default"
7197
98+ with pytest .raises (ProductNotFoundError ):
99+ await products_service .get_product_ui (repo , product_name = "undefined" )
100+
101+
102+ async def test_get_credit_amount (
103+ app : web .Application , default_product_name : ProductName , mocker : MockerFixture
104+ ):
105+ # Test when ProductPriceNotDefinedError is raised
106+ with pytest .raises (ProductPriceNotDefinedError ):
107+ await products_service .get_credit_amount (
108+ app , dollar_amount = 1 , product_name = default_product_name
109+ )
110+
111+
112+ async def test_get_credit_amount_with_repo_faking_data (
113+ default_product_name : ProductName , mocker : MockerFixture
114+ ):
115+ # NO need of database since repo is mocked
116+ app = web .Application ()
117+
118+ # Mock the repository to return a valid price info
119+ mock_repo = mocker .patch (
120+ "simcore_service_webserver.products._service.ProductRepository.create_from_app"
121+ )
122+
123+ async def _get_product_latest_price_info_or_none (* args , ** kwargs ):
124+ return ProductPriceInfo (
125+ usd_per_credit = Decimal ("10.0" ), min_payment_amount_usd = Decimal ("5.0" )
126+ )
127+
128+ mock_repo .return_value .get_product_latest_price_info_or_none .side_effect = (
129+ _get_product_latest_price_info_or_none
130+ )
131+
132+ # Test when BelowMinimumPaymentError is raised
133+ with pytest .raises (BelowMinimumPaymentError ):
134+ await products_service .get_credit_amount (
135+ app , dollar_amount = Decimal ("3.0" ), product_name = default_product_name
136+ )
137+
138+ # Test when CreditResultGet is returned successfully
139+ credit_result = await products_service .get_credit_amount (
140+ app , dollar_amount = Decimal ("10.0" ), product_name = default_product_name
141+ )
142+ assert credit_result .credit_amount == Decimal ("1.0" )
143+ assert credit_result .product_name == default_product_name
144+
72145
73146async def test_get_product_stripe_info (
74147 app : web .Application , default_product_name : ProductName
75148):
76- # this feature is currently setup from adminer by an operator
77-
78- # default is not configured
149+ # database has no info
79150 with pytest .raises (MissingStripeConfigError , match = default_product_name ):
80151 await products_service .get_product_stripe_info (
81152 app , product_name = default_product_name
82153 )
83154
84155
85- async def test_get_credit_amount (
86- app : web . Application , default_product_name : ProductName
156+ async def test_get_product_stripe_info_with_repo_faking_data (
157+ default_product_name : ProductName , mocker : MockerFixture
87158):
88- # this feature is currently setup from adminer by an operator
159+ # NO need of database since repo is mocked
160+ app = web .Application ()
89161
90- # default is not configured
91- with pytest .raises (ProductPriceNotDefinedError ):
92- await products_service .get_credit_amount (
93- app , dollar_amount = 1 , product_name = default_product_name
94- )
162+ # Mock the repository to return a valid stripe info
163+ mock_repo = mocker .patch (
164+ "simcore_service_webserver.products._service.ProductRepository.create_from_app"
165+ )
95166
167+ # Test when stripe info is returned successfully
168+ expected_stripe_info = ProductStripeInfoGet (
169+ stripe_price_id = "price_id" , stripe_tax_rate_id = "tax_id"
170+ )
96171
97- async def test_list_products_names (app : web .Application ):
98- product_names = await products_service .list_products_names (app )
99- assert isinstance (product_names , list )
100- assert all (isinstance (name , ProductName ) for name in product_names )
172+ async def _mock (* args , ** kw ):
173+ return expected_stripe_info
101174
175+ mock_repo .return_value .get_product_stripe_info_or_none .side_effect = _mock
102176
103- async def test_get_credit_price_info (
104- app : web .Application , default_product_name : ProductName
105- ):
106- price_info = await _service .get_credit_price_info (
177+ stripe_info = await products_service .get_product_stripe_info (
107178 app , product_name = default_product_name
108179 )
109- assert price_info is None or isinstance ( price_info , ProductPriceInfo )
180+ assert stripe_info == expected_stripe_info
110181
111182
112183async def test_get_template_content (app : web .Application ):
@@ -118,5 +189,7 @@ async def test_get_template_content(app: web.Application):
118189async def test_auto_create_products_groups (app : web .Application ):
119190 groups = await _service .auto_create_products_groups (app )
120191 assert isinstance (groups , dict )
121- assert all (isinstance (name , ProductName ) for name in groups .keys ())
122- assert all (isinstance (group_id , GroupID ) for group_id in groups .values ())
192+
193+ assert all (
194+ group_id is not None for group_id in groups .values ()
195+ ), f"Invalid { groups } "
0 commit comments