matlab-proxy 0.24.2__py3-none-any.whl → 0.25.1__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.

tests/unit/test_app.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2020-2024 The MathWorks, Inc.
1
+ # Copyright 2020-2025 The MathWorks, Inc.
2
2
 
3
3
  import asyncio
4
4
  import datetime
@@ -9,13 +9,18 @@ import time
9
9
  from datetime import timedelta, timezone
10
10
  from http import HTTPStatus
11
11
 
12
- import aiohttp
13
12
  import pytest
14
- import tests.unit.test_constants as test_constants
13
+ from aiohttp import WSMsgType
14
+ from aiohttp.web import WebSocketResponse
15
+ from multidict import CIMultiDict
15
16
 
17
+ import tests.unit.test_constants as test_constants
16
18
  from matlab_proxy import app, util
19
+ from matlab_proxy.app import matlab_view
17
20
  from matlab_proxy.util.mwi import environment_variables as mwi_env
18
21
  from matlab_proxy.util.mwi.exceptions import EntitlementError, MatlabInstallError
22
+ from tests.unit.fixtures.fixture_auth import patch_authenticate_access_decorator
23
+ from tests.unit.mocks.mock_client import MockWebSocketClient
19
24
 
20
25
 
21
26
  @pytest.mark.parametrize(
@@ -61,7 +66,7 @@ def test_configure_no_proxy_in_env(monkeypatch, no_proxy_user_configuration):
61
66
  )
62
67
 
63
68
 
64
- def test_create_app(loop):
69
+ def test_create_app(event_loop):
65
70
  """Test if aiohttp server is being created successfully.
66
71
 
67
72
  Checks if the aiohttp server is created successfully, routes, startup and cleanup
@@ -75,7 +80,7 @@ def test_create_app(loop):
75
80
  # Verify app server has a cleanup task
76
81
  # By default there is 1 for clean up task
77
82
  assert len(test_server.on_cleanup) > 1
78
- loop.run_until_complete(test_server["state"].stop_server_tasks())
83
+ event_loop.run_until_complete(test_server["state"].stop_server_tasks())
79
84
 
80
85
 
81
86
  def get_email():
@@ -245,9 +250,33 @@ class FakeServer:
245
250
  self.loop.run_until_complete(self.server.cleanup())
246
251
 
247
252
 
253
+ @pytest.fixture
254
+ def mock_request(mocker):
255
+ """Creates a mock request with required attributes"""
256
+ req = mocker.MagicMock()
257
+ req.app = {
258
+ "state": mocker.MagicMock(matlab_port=8000),
259
+ "settings": {"matlab_protocol": "http", "mwapikey": "test-key"},
260
+ }
261
+ req.headers = CIMultiDict()
262
+ req.cookies = {}
263
+ return req
264
+
265
+
266
+ @pytest.fixture(name="mock_websocket_messages")
267
+ def mock_messages(mocker):
268
+ # Mock WebSocket messages
269
+ return [
270
+ mocker.MagicMock(type=WSMsgType.TEXT, data="test message"),
271
+ mocker.MagicMock(type=WSMsgType.BINARY, data=b"test binary"),
272
+ mocker.MagicMock(type=WSMsgType.PING),
273
+ mocker.MagicMock(type=WSMsgType.PONG),
274
+ ]
275
+
276
+
248
277
  @pytest.fixture(name="test_server")
249
278
  def test_server_fixture(
250
- loop,
279
+ event_loop,
251
280
  aiohttp_client,
252
281
  monkeypatch,
253
282
  ):
@@ -263,7 +292,7 @@ def test_server_fixture(
263
292
  # Disabling the authentication token mechanism explicitly
264
293
  monkeypatch.setenv(mwi_env.get_env_name_enable_mwi_auth_token(), "False")
265
294
  try:
266
- with FakeServer(loop, aiohttp_client) as test_server:
295
+ with FakeServer(event_loop, aiohttp_client) as test_server:
267
296
  yield test_server
268
297
  except ProcessLookupError:
269
298
  pass
@@ -306,8 +335,6 @@ async def test_get_env_config(test_server):
306
335
  test_server (aiohttp_client): A aiohttp_client server for sending GET request.
307
336
  """
