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.
- intuned_cli/__init__.py +15 -24
- intuned_cli/commands/__init__.py +6 -1
- intuned_cli/commands/attempt_api_command.py +8 -0
- intuned_cli/commands/attempt_authsession_check_command.py +8 -0
- intuned_cli/commands/attempt_authsession_command.py +4 -4
- intuned_cli/commands/attempt_authsession_create_command.py +9 -1
- intuned_cli/commands/attempt_command.py +4 -4
- intuned_cli/commands/authsession_command.py +12 -0
- intuned_cli/commands/authsession_record_command.py +54 -0
- intuned_cli/commands/command.py +6 -4
- intuned_cli/commands/deploy_command.py +2 -0
- intuned_cli/commands/init_command.py +2 -0
- intuned_cli/commands/run_api_command.py +9 -1
- intuned_cli/commands/run_authsession_command.py +4 -4
- intuned_cli/commands/run_authsession_create_command.py +34 -4
- intuned_cli/commands/run_authsession_update_command.py +33 -4
- intuned_cli/commands/run_authsession_validate_command.py +32 -3
- intuned_cli/commands/run_command.py +4 -4
- intuned_cli/commands/save_command.py +2 -0
- intuned_cli/controller/__test__/test_api.py +159 -18
- intuned_cli/controller/__test__/test_authsession.py +497 -6
- intuned_cli/controller/api.py +40 -39
- intuned_cli/controller/authsession.py +213 -110
- intuned_cli/controller/deploy.py +3 -3
- intuned_cli/controller/save.py +47 -48
- intuned_cli/types.py +14 -0
- intuned_cli/utils/__test__/test_browser.py +132 -0
- intuned_cli/utils/__test__/test_traces.py +27 -0
- intuned_cli/utils/api_helpers.py +54 -5
- intuned_cli/utils/auth_session_helpers.py +42 -7
- intuned_cli/utils/backend.py +4 -1
- intuned_cli/utils/browser.py +63 -0
- intuned_cli/utils/error.py +14 -0
- intuned_cli/utils/exclusions.py +1 -0
- intuned_cli/utils/help.py +9 -0
- intuned_cli/utils/traces.py +31 -0
- intuned_cli/utils/wrapper.py +58 -0
- intuned_internal_cli/__init__.py +7 -0
- {intuned_runtime-1.3.1.dist-info → intuned_runtime-1.3.2.dist-info}/METADATA +4 -2
- {intuned_runtime-1.3.1.dist-info → intuned_runtime-1.3.2.dist-info}/RECORD +45 -37
- runtime/browser/launch_chromium.py +4 -0
- runtime/types/settings_types.py +13 -4
- {intuned_runtime-1.3.1.dist-info → intuned_runtime-1.3.2.dist-info}/WHEEL +0 -0
- {intuned_runtime-1.3.1.dist-info → intuned_runtime-1.3.2.dist-info}/entry_points.txt +0 -0
- {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 =
|
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")
|