intuned-runtime 1.0.0__py3-none-any.whl → 1.1.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.
Files changed (78) hide show
  1. intuned_cli/__init__.py +40 -0
  2. intuned_cli/commands/__init__.py +18 -0
  3. intuned_cli/commands/attempt_api_command.py +51 -0
  4. intuned_cli/commands/attempt_authsession_check_command.py +38 -0
  5. intuned_cli/commands/attempt_authsession_command.py +12 -0
  6. intuned_cli/commands/attempt_authsession_create_command.py +44 -0
  7. intuned_cli/commands/attempt_command.py +12 -0
  8. intuned_cli/commands/command.py +26 -0
  9. intuned_cli/commands/deploy_command.py +47 -0
  10. intuned_cli/commands/init_command.py +21 -0
  11. intuned_cli/commands/run_api_command.py +69 -0
  12. intuned_cli/commands/run_authsession_command.py +12 -0
  13. intuned_cli/commands/run_authsession_create_command.py +50 -0
  14. intuned_cli/commands/run_authsession_update_command.py +52 -0
  15. intuned_cli/commands/run_authsession_validate_command.py +49 -0
  16. intuned_cli/commands/run_command.py +12 -0
  17. intuned_cli/constants/__init__.py +1 -0
  18. intuned_cli/constants/readme.py +134 -0
  19. intuned_cli/controller/__test__/__init__.py +0 -0
  20. intuned_cli/controller/__test__/test_api.py +529 -0
  21. intuned_cli/controller/__test__/test_authsession.py +907 -0
  22. intuned_cli/controller/api.py +212 -0
  23. intuned_cli/controller/authsession.py +458 -0
  24. intuned_cli/controller/deploy.py +352 -0
  25. intuned_cli/controller/init.py +97 -0
  26. intuned_cli/types.py +33 -0
  27. intuned_cli/utils/api_helpers.py +32 -0
  28. intuned_cli/utils/auth_session_helpers.py +57 -0
  29. intuned_cli/utils/backend.py +5 -0
  30. intuned_cli/utils/confirmation.py +0 -0
  31. intuned_cli/utils/console.py +6 -0
  32. intuned_cli/utils/error.py +27 -0
  33. intuned_cli/utils/exclusions.py +40 -0
  34. intuned_cli/utils/get_auth_parameters.py +18 -0
  35. intuned_cli/utils/import_function.py +15 -0
  36. intuned_cli/utils/timeout.py +25 -0
  37. {cli → intuned_internal_cli}/__init__.py +1 -1
  38. {cli → intuned_internal_cli}/commands/__init__.py +2 -0
  39. {cli → intuned_internal_cli}/commands/ai_source/deploy.py +1 -1
  40. {cli → intuned_internal_cli}/commands/project/type_check.py +39 -32
  41. intuned_internal_cli/commands/root.py +15 -0
  42. {intuned_runtime-1.0.0.dist-info → intuned_runtime-1.1.0.dist-info}/METADATA +3 -1
  43. intuned_runtime-1.1.0.dist-info/RECORD +96 -0
  44. intuned_runtime-1.1.0.dist-info/entry_points.txt +4 -0
  45. runtime/__init__.py +2 -1
  46. runtime/backend_functions/_call_backend_function.py +0 -5
  47. runtime/browser/__init__.py +2 -1
  48. runtime/browser/launch_chromium.py +68 -49
  49. runtime/browser/storage_state.py +11 -12
  50. runtime/errors/run_api_errors.py +14 -10
  51. runtime/run/playwright_constructs.py +4 -2
  52. runtime/run/pydantic_encoder.py +15 -0
  53. runtime/run/run_api.py +5 -4
  54. runtime/types/run_types.py +16 -0
  55. intuned_runtime-1.0.0.dist-info/RECORD +0 -58
  56. intuned_runtime-1.0.0.dist-info/entry_points.txt +0 -3
  57. {cli → intuned_internal_cli}/commands/ai_source/__init__.py +0 -0
  58. {cli → intuned_internal_cli}/commands/ai_source/ai_source.py +0 -0
  59. {cli → intuned_internal_cli}/commands/browser/__init__.py +0 -0
  60. {cli → intuned_internal_cli}/commands/browser/save_state.py +0 -0
  61. {cli → intuned_internal_cli}/commands/init.py +0 -0
  62. {cli → intuned_internal_cli}/commands/project/__init__.py +0 -0
  63. {cli → intuned_internal_cli}/commands/project/auth_session/__init__.py +0 -0
  64. {cli → intuned_internal_cli}/commands/project/auth_session/check.py +0 -0
  65. {cli → intuned_internal_cli}/commands/project/auth_session/create.py +0 -0
  66. {cli → intuned_internal_cli}/commands/project/auth_session/load.py +0 -0
  67. {cli → intuned_internal_cli}/commands/project/project.py +0 -0
  68. {cli → intuned_internal_cli}/commands/project/run.py +0 -0
  69. {cli → intuned_internal_cli}/commands/project/run_interface.py +0 -0
  70. {cli → intuned_internal_cli}/commands/project/upgrade.py +0 -0
  71. {cli → intuned_internal_cli}/commands/publish_packages.py +0 -0
  72. {cli → intuned_internal_cli}/logger.py +0 -0
  73. {cli → intuned_internal_cli}/utils/ai_source_project.py +0 -0
  74. {cli → intuned_internal_cli}/utils/code_tree.py +0 -0
  75. {cli → intuned_internal_cli}/utils/run_apis.py +0 -0
  76. {cli → intuned_internal_cli}/utils/unix_socket.py +0 -0
  77. {intuned_runtime-1.0.0.dist-info → intuned_runtime-1.1.0.dist-info}/LICENSE +0 -0
  78. {intuned_runtime-1.0.0.dist-info → intuned_runtime-1.1.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,907 @@
1
+ from dataclasses import dataclass
2
+ from typing import Any
3
+ from typing import Generator
4
+ from unittest.mock import AsyncMock
5
+ from unittest.mock import Mock
6
+ from unittest.mock import patch
7
+
8
+ import pytest
9
+
10
+ from intuned_cli.controller.authsession import execute_attempt_check_auth_session_cli
11
+ from intuned_cli.controller.authsession import execute_attempt_create_auth_session_cli
12
+ from intuned_cli.controller.authsession import execute_run_create_auth_session_cli
13
+ from intuned_cli.controller.authsession import execute_run_update_auth_session_cli
14
+ from intuned_cli.controller.authsession import execute_run_validate_auth_session_cli
15
+ from intuned_cli.controller.authsession import run_check
16
+ from intuned_cli.controller.authsession import run_check_with_retries
17
+ from intuned_cli.controller.authsession import run_create
18
+ from intuned_cli.controller.authsession import run_create_with_retries
19
+ from intuned_cli.utils.error import CLIError
20
+ from runtime.errors.run_api_errors import AutomationError
21
+ from runtime.types.run_types import ProxyConfig
22
+ from runtime.types.run_types import StorageState
23
+
24
+ from .test_api import AsyncContextManagerMock
25
+
26
+
27
+ def get_mock_console():
28
+ """Create a mock console that tracks calls."""
29
+ mock_console = Mock()
30
+ mock_console.print = Mock()
31
+ return mock_console
32
+
33
+
34
+ def _get_empty_auth_session():
35
+ return StorageState(cookies=[], origins=[], session_storage=[])
36
+
37
+
38
+ # Test data helpers
39
+ def _check_passed_result():
40
+ mock_result = Mock()
41
+ mock_result.result = True
42
+ return mock_result
43
+
44
+
45
+ def _check_failed_result():
46
+ mock_result = Mock()
47
+ mock_result.result = False
48
+ return mock_result
49
+
50
+
51
+ def _create_success_result(session_data: Any):
52
+ mock_result = Mock()
53
+ mock_result.session = session_data
54
+ return mock_result
55
+
56
+
57
+ @dataclass
58
+ class SharedMocks:
59
+ console: Mock
60
+ assert_api_file_exists: AsyncMock
61
+ register_get_auth_session_parameters: AsyncMock
62
+
63
+
64
+ @pytest.fixture(autouse=True)
65
+ def shared_mocks() -> Generator[SharedMocks, Any, None]:
66
+ """Mock dependencies for API controller tests."""
67
+ _mock_console_patch = patch("intuned_cli.controller.authsession.console", get_mock_console())
68
+ _mock_assert_api_patch = patch("intuned_cli.controller.authsession.assert_api_file_exists", new_callable=AsyncMock)
69
+ _mock_register_auth_patch = patch("intuned_cli.controller.authsession.register_get_auth_session_parameters")
70
+
71
+ with (
72
+ _mock_console_patch,
73
+ _mock_assert_api_patch as mock_assert_api,
74
+ _mock_register_auth_patch as mock_register_auth,
75
+ ):
76
+ # Setup default return values
77
+ mock_assert_api.return_value = None
78
+ mock_register_auth.return_value = None
79
+
80
+ yield SharedMocks(
81
+ console=get_mock_console(),
82
+ assert_api_file_exists=mock_assert_api,
83
+ register_get_auth_session_parameters=mock_register_auth,
84
+ )
85
+
86
+
87
+ @dataclass
88
+ class AttemptApiMocks:
89
+ extendable_timeout: AsyncMock
90
+ run_api: AsyncMock
91
+
92
+
93
+ @pytest.fixture
94
+ def attempt_api_mocks() -> Generator[AttemptApiMocks, Any, None]:
95
+ """Mock dependencies for attempt_api tests."""
96
+ _mock_timeout_patch = patch("intuned_cli.controller.authsession.extendable_timeout")
97
+ _mock_run_api_patch = patch("intuned_cli.controller.authsession.run_api", new_callable=AsyncMock)
98
+
99
+ with (
100
+ _mock_timeout_patch as mock_timeout,
101
+ _mock_run_api_patch as mock_run_api,
102
+ ):
103
+ # Setup default return values
104
+ mock_timeout.return_value = AsyncContextManagerMock()
105
+
106
+ # Mock run_api to return success by default
107
+ mock_result = Mock()
108
+ mock_result.result = "test_result"
109
+ mock_result.payload_to_append = []
110
+ mock_run_api.return_value = mock_result
111
+
112
+ yield AttemptApiMocks(
113
+ extendable_timeout=mock_timeout,
114
+ run_api=mock_run_api,
115
+ )
116
+
117
+
118
+ @dataclass
119
+ class WithRetriesMocks:
120
+ run_check: AsyncMock
121
+ run_create: AsyncMock
122
+ store_auth_session_instance: AsyncMock
123
+
124
+
125
+ @pytest.fixture
126
+ def with_retries_mocks() -> Generator[WithRetriesMocks, Any, None]:
127
+ """Mock dependencies for with_retries tests."""
128
+ _mock_run_check_patch = patch("intuned_cli.controller.authsession.run_check", new_callable=AsyncMock)
129
+ _mock_run_create_patch = patch("intuned_cli.controller.authsession.run_create", new_callable=AsyncMock)
130
+ _mock_store_auth_session_instance_patch = patch(
131
+ "intuned_cli.controller.authsession.store_auth_session_instance", new_callable=AsyncMock
132
+ )
133
+
134
+ with (
135
+ _mock_run_check_patch as mock_run_check,
136
+ _mock_run_create_patch as mock_run_create,
137
+ _mock_store_auth_session_instance_patch as mock_store_auth_session_instance,
138
+ ):
139
+ # Setup default return values
140
+ mock_run_check.return_value = True
141
+ mock_run_create.return_value = "test_result"
142
+
143
+ yield WithRetriesMocks(
144
+ run_check=mock_run_check,
145
+ run_create=mock_run_create,
146
+ store_auth_session_instance=mock_store_auth_session_instance,
147
+ )
148
+
149
+
150
+ @dataclass
151
+ class ExecuteCLIMocks:
152
+ run_create_with_retries: AsyncMock
153
+ run_check_with_retries: AsyncMock
154
+ load_auth_session_instance: AsyncMock
155
+
156
+
157
+ @pytest.fixture
158
+ def execute_cli_mocks() -> Generator[ExecuteCLIMocks, Any, None]:
159
+ """Mock dependencies for execute_*_cli tests."""
160
+ _mock_run_create_with_retries_patch = patch(
161
+ "intuned_cli.controller.authsession.run_create_with_retries", new_callable=AsyncMock
162
+ )
163
+ _mock_run_check_with_retries_patch = patch(
164
+ "intuned_cli.controller.authsession.run_check_with_retries", new_callable=AsyncMock
165
+ )
166
+ _mock_load_auth_session_instance_patch = patch(
167
+ "intuned_cli.controller.authsession.load_auth_session_instance", new_callable=AsyncMock
168
+ )
169
+
170
+ with (
171
+ _mock_run_create_with_retries_patch as mock_run_create_with_retries,
172
+ _mock_run_check_with_retries_patch as mock_run_check_with_retries,
173
+ _mock_load_auth_session_instance_patch as mock_load_auth_session_instance,
174
+ ):
175
+ # Setup default return values
176
+ mock_run_check_with_retries.return_value = True
177
+ mock_run_create_with_retries.return_value = "test_result"
178
+ mock_load_auth_session_instance.return_value = ({}, {})
179
+
180
+ yield ExecuteCLIMocks(
181
+ run_create_with_retries=mock_run_create_with_retries,
182
+ run_check_with_retries=mock_run_check_with_retries,
183
+ load_auth_session_instance=mock_load_auth_session_instance,
184
+ )
185
+
186
+
187
+ class TestRunCheck:
188
+ """Test suite for Auth Session controller functions."""
189
+
190
+ @pytest.mark.asyncio
191
+ async def test_calls_timeout_middleware_with_timeout(self, attempt_api_mocks: AttemptApiMocks):
192
+ """Test that run_check calls timeout middleware with the correct timeout."""
193
+ await run_check(
194
+ auth=_get_empty_auth_session(),
195
+ headless=False,
196
+ timeout=6000,
197
+ )
198
+
199
+ attempt_api_mocks.extendable_timeout.assert_called_once_with(6000)
200
+
201
+ @pytest.mark.asyncio
202
+ async def test_calls_run_api_with_correct_parameters_and_parses_proxy(self, attempt_api_mocks: AttemptApiMocks):
203
+ """Test that run_check calls run_api with correct parameters and parses proxy."""
204
+ with patch("intuned_cli.controller.authsession.ProxyConfig.parse_from_str") as mock_parse_proxy:
205
+ proxy_config = ProxyConfig(
206
+ username="user",
207
+ password="pass",
208
+ server="proxy-server",
209
+ )
210
+ mock_parse_proxy.return_value = proxy_config
211
+
212
+ auth = _get_empty_auth_session()
213
+
214
+ await run_check(
215
+ auth=auth,
216
+ headless=False,
217
+ timeout=999999999,
218
+ proxy="proxy",
219
+ )
220
+
221
+ mock_parse_proxy.assert_called_once_with("proxy")
222
+ attempt_api_mocks.run_api.assert_called_once()
223
+
224
+ # Verify the call arguments
225
+ call_args = attempt_api_mocks.run_api.call_args[0][0]
226
+ assert call_args.automation_function.name == "auth-sessions/check"
227
+ assert call_args.run_options.headless is False
228
+ assert call_args.run_options.proxy is proxy_config
229
+ assert call_args.auth.session.state is auth
230
+
231
+ @pytest.mark.asyncio
232
+ async def test_returns_if_check_returns(self, attempt_api_mocks: AttemptApiMocks):
233
+ """Test that run_check returns the correct boolean values."""
234
+ attempt_api_mocks.run_api.return_value = _check_passed_result()
235
+
236
+ result_true = await run_check(
237
+ auth=StorageState(cookies=[], origins=[], session_storage=[]),
238
+ headless=False,
239
+ timeout=999999999,
240
+ )
241
+
242
+ assert result_true is True
243
+
244
+ attempt_api_mocks.run_api.return_value = _check_failed_result()
245
+
246
+ result_false = await run_check(
247
+ auth=_get_empty_auth_session(),
248
+ headless=False,
249
+ timeout=999999999,
250
+ )
251
+
252
+ assert result_false is False
253
+
254
+ @pytest.mark.asyncio
255
+ async def test_throws_error_when_check_fails_with_error(self, attempt_api_mocks: AttemptApiMocks):
256
+ """Test that run_check handles errors correctly."""
257
+ error = Exception("runApi failed")
258
+ attempt_api_mocks.run_api.side_effect = error
259
+
260
+ with pytest.raises(Exception, match="runApi failed"):
261
+ await run_check(
262
+ auth=StorageState(cookies=[], origins=[], session_storage=[]),
263
+ headless=False,
264
+ timeout=999999999,
265
+ )
266
+
267
+ # Test with AutomationError - should return False, not throw
268
+ attempt_api_mocks.run_api.side_effect = AutomationError(Exception("failed"))
269
+
270
+ with pytest.raises(Exception, match="failed"):
271
+ await run_check(
272
+ auth=StorageState(cookies=[], origins=[], session_storage=[]),
273
+ headless=False,
274
+ timeout=999999999,
275
+ )
276
+
277
+
278
+ class TestRunCheckWithRetries:
279
+ @pytest.mark.asyncio
280
+ async def test_retries_the_check_if_it_fails(self, with_retries_mocks: WithRetriesMocks):
281
+ """Test that run_check_with_retries retries the check if it fails."""
282
+ with_retries_mocks.run_check.side_effect = [False, True]
283
+
284
+ result = await run_check_with_retries(
285
+ auth=_get_empty_auth_session(),
286
+ retries=10,
287
+ headless=False,
288
+ timeout=999999999,
289
+ )
290
+
291
+ assert result is True
292
+ assert with_retries_mocks.run_check.call_count == 2
293
+
294
+ @pytest.mark.asyncio
295
+ async def test_returns_false_if_all_retries_fail(self, with_retries_mocks: WithRetriesMocks):
296
+ """Test that run_check_with_retries returns false if all retries fail."""
297
+ with_retries_mocks.run_check.return_value = False
298
+
299
+ result = await run_check_with_retries(
300
+ auth=_get_empty_auth_session(),
301
+ retries=10,
302
+ headless=False,
303
+ timeout=999999999,
304
+ )
305
+
306
+ assert result is False
307
+ assert with_retries_mocks.run_check.call_count == 10
308
+
309
+ @pytest.mark.asyncio
310
+ async def test_continues_retrying_if_check_fails_due_to_automation_error(
311
+ self, with_retries_mocks: WithRetriesMocks
312
+ ):
313
+ """Test that run_check_with_retries continues retrying if check fails due to an automation error."""
314
+ with_retries_mocks.run_check.side_effect = [AutomationError(Exception("failed")), True]
315
+
316
+ result = await run_check_with_retries(
317
+ auth=_get_empty_auth_session(),
318
+ retries=10,
319
+ headless=False,
320
+ timeout=999999999,
321
+ )
322
+
323
+ assert result is True
324
+ assert with_retries_mocks.run_check.call_count == 2
325
+
326
+ @pytest.mark.asyncio
327
+ async def test_throws_error_if_check_fails_with_non_automation_error(self, with_retries_mocks: WithRetriesMocks):
328
+ """Test that run_check_with_retries throws the error if check fails with a non-automation error."""
329
+ error = Exception("runCheck failed")
330
+ with_retries_mocks.run_check.side_effect = error
331
+
332
+ with pytest.raises(Exception, match="runCheck failed"):
333
+ await run_check_with_retries(
334
+ auth=_get_empty_auth_session(),
335
+ retries=10,
336
+ headless=False,
337
+ timeout=999999999,
338
+ )
339
+
340
+ with_retries_mocks.run_check.assert_called_once()
341
+
342
+
343
+ class TestRunCreate:
344
+ @pytest.mark.asyncio
345
+ async def test_calls_timeout_middleware_with_timeout(self, attempt_api_mocks: AttemptApiMocks):
346
+ """Test that run_create calls timeout middleware with the correct timeout."""
347
+ attempt_api_mocks.run_api.return_value = _create_success_result("session")
348
+
349
+ await run_create(
350
+ auth_session_input={},
351
+ headless=False,
352
+ timeout=6000,
353
+ )
354
+
355
+ attempt_api_mocks.extendable_timeout.assert_called_once_with(6000)
356
+
357
+ @pytest.mark.asyncio
358
+ async def test_calls_run_api_with_correct_parameters_and_parses_proxy(self, attempt_api_mocks: AttemptApiMocks):
359
+ """Test that run_create calls run_api with correct parameters and parses proxy."""
360
+ with patch("intuned_cli.controller.authsession.ProxyConfig.parse_from_str") as mock_parse_proxy:
361
+ proxy_config = ProxyConfig(
362
+ username="user",
363
+ password="pass",
364
+ server="proxy-server",
365
+ )
366
+ mock_parse_proxy.return_value = proxy_config
367
+ attempt_api_mocks.run_api.return_value = _create_success_result("session")
368
+
369
+ auth_session_input = {"some": "input"}
370
+ await run_create(
371
+ auth_session_input=auth_session_input,
372
+ headless=False,
373
+ timeout=999999999,
374
+ proxy="proxy",
375
+ )
376
+
377
+ mock_parse_proxy.assert_called_once_with("proxy")
378
+ attempt_api_mocks.run_api.assert_called_once()
379
+
380
+ # Verify the call arguments
381
+ call_args = attempt_api_mocks.run_api.call_args[0][0]
382
+ assert call_args.automation_function.name == "auth-sessions/create"
383
+ assert call_args.automation_function.params is auth_session_input
384
+ assert call_args.run_options.headless is False
385
+ assert call_args.run_options.proxy is proxy_config
386
+ assert call_args.retrieve_session is True
387
+
388
+ @pytest.mark.asyncio
389
+ async def test_returns_if_create_returns(self, attempt_api_mocks: AttemptApiMocks):
390
+ """Test that run_create returns the session."""
391
+ attempt_api_mocks.run_api.return_value = _create_success_result("session")
392
+
393
+ storage_state = await run_create(
394
+ auth_session_input={},
395
+ headless=False,
396
+ timeout=999999999,
397
+ )
398
+
399
+ assert storage_state == "session"
400
+
401
+ @pytest.mark.asyncio
402
+ async def test_throws_error_when_create_fails_with_error(self, attempt_api_mocks: AttemptApiMocks):
403
+ """Test that run_create throws the error when create fails."""
404
+ error = Exception("runApi failed")
405
+ attempt_api_mocks.run_api.side_effect = error
406
+
407
+ with pytest.raises(Exception, match="runApi failed"):
408
+ await run_create(
409
+ auth_session_input={},
410
+ headless=False,
411
+ timeout=999999999,
412
+ )
413
+
414
+
415
+ class TestRunCreateWithRetries:
416
+ @pytest.mark.asyncio
417
+ async def test_saves_the_auth_session_instance(self, with_retries_mocks: WithRetriesMocks):
418
+ """Test that run_create_with_retries saves the auth session instance."""
419
+ with_retries_mocks.run_create.return_value = "session"
420
+
421
+ input: Any = dict() # type: ignore
422
+ metadata: Any = dict() # type: ignore
423
+ await run_create_with_retries(
424
+ auth_session_id="testId",
425
+ auth_session_input=input,
426
+ metadata=metadata,
427
+ retries=1,
428
+ headless=False,
429
+ timeout=30,
430
+ )
431
+
432
+ assert with_retries_mocks.run_create.call_count == 1
433
+ with_retries_mocks.store_auth_session_instance.assert_called_once()
434
+ call_args = with_retries_mocks.store_auth_session_instance.call_args.args
435
+ assert call_args[0] == "session"
436
+ assert call_args[1] == "testId"
437
+ assert call_args[2] is input
438
+ call_kwargs = with_retries_mocks.store_auth_session_instance.call_args.kwargs
439
+ assert call_kwargs["metadata"] is metadata
440
+
441
+ @pytest.mark.asyncio
442
+ async def test_retries_if_it_fails_with_automation_error(self, with_retries_mocks: WithRetriesMocks):
443
+ """Test that run_create_with_retries retries if it fails with automation error."""
444
+ with_retries_mocks.run_create.side_effect = [AutomationError(Exception("failed")), "session"]
445
+
446
+ await run_create_with_retries(
447
+ auth_session_id="testId",
448
+ auth_session_input={},
449
+ retries=10,
450
+ headless=False,
451
+ timeout=30,
452
+ )
453
+
454
+ assert with_retries_mocks.run_create.call_count == 2
455
+
456
+ @pytest.mark.asyncio
457
+ async def test_throws_if_all_retries_fail(self, with_retries_mocks: WithRetriesMocks):
458
+ """Test that run_create_with_retries throws if all retries fail."""
459
+ with_retries_mocks.run_create.side_effect = AutomationError(Exception("failed"))
460
+
461
+ with pytest.raises(CLIError):
462
+ await run_create_with_retries(
463
+ auth_session_id="testId",
464
+ auth_session_input={},
465
+ retries=3,
466
+ headless=False,
467
+ timeout=30,
468
+ )
469
+
470
+ assert with_retries_mocks.run_create.call_count == 3
471
+
472
+ @pytest.mark.asyncio
473
+ async def test_throws_error_if_create_fails_with_non_automation_error(self, with_retries_mocks: WithRetriesMocks):
474
+ """Test that run_create_with_retries throws the error if create fails with a non-automation error."""
475
+ error = Exception("create failed")
476
+ with_retries_mocks.run_create.side_effect = error
477
+
478
+ with pytest.raises(Exception, match="create failed"):
479
+ await run_create_with_retries(
480
+ auth_session_id="testId",
481
+ auth_session_input={},
482
+ retries=10,
483
+ headless=False,
484
+ timeout=30,
485
+ )
486
+
487
+ with_retries_mocks.run_create.assert_called_once()
488
+
489
+
490
+ class TestExecuteAuthSessionValidateCLI:
491
+ @pytest.mark.asyncio
492
+ async def test_asserts_check_api_file_exists(self, execute_cli_mocks: ExecuteCLIMocks, shared_mocks: SharedMocks):
493
+ """Test that executeRunValidateAuthSessionCLI asserts check API file exists."""
494
+ await execute_run_validate_auth_session_cli(
495
+ id="testId",
496
+ auto_recreate=False,
497
+ check_retries=1,
498
+ create_retries=1,
499
+ headless=False,
500
+ timeout=30,
501
+ )
502
+
503
+ shared_mocks.assert_api_file_exists.assert_called_once_with("auth-sessions", "check")
504
+
505
+ @pytest.mark.asyncio
506
+ async def test_succeeds_if_check_succeeds(self, execute_cli_mocks: ExecuteCLIMocks):
507
+ """Test that executeRunValidateAuthSessionCLI succeeds if check succeeds."""
508
+
509
+ auth = _get_empty_auth_session()
510
+ execute_cli_mocks.load_auth_session_instance.return_value = (auth, {})
511
+
512
+ result = await execute_run_validate_auth_session_cli(
513
+ id="testId",
514
+ auto_recreate=False,
515
+ check_retries=1,
516
+ create_retries=1,
517
+ headless=False,
518
+ timeout=30,
519
+ )
520
+
521
+ assert execute_cli_mocks.run_check_with_retries.call_count == 1
522
+ assert execute_cli_mocks.run_create_with_retries.call_count == 0
523
+ assert result == auth
524
+
525
+ @pytest.mark.asyncio
526
+ async def test_throws_if_check_fails_with_auto_recreate_disabled(self, execute_cli_mocks: ExecuteCLIMocks):
527
+ """Test that executeRunValidateAuthSessionCLI throws if check fails with auto recreate disabled."""
528
+ execute_cli_mocks.run_check_with_retries.return_value = False
529
+
530
+ with pytest.raises(CLIError, match="Auto recreate is disabled"):
531
+ await execute_run_validate_auth_session_cli(
532
+ id="testId",
533
+ auto_recreate=False,
534
+ check_retries=1,
535
+ create_retries=1,
536
+ headless=False,
537
+ timeout=30,
538
+ )
539
+
540
+ @pytest.mark.asyncio
541
+ async def test_asserts_create_exists_if_check_fails_with_auto_recreate_enabled(
542
+ self, execute_cli_mocks: ExecuteCLIMocks, shared_mocks: SharedMocks
543
+ ):
544
+ """Test that executeRunValidateAuthSessionCLI recreates if check fails with auto recreate enabled."""
545
+ # First check fails, create succeeds, second check succeeds
546
+ execute_cli_mocks.run_check_with_retries.side_effect = [False, True]
547
+ auth = _get_empty_auth_session()
548
+ execute_cli_mocks.run_create_with_retries.return_value = auth
549
+
550
+ await execute_run_validate_auth_session_cli(
551
+ id="testId",
552
+ auto_recreate=True,
553
+ check_retries=1,
554
+ create_retries=1,
555
+ headless=False,
556
+ timeout=30,
557
+ )
558
+
559
+ # Should call assert for both check and create APIs
560
+ shared_mocks.assert_api_file_exists.assert_any_call("auth-sessions", "create")
561
+
562
+ @pytest.mark.asyncio
563
+ async def test_raises_if_auth_session_is_manual_if_check_fails_with_auto_recreate_enabled(
564
+ self, execute_cli_mocks: ExecuteCLIMocks
565
+ ):
566
+ """Test that executeRunValidateAuthSessionCLI raises if auth session is manual."""
567
+
568
+ mock_metadata = Mock()
569
+ mock_metadata.auth_session_type = "MANUAL"
570
+ execute_cli_mocks.load_auth_session_instance.return_value = ({}, mock_metadata)
571
+ execute_cli_mocks.run_check_with_retries.return_value = False
572
+
573
+ with pytest.raises(CLIError):
574
+ await execute_run_validate_auth_session_cli(
575
+ id="testId",
576
+ auto_recreate=True,
577
+ check_retries=1,
578
+ create_retries=1,
579
+ headless=False,
580
+ timeout=30,
581
+ )
582
+
583
+ @pytest.mark.asyncio
584
+ async def test_raises_if_check_fails_then_create_fails_with_auto_recreate_enabled(
585
+ self, execute_cli_mocks: ExecuteCLIMocks
586
+ ):
587
+ """Test that executeRunValidateAuthSessionCLI raises if create fails after check fails."""
588
+ execute_cli_mocks.run_check_with_retries.return_value = False
589
+ execute_cli_mocks.run_create_with_retries.side_effect = CLIError("create failed")
590
+
591
+ with pytest.raises(
592
+ CLIError,
593
+ ):
594
+ await execute_run_validate_auth_session_cli(
595
+ id="testId",
596
+ auto_recreate=True,
597
+ check_retries=1,
598
+ create_retries=1,
599
+ headless=False,
600
+ timeout=30,
601
+ )
602
+
603
+ @pytest.mark.asyncio
604
+ async def test_raises_if_check_fails_then_create_succeeds_then_check_fails_with_auto_recreate_enabled(
605
+ self, execute_cli_mocks: ExecuteCLIMocks
606
+ ):
607
+ """Test that executeRunValidateAuthSessionCLI raises if create fails after check fails."""
608
+ execute_cli_mocks.run_check_with_retries.return_value = False
609
+ execute_cli_mocks.run_create_with_retries.return_value = _get_empty_auth_session()
610
+
611
+ with pytest.raises(
612
+ CLIError,
613
+ ):
614
+ await execute_run_validate_auth_session_cli(
615
+ id="testId",
616
+ auto_recreate=True,
617
+ check_retries=1,
618
+ create_retries=1,
619
+ headless=False,
620
+ timeout=30,
621
+ )
622
+
623
+ @pytest.mark.asyncio
624
+ async def test_succeeds_if_check_fails_then_create_succeeds_then_check_succeeds_with_auto_recreate_enabled(
625
+ self, execute_cli_mocks: ExecuteCLIMocks
626
+ ):
627
+ """Test that executeRunValidateAuthSessionCLI raises if create fails after check fails."""
628
+ auth = _get_empty_auth_session()
629
+ execute_cli_mocks.run_check_with_retries.side_effect = [False, True]
630
+ execute_cli_mocks.run_create_with_retries.return_value = auth
631
+
632
+ result = await execute_run_validate_auth_session_cli(
633
+ id="testId",
634
+ auto_recreate=True,
635
+ check_retries=1,
636
+ create_retries=1,
637
+ headless=False,
638
+ timeout=30,
639
+ )
640
+
641
+ assert result == auth
642
+
643
+
644
+ class TestExecuteAuthSessionCreateCLI:
645
+ @pytest.mark.asyncio
646
+ async def test_asserts_create_and_check_api_files_exist(
647
+ self, execute_cli_mocks: ExecuteCLIMocks, shared_mocks: SharedMocks
648
+ ):
649
+ """Test that executeRunCreateAuthSessionCLI asserts create and check API files exist."""
650
+ execute_cli_mocks.run_create_with_retries.return_value = _get_empty_auth_session()
651
+
652
+ await execute_run_create_auth_session_cli(
653
+ input_data={},
654
+ check_retries=1,
655
+ create_retries=1,
656
+ headless=False,
657
+ timeout=30,
658
+ )
659
+
660
+ # Should assert both create and check API files exist
661
+ assert shared_mocks.assert_api_file_exists.call_count == 2
662
+ shared_mocks.assert_api_file_exists.assert_any_call("auth-sessions", "create")
663
+ shared_mocks.assert_api_file_exists.assert_any_call("auth-sessions", "check")
664
+
665
+ @pytest.mark.asyncio
666
+ async def test_throws_if_create_fails(self, execute_cli_mocks: ExecuteCLIMocks):
667
+ """Test that executeRunCreateAuthSessionCLI throws if create fails."""
668
+ execute_cli_mocks.run_create_with_retries.side_effect = CLIError("create failed")
669
+
670
+ with pytest.raises(CLIError):
671
+ await execute_run_create_auth_session_cli(
672
+ input_data={},
673
+ check_retries=1,
674
+ create_retries=1,
675
+ headless=False,
676
+ timeout=30,
677
+ )
678
+
679
+ @pytest.mark.asyncio
680
+ async def test_throws_if_check_fails_after_create_succeeds(
681
+ self,
682
+ execute_cli_mocks: ExecuteCLIMocks,
683
+ ):
684
+ """Test that executeRunCreateAuthSessionCLI throws if check fails after create succeeds."""
685
+ execute_cli_mocks.run_create_with_retries.return_value = _get_empty_auth_session()
686
+ execute_cli_mocks.run_check_with_retries.return_value = False
687
+
688
+ with pytest.raises(CLIError, match="Failed to create auth session"):
689
+ await execute_run_create_auth_session_cli(
690
+ input_data={},
691
+ check_retries=1,
692
+ create_retries=1,
693
+ headless=False,
694
+ timeout=30,
695
+ )
696
+
697
+ @pytest.mark.asyncio
698
+ async def test_saves_to_auth_session_instance_path_if_create_and_check_succeed(
699
+ self, execute_cli_mocks: ExecuteCLIMocks
700
+ ):
701
+ """Test that executeRunCreateAuthSessionCLI saves to auth session instance path if create and check succeed."""
702
+ execute_cli_mocks.run_create_with_retries.return_value = _get_empty_auth_session()
703
+ execute_cli_mocks.run_check_with_retries.return_value = True
704
+
705
+ input: Any = dict() # type: ignore
706
+ await execute_run_create_auth_session_cli(
707
+ input_data=input,
708
+ check_retries=1,
709
+ create_retries=1,
710
+ headless=False,
711
+ timeout=30,
712
+ )
713
+
714
+ # run_create_with_retries is tested to save in its own tests, so just verify it was called here
715
+ assert execute_cli_mocks.run_create_with_retries.call_count == 1
716
+ call_args = execute_cli_mocks.run_create_with_retries.call_args.kwargs
717
+ assert call_args["auth_session_input"] is input
718
+
719
+ @pytest.mark.asyncio
720
+ async def test_uses_auth_session_id_to_save_if_provided(self, execute_cli_mocks: ExecuteCLIMocks):
721
+ """Test that executeRunCreateAuthSessionCLI uses auth session id to save if provided."""
722
+ execute_cli_mocks.run_create_with_retries.side_effect = [
723
+ _create_success_result("session"),
724
+ _check_passed_result(),
725
+ ]
726
+
727
+ await execute_run_create_auth_session_cli(
728
+ id="customId",
729
+ input_data={},
730
+ check_retries=1,
731
+ create_retries=1,
732
+ headless=False,
733
+ timeout=30,
734
+ )
735
+
736
+ # Verify that store was called with the provided id
737
+ assert execute_cli_mocks.run_create_with_retries.call_count == 1
738
+ call_args = execute_cli_mocks.run_create_with_retries.call_args.kwargs
739
+ assert call_args["auth_session_id"] == "customId"
740
+
741
+
742
+ class TestExecuteAuthSessionUpdateCLI:
743
+ @pytest.mark.asyncio
744
+ async def test_throws_if_auth_session_is_manual(self, execute_cli_mocks: ExecuteCLIMocks):
745
+ """Test that executeRunUpdateAuthSessionCLI throws if auth session is manual."""
746
+ mock_metadata = Mock()
747
+ mock_metadata.auth_session_type = "MANUAL"
748
+ execute_cli_mocks.load_auth_session_instance.return_value = ({}, mock_metadata)
749
+
750
+ with pytest.raises(CLIError):
751
+ await execute_run_update_auth_session_cli(
752
+ id="testId",
753
+ check_retries=1,
754
+ create_retries=1,
755
+ headless=False,
756
+ timeout=30,
757
+ )
758
+
759
+ @pytest.mark.asyncio
760
+ async def test_calls_create_with_existing_input_if_no_input_provided(self, execute_cli_mocks: ExecuteCLIMocks):
761
+ """Test that executeRunUpdateAuthSessionCLI calls create with existing input if no input provided."""
762
+ mock_metadata = Mock()
763
+ mock_metadata.auth_session_input = {"existing": "data"}
764
+ execute_cli_mocks.load_auth_session_instance.return_value = ({}, mock_metadata)
765
+
766
+ with patch("intuned_cli.controller.authsession.execute_run_create_auth_session_cli") as mock_create_cli:
767
+ await execute_run_update_auth_session_cli(
768
+ id="testId",
769
+ check_retries=1,
770
+ create_retries=1,
771
+ headless=False,
772
+ timeout=30,
773
+ )
774
+
775
+ # Should use the provided input data
776
+ mock_create_cli.assert_called_once_with(
777
+ id="testId",
778
+ input_data={"existing": "data"},
779
+ check_retries=1,
780
+ create_retries=1,
781
+ headless=False,
782
+ timeout=30,
783
+ proxy=None,
784
+ metadata=mock_metadata,
785
+ log=False,
786
+ )
787
+
788
+ @pytest.mark.asyncio
789
+ async def test_calls_create_with_new_input_if_provided(self, execute_cli_mocks: ExecuteCLIMocks):
790
+ """Test that executeRunUpdateAuthSessionCLI calls create with new input if provided."""
791
+ execute_cli_mocks.load_auth_session_instance.return_value = ({}, {})
792
+
793
+ with patch("intuned_cli.controller.authsession.execute_run_create_auth_session_cli") as mock_create_cli:
794
+ await execute_run_update_auth_session_cli(
795
+ id="testId",
796
+ input_data={"new": "data"},
797
+ check_retries=1,
798
+ create_retries=1,
799
+ headless=False,
800
+ timeout=30,
801
+ )
802
+
803
+ # Should use the provided input data
804
+ call_args = mock_create_cli.call_args.kwargs
805
+ assert call_args["input_data"] == {"new": "data"}
806
+
807
+
808
+ class TestExecuteAttemptAuthSessionCLI:
809
+ @pytest.mark.asyncio
810
+ async def test_asserts_check_api_file_exists(self, execute_cli_mocks: ExecuteCLIMocks, shared_mocks: SharedMocks):
811
+ """Test that executeAttemptCheckAuthSessionCLI asserts check API file exists."""
812
+
813
+ await execute_attempt_check_auth_session_cli(
814
+ id="testId",
815
+ headless=False,
816
+ timeout=30,
817
+ )
818
+
819
+ shared_mocks.assert_api_file_exists.assert_called_once_with("auth-sessions", "check")
820
+
821
+ @pytest.mark.asyncio
822
+ async def test_succeeds_if_check_succeeds(self, execute_cli_mocks: ExecuteCLIMocks):
823
+ """Test that executeAttemptCheckAuthSessionCLI succeeds if check succeeds."""
824
+ execute_cli_mocks.run_check_with_retries.return_value = True
825
+
826
+ await execute_attempt_check_auth_session_cli(
827
+ id="testId",
828
+ headless=False,
829
+ timeout=30,
830
+ )
831
+
832
+ @pytest.mark.asyncio
833
+ async def test_throws_if_check_fails(self, execute_cli_mocks: ExecuteCLIMocks):
834
+ """Test that executeAttemptCheckAuthSessionCLI throws if check fails."""
835
+ execute_cli_mocks.run_check_with_retries.return_value = False
836
+
837
+ with pytest.raises(CLIError):
838
+ await execute_attempt_check_auth_session_cli(
839
+ id="testId",
840
+ headless=False,
841
+ timeout=30,
842
+ )
843
+
844
+
845
+ class TestExecuteAttemptCreateAuthSessionCLI:
846
+ @pytest.mark.asyncio
847
+ async def test_asserts_create_api_file_exists(self, execute_cli_mocks: ExecuteCLIMocks, shared_mocks: SharedMocks):
848
+ """Test that executeAttemptCreateAuthSessionCLI asserts create API file exists."""
849
+
850
+ await execute_attempt_create_auth_session_cli(
851
+ input_data={},
852
+ headless=False,
853
+ timeout=30,
854
+ )
855
+
856
+ shared_mocks.assert_api_file_exists.assert_called_once_with("auth-sessions", "create")
857
+
858
+ @pytest.mark.asyncio
859
+ async def test_throws_if_create_fails(
860
+ self,
861
+ execute_cli_mocks: ExecuteCLIMocks,
862
+ ):
863
+ """Test that executeAttemptCreateAuthSessionCLI throws if create fails."""
864
+ execute_cli_mocks.run_create_with_retries.side_effect = CLIError("create failed")
865
+
866
+ with pytest.raises(CLIError):
867
+ await execute_attempt_create_auth_session_cli(
868
+ input_data={},
869
+ headless=False,
870
+ timeout=30,
871
+ )
872
+
873
+ @pytest.mark.asyncio
874
+ async def test_saves_to_auth_session_instance_path_if_create_succeeds(self, execute_cli_mocks: ExecuteCLIMocks):
875
+ """Test that executeAttemptCreateAuthSessionCLI saves to auth session instance path if create succeeds."""
876
+ execute_cli_mocks.run_create_with_retries.return_value = _get_empty_auth_session()
877
+
878
+ await execute_attempt_create_auth_session_cli(
879
+ input_data={},
880
+ headless=False,
881
+ timeout=30,
882
+ )
883
+
884
+ execute_cli_mocks.run_create_with_retries.assert_called_once()
885
+ call_args = execute_cli_mocks.run_create_with_retries.call_args.kwargs
886
+ assert call_args["auth_session_input"] == {}
887
+ assert call_args["retries"] == 1
888
+ assert call_args["headless"] is False
889
+ assert call_args["timeout"] == 30
890
+ assert call_args["proxy"] is None
891
+
892
+ @pytest.mark.asyncio
893
+ async def test_uses_auth_session_id_to_save_if_provided(self, execute_cli_mocks: ExecuteCLIMocks):
894
+ """Test that executeAttemptCreateAuthSessionCLI uses auth session id to save if provided."""
895
+ execute_cli_mocks.run_create_with_retries.return_value = _get_empty_auth_session()
896
+
897
+ await execute_attempt_create_auth_session_cli(
898
+ id="customId",
899
+ input_data={},
900
+ headless=False,
901
+ timeout=30,
902
+ )
903
+
904
+ # Verify that store was called with the provided id
905
+ execute_cli_mocks.run_create_with_retries.assert_called_once()
906
+ call_args = execute_cli_mocks.run_create_with_retries.call_args.kwargs
907
+ assert call_args["auth_session_id"] == "customId"