intuned-runtime 1.3.1__py3-none-any.whl → 1.3.2__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.
Files changed (45) hide show
  1. intuned_cli/__init__.py +15 -24
  2. intuned_cli/commands/__init__.py +6 -1
  3. intuned_cli/commands/attempt_api_command.py +8 -0
  4. intuned_cli/commands/attempt_authsession_check_command.py +8 -0
  5. intuned_cli/commands/attempt_authsession_command.py +4 -4
  6. intuned_cli/commands/attempt_authsession_create_command.py +9 -1
  7. intuned_cli/commands/attempt_command.py +4 -4
  8. intuned_cli/commands/authsession_command.py +12 -0
  9. intuned_cli/commands/authsession_record_command.py +54 -0
  10. intuned_cli/commands/command.py +6 -4
  11. intuned_cli/commands/deploy_command.py +2 -0
  12. intuned_cli/commands/init_command.py +2 -0
  13. intuned_cli/commands/run_api_command.py +9 -1
  14. intuned_cli/commands/run_authsession_command.py +4 -4
  15. intuned_cli/commands/run_authsession_create_command.py +34 -4
  16. intuned_cli/commands/run_authsession_update_command.py +33 -4
  17. intuned_cli/commands/run_authsession_validate_command.py +32 -3
  18. intuned_cli/commands/run_command.py +4 -4
  19. intuned_cli/commands/save_command.py +2 -0
  20. intuned_cli/controller/__test__/test_api.py +159 -18
  21. intuned_cli/controller/__test__/test_authsession.py +497 -6
  22. intuned_cli/controller/api.py +40 -39
  23. intuned_cli/controller/authsession.py +213 -110
  24. intuned_cli/controller/deploy.py +3 -3
  25. intuned_cli/controller/save.py +47 -48
  26. intuned_cli/types.py +14 -0
  27. intuned_cli/utils/__test__/test_browser.py +132 -0
  28. intuned_cli/utils/__test__/test_traces.py +27 -0
  29. intuned_cli/utils/api_helpers.py +54 -5
  30. intuned_cli/utils/auth_session_helpers.py +42 -7
  31. intuned_cli/utils/backend.py +4 -1
  32. intuned_cli/utils/browser.py +63 -0
  33. intuned_cli/utils/error.py +14 -0
  34. intuned_cli/utils/exclusions.py +1 -0
  35. intuned_cli/utils/help.py +9 -0
  36. intuned_cli/utils/traces.py +31 -0
  37. intuned_cli/utils/wrapper.py +58 -0
  38. intuned_internal_cli/__init__.py +7 -0
  39. {intuned_runtime-1.3.1.dist-info → intuned_runtime-1.3.2.dist-info}/METADATA +4 -2
  40. {intuned_runtime-1.3.1.dist-info → intuned_runtime-1.3.2.dist-info}/RECORD +45 -37
  41. runtime/browser/launch_chromium.py +4 -0
  42. runtime/types/settings_types.py +13 -4
  43. {intuned_runtime-1.3.1.dist-info → intuned_runtime-1.3.2.dist-info}/WHEEL +0 -0
  44. {intuned_runtime-1.3.1.dist-info → intuned_runtime-1.3.2.dist-info}/entry_points.txt +0 -0
  45. {intuned_runtime-1.3.1.dist-info → intuned_runtime-1.3.2.dist-info}/licenses/LICENSE +0 -0
@@ -2,13 +2,16 @@ from dataclasses import dataclass
2
2
  from typing import Any
3
3
  from typing import Generator
4
4
  from unittest.mock import AsyncMock
5
+ from unittest.mock import MagicMock
5
6
  from unittest.mock import Mock
6
7
  from unittest.mock import patch
7
8
 
8
9
  import pytest
9
10
 
11
+ from intuned_cli.controller.api import get_cli_run_options as get_cli_run_options
10
12
  from runtime.types.run_types import PayloadToAppend
11
13
  from runtime.types.run_types import StorageState
14
+ from runtime.types.run_types import TracingDisabled
12
15
 
13
16
 
14
17
  def get_mock_console():
@@ -18,23 +21,6 @@ def get_mock_console():
18
21
  return mock_console
19
22
 
20
23
 
