4
4
# pylint: disable=too-many-arguments
5
5
6
6
7
+ from decimal import Decimal
8
+
7
9
import pytest
8
10
from aiohttp import web
9
11
from 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
13
15
from servicelib .exceptions import InvalidConfig
14
16
from simcore_postgres_database .utils_products_prices import ProductPriceInfo
15
17
from simcore_service_webserver .products import _service , products_service
16
18
from simcore_service_webserver .products ._repository import ProductRepository
17
19
from simcore_service_webserver .products .errors import (
20
+ BelowMinimumPaymentError ,
18
21
MissingStripeConfigError ,
22
+ ProductNotFoundError ,
19
23
ProductPriceNotDefinedError ,
20
24
ProductTemplateNotFoundError ,
21
25
)
@@ -41,7 +45,11 @@ async def test_load_products_validation_error(app: web.Application, mocker):
41
45
mock_repo = mocker .patch (
42
46
"simcore_service_webserver.products._service.ProductRepository.create_from_app"
43
47
)
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
45
53
46
54
with pytest .raises (InvalidConfig , match = "Invalid product configuration in db" ):
47
55
await _service .load_products (app )
@@ -53,7 +61,6 @@ async def test_get_default_product_name(app: web.Application):
53
61
54
62
55
63
async def test_get_product (app : web .Application , default_product_name : ProductName ):
56
-
57
64
product = products_service .get_product (app , product_name = default_product_name )
58
65
assert product .name == default_product_name
59
66
@@ -62,51 +69,115 @@ async def test_get_product(app: web.Application, default_product_name: ProductNa
62
69
assert products [0 ] == product
63
70
64
71
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
+
67
92
93
+ async def test_get_product_ui (app : web .Application , default_product_name : ProductName ):
68
94
repo = ProductRepository .create_from_app (app )
69
95
ui = await products_service .get_product_ui (repo , product_name = default_product_name )
70
96
assert ui == {}, "Expected empty by default"
71
97
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
+
72
145
73
146
async def test_get_product_stripe_info (
74
147
app : web .Application , default_product_name : ProductName
75
148
):
76
- # this feature is currently setup from adminer by an operator
77
-
78
- # default is not configured
149
+ # database has no info
79
150
with pytest .raises (MissingStripeConfigError , match = default_product_name ):
80
151
await products_service .get_product_stripe_info (
81
152
app , product_name = default_product_name
82
153
)
83
154
84
155
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
87
158
):
88
- # this feature is currently setup from adminer by an operator
159
+ # NO need of database since repo is mocked
160
+ app = web .Application ()
89
161
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
+ )
95
166
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
+ )
96
171
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
101
174
175
+ mock_repo .return_value .get_product_stripe_info_or_none .side_effect = _mock
102
176
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 (
107
178
app , product_name = default_product_name
108
179
)
109
- assert price_info is None or isinstance ( price_info , ProductPriceInfo )
180
+ assert stripe_info == expected_stripe_info
110
181
111
182
112
183
async def test_get_template_content (app : web .Application ):
@@ -118,5 +189,7 @@ async def test_get_template_content(app: web.Application):
118
189
async def test_auto_create_products_groups (app : web .Application ):
119
190
groups = await _service .auto_create_products_groups (app )
120
191
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