matlab-proxy 0.15.0__py3-none-any.whl → 0.16.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of matlab-proxy might be problematic. Click here for more details.
- matlab_proxy/app.py +13 -7
- matlab_proxy/app_state.py +9 -6
- matlab_proxy/constants.py +1 -0
- matlab_proxy/gui/asset-manifest.json +3 -3
- matlab_proxy/gui/index.html +1 -1
- matlab_proxy/gui/static/js/{main.14aa7840.js → main.522d83ba.js} +3 -3
- matlab_proxy/gui/static/js/main.522d83ba.js.map +1 -0
- matlab_proxy/settings.py +7 -4
- matlab_proxy/util/__init__.py +8 -1
- matlab_proxy/util/mwi/token_auth.py +19 -5
- {matlab_proxy-0.15.0.dist-info → matlab_proxy-0.16.0.dist-info}/METADATA +1 -1
- {matlab_proxy-0.15.0.dist-info → matlab_proxy-0.16.0.dist-info}/RECORD +40 -20
- tests/integration/integration_tests_with_license/test_http_end_points.py +4 -4
- tests/integration/integration_tests_without_license/conftest.py +4 -3
- tests/integration/integration_tests_without_license/test_matlab_is_down_if_unlicensed.py +3 -0
- tests/unit/__init__.py +1 -0
- tests/unit/conftest.py +67 -0
- tests/unit/test_app.py +1113 -0
- tests/unit/test_app_state.py +586 -0
- tests/unit/test_constants.py +6 -0
- tests/unit/test_ddux.py +22 -0
- tests/unit/test_devel.py +246 -0
- tests/unit/test_non_dev_mode.py +169 -0
- tests/unit/test_settings.py +460 -0
- tests/unit/util/__init__.py +3 -0
- tests/unit/util/mwi/__init__.py +1 -0
- tests/unit/util/mwi/embedded_connector/__init__.py +1 -0
- tests/unit/util/mwi/embedded_connector/test_helpers.py +29 -0
- tests/unit/util/mwi/embedded_connector/test_request.py +64 -0
- tests/unit/util/mwi/test_custom_http_headers.py +281 -0
- tests/unit/util/mwi/test_logger.py +49 -0
- tests/unit/util/mwi/test_token_auth.py +303 -0
- tests/unit/util/mwi/test_validators.py +331 -0
- tests/unit/util/test_mw.py +550 -0
- tests/unit/util/test_util.py +135 -0
- matlab_proxy/gui/static/js/main.14aa7840.js.map +0 -1
- /matlab_proxy/gui/static/js/{main.14aa7840.js.LICENSE.txt → main.522d83ba.js.LICENSE.txt} +0 -0
- {matlab_proxy-0.15.0.dist-info → matlab_proxy-0.16.0.dist-info}/LICENSE.md +0 -0
- {matlab_proxy-0.15.0.dist-info → matlab_proxy-0.16.0.dist-info}/WHEEL +0 -0
- {matlab_proxy-0.15.0.dist-info → matlab_proxy-0.16.0.dist-info}/entry_points.txt +0 -0
- {matlab_proxy-0.15.0.dist-info → matlab_proxy-0.16.0.dist-info}/top_level.txt +0 -0
tests/unit/test_app.py
ADDED
|
@@ -0,0 +1,1113 @@
|
|
|
1
|
+
# Copyright 2020-2024 The MathWorks, Inc.
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import datetime
|
|
5
|
+
import json
|
|
6
|
+
import platform
|
|
7
|
+
import random
|
|
8
|
+
import time
|
|
9
|
+
from datetime import timedelta, timezone
|
|
10
|
+
from http import HTTPStatus
|
|
11
|
+
|
|
12
|
+
import aiohttp
|
|
13
|
+
import pytest
|
|
14
|
+
import tests.unit.test_constants as test_constants
|
|
15
|
+
|
|
16
|
+
from matlab_proxy import app, util
|
|
17
|
+
from matlab_proxy.util.mwi import environment_variables as mwi_env
|
|
18
|
+
from matlab_proxy.util.mwi.exceptions import EntitlementError, MatlabInstallError
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@pytest.mark.parametrize(
|
|
22
|
+
"no_proxy_user_configuration",
|
|
23
|
+
[
|
|
24
|
+
"",
|
|
25
|
+
"1234.1234.1234, localhost , 0.0.0.0,1.2.3.4",
|
|
26
|
+
"0.0.0.0",
|
|
27
|
+
"1234.1234.1234",
|
|
28
|
+
" 1234.1234.1234 ",
|
|
29
|
+
],
|
|
30
|
+
)
|
|
31
|
+
def test_configure_no_proxy_in_env(monkeypatch, no_proxy_user_configuration):
|
|
32
|
+
"""Tests the behavior of the configure_no_proxy_in_env function
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
monkeypatch (environment): MonkeyPatches the environment to mimic possible user environment settings
|
|
36
|
+
"""
|
|
37
|
+
no_proxy_user_configuration_set = set(
|
|
38
|
+
[val.lstrip().rstrip() for val in no_proxy_user_configuration.split(",")]
|
|
39
|
+
)
|
|
40
|
+
# Update the environment to simulate user environment
|
|
41
|
+
monkeypatch.setenv("no_proxy", no_proxy_user_configuration)
|
|
42
|
+
|
|
43
|
+
# This function will modify the environment variables to include 0.0.0.0, localhost & 127.0.0.1
|
|
44
|
+
app.configure_no_proxy_in_env()
|
|
45
|
+
|
|
46
|
+
import os
|
|
47
|
+
|
|
48
|
+
modified_no_proxy_env = os.environ.get("no_proxy")
|
|
49
|
+
|
|
50
|
+
# Convert to set to compare, as list generated need not be ordered
|
|
51
|
+
modified_no_proxy_env_set = set(
|
|
52
|
+
[val.lstrip().rstrip() for val in modified_no_proxy_env.split(",")]
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
expected_no_proxy_configuration_set = {"0.0.0.0", "localhost", "127.0.0.1"}
|
|
56
|
+
|
|
57
|
+
# We expect the modified set of values to include the localhost configurations
|
|
58
|
+
# along with whatever else the user had set with no duplicates.
|
|
59
|
+
assert modified_no_proxy_env_set == no_proxy_user_configuration_set.union(
|
|
60
|
+
expected_no_proxy_configuration_set
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_create_app():
|
|
65
|
+
"""Test if aiohttp server is being created successfully.
|
|
66
|
+
|
|
67
|
+
Checks if the aiohttp server is created successfully, routes, startup and cleanup
|
|
68
|
+
tasks are added.
|
|
69
|
+
"""
|
|
70
|
+
test_server = app.create_app()
|
|
71
|
+
|
|
72
|
+
# Verify router is configured with some routes
|
|
73
|
+
assert test_server.router._resources is not None
|
|
74
|
+
|
|
75
|
+
# Verify app server has a cleanup task
|
|
76
|
+
# By default there is 1 for clean up task
|
|
77
|
+
assert len(test_server.on_cleanup) > 1
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def get_email():
|
|
81
|
+
"""Returns a placeholder email
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
String: A placeholder email as a string.
|
|
85
|
+
"""
|
|
86
|
+
return "abc@mathworks.com"
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def get_connection_string():
|
|
90
|
+
"""Returns a placeholder nlm connection string
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
String : A placeholder nlm connection string
|
|
94
|
+
"""
|
|
95
|
+
return "nlm@localhost.com"
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
async def wait_for_matlab_to_be_up(test_server, sleep_seconds):
|
|
99
|
+
"""Checks at max five times for the MATLAB status to be up and throws ConnectionError
|
|
100
|
+
if MATLAB status is not up.
|
|
101
|
+
|
|
102
|
+
This function mitigates the scenario where the tests may try to send the request
|
|
103
|
+
to the test server and the MATLAB status is not up yet which may cause the test to fail
|
|
104
|
+
unexpectedly.
|
|
105
|
+
|
|
106
|
+
Use this function if the test intends to wait for the matlab status to be up before
|
|
107
|
+
sending any requests.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
test_server (aiohttp_client) : A aiohttp_client server to send HTTP GET request.
|
|
111
|
+
sleep_seconds : Seconds to be sent to the asyncio.sleep method
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
count = 0
|
|
115
|
+
while True:
|
|
116
|
+
resp = await test_server.get("/get_status")
|
|
117
|
+
assert resp.status == HTTPStatus.OK
|
|
118
|
+
|
|
119
|
+
resp_json = json.loads(await resp.text())
|
|
120
|
+
|
|
121
|
+
if resp_json["matlab"]["status"] == "up":
|
|
122
|
+
break
|
|
123
|
+
else:
|
|
124
|
+
count += 1
|
|
125
|
+
await asyncio.sleep(sleep_seconds)
|
|
126
|
+
if count > test_constants.FIVE_MAX_TRIES:
|
|
127
|
+
raise ConnectionError
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@pytest.fixture(
|
|
131
|
+
name="licensing_data",
|
|
132
|
+
params=[
|
|
133
|
+
{"input": None, "expected": None},
|
|
134
|
+
{
|
|
135
|
+
"input": {"type": "mhlm", "email_addr": get_email()},
|
|
136
|
+
"expected": {
|
|
137
|
+
"type": "mhlm",
|
|
138
|
+
"emailAddress": get_email(),
|
|
139
|
+
"entitlements": [],
|
|
140
|
+
"entitlementId": None,
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"input": {"type": "nlm", "conn_str": get_connection_string()},
|
|
145
|
+
"expected": {"type": "nlm", "connectionString": get_connection_string()},
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"input": {"type": "existing_license"},
|
|
149
|
+
"expected": {"type": "existing_license"},
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
ids=[
|
|
153
|
+
"No Licensing info supplied",
|
|
154
|
+
"Licensing type is mhlm",
|
|
155
|
+
"Licensing type is nlm",
|
|
156
|
+
"Licensing type is existing_license",
|
|
157
|
+
],
|
|
158
|
+
)
|
|
159
|
+
def licensing_info_fixture(request):
|
|
160
|
+
"""A pytest fixture which returns licensing_data
|
|
161
|
+
|
|
162
|
+
A parameterized pytest fixture which returns a licensing_data dict.
|
|
163
|
+
licensing_data of three types:
|
|
164
|
+
None : No licensing
|
|
165
|
+
MHLM : Matlab Hosted License Manager
|
|
166
|
+
NLM : Network License Manager.
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
request : A built-in pytest fixture
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
Array : Containing expected and actual licensing data.
|
|
174
|
+
"""
|
|
175
|
+
return request.param
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def test_marshal_licensing_info(licensing_data):
|
|
179
|
+
"""Test app.marshal_licensing_info method works correctly
|
|
180
|
+
|
|
181
|
+
This test checks if app.marshal_licensing_info returns correct licensing data.
|
|
182
|
+
Test checks for 3 cases:
|
|
183
|
+
1) No Licensing Provided
|
|
184
|
+
2) MHLM type Licensing
|
|
185
|
+
3) NLM type licensing
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
licensing_data (Array): An array containing actual and expected licensing data to assert.
|
|
189
|
+
"""
|
|
190
|
+
|
|
191
|
+
actual_licensing_info = licensing_data["input"]
|
|
192
|
+
expected_licensing_info = licensing_data["expected"]
|
|
193
|
+
|
|
194
|
+
assert app.marshal_licensing_info(actual_licensing_info) == expected_licensing_info
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
@pytest.mark.parametrize(
|
|
198
|
+
"actual_error, expected_error",
|
|
199
|
+
[
|
|
200
|
+
(None, None),
|
|
201
|
+
(
|
|
202
|
+
MatlabInstallError("'matlab' executable not found in PATH"),
|
|
203
|
+
{
|
|
204
|
+
"message": "'matlab' executable not found in PATH",
|
|
205
|
+
"logs": None,
|
|
206
|
+
"type": MatlabInstallError.__name__,
|
|
207
|
+
},
|
|
208
|
+
),
|
|
209
|
+
],
|
|
210
|
+
ids=["No error", "Raise Matlab Install Error"],
|
|
211
|
+
)
|
|
212
|
+
def test_marshal_error(actual_error, expected_error):
|
|
213
|
+
"""Test if marshal_error returns an expected Dict when an error is raised
|
|
214
|
+
|
|
215
|
+
Upon raising MatlabInstallError, checks if the the relevant information is returned as a
|
|
216
|
+
Dict.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
actual_error (Exception): An instance of Exception class
|
|
220
|
+
expected_error (Dict): A python Dict containing information on the type of Exception
|
|
221
|
+
"""
|
|
222
|
+
assert app.marshal_error(actual_error) == expected_error
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
class FakeServer:
|
|
226
|
+
"""Context Manager class which returns a web server wrapped in aiohttp_client pytest fixture
|
|
227
|
+
for testing.
|
|
228
|
+
|
|
229
|
+
The server setup and startup does not need to mimic the way it is being done in main() method in app.py.
|
|
230
|
+
Setting up the server in the context of Pytest.
|
|
231
|
+
"""
|
|
232
|
+
|
|
233
|
+
def __init__(self, loop, aiohttp_client):
|
|
234
|
+
self.loop = loop
|
|
235
|
+
self.aiohttp_client = aiohttp_client
|
|
236
|
+
|
|
237
|
+
def __enter__(self):
|
|
238
|
+
server = app.create_app()
|
|
239
|
+
self.server = app.configure_and_start(server)
|
|
240
|
+
return self.loop.run_until_complete(self.aiohttp_client(self.server))
|
|
241
|
+
|
|
242
|
+
def __exit__(self, exc_type, exc_value, exc_traceback):
|
|
243
|
+
self.loop.run_until_complete(self.server.shutdown())
|
|
244
|
+
self.loop.run_until_complete(self.server.cleanup())
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
@pytest.fixture(name="test_server")
|
|
248
|
+
def test_server_fixture(
|
|
249
|
+
loop,
|
|
250
|
+
aiohttp_client,
|
|
251
|
+
monkeypatch,
|
|
252
|
+
):
|
|
253
|
+
"""A pytest fixture which yields a test server to be used by tests.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
loop (Event loop): The built-in event loop provided by pytest.
|
|
257
|
+
aiohttp_client (aiohttp_client): Built-in pytest fixture used as a wrapper to the aiohttp web server.
|
|
258
|
+
|
|
259
|
+
Yields:
|
|
260
|
+
aiohttp_client : A aiohttp_client server used by tests.
|
|
261
|
+
"""
|
|
262
|
+
# Disabling the authentication token mechanism explicitly
|
|
263
|
+
monkeypatch.setenv(mwi_env.get_env_name_enable_mwi_auth_token(), "False")
|
|
264
|
+
try:
|
|
265
|
+
with FakeServer(loop, aiohttp_client) as test_server:
|
|
266
|
+
yield test_server
|
|
267
|
+
except ProcessLookupError:
|
|
268
|
+
pass
|
|
269
|
+
finally:
|
|
270
|
+
# Cleaning up the env variable related to auth token
|
|
271
|
+
monkeypatch.delenv(
|
|
272
|
+
mwi_env.get_env_name_enable_mwi_auth_token(), raising="False"
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
async def test_get_status_route(test_server):
|
|
277
|
+
"""Test to check endpoint : "/get_status"
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
test_server (aiohttp_client): A aiohttp_client server for sending GET request.
|
|
281
|
+
"""
|
|
282
|
+
|
|
283
|
+
resp = await test_server.get("/get_status")
|
|
284
|
+
assert resp.status == HTTPStatus.OK
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
async def test_get_env_config(test_server):
|
|
288
|
+
"""Test to check endpoint : "/get_env_config"
|
|
289
|
+
|
|
290
|
+
Args:
|
|
291
|
+
test_server (aiohttp_client): A aiohttp_client server for sending GET request.
|
|
292
|
+
"""
|
|
293
|
+
expected_json_structure = {
|
|
294
|
+
"useMOS": False,
|
|
295
|
+
"useMRE": False,
|
|
296
|
+
"authentication": {"enabled": False, "status": False},
|
|
297
|
+
"matlab": {
|
|
298
|
+
"status": "up",
|
|
299
|
+
"version": "R2023a",
|
|
300
|
+
"supported_versions": ["R2020b", "R2023a"],
|
|
301
|
+
},
|
|
302
|
+
"doc_url": "foo",
|
|
303
|
+
"extension_name": "bar",
|
|
304
|
+
"extension_name_short_description": "foobar",
|
|
305
|
+
"isConcurrencyEnabled": "foobar",
|
|
306
|
+
}
|
|
307
|
+
resp = await test_server.get("/get_env_config")
|
|
308
|
+
assert resp.status == HTTPStatus.OK
|
|
309
|
+
|
|
310
|
+
text = await resp.json()
|
|
311
|
+
assert text is not None
|
|
312
|
+
assert set(expected_json_structure.keys()) == set(text.keys())
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
async def test_start_matlab_route(test_server):
|
|
316
|
+
"""Test to check endpoint : "/start_matlab"
|
|
317
|
+
|
|
318
|
+
Test waits for matlab status to be "up" before sending the GET request to start matlab
|
|
319
|
+
Checks whether matlab restarts.
|
|
320
|
+
|
|
321
|
+
Args:
|
|
322
|
+
test_server (aiohttp_client): A aiohttp_client server to send GET request to.
|
|
323
|
+
"""
|
|
324
|
+
# Waiting for the matlab process to start up.
|
|
325
|
+
sleep_interval = 1
|
|
326
|
+
await wait_for_matlab_to_be_up(test_server, sleep_interval)
|
|
327
|
+
|
|
328
|
+
# Send get request to end point
|
|
329
|
+
await test_server.put("/start_matlab")
|
|
330
|
+
|
|
331
|
+
# Check if Matlab restarted successfully
|
|
332
|
+
await check_for_matlab_startup(test_server)
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
async def check_for_matlab_startup(test_server):
|
|
336
|
+
count = 0
|
|
337
|
+
while True:
|
|
338
|
+
resp = await test_server.get("/get_status")
|
|
339
|
+
assert resp.status == HTTPStatus.OK
|
|
340
|
+
resp_json = json.loads(await resp.text())
|
|
341
|
+
if resp_json["matlab"]["status"] != "down":
|
|
342
|
+
break
|
|
343
|
+
else:
|
|
344
|
+
count += 1
|
|
345
|
+
await asyncio.sleep(0.5)
|
|
346
|
+
if count > test_constants.FIVE_MAX_TRIES:
|
|
347
|
+
raise ConnectionError
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
async def test_stop_matlab_route(test_server):
|
|
351
|
+
"""Test to check endpoint : "/stop_matlab"
|
|
352
|
+
|
|
353
|
+
Sends HTTP DELETE request to stop matlab and checks if matlab status is down.
|
|
354
|
+
Args:
|
|
355
|
+
test_server (aiohttp_client): A aiohttp_client server to send HTTP DELETE request.
|
|
356
|
+
"""
|
|
357
|
+
resp = await test_server.delete("/stop_matlab")
|
|
358
|
+
assert resp.status == HTTPStatus.OK
|
|
359
|
+
|
|
360
|
+
resp_json = json.loads(await resp.text())
|
|
361
|
+
assert resp_json["matlab"]["status"] == "down"
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
async def test_root_redirect(test_server):
|
|
365
|
+
"""Test to check endpoint : "/"
|
|
366
|
+
|
|
367
|
+
Should throw a 404 error. This will look for index.html in root directory of the project
|
|
368
|
+
(In non-dev mode, root directory is the package)
|
|
369
|
+
This file will not be available in the expected location in dev mode.
|
|
370
|
+
|
|
371
|
+
Args:
|
|
372
|
+
test_server (aiohttp_client): A aiohttp_client server to send HTTP GET request.
|
|
373
|
+
|
|
374
|
+
"""
|
|
375
|
+
count = 0
|
|
376
|
+
while True:
|
|
377
|
+
resp = await test_server.get("/")
|
|
378
|
+
if resp.status == HTTPStatus.SERVICE_UNAVAILABLE:
|
|
379
|
+
time.sleep(test_constants.ONE_SECOND_DELAY)
|
|
380
|
+
count += 1
|
|
381
|
+
else:
|
|
382
|
+
assert resp.status == HTTPStatus.NOT_FOUND
|
|
383
|
+
break
|
|
384
|
+
|
|
385
|
+
if count > test_constants.FIVE_MAX_TRIES:
|
|
386
|
+
raise ConnectionError
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
@pytest.fixture(name="proxy_payload")
|
|
390
|
+
def proxy_payload_fixture():
|
|
391
|
+
"""Pytest fixture which returns a Dict representing the payload.
|
|
392
|
+
|
|
393
|
+
Returns:
|
|
394
|
+
Dict: A Dict representing the payload for HTTP request.
|
|
395
|
+
"""
|
|
396
|
+
payload = {"messages": {"ClientType": [{"properties": {"TYPE": "jsd"}}]}}
|
|
397
|
+
|
|
398
|
+
return payload
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
async def test_matlab_proxy_404(proxy_payload, test_server):
|
|
402
|
+
"""Test to check if test_server is able to proxy HTTP request to fake matlab server
|
|
403
|
+
for a non-existing file. Should return 404 status code in response
|
|
404
|
+
|
|
405
|
+
Args:
|
|
406
|
+
proxy_payload (Dict): Pytest fixture which returns a Dict.
|
|
407
|
+
test_server (aiohttp_client): Test server to send HTTP requests.
|
|
408
|
+
"""
|
|
409
|
+
|
|
410
|
+
headers = {"content-type": "application/json"}
|
|
411
|
+
|
|
412
|
+
# Request a non-existing html file.
|
|
413
|
+
# Request gets proxied to app.matlab_view() which should raise HTTPNotFound() exception ie. return HTTP status code 404
|
|
414
|
+
|
|
415
|
+
count = 0
|
|
416
|
+
while True:
|
|
417
|
+
resp = await test_server.post(
|
|
418
|
+
"./1234.html", data=json.dumps(proxy_payload), headers=headers
|
|
419
|
+
)
|
|
420
|
+
if resp.status == HTTPStatus.SERVICE_UNAVAILABLE:
|
|
421
|
+
time.sleep(test_constants.ONE_SECOND_DELAY)
|
|
422
|
+
count += 1
|
|
423
|
+
else:
|
|
424
|
+
assert resp.status == HTTPStatus.NOT_FOUND
|
|
425
|
+
break
|
|
426
|
+
|
|
427
|
+
if count > test_constants.FIVE_MAX_TRIES:
|
|
428
|
+
raise ConnectionError
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
async def test_matlab_proxy_http_get_request(proxy_payload, test_server):
|
|
432
|
+
"""Test to check if test_server proxies a HTTP request to fake matlab server and returns
|
|
433
|
+
the response back
|
|
434
|
+
|
|
435
|
+
Args:
|
|
436
|
+
proxy_payload (Dict): Pytest fixture which returns a Dict representing payload for the HTTP request
|
|
437
|
+
test_server (aiohttp_client): Test server to send HTTP requests.
|
|
438
|
+
|
|
439
|
+
Raises:
|
|
440
|
+
ConnectionError: If fake matlab server is not reachable from the test server, raises ConnectionError
|
|
441
|
+
"""
|
|
442
|
+
|
|
443
|
+
max_tries = 5
|
|
444
|
+
count = 0
|
|
445
|
+
|
|
446
|
+
while True:
|
|
447
|
+
resp = await test_server.get(
|
|
448
|
+
"/http_get_request.html", data=json.dumps(proxy_payload)
|
|
449
|
+
)
|
|
450
|
+
|
|
451
|
+
if resp.status in (HTTPStatus.NOT_FOUND, HTTPStatus.SERVICE_UNAVAILABLE):
|
|
452
|
+
time.sleep(1)
|
|
453
|
+
count += 1
|
|
454
|
+
|
|
455
|
+
else:
|
|
456
|
+
resp_body = await resp.text()
|
|
457
|
+
assert json.dumps(proxy_payload) == resp_body
|
|
458
|
+
break
|
|
459
|
+
|
|
460
|
+
if count > max_tries:
|
|
461
|
+
raise ConnectionError
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
async def test_matlab_proxy_http_put_request(proxy_payload, test_server):
|
|
465
|
+
"""Test to check if test_server proxies a HTTP request to fake matlab server and returns
|
|
466
|
+
the response back
|
|
467
|
+
|
|
468
|
+
Args:
|
|
469
|
+
proxy_payload (Dict): Pytest fixture which returns a Dict representing payload for the HTTP request
|
|
470
|
+
test_server (aiohttp_client): Test server to send HTTP requests.
|
|
471
|
+
|
|
472
|
+
Raises:
|
|
473
|
+
ConnectionError: If fake matlab server is not reachable from the test server, raises ConnectionError
|
|
474
|
+
"""
|
|
475
|
+
|
|
476
|
+
max_tries = 5
|
|
477
|
+
count = 0
|
|
478
|
+
|
|
479
|
+
while True:
|
|
480
|
+
resp = await test_server.put(
|
|
481
|
+
"/http_put_request.html", data=json.dumps(proxy_payload)
|
|
482
|
+
)
|
|
483
|
+
|
|
484
|
+
if resp.status in (HTTPStatus.NOT_FOUND, HTTPStatus.SERVICE_UNAVAILABLE):
|
|
485
|
+
time.sleep(1)
|
|
486
|
+
count += 1
|
|
487
|
+
|
|
488
|
+
else:
|
|
489
|
+
resp_body = await resp.text()
|
|
490
|
+
assert json.dumps(proxy_payload) == resp_body
|
|
491
|
+
break
|
|
492
|
+
|
|
493
|
+
if count > max_tries:
|
|
494
|
+
raise ConnectionError
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
async def test_matlab_proxy_http_delete_request(proxy_payload, test_server):
|
|
498
|
+
"""Test to check if test_server proxies a HTTP request to fake matlab server and returns
|
|
499
|
+
the response back
|
|
500
|
+
|
|
501
|
+
Args:
|
|
502
|
+
proxy_payload (Dict): Pytest fixture which returns a Dict representing payload for the HTTP request
|
|
503
|
+
test_server (aiohttp_client): Test server to send HTTP requests.
|
|
504
|
+
|
|
505
|
+
Raises:
|
|
506
|
+
ConnectionError: If fake matlab server is not reachable from the test server, raises ConnectionError
|
|
507
|
+
"""
|
|
508
|
+
|
|
509
|
+
max_tries = 5
|
|
510
|
+
count = 0
|
|
511
|
+
|
|
512
|
+
while True:
|
|
513
|
+
resp = await test_server.delete(
|
|
514
|
+
"/http_delete_request.html", data=json.dumps(proxy_payload)
|
|
515
|
+
)
|
|
516
|
+
|
|
517
|
+
if resp.status in (HTTPStatus.NOT_FOUND, HTTPStatus.SERVICE_UNAVAILABLE):
|
|
518
|
+
time.sleep(1)
|
|
519
|
+
count += 1
|
|
520
|
+
|
|
521
|
+
else:
|
|
522
|
+
resp_body = await resp.text()
|
|
523
|
+
assert json.dumps(proxy_payload) == resp_body
|
|
524
|
+
break
|
|
525
|
+
|
|
526
|
+
if count > max_tries:
|
|
527
|
+
raise ConnectionError
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
async def test_matlab_proxy_http_post_request(proxy_payload, test_server):
|
|
531
|
+
"""Test to check if test_server proxies http post request to fake matlab server.
|
|
532
|
+
Checks if payload is being modified before proxying.
|
|
533
|
+
Args:
|
|
534
|
+
proxy_payload (Dict): Pytest fixture which returns a Dict representing payload for the HTTP Request
|
|
535
|
+
test_server (aiohttp_client): Test server to send HTTP requests
|
|
536
|
+
|
|
537
|
+
Raises:
|
|
538
|
+
ConnectionError: If unable to proxy to fake matlab server raise Connection error
|
|
539
|
+
"""
|
|
540
|
+
max_tries = 5
|
|
541
|
+
count = 0
|
|
542
|
+
|
|
543
|
+
while True:
|
|
544
|
+
resp = await test_server.post(
|
|
545
|
+
"/messageservice/json/secure",
|
|
546
|
+
data=json.dumps(proxy_payload),
|
|
547
|
+
)
|
|
548
|
+
|
|
549
|
+
if resp.status in (HTTPStatus.NOT_FOUND, HTTPStatus.SERVICE_UNAVAILABLE):
|
|
550
|
+
time.sleep(1)
|
|
551
|
+
count += 1
|
|
552
|
+
|
|
553
|
+
else:
|
|
554
|
+
resp_json = await resp.json()
|
|
555
|
+
assert set(resp_json.keys()).issubset(proxy_payload.keys())
|
|
556
|
+
break
|
|
557
|
+
|
|
558
|
+
if count > max_tries:
|
|
559
|
+
raise ConnectionError
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
# While acceessing matlab-proxy directly, the web socket request looks like
|
|
563
|
+
# {
|
|
564
|
+
# "connection": "Upgrade",
|
|
565
|
+
# "Upgrade": "websocket",
|
|
566
|
+
# }
|
|
567
|
+
# whereas while accessing matlab-proxy with nginx as the reverse proxy, the nginx server
|
|
568
|
+
# modifies the web socket request to
|
|
569
|
+
# {
|
|
570
|
+
# "connection": "upgrade",
|
|
571
|
+
# "upgrade": "websocket",
|
|
572
|
+
# }
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
async def test_set_licensing_info_put_nlm(test_server):
|
|
576
|
+
"""Test to check endpoint : "/set_licensing_info"
|
|
577
|
+
|
|
578
|
+
Test which sends HTTP PUT request with NLM licensing information.
|
|
579
|
+
Args:
|
|
580
|
+
test_server (aiohttp_client): A aiohttp_client server to send HTTP GET request.
|
|
581
|
+
"""
|
|
582
|
+
|
|
583
|
+
data = {
|
|
584
|
+
"type": "nlm",
|
|
585
|
+
"status": "starting",
|
|
586
|
+
"version": "R2020b",
|
|
587
|
+
"connectionString": "123@nlm",
|
|
588
|
+
}
|
|
589
|
+
resp = await test_server.put("/set_licensing_info", data=json.dumps(data))
|
|
590
|
+
assert resp.status == HTTPStatus.OK
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
async def test_set_licensing_info_put_invalid_license(test_server):
|
|
594
|
+
"""Test to check endpoint : "/set_licensing_info"
|
|
595
|
+
|
|
596
|
+
Test which sends HTTP PUT request with INVALID licensing information type.
|
|
597
|
+
Args:
|
|
598
|
+
test_server (aiohttp_client): A aiohttp_client server to send HTTP GET request.
|
|
599
|
+
"""
|
|
600
|
+
|
|
601
|
+
data = {
|
|
602
|
+
"type": "INVALID_TYPE",
|
|
603
|
+
"status": "starting",
|
|
604
|
+
"version": "R2020b",
|
|
605
|
+
"connectionString": "123@nlm",
|
|
606
|
+
}
|
|
607
|
+
resp = await test_server.put("/set_licensing_info", data=json.dumps(data))
|
|
608
|
+
assert resp.status == HTTPStatus.BAD_REQUEST
|
|
609
|
+
|
|
610
|
+
|
|
611
|
+
@pytest.mark.parametrize(
|
|
612
|
+
"headers",
|
|
613
|
+
[
|
|
614
|
+
{
|
|
615
|
+
"connection": "Upgrade",
|
|
616
|
+
"Upgrade": "websocket",
|
|
617
|
+
},
|
|
618
|
+
{
|
|
619
|
+
"connection": "upgrade",
|
|
620
|
+
"upgrade": "websocket",
|
|
621
|
+
},
|
|
622
|
+
],
|
|
623
|
+
ids=["Uppercase header", "Lowercase header"],
|
|
624
|
+
)
|
|
625
|
+
async def test_matlab_proxy_web_socket(test_server, headers):
|
|
626
|
+
"""Test to check if test_server proxies web socket request to fake matlab server
|
|
627
|
+
|
|
628
|
+
Args:
|
|
629
|
+
test_server (aiohttp_client): Test Server to send HTTP Requests.
|
|
630
|
+
"""
|
|
631
|
+
|
|
632
|
+
sleep_interval = 1
|
|
633
|
+
await wait_for_matlab_to_be_up(test_server, sleep_interval)
|
|
634
|
+
resp = await test_server.ws_connect("/http_ws_request.html/", headers=headers)
|
|
635
|
+
text = await resp.receive()
|
|
636
|
+
websocket_response_string = (
|
|
637
|
+
"Hello world" # This string is set by the web_socket_handler in devel.py
|
|
638
|
+
)
|
|
639
|
+
assert text.type == aiohttp.WSMsgType.TEXT
|
|
640
|
+
assert text.data == websocket_response_string
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
async def test_set_licensing_info_put_mhlm(test_server):
|
|
644
|
+
"""Test to check endpoint : "/set_licensing_info"
|
|
645
|
+
|
|
646
|
+
Test which sends HTTP PUT request with MHLM licensing information.
|
|
647
|
+
Args:
|
|
648
|
+
test_server (aiohttp_client): A aiohttp_client server to send HTTP GET request.
|
|
649
|
+
"""
|
|
650
|
+
# FIXME: This test is talking to production loginws endpoint and is resulting in an exception.
|
|
651
|
+
# TODO: Use mocks to test the mhlm workflows is working as expected
|
|
652
|
+
data = {
|
|
653
|
+
"type": "mhlm",
|
|
654
|
+
"status": "starting",
|
|
655
|
+
"version": "R2020b",
|
|
656
|
+
"token": "123@nlm",
|
|
657
|
+
"emailaddress": "123@nlm",
|
|
658
|
+
"sourceId": "123@nlm",
|
|
659
|
+
}
|
|
660
|
+
resp = await test_server.put("/set_licensing_info", data=json.dumps(data))
|
|
661
|
+
assert resp.status == HTTPStatus.OK
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
async def test_set_licensing_info_put_existing_license(test_server):
|
|
665
|
+
"""Test to check endpoint : "/set_licensing_info"
|
|
666
|
+
|
|
667
|
+
Test which sends HTTP PUT request with local licensing information.
|
|
668
|
+
Args:
|
|
669
|
+
test_server (aiohttp_client): A aiohttp_client server to send HTTP GET request.
|
|
670
|
+
"""
|
|
671
|
+
|
|
672
|
+
data = {"type": "existing_license"}
|
|
673
|
+
resp = await test_server.put("/set_licensing_info", data=json.dumps(data))
|
|
674
|
+
assert resp.status == HTTPStatus.OK
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
async def test_set_licensing_info_delete(test_server):
|
|
678
|
+
"""Test to check endpoint : "/set_licensing_info"
|
|
679
|
+
|
|
680
|
+
Test which sends HTTP DELETE request to remove licensing. Checks if licensing is set to None
|
|
681
|
+
After request is sent.
|
|
682
|
+
Args:
|
|
683
|
+
test_server (aiohttp_client): A aiohttp_client server to send HTTP GET request.
|
|
684
|
+
"""
|
|
685
|
+
|
|
686
|
+
resp = await test_server.delete("/set_licensing_info")
|
|
687
|
+
resp_json = json.loads(await resp.text())
|
|
688
|
+
assert resp.status == HTTPStatus.OK and resp_json["licensing"] is None
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
async def test_set_termination_integration_delete(test_server):
|
|
692
|
+
"""Test to check endpoint : "/terminate_integration"
|
|
693
|
+
|
|
694
|
+
Test which sends HTTP DELETE request to terminate integration. Checks if integration is terminated
|
|
695
|
+
successfully.
|
|
696
|
+
Args:
|
|
697
|
+
test_server (aiohttp_client): A aiohttp_client server to send HTTP GET request.
|
|
698
|
+
"""
|
|
699
|
+
try:
|
|
700
|
+
resp = await test_server.delete("/terminate_integration")
|
|
701
|
+
resp_json = json.loads(await resp.text())
|
|
702
|
+
assert resp.status == HTTPStatus.OK and resp_json["loadUrl"] == "../"
|
|
703
|
+
except ProcessLookupError:
|
|
704
|
+
pass
|
|
705
|
+
|
|
706
|
+
|
|
707
|
+
def test_get_access_url(test_server):
|
|
708
|
+
"""Should return a url with 127.0.0.1 in test mode
|
|
709
|
+
|
|
710
|
+
Args:
|
|
711
|
+
test_server (aiohttp.web.Application): Application Server
|
|
712
|
+
"""
|
|
713
|
+
assert "127.0.0.1" in util.get_access_url(test_server.app)
|
|
714
|
+
|
|
715
|
+
|
|
716
|
+
@pytest.fixture(name="non_test_env")
|
|
717
|
+
def non_test_env_fixture(monkeypatch):
|
|
718
|
+
"""Monkeypatches MWI_TEST env var to false
|
|
719
|
+
|
|
720
|
+
Args:
|
|
721
|
+
monkeypatch (_pytest.monkeypatch.MonkeyPatch): To monkeypatch env vars
|
|
722
|
+
"""
|
|
723
|
+
monkeypatch.setenv(mwi_env.get_env_name_testing(), "false")
|
|
724
|
+
|
|
725
|
+
|
|
726
|
+
@pytest.fixture(name="non_default_host_interface")
|
|
727
|
+
def non_default_host_interface_fixture(monkeypatch):
|
|
728
|
+
"""Monkeypatches MWI_TEST env var to false
|
|
729
|
+
|
|
730
|
+
Args:
|
|
731
|
+
monkeypatch (_pytest.monkeypatch.MonkeyPatch): To monkeypatch env vars
|
|
732
|
+
"""
|
|
733
|
+
monkeypatch.setenv(mwi_env.get_env_name_app_host(), "0.0.0.0")
|
|
734
|
+
|
|
735
|
+
|
|
736
|
+
# For pytest fixtures, order of arguments matter.
|
|
737
|
+
# First set the default host interface to a non-default value
|
|
738
|
+
# Then set MWI_TEST to false and then create an instance of the test_server
|
|
739
|
+
# This order will set the test_server with appropriate values.
|
|
740
|
+
|
|
741
|
+
|
|
742
|
+
@pytest.mark.skipif(
|
|
743
|
+
platform.system() == "Linux" or platform.system() == "Darwin",
|
|
744
|
+
reason="Testing the windows access URL",
|
|
745
|
+
)
|
|
746
|
+
def test_get_access_url_non_dev_windows(
|
|
747
|
+
non_default_host_interface, non_test_env, test_server
|
|
748
|
+
):
|
|
749
|
+
"""Test to check access url to be 127.0.0.1 in non-dev mode on Windows"""
|
|
750
|
+
assert "127.0.0.1" in util.get_access_url(test_server.app)
|
|
751
|
+
|
|
752
|
+
|
|
753
|
+
@pytest.mark.skipif(
|
|
754
|
+
platform.system() == "Windows", reason="Testing the non-Windows access URL"
|
|
755
|
+
)
|
|
756
|
+
def test_get_access_url_non_dev_posix(
|
|
757
|
+
non_default_host_interface, non_test_env, test_server
|
|
758
|
+
):
|
|
759
|
+
"""Test to check access url to be 0.0.0.0 in non-dev mode on Linux/Darwin"""
|
|
760
|
+
assert "0.0.0.0" in util.get_access_url(test_server.app)
|
|
761
|
+
|
|
762
|
+
|
|
763
|
+
@pytest.fixture(name="set_licensing_info_mock_fetch_single_entitlement")
|
|
764
|
+
def set_licensing_info_mock_fetch_single_entitlement_fixture():
|
|
765
|
+
"""Fixture that returns a single entitlement
|
|
766
|
+
|
|
767
|
+
Returns:
|
|
768
|
+
json array: An array consisting of single entitlement information
|
|
769
|
+
"""
|
|
770
|
+
return [
|
|
771
|
+
{"id": "Entitlement3", "label": "Label3", "license_number": "License3"},
|
|
772
|
+
]
|
|
773
|
+
|
|
774
|
+
|
|
775
|
+
@pytest.fixture(name="set_licensing_info_mock_fetch_multiple_entitlements")
|
|
776
|
+
def set_licensing_info_mock_fetch_multiple_entitlements_fixture():
|
|
777
|
+
"""Fixture that returns multiple entitlements
|
|
778
|
+
|
|
779
|
+
Returns:
|
|
780
|
+
json array: An array consisting of multiple entitlements
|
|
781
|
+
"""
|
|
782
|
+
return [
|
|
783
|
+
{"id": "Entitlement1", "label": "Label1", "license_number": "License1"},
|
|
784
|
+
{"id": "Entitlement2", "label": "Label2", "license_number": "License2"},
|
|
785
|
+
]
|
|
786
|
+
|
|
787
|
+
|
|
788
|
+
@pytest.fixture(name="set_licensing_info_mock_access_token")
|
|
789
|
+
def set_licensing_info_mock_access_token_fixture():
|
|
790
|
+
"""Pytest fixture that returns a mock token that mimics mw.fetch_access_token() response"""
|
|
791
|
+
access_token_string = int("".join([str(random.randint(0, 10)) for _ in range(272)]))
|
|
792
|
+
return {
|
|
793
|
+
"token": str(access_token_string),
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
|
|
797
|
+
@pytest.fixture(name="set_licensing_info_mock_expand_token")
|
|
798
|
+
def set_licensing_info_mock_expand_token_fixture():
|
|
799
|
+
"""Pytest fixture which returns a dict
|
|
800
|
+
|
|
801
|
+
The return value represents a valid json response when mw.fetch_expand_token function is called.
|
|
802
|
+
|
|
803
|
+
Returns:
|
|
804
|
+
json data with mimics mw.fetch_expand_token() response
|
|
805
|
+
"""
|
|
806
|
+
now = datetime.datetime.now(timezone.utc)
|
|
807
|
+
first_name = "abc"
|
|
808
|
+
|
|
809
|
+
json_data = {
|
|
810
|
+
"expiry": str((now + timedelta(days=1)).strftime("%Y-%m-%dT%H:%M:%S.%f%z")),
|
|
811
|
+
"first_name": first_name,
|
|
812
|
+
"last_name": "def",
|
|
813
|
+
"display_name": first_name,
|
|
814
|
+
"email_addr": "test@test.com",
|
|
815
|
+
"user_id": "".join([str(random.randint(0, 10)) for _ in range(13)]),
|
|
816
|
+
"profile_id": "".join([str(random.randint(0, 10)) for _ in range(8)]),
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
return json_data
|
|
820
|
+
|
|
821
|
+
|
|
822
|
+
@pytest.fixture(name="set_licensing_info")
|
|
823
|
+
async def set_licensing_info_fixture(
|
|
824
|
+
mocker,
|
|
825
|
+
test_server,
|
|
826
|
+
set_licensing_info_mock_expand_token,
|
|
827
|
+
set_licensing_info_mock_access_token,
|
|
828
|
+
set_licensing_info_mock_fetch_multiple_entitlements,
|
|
829
|
+
):
|
|
830
|
+
"""Fixture to setup correct licensing state on the server"""
|
|
831
|
+
mocker.patch(
|
|
832
|
+
"matlab_proxy.app_state.mw.fetch_expand_token",
|
|
833
|
+
return_value=set_licensing_info_mock_expand_token,
|
|
834
|
+
)
|
|
835
|
+
|
|
836
|
+
mocker.patch(
|
|
837
|
+
"matlab_proxy.app_state.mw.fetch_access_token",
|
|
838
|
+
return_value=set_licensing_info_mock_access_token,
|
|
839
|
+
)
|
|
840
|
+
|
|
841
|
+
mocker.patch(
|
|
842
|
+
"matlab_proxy.app_state.mw.fetch_entitlements",
|
|
843
|
+
return_value=set_licensing_info_mock_fetch_multiple_entitlements,
|
|
844
|
+
)
|
|
845
|
+
|
|
846
|
+
data = {
|
|
847
|
+
"type": "mhlm",
|
|
848
|
+
"status": "starting",
|
|
849
|
+
"version": "R2020b",
|
|
850
|
+
"token": "abc@nlm",
|
|
851
|
+
"emailAddress": "abc@nlm",
|
|
852
|
+
"sourceId": "abc@nlm",
|
|
853
|
+
"matlabVersion": "R2023a",
|
|
854
|
+
}
|
|
855
|
+
# Set matlab_version to None to check if the version is updated
|
|
856
|
+
# after sending a request t o /set_licensing_info endpoint
|
|
857
|
+
test_server.server.app["settings"]["matlab_version"] = None
|
|
858
|
+
|
|
859
|
+
# Pre-req: stop the matlab that got started during test server startup
|
|
860
|
+
resp = await test_server.delete("/stop_matlab")
|
|
861
|
+
assert resp.status == HTTPStatus.OK
|
|
862
|
+
|
|
863
|
+
resp = await test_server.put("/set_licensing_info", data=json.dumps(data))
|
|
864
|
+
assert resp.status == HTTPStatus.OK
|
|
865
|
+
|
|
866
|
+
# Assert whether the matlab_version was updated from None when licensing type is mhlm
|
|
867
|
+
assert test_server.server.app["settings"]["matlab_version"] == "R2023a"
|
|
868
|
+
|
|
869
|
+
return test_server
|
|
870
|
+
|
|
871
|
+
|
|
872
|
+
async def test_set_licensing_mhlm_zero_entitlement(
|
|
873
|
+
mocker,
|
|
874
|
+
set_licensing_info_mock_expand_token,
|
|
875
|
+
set_licensing_info_mock_access_token,
|
|
876
|
+
test_server,
|
|
877
|
+
):
|
|
878
|
+
# Patching the functions where it is used (and not where it is defined)
|
|
879
|
+
mocker.patch(
|
|
880
|
+
"matlab_proxy.app_state.mw.fetch_expand_token",
|
|
881
|
+
return_value=set_licensing_info_mock_expand_token,
|
|
882
|
+
)
|
|
883
|
+
|
|
884
|
+
mocker.patch(
|
|
885
|
+
"matlab_proxy.app_state.mw.fetch_access_token",
|
|
886
|
+
return_value=set_licensing_info_mock_access_token,
|
|
887
|
+
)
|
|
888
|
+
|
|
889
|
+
mocker.patch(
|
|
890
|
+
"matlab_proxy.app_state.mw.fetch_entitlements",
|
|
891
|
+
side_effect=EntitlementError(
|
|
892
|
+
"Your MathWorks account is not linked to a valid license for MATLAB"
|
|
893
|
+
),
|
|
894
|
+
)
|
|
895
|
+
|
|
896
|
+
data = {
|
|
897
|
+
"type": "mhlm",
|
|
898
|
+
"status": "starting",
|
|
899
|
+
"version": "R2020b",
|
|
900
|
+
"token": "abc@nlm",
|
|
901
|
+
"emailaddress": "abc@nlm",
|
|
902
|
+
"sourceId": "abc@nlm",
|
|
903
|
+
}
|
|
904
|
+
# Pre-req: stop the matlab that got started as during test server startup
|
|
905
|
+
resp = await test_server.delete("/stop_matlab")
|
|
906
|
+
assert resp.status == HTTPStatus.OK
|
|
907
|
+
|
|
908
|
+
resp = await test_server.put("/set_licensing_info", data=json.dumps(data))
|
|
909
|
+
assert resp.status == HTTPStatus.OK
|
|
910
|
+
resp_json = await resp.json()
|
|
911
|
+
expectedError = EntitlementError(message="entitlement error")
|
|
912
|
+
assert resp_json["error"]["type"] == type(expectedError).__name__
|
|
913
|
+
|
|
914
|
+
|
|
915
|
+
async def test_set_licensing_mhlm_single_entitlement(
|
|
916
|
+
mocker,
|
|
917
|
+
test_server,
|
|
918
|
+
set_licensing_info_mock_expand_token,
|
|
919
|
+
set_licensing_info_mock_access_token,
|
|
920
|
+
set_licensing_info_mock_fetch_single_entitlement,
|
|
921
|
+
):
|
|
922
|
+
mocker.patch(
|
|
923
|
+
"matlab_proxy.app_state.mw.fetch_expand_token",
|
|
924
|
+
return_value=set_licensing_info_mock_expand_token,
|
|
925
|
+
)
|
|
926
|
+
|
|
927
|
+
mocker.patch(
|
|
928
|
+
"matlab_proxy.app_state.mw.fetch_access_token",
|
|
929
|
+
return_value=set_licensing_info_mock_access_token,
|
|
930
|
+
)
|
|
931
|
+
|
|
932
|
+
mocker.patch(
|
|
933
|
+
"matlab_proxy.app_state.mw.fetch_entitlements",
|
|
934
|
+
return_value=set_licensing_info_mock_fetch_single_entitlement,
|
|
935
|
+
)
|
|
936
|
+
|
|
937
|
+
data = {
|
|
938
|
+
"type": "mhlm",
|
|
939
|
+
"status": "starting",
|
|
940
|
+
"version": "R2020b",
|
|
941
|
+
"token": "abc@nlm",
|
|
942
|
+
"emailAddress": "abc@nlm",
|
|
943
|
+
"sourceId": "abc@nlm",
|
|
944
|
+
}
|
|
945
|
+
# Pre-req: stop the matlab that got started during test server startup
|
|
946
|
+
resp = await test_server.delete("/stop_matlab")
|
|
947
|
+
assert resp.status == HTTPStatus.OK
|
|
948
|
+
|
|
949
|
+
resp = await test_server.put("/set_licensing_info", data=json.dumps(data))
|
|
950
|
+
assert resp.status == HTTPStatus.OK
|
|
951
|
+
resp_json = await resp.json()
|
|
952
|
+
assert len(resp_json["licensing"]["entitlements"]) == 1
|
|
953
|
+
assert resp_json["licensing"]["entitlementId"] == "Entitlement3"
|
|
954
|
+
|
|
955
|
+
# validate that MATLAB has started correctly
|
|
956
|
+
await check_for_matlab_startup(test_server)
|
|
957
|
+
|
|
958
|
+
# test-cleanup: unset licensing
|
|
959
|
+
# without this, we can leave test drool related to cached license file
|
|
960
|
+
# which can impact other non-dev workflows
|
|
961
|
+
resp = await test_server.delete("/set_licensing_info")
|
|
962
|
+
assert resp.status == HTTPStatus.OK
|
|
963
|
+
|
|
964
|
+
|
|
965
|
+
async def test_set_licensing_mhlm_multi_entitlements(
|
|
966
|
+
mocker,
|
|
967
|
+
test_server,
|
|
968
|
+
set_licensing_info_mock_expand_token,
|
|
969
|
+
set_licensing_info_mock_access_token,
|
|
970
|
+
set_licensing_info_mock_fetch_multiple_entitlements,
|
|
971
|
+
):
|
|
972
|
+
mocker.patch(
|
|
973
|
+
"matlab_proxy.app_state.mw.fetch_expand_token",
|
|
974
|
+
return_value=set_licensing_info_mock_expand_token,
|
|
975
|
+
)
|
|
976
|
+
|
|
977
|
+
mocker.patch(
|
|
978
|
+
"matlab_proxy.app_state.mw.fetch_access_token",
|
|
979
|
+
return_value=set_licensing_info_mock_access_token,
|
|
980
|
+
)
|
|
981
|
+
|
|
982
|
+
mocker.patch(
|
|
983
|
+
"matlab_proxy.app_state.mw.fetch_entitlements",
|
|
984
|
+
return_value=set_licensing_info_mock_fetch_multiple_entitlements,
|
|
985
|
+
)
|
|
986
|
+
|
|
987
|
+
data = {
|
|
988
|
+
"type": "mhlm",
|
|
989
|
+
"status": "starting",
|
|
990
|
+
"version": "R2020b",
|
|
991
|
+
"token": "abc@nlm",
|
|
992
|
+
"emailaddress": "abc@nlm",
|
|
993
|
+
"sourceId": "abc@nlm",
|
|
994
|
+
}
|
|
995
|
+
# Pre-req: stop the matlab that got started as during test server startup
|
|
996
|
+
resp = await test_server.delete("/stop_matlab")
|
|
997
|
+
assert resp.status == HTTPStatus.OK
|
|
998
|
+
|
|
999
|
+
resp = await test_server.put("/set_licensing_info", data=json.dumps(data))
|
|
1000
|
+
assert resp.status == HTTPStatus.OK
|
|
1001
|
+
resp_json = await resp.json()
|
|
1002
|
+
assert len(resp_json["licensing"]["entitlements"]) == 2
|
|
1003
|
+
assert resp_json["licensing"]["entitlementId"] == None
|
|
1004
|
+
|
|
1005
|
+
# MATLAB should not start if there are multiple entitlements and
|
|
1006
|
+
# user hasn't selected the license yet
|
|
1007
|
+
resp = await test_server.get("/get_status")
|
|
1008
|
+
assert resp.status == HTTPStatus.OK
|
|
1009
|
+
resp_json = json.loads(await resp.text())
|
|
1010
|
+
assert resp_json["matlab"]["status"] == "down"
|
|
1011
|
+
|
|
1012
|
+
# test-cleanup: unset licensing
|
|
1013
|
+
resp = await test_server.delete("/set_licensing_info")
|
|
1014
|
+
assert resp.status == HTTPStatus.OK
|
|
1015
|
+
|
|
1016
|
+
|
|
1017
|
+
async def test_update_entitlement_with_correct_entitlement(set_licensing_info):
|
|
1018
|
+
data = {
|
|
1019
|
+
"type": "mhlm",
|
|
1020
|
+
"entitlement_id": "Entitlement1",
|
|
1021
|
+
}
|
|
1022
|
+
# This test_server is pre-configured with multiple entitlements on app state but no entitlmentId
|
|
1023
|
+
test_server = set_licensing_info
|
|
1024
|
+
resp = await test_server.put("/update_entitlement", data=json.dumps(data))
|
|
1025
|
+
assert resp.status == HTTPStatus.OK
|
|
1026
|
+
resp_json = await resp.json()
|
|
1027
|
+
assert resp_json["matlab"]["status"] != "down"
|
|
1028
|
+
|
|
1029
|
+
# test-cleanup: unset licensing
|
|
1030
|
+
resp = await test_server.delete("/set_licensing_info")
|
|
1031
|
+
assert resp.status == HTTPStatus.OK
|
|
1032
|
+
|
|
1033
|
+
|
|
1034
|
+
async def test_get_auth_token_route(test_server):
|
|
1035
|
+
"""Test to check endpoint : "/get_auth_token"
|
|
1036
|
+
|
|
1037
|
+
Args:
|
|
1038
|
+
test_server (aiohttp_client): A aiohttp_client server for sending GET request.
|
|
1039
|
+
"""
|
|
1040
|
+
resp = await test_server.get("/get_auth_token")
|
|
1041
|
+
res_json = await resp.json()
|
|
1042
|
+
# Testing the default dev configuration where the auth is disabled
|
|
1043
|
+
assert res_json["token"] == None
|
|
1044
|
+
assert resp.status == HTTPStatus.OK
|
|
1045
|
+
|
|
1046
|
+
|
|
1047
|
+
async def test_check_for_concurrency(test_server):
|
|
1048
|
+
"""Test to check the response from endpoint : "/get_status" with different query parameters
|
|
1049
|
+
|
|
1050
|
+
Test requests the "/get_status" endpoint with different query parameters to check
|
|
1051
|
+
how the server responds.
|
|
1052
|
+
|
|
1053
|
+
Args:
|
|
1054
|
+
test_server (aiohttp_client): A aiohttp_client server to send GET request to.
|
|
1055
|
+
"""
|
|
1056
|
+
# Request server to check if concurrency check is enabled.
|
|
1057
|
+
|
|
1058
|
+
env_resp = await test_server.get("/get_env_config")
|
|
1059
|
+
assert env_resp.status == HTTPStatus.OK
|
|
1060
|
+
env_resp_json = json.loads(await env_resp.text())
|
|
1061
|
+
if env_resp_json["isConcurrencyEnabled"]:
|
|
1062
|
+
# A normal request should not repond with client id or active status
|
|
1063
|
+
status_resp = await test_server.get("/get_status")
|
|
1064
|
+
assert status_resp.status == HTTPStatus.OK
|
|
1065
|
+
status_resp_json = json.loads(await status_resp.text())
|
|
1066
|
+
assert "clientId" not in status_resp_json
|
|
1067
|
+
assert "isActiveClient" not in status_resp_json
|
|
1068
|
+
|
|
1069
|
+
# When the request comes from the desktop app the server should respond with client id and active status
|
|
1070
|
+
status_resp = await test_server.get('/get_status?IS_DESKTOP="true"')
|
|
1071
|
+
assert status_resp.status == HTTPStatus.OK
|
|
1072
|
+
status_resp_json = json.loads(await status_resp.text())
|
|
1073
|
+
assert "clientId" in status_resp_json
|
|
1074
|
+
assert "isActiveClient" in status_resp_json
|
|
1075
|
+
|
|
1076
|
+
# When the desktop client requests for a session transfer without client id respond with cliend id and active status should be true
|
|
1077
|
+
status_resp = await test_server.get(
|
|
1078
|
+
'/get_status?IS_DESKTOP="true"&TRANSFER_SESSION="true"'
|
|
1079
|
+
)
|
|
1080
|
+
assert status_resp.status == HTTPStatus.OK
|
|
1081
|
+
status_resp_json = json.loads(await status_resp.text())
|
|
1082
|
+
assert "clientId" in status_resp_json
|
|
1083
|
+
assert status_resp_json["isActiveClient"] == True
|
|
1084
|
+
|
|
1085
|
+
# When transfering the session is requested by a client whihc is not a desktop client it should be ignored
|
|
1086
|
+
status_resp = await test_server.get('/get_status?TRANSFER_SESSION="true"')
|
|
1087
|
+
assert status_resp.status == HTTPStatus.OK
|
|
1088
|
+
status_resp_json = json.loads(await status_resp.text())
|
|
1089
|
+
assert "clientId" not in status_resp_json
|
|
1090
|
+
assert "isActiveClient" not in status_resp_json
|
|
1091
|
+
|
|
1092
|
+
# When the desktop client requests for a session transfer with a client id then respond only with active status
|
|
1093
|
+
status_resp = await test_server.get(
|
|
1094
|
+
'/get_status?IS_DESKTOP="true"&MWI_CLIENT_ID="foobar"&TRANSFER_SESSION="true"'
|
|
1095
|
+
)
|
|
1096
|
+
assert status_resp.status == HTTPStatus.OK
|
|
1097
|
+
status_resp_json = json.loads(await status_resp.text())
|
|
1098
|
+
assert "clientId" not in status_resp_json
|
|
1099
|
+
assert status_resp_json["isActiveClient"] == True
|
|
1100
|
+
else:
|
|
1101
|
+
# When Concurrency check is disabled the response should not contain client id or active status
|
|
1102
|
+
status_resp = await test_server.get("/get_status")
|
|
1103
|
+
assert status_resp.status == HTTPStatus.OK
|
|
1104
|
+
status_resp_json = json.loads(await status_resp.text())
|
|
1105
|
+
assert "clientId" not in status_resp_json
|
|
1106
|
+
assert "isActiveClient" not in status_resp_json
|
|
1107
|
+
status_resp = await test_server.get(
|
|
1108
|
+
'/get_status?IS_DESKTOP="true"&MWI_CLIENT_ID="foobar"&TRANSFER_SESSION="true"'
|
|
1109
|
+
)
|
|
1110
|
+
assert status_resp.status == HTTPStatus.OK
|
|
1111
|
+
status_resp_json = json.loads(await status_resp.text())
|
|
1112
|
+
assert "clientId" not in status_resp_json
|
|
1113
|
+
assert "isActiveClient" not in status_resp_json
|