308
337
  expected_json_structure = {
309
- "useMOS": False,
310
- "useMRE": False,
311
338
  "authentication": {"enabled": False, "status": False},
312
339
  "matlab": {
313
340
  "status": "up",
@@ -350,7 +377,7 @@ async def test_start_matlab_route(test_server):
350
377
  await __check_for_matlab_status(test_server, "starting")
351
378
 
352
379
 
353
- async def __check_for_matlab_status(test_server, status):
380
+ async def __check_for_matlab_status(test_server, status, sleep_interval=0.5):
354
381
  """Helper function to check if the status of MATLAB returned by the server is either of the values mentioned in statuses
355
382
 
356
383
  Args:
@@ -369,7 +396,7 @@ async def __check_for_matlab_status(test_server, status):
369
396
  break
370
397
  else:
371
398
  count += 1
372
- await asyncio.sleep(0.5)
399
+ await asyncio.sleep(sleep_interval)
373
400
  if count > test_constants.FIVE_MAX_TRIES:
374
401
  raise ConnectionError
375
402
 
@@ -592,19 +619,6 @@ async def test_matlab_proxy_http_post_request(proxy_payload, test_server):
592
619
  raise ConnectionError
593
620
 
594
621
 
595
- # While acceessing matlab-proxy directly, the web socket request looks like
596
- # {
597
- # "connection": "Upgrade",
598
- # "Upgrade": "websocket",
599
- # }
600
- # whereas while accessing matlab-proxy with nginx as the reverse proxy, the nginx server
601
- # modifies the web socket request to
602
- # {
603
- # "connection": "upgrade",
604
- # "upgrade": "websocket",
605
- # }
606
-
607
-
608
622
  async def test_set_licensing_info_put_nlm(test_server):
609
623
  """Test to check endpoint : "/set_licensing_info"
610
624
 
@@ -641,35 +655,70 @@ async def test_set_licensing_info_put_invalid_license(test_server):
641
655
  assert resp.status == HTTPStatus.BAD_REQUEST
642
656
 
643
657
 
658
+ # While acceessing matlab-proxy directly, the web socket request looks like
659
+ # {
660
+ # "connection": "Upgrade",
661
+ # "Upgrade": "websocket",
662
+ # }
663
+ # whereas while accessing matlab-proxy with nginx as the reverse proxy, the nginx server
664
+ # modifies the web socket request to
665
+ # {
666
+ # "connection": "upgrade",
667
+ # "upgrade": "websocket",
668
+ # }
644
669
  @pytest.mark.parametrize(
645
670
  "headers",
646
671
  [
647
- {
648
- "connection": "Upgrade",
649
- "Upgrade": "websocket",
650
- },
651
- {
652
- "connection": "upgrade",
653
- "upgrade": "websocket",
654
- },
672
+ CIMultiDict(
673
+ {
674
+ "connection": "Upgrade",
675
+ "Upgrade": "websocket",
676
+ }
677
+ ),
678
+ CIMultiDict(
679
+ {
680
+ "connection": "upgrade",
681
+ "upgrade": "websocket",
682
+ }
683
+ ),
655
684
  ],
656
685
  ids=["Uppercase header", "Lowercase header"],
657
686
  )
658
- async def test_matlab_proxy_web_socket(test_server, headers):
659
- """Test to check if test_server proxies web socket request to fake matlab server
687
+ async def test_matlab_view_websocket_success(
688
+ mocker,
689
+ mock_request,
690
+ mock_websocket_messages,
691
+ headers,
692
+ patch_authenticate_access_decorator,
693
+ ):
694
+ """Test successful websocket connection and message forwarding"""
660
695
 
661
- Args:
662
- test_server (aiohttp_client): Test Server to send HTTP Requests.
663
- """
696
+ # Configure request for WebSocket
697
+ mock_request.headers = headers
698
+ mock_request.method = "GET"
699
+ mock_request.path_qs = "/test"
664
700
 
665
- await wait_for_matlab_to_be_up(test_server, test_constants.ONE_SECOND_DELAY)
666
- resp = await test_server.ws_connect("/http_ws_request.html/", headers=headers)
667
- text = await resp.receive()
668
- websocket_response_string = (
669
- "Hello world" # This string is set by the web_socket_handler in devel.py
701
+ # Mock WebSocket setup
702
+ mock_ws_server = mocker.MagicMock(spec=WebSocketResponse)
703
+ mocker.patch(
704
+ "matlab_proxy.app.aiohttp.web.WebSocketResponse", return_value=mock_ws_server
705
+ )
706
+
707
+ # Mock WebSocket client
708
+ mock_ws_client = MockWebSocketClient(messages=mock_websocket_messages)
709
+ mocker.patch(
710
+ "matlab_proxy.app.aiohttp.ClientSession.ws_connect", return_value=mock_ws_client
670
711
  )
671
- assert text.type == aiohttp.WSMsgType.TEXT
672
- assert text.data == websocket_response_string
712
+
713
+ # Execute
714
+ result = await matlab_view(mock_request)
715
+
716
+ # Assertions
717
+ assert result == mock_ws_server
718
+ assert mock_ws_server.send_str.call_count == 1
719
+ assert mock_ws_server.send_bytes.call_count == 1
720
+ assert mock_ws_server.ping.call_count == 1
721
+ assert mock_ws_server.pong.call_count == 1
673
722
 
674
723
 
675
724
  async def test_set_licensing_info_put_mhlm(test_server):
@@ -992,7 +1041,7 @@ async def test_set_licensing_mhlm_single_entitlement(
992
1041
  assert resp_json["licensing"]["entitlementId"] == "Entitlement3"
993
1042
 
994
1043
  # validate that MATLAB has started correctly
995
- await __check_for_matlab_status(test_server, "up")
1044
+ await __check_for_matlab_status(test_server, "up", sleep_interval=2)
996
1045
 
997
1046
  # test-cleanup: unset licensing
998
1047
  # without this, we can leave test drool related to cached license file
@@ -43,6 +43,7 @@ def sample_settings_fixture(tmp_path):
43
43
  "warnings": [],
44
44
  "matlab_config_file": tmp_file,
45
45
  "is_xvfb_available": True,
46
+ "is_windowmanager_available": True,
46
47
  "mwi_server_url": "dummy",
47
48
  "mwi_logs_root_dir": Path(settings.get_mwi_config_folder(dev=True)),
48
49
  "app_port": 12345,
@@ -55,12 +56,12 @@ def sample_settings_fixture(tmp_path):
55
56
 
56
57
 
57
58
  @pytest.fixture
58
- def app_state_fixture(sample_settings_fixture, loop):
59
+ def app_state_fixture(sample_settings_fixture, event_loop):
59
60
  """A pytest fixture which returns an instance of AppState class with no errors.
60
61
 
61
62
  Args:
62
63
  sample_settings_fixture (dict): A dictionary of sample settings to be used by
63
- loop : A pytest builtin fixture
64
+ event_loop : A pytest builtin fixture
64
65
 
65
66
  Returns:
66
67
  AppState: An object of the AppState class
@@ -71,7 +72,7 @@ def app_state_fixture(sample_settings_fixture, loop):
71
72
 
72
73
  yield app_state
73
74
 
74
- loop.run_until_complete(app_state.stop_server_tasks())
75
+ event_loop.run_until_complete(app_state.stop_server_tasks())
75
76
 
76
77
 
77
78
  @pytest.fixture
@@ -108,13 +109,13 @@ def app_state_with_token_auth_fixture(
108
109
 
109
110
 
110
111
  @pytest.fixture
111
- def mocker_os_patching_fixture(mocker, platform, loop):
112
+ def mocker_os_patching_fixture(mocker, platform, event_loop):
112
113
  """A pytest fixture which patches the is_* functions in system.py module
113
114
 
114
115
  Args:
115
116
  mocker : Built in pytest fixture
116
117
  platform (str): A string representing "windows", "linux" or "mac"
117
- loop : A pytest builtin fixture
118
+ event_loop : A pytest builtin fixture
118
119
 
119
120
  Returns:
120
121
  mocker: Built in pytest fixture with patched calls to system.py module.
@@ -123,7 +124,7 @@ def mocker_os_patching_fixture(mocker, platform, loop):
123
124
  mocker.patch("matlab_proxy.app_state.system.is_windows", return_value=False)
124
125
  mocker.patch("matlab_proxy.app_state.system.is_mac", return_value=False)
125
126
  mocker.patch("matlab_proxy.app_state.system.is_posix", return_value=False)
126
- mocker.patch("matlab_proxy.app_state.util.get_event_loop", return_value=loop)
127
+ mocker.patch("matlab_proxy.app_state.util.get_event_loop", return_value=event_loop)
127
128
 
128
129
  if platform == "linux":
129
130
  mocker.patch("matlab_proxy.app_state.system.is_linux", return_value=True)
@@ -820,7 +821,7 @@ async def test_decrement_timer_runs_out(sample_settings_fixture, mocker):
820
821
  app_state.processes = {"matlab": None, "xvfb": None}
821
822
  app_state.licensing = {"type": "existing_license"}
822
823
 
823
- # mock util.get_event_loop() to return a new loop for the test to assert
824
+ # mock util.get_event_loop() to return a new event_loop for the test to assert
824
825
  mock_loop = asyncio.new_event_loop()
825
826
  mocker.patch("matlab_proxy.app_state.util.get_event_loop", return_value=mock_loop)
826
827
 
@@ -1,4 +1,4 @@
1
- # Copyright 2020-2022 The MathWorks, Inc.
1
+ # Copyright 2020-2025 The MathWorks, Inc.
2
2
 
3
3
  import os
4
4
  import shutil
@@ -126,7 +126,7 @@ def mock_settings_get_fixture(mocker):
126
126
 
127
127
  @pytest.fixture(name="test_server")
128
128
  def test_server_fixture(
129
- loop,
129
+ event_loop,
130
130
  aiohttp_client,
131
131
  build_frontend,
132
132
  matlab_port_setup,
@@ -140,7 +140,7 @@ def test_server_fixture(
140
140
  This fixture 'initializes' the test server with different constraints from the test server in test_app.py
141
141
 
142
142
  Args:
143
- loop : Event Loop
143
+ event_loop : Event loop
144
144
  aiohttp_client : A built-in pytest fixture
145
145
  build_frontend: Pytest fixture which generates the directory structure of static files with some placeholder content
146
146
  matlab_port_setup: Pytest fixture which monkeypatches 'MWI_DEV' env to False. This is required for the test_server to add static content
@@ -150,7 +150,7 @@ def test_server_fixture(
150
150
  [aiohttp_client]: A aiohttp_client to send HTTP requests.
151
151
  """
152
152
 
153
- with FakeServer(loop, aiohttp_client) as test_server:
153
+ with FakeServer(event_loop, aiohttp_client) as test_server:
154
154
  yield test_server
155
155
 
156
156
 
@@ -543,21 +543,6 @@ def test_get_matlab_cmd_windows(mocker):
543
543
  assert ".exe" in cmd[0] # Assert .exe suffix in matlab_executable_path
544
544
 
545
545
 
546
- def test_get_matlab_cmd_with_mpa_flags(mocker):
547
- # Arrange
548
- mocker.patch(
549
- "matlab_proxy.settings.mwi_env.Experimental.is_mpa_enabled", return_value=True
550
- )
551
- matlab_executable_path = "/path/to/matlab"
552
- code_to_execute = "disp('Test')"
553
-
554
- # Act
555
- cmd = settings._get_matlab_cmd(matlab_executable_path, code_to_execute, None)
556
-
557
- # Assert
558
- assert all(mpa_flag in cmd for mpa_flag in mwi_env.Experimental.get_mpa_flags())
559
-
560
-
561
546
  def test_get_matlab_cmd_with_startup_profiling(mocker):
562
547
  # Arrange
563
548
  mocker.patch("matlab_proxy.settings.system.is_windows", return_value=False)
@@ -1,4 +1,4 @@
1
- # Copyright 2023-2024 The MathWorks, Inc.
1
+ # Copyright 2023-2025 The MathWorks, Inc.
2
2
 
3
3
  import pytest
4
4
  from aiohttp import web
@@ -90,7 +90,7 @@ async def fake_endpoint(request):
90
90
 
91
91
  @pytest.fixture
92
92
  def fake_server_with_auth_enabled(
93
- loop, aiohttp_client, monkeypatch, get_custom_auth_token_str
93
+ event_loop, aiohttp_client, monkeypatch, get_custom_auth_token_str
94
94
  ):
95
95
  auth_token = get_custom_auth_token_str
96
96
  auth_enablement = "True"
@@ -120,7 +120,7 @@ def fake_server_with_auth_enabled(
120
120
  aiohttp_session_setup(
121
121
  app, EncryptedCookieStorage(f, cookie_name="matlab-proxy-session")
122
122
  )
123
- return loop.run_until_complete(aiohttp_client(app))
123
+ return event_loop.run_until_complete(aiohttp_client(app))
124
124
 
125
125
 
126
126
  async def test_set_value_with_token(
@@ -256,7 +256,7 @@ async def test_get_value_with_token_in_query_params(
256
256
 
257
257
 
258
258
  @pytest.fixture
259
- def fake_server_without_auth_enabled(loop, aiohttp_client, monkeypatch):
259
+ def fake_server_without_auth_enabled(event_loop, aiohttp_client, monkeypatch):
260
260
  auth_enablement = "False"
261
261
  monkeypatch.setenv(
262
262
  mwi_env.get_env_name_enable_mwi_auth_token(), str(auth_enablement)
@@ -281,7 +281,7 @@ def fake_server_without_auth_enabled(loop, aiohttp_client, monkeypatch):
281
281
  aiohttp_session_setup(
282
282
  app, EncryptedCookieStorage(f, cookie_name="matlab-proxy-session")
283
283
  )
284
- return loop.run_until_complete(aiohttp_client(app))
284
+ return event_loop.run_until_complete(aiohttp_client(app))
285
285
 
286
286
 
287
287
  async def test_get_value(fake_server_without_auth_enabled):
@@ -1,4 +1,4 @@
1
- # Copyright 2020-2023 The MathWorks, Inc.
1
+ # Copyright 2020-2025 The MathWorks, Inc.
2
2
 
3
3
  import datetime
4
4
  import random
@@ -519,7 +519,7 @@ def test_range_matlab_connector_ports():
519
519
  not system.is_linux(),
520
520
  reason="Xvfb is only required on linux based operating systems",
521
521
  )
522
- async def test_create_xvfb_process(loop):
522
+ async def test_create_xvfb_process(event_loop):
523
523
  """Test to check if more than 1 xvfb process can be created with -displayfd flag
524
524
 
525
525
  Creates 2 xvfb processes with '-displayfd' flag and checks if the processes are
@@ -1,4 +1,4 @@
1
- # Copyright 2020-2024 The MathWorks, Inc.
1
+ # Copyright 2020-2025 The MathWorks, Inc.
2
2
 
3
3
  import asyncio
4
4
  import pytest
@@ -19,19 +19,19 @@ def test_get_supported_termination_signals():
19
19
  assert len(system.get_supported_termination_signals()) >= 1
20
20
 
21
21
 
22
- def test_add_signal_handlers(loop: asyncio.AbstractEventLoop):
23
- """Test to check if signal handlers are being added to asyncio loop
22
+ def test_add_signal_handlers(event_loop: asyncio.AbstractEventLoop):
23
+ """Test to check if signal handlers are being added to asyncio event_loop
24
24
 
25
25
  Args:
26
- loop (asyncio loop): In built-in pytest fixture.
26
+ event_loop (asyncio event loop): built-in pytest fixture.
27
27
  """
28
28
 
29
- loop = add_signal_handlers(loop)
29
+ event_loop = add_signal_handlers(event_loop)
30
30
 
31
31
  # In posix systems, event loop is modified with new signal handlers
32
32
  if system.is_posix():
33
- assert loop._signal_handlers is not None
34
- assert loop._signal_handlers.items() is not None
33
+ assert event_loop._signal_handlers is not None
34
+ assert event_loop._signal_handlers.items() is not None
35
35
 
36
36
  else:
37
37
  import signal