21
- class AsyncContextManagerMock:
22
- def __init__(self):
23
- self.aenter_called = False
24
- self.aexit_called = False
25
- self.exception_info = None
26
-
27
- async def __aenter__(self):
28
- self.aenter_called = True
29
- return self
30
-
31
- async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any):
32
- self.aexit_called = True
33
- self.exception_info = (exc_type, exc_val, exc_tb)
34
- # Return False/None to propagate exceptions
35
- return False
36
-
37
-
38
24
  @dataclass
39
25
  class SharedMocks:
40
26
  console: Mock
@@ -69,6 +55,8 @@ def shared_mocks() -> Generator[SharedMocks, Any, None]:
69
55
  class AttemptApiMocks:
70
56
  extendable_timeout: AsyncMock
71
57
  run_api: AsyncMock
58
+ cli_trace: AsyncMock
59
+ get_cli_run_options: AsyncMock
72
60
 
73
61
 
74
62
  @pytest.fixture
@@ -76,13 +64,17 @@ def attempt_api_mocks() -> Generator[AttemptApiMocks, Any, None]:
76
64
  """Mock dependencies for attempt_api tests."""
77
65
  _mock_timeout_patch = patch("intuned_cli.controller.api.extendable_timeout")
78
66
  _mock_run_api_patch = patch("intuned_cli.controller.api.run_api", new_callable=AsyncMock)
67
+ _mock_cli_trace_patch = patch("intuned_cli.controller.api.cli_trace")
68
+ _mock_get_cli_run_options_patch = patch("intuned_cli.controller.api.get_cli_run_options", new_callable=AsyncMock)
79
69
 
80
70
  with (
81
71
  _mock_timeout_patch as mock_timeout,
82
72
  _mock_run_api_patch as mock_run_api,
73
+ _mock_cli_trace_patch as mock_cli_trace,
74
+ _mock_get_cli_run_options_patch as mock_get_cli_run_options,
83
75
  ):
84
76
  # Setup default return values
85
- mock_timeout.return_value = AsyncContextManagerMock()
77
+ mock_timeout.return_value = MagicMock()
86
78
 
87
79
  # Mock run_api to return success by default
88
80
  mock_result = Mock()
@@ -90,9 +82,23 @@ def attempt_api_mocks() -> Generator[AttemptApiMocks, Any, None]:
90
82
  mock_result.payload_to_append = []
91
83
  mock_run_api.return_value = mock_result
92
84
 
85
+ mock_cli_trace_return_value = MagicMock()
86
+ mock_cli_trace_return_value.__enter__.return_value = TracingDisabled()
87
+ mock_cli_trace.return_value = mock_cli_trace_return_value
88
+
89
+ # _mock_get_cli_run_options_patch keeps the original implementation but always passes keep_browser_open=False
90
+ async def get_cli_run_options_side_effect(*args: Any, **kwargs: Any):
91
+ # Force keep_browser_open to False
92
+ kwargs["keep_browser_open"] = False
93
+ return await get_cli_run_options(*args, **kwargs)
94
+
95
+ mock_get_cli_run_options.side_effect = get_cli_run_options_side_effect
96
+
93
97
  yield AttemptApiMocks(
94
98
  extendable_timeout=mock_timeout,
95
99
  run_api=mock_run_api,
100
+ cli_trace=mock_cli_trace,
101
+ get_cli_run_options=mock_get_cli_run_options,
96
102
  )
97
103
 
98
104
 
@@ -146,10 +152,77 @@ class TestAttemptApi:
146
152
  parameters={},
147
153
  headless=False,
148
154
  timeout=6000,
155
+ trace_id=None,
156
+ keep_browser_open=False,
149
157
  )
150
158
 
151
159
  attempt_api_mocks.extendable_timeout.assert_called_once_with(6000)
152
160
 
161
+ @pytest.mark.asyncio
162
+ async def test_attempt_api_uses_trace_context_manager(self, attempt_api_mocks: AttemptApiMocks):
163
+ """Test that attempt_api uses the trace context manager."""
164
+ # Import inside test to avoid circular import issues
165
+ from intuned_cli.controller.api import attempt_api
166
+
167
+ await attempt_api(
168
+ api_name="testApi",
169
+ parameters={},
170
+ headless=False,
171
+ timeout=6000,
172
+ trace_id=None,
173
+ keep_browser_open=False,
174
+ )
175
+
176
+ attempt_api_mocks.cli_trace.assert_called_once_with(None)
177
+
178
+ attempt_api_mocks.cli_trace.reset_mock()
179
+ await attempt_api(
180
+ api_name="testApi",
181
+ parameters={},
182
+ headless=False,
183
+ timeout=6000,
184
+ trace_id="trace-id",
185
+ keep_browser_open=False,
186
+ )
187
+
188
+ attempt_api_mocks.cli_trace.assert_called_once_with("trace-id")
189
+
190
+ @pytest.mark.asyncio
191
+ async def test_attempt_api_uses_get_cli_run_options(self, attempt_api_mocks: AttemptApiMocks):
192
+ """Test that attempt_api calls get_cli_run_options with the correct parameters."""
193
+ # Import inside test to avoid circular import issues
194
+ from intuned_cli.controller.api import attempt_api
195
+
196
+ await attempt_api(
197
+ api_name="testApi",
198
+ parameters={},
199
+ headless=False,
200
+ timeout=6000,
201
+ trace_id=None,
202
+ keep_browser_open=False,
203
+ )
204
+
205
+ attempt_api_mocks.get_cli_run_options.assert_called_once_with(
206
+ headless=False,
207
+ proxy=None,
208
+ keep_browser_open=False,
209
+ )
210
+
211
+ attempt_api_mocks.get_cli_run_options.reset_mock()
212
+ await attempt_api(
213
+ api_name="testApi",
214
+ parameters={},
215
+ headless=True,
216
+ proxy="proxy",
217
+ timeout=6000,
218
+ trace_id=None,
219
+ keep_browser_open=True,
220
+ )
221
+ call_args = attempt_api_mocks.get_cli_run_options.call_args[1]
222
+ assert call_args["headless"] is True
223
+ assert call_args["proxy"] is not None
224
+ assert call_args["keep_browser_open"] is True
225
+
153
226
  @pytest.mark.asyncio
154
227
  async def test_attempt_api_calls_run_api_with_correct_parameters_and_parses_proxy(
155
228
  self, attempt_api_mocks: AttemptApiMocks
@@ -175,6 +248,8 @@ class TestAttemptApi:
175
248
  auth=auth,
176
249
  proxy="proxy",
177
250
  timeout=999999999,
251
+ trace_id=None,
252
+ keep_browser_open=False,
178
253
  )
179
254
 
180
255
  # mock_parse_proxy.assert_called_once_with("proxy")
@@ -208,6 +283,8 @@ class TestAttemptApi:
208
283
  parameters="inputData",
209
284
  headless=False,
210
285
  timeout=999999999,
286
+ trace_id=None,
287
+ keep_browser_open=False,
211
288
  )
212
289
 
213
290
  assert result is expected_result
@@ -227,12 +304,50 @@ class TestAttemptApi:
227
304
  parameters={},
228
305
  headless=False,
229
306
  timeout=999999999,
307
+ trace_id=None,
308
+ keep_browser_open=False,
230
309
  )
231
310
 
232
311
 
233
312
  class TestExecuteRunApiCli:
234
313
  """Test suite for API controller functions."""
235
314
 
315
+ @pytest.mark.asyncio
316
+ async def test_execute_run_api_cli_calls_attempt_api_with_trace_id_correctly(
317
+ self, execute_cli_mocks: ExecuteCLIMocks
318
+ ):
319
+ """Test that execute_run_api_cli calls attemptApi with trace_id correctly."""
320
+ from intuned_cli.controller.api import execute_run_api_cli
321
+
322
+ await execute_run_api_cli(
323
+ api_name="testApi",
324
+ input_data={},
325
+ retries=3,
326
+ headless=False,
327
+ timeout=30,
328
+ trace=False,
329
+ keep_browser_open=False,
330
+ )
331
+
332
+ execute_cli_mocks.attempt_api.assert_called()
333
+ call_args = execute_cli_mocks.attempt_api.call_args
334
+ assert call_args[1]["trace_id"] is None
335
+
336
+ execute_cli_mocks.attempt_api.reset_mock()
337
+
338
+ await execute_run_api_cli(
339
+ api_name="testApi",
340
+ input_data={},
341
+ retries=3,
342
+ headless=False,
343
+ timeout=30,
344
+ trace=True,
345
+ keep_browser_open=False,
346
+ )
347
+ execute_cli_mocks.attempt_api.assert_called()
348
+ call_args = execute_cli_mocks.attempt_api.call_args
349
+ assert isinstance(call_args[1]["trace_id"], str)
350
+
236
351
  @pytest.mark.asyncio
237
352
  async def test_execute_run_api_cli_calls_attempt_api_once_if_success(self, execute_cli_mocks: ExecuteCLIMocks):
238
353
  """Test that execute_run_api_cli calls attemptApi once if successful."""
@@ -244,6 +359,8 @@ class TestExecuteRunApiCli:
244
359
  retries=3,
245
360
  headless=False,
246
361
  timeout=30,
362
+ trace=False,
363
+ keep_browser_open=False,
247
364
  )
248
365
 
249
366
  execute_cli_mocks.attempt_api.assert_called_once()
@@ -264,6 +381,8 @@ class TestExecuteRunApiCli:
264
381
  retries=10,
265
382
  headless=False,
266
383
  timeout=30,
384
+ trace=False,
385
+ keep_browser_open=False,
267
386
  )
268
387
 
269
388
  assert execute_cli_mocks.attempt_api.call_count == 10
@@ -284,6 +403,8 @@ class TestExecuteRunApiCli:
284
403
  retries=3,
285
404
  headless=False,
286
405
  timeout=30,
406
+ trace=False,
407
+ keep_browser_open=False,
287
408
  )
288
409
 
289
410
  execute_cli_mocks.attempt_api.assert_called_once()
@@ -305,6 +426,8 @@ class TestExecuteRunApiCli:
305
426
  retries=10,
306
427
  headless=False,
307
428
  timeout=30,
429
+ trace=False,
430
+ keep_browser_open=False,
308
431
  )
309
432
 
310
433
  assert execute_cli_mocks.attempt_api.call_count == 2
@@ -340,6 +463,8 @@ class TestExecuteRunApiCli:
340
463
  retries=10,
341
464
  headless=False,
342
465
  timeout=30,
466
+ trace=False,
467
+ keep_browser_open=False,
343
468
  )
344
469
 
345
470
  assert mock_validate_auth.call_count == 2
@@ -363,6 +488,8 @@ class TestExecuteRunApiCli:
363
488
  retries=1,
364
489
  headless=False,
365
490
  timeout=30,
491
+ trace=False,
492
+ keep_browser_open=False,
366
493
  )
367
494
 
368
495
  execute_cli_mocks.execute_run_validate_auth_session_cli.assert_not_called()
@@ -393,6 +520,8 @@ class TestExecuteRunApiCli:
393
520
  retries=10,
394
521
  headless=False,
395
522
  timeout=30,
523
+ trace=False,
524
+ keep_browser_open=False,
396
525
  )
397
526
 
398
527
  # Verify auth validation was called
@@ -425,6 +554,8 @@ class TestExecuteRunApiCli:
425
554
  retries=1,
426
555
  headless=False,
427
556
  timeout=30,
557
+ trace=False,
558
+ keep_browser_open=False,
428
559
  )
429
560
 
430
561
  execute_cli_mocks.write_results_to_file.assert_called_once()
@@ -446,6 +577,8 @@ class TestExecuteRunApiCli:
446
577
  retries=1,
447
578
  headless=False,
448
579
  timeout=30,
580
+ trace=False,
581
+ keep_browser_open=False,
449
582
  )
450
583
 
451
584
  shared_mocks.assert_api_file_exists.assert_called_once_with("api", "testApi")
@@ -465,6 +598,8 @@ class TestExecuteAttemptApiCli:
465
598
  input_data={},
466
599
  headless=False,
467
600
  timeout=30,
601
+ trace=False,
602
+ keep_browser_open=False,
468
603
  )
469
604
 
470
605
  execute_cli_mocks.attempt_api.assert_called_once()
@@ -479,6 +614,8 @@ class TestExecuteAttemptApiCli:
479
614
  input_data={},
480
615
  headless=False,
481
616
  timeout=30,
617
+ trace=False,
618
+ keep_browser_open=False,
482
619
  )
483
620
 
484
621
  execute_cli_mocks.attempt_api.assert_called_once()
@@ -502,6 +639,8 @@ class TestExecuteAttemptApiCli:
502
639
  output_file="output.json",
503
640
  headless=False,
504
641
  timeout=30,
642
+ trace=False,
643
+ keep_browser_open=False,
505
644
  )
506
645
 
507
646
  # Verify file was written
@@ -523,6 +662,8 @@ class TestExecuteAttemptApiCli:
523
662
  input_data={},
524
663
  headless=False,
525
664
  timeout=30,
665
+ trace=False,
666
+ keep_browser_open=False,
526
667
  )
527
668
 
528
669
  shared_mocks.assert_api_file_exists.assert_called_once_with("api", "testApi")