pygeai 0.6.0b6__py3-none-any.whl → 0.6.0b10__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.
- pygeai/_docs/source/conf.py +78 -6
- pygeai/_docs/source/content/api_reference/admin.rst +161 -0
- pygeai/_docs/source/content/api_reference/assistant.rst +326 -0
- pygeai/_docs/source/content/api_reference/auth.rst +379 -0
- pygeai/_docs/source/content/api_reference/embeddings.rst +31 -1
- pygeai/_docs/source/content/api_reference/evaluation.rst +590 -0
- pygeai/_docs/source/content/api_reference/feedback.rst +237 -0
- pygeai/_docs/source/content/api_reference/files.rst +592 -0
- pygeai/_docs/source/content/api_reference/gam.rst +401 -0
- pygeai/_docs/source/content/api_reference/health.rst +58 -0
- pygeai/_docs/source/content/api_reference/project.rst +20 -18
- pygeai/_docs/source/content/api_reference/proxy.rst +318 -0
- pygeai/_docs/source/content/api_reference/rerank.rst +94 -0
- pygeai/_docs/source/content/api_reference/secrets.rst +495 -0
- pygeai/_docs/source/content/api_reference/usage_limits.rst +390 -0
- pygeai/_docs/source/content/api_reference.rst +13 -1
- pygeai/_docs/source/content/debugger.rst +376 -83
- pygeai/_docs/source/content/migration.rst +528 -0
- pygeai/_docs/source/content/modules.rst +1 -1
- pygeai/_docs/source/index.rst +59 -7
- pygeai/_docs/source/pygeai.auth.rst +29 -0
- pygeai/_docs/source/pygeai.cli.commands.rst +16 -0
- pygeai/_docs/source/pygeai.cli.rst +8 -0
- pygeai/_docs/source/pygeai.core.utils.rst +16 -0
- pygeai/_docs/source/pygeai.rst +1 -0
- pygeai/_docs/source/pygeai.tests.auth.rst +21 -0
- pygeai/_docs/source/pygeai.tests.cli.commands.rst +16 -0
- pygeai/_docs/source/pygeai.tests.cli.rst +16 -0
- pygeai/_docs/source/pygeai.tests.core.base.rst +8 -0
- pygeai/_docs/source/pygeai.tests.core.embeddings.rst +16 -0
- pygeai/_docs/source/pygeai.tests.core.files.rst +8 -0
- pygeai/_docs/source/pygeai.tests.core.plugins.rst +21 -0
- pygeai/_docs/source/pygeai.tests.core.rst +1 -0
- pygeai/_docs/source/pygeai.tests.evaluation.dataset.rst +21 -0
- pygeai/_docs/source/pygeai.tests.evaluation.plan.rst +21 -0
- pygeai/_docs/source/pygeai.tests.evaluation.result.rst +21 -0
- pygeai/_docs/source/pygeai.tests.evaluation.rst +20 -0
- pygeai/_docs/source/pygeai.tests.integration.lab.processes.rst +8 -0
- pygeai/_docs/source/pygeai.tests.organization.rst +8 -0
- pygeai/_docs/source/pygeai.tests.rst +2 -0
- pygeai/_docs/source/pygeai.tests.snippets.auth.rst +10 -0
- pygeai/_docs/source/pygeai.tests.snippets.chat.rst +40 -0
- pygeai/_docs/source/pygeai.tests.snippets.dbg.rst +45 -0
- pygeai/_docs/source/pygeai.tests.snippets.embeddings.rst +40 -0
- pygeai/_docs/source/pygeai.tests.snippets.evaluation.dataset.rst +197 -0
- pygeai/_docs/source/pygeai.tests.snippets.evaluation.plan.rst +133 -0
- pygeai/_docs/source/pygeai.tests.snippets.evaluation.result.rst +37 -0
- pygeai/_docs/source/pygeai.tests.snippets.evaluation.rst +10 -0
- pygeai/_docs/source/pygeai.tests.snippets.organization.rst +40 -0
- pygeai/_docs/source/pygeai.tests.snippets.rst +2 -0
- pygeai/admin/clients.py +12 -32
- pygeai/assistant/clients.py +16 -44
- pygeai/assistant/data/clients.py +1 -0
- pygeai/assistant/data_analyst/clients.py +6 -13
- pygeai/assistant/rag/clients.py +24 -67
- pygeai/auth/clients.py +88 -14
- pygeai/auth/endpoints.py +4 -0
- pygeai/chat/clients.py +192 -25
- pygeai/chat/endpoints.py +2 -1
- pygeai/cli/commands/auth.py +178 -2
- pygeai/cli/commands/chat.py +227 -1
- pygeai/cli/commands/embeddings.py +56 -8
- pygeai/cli/commands/lab/ai_lab.py +0 -2
- pygeai/cli/commands/migrate.py +994 -434
- pygeai/cli/commands/organization.py +241 -0
- pygeai/cli/error_handler.py +116 -0
- pygeai/cli/geai.py +28 -10
- pygeai/cli/parsers.py +8 -2
- pygeai/core/base/clients.py +4 -1
- pygeai/core/common/exceptions.py +11 -10
- pygeai/core/embeddings/__init__.py +19 -0
- pygeai/core/embeddings/clients.py +20 -9
- pygeai/core/embeddings/mappers.py +16 -2
- pygeai/core/embeddings/responses.py +9 -2
- pygeai/core/feedback/clients.py +4 -8
- pygeai/core/files/clients.py +10 -25
- pygeai/core/files/managers.py +42 -0
- pygeai/core/llm/clients.py +11 -26
- pygeai/core/models.py +107 -0
- pygeai/core/plugins/clients.py +4 -7
- pygeai/core/rerank/clients.py +4 -8
- pygeai/core/secrets/clients.py +14 -37
- pygeai/core/services/rest.py +1 -1
- pygeai/core/utils/parsers.py +32 -0
- pygeai/core/utils/validators.py +10 -0
- pygeai/dbg/__init__.py +3 -0
- pygeai/dbg/debugger.py +565 -70
- pygeai/evaluation/clients.py +2 -1
- pygeai/evaluation/dataset/clients.py +46 -44
- pygeai/evaluation/plan/clients.py +28 -26
- pygeai/evaluation/result/clients.py +38 -5
- pygeai/gam/clients.py +10 -25
- pygeai/health/clients.py +4 -7
- pygeai/lab/agents/clients.py +21 -54
- pygeai/lab/agents/endpoints.py +2 -0
- pygeai/lab/clients.py +1 -0
- pygeai/lab/models.py +3 -3
- pygeai/lab/processes/clients.py +45 -127
- pygeai/lab/strategies/clients.py +11 -25
- pygeai/lab/tools/clients.py +23 -67
- pygeai/lab/tools/endpoints.py +3 -0
- pygeai/migration/__init__.py +31 -0
- pygeai/migration/strategies.py +404 -155
- pygeai/migration/tools.py +170 -3
- pygeai/organization/clients.py +135 -51
- pygeai/organization/endpoints.py +6 -1
- pygeai/organization/limits/clients.py +32 -91
- pygeai/organization/managers.py +157 -1
- pygeai/organization/mappers.py +76 -2
- pygeai/organization/responses.py +25 -1
- pygeai/proxy/clients.py +4 -1
- pygeai/tests/admin/test_clients.py +16 -11
- pygeai/tests/assistants/rag/test_clients.py +35 -23
- pygeai/tests/assistants/test_clients.py +22 -15
- pygeai/tests/auth/test_clients.py +191 -7
- pygeai/tests/chat/test_clients.py +211 -1
- pygeai/tests/cli/commands/test_embeddings.py +32 -9
- pygeai/tests/cli/commands/test_evaluation.py +7 -0
- pygeai/tests/cli/commands/test_migrate.py +112 -243
- pygeai/tests/cli/test_error_handler.py +225 -0
- pygeai/tests/cli/test_geai_driver.py +154 -0
- pygeai/tests/cli/test_parsers.py +5 -5
- pygeai/tests/core/embeddings/test_clients.py +144 -0
- pygeai/tests/core/embeddings/test_managers.py +171 -0
- pygeai/tests/core/embeddings/test_mappers.py +142 -0
- pygeai/tests/core/feedback/test_clients.py +2 -0
- pygeai/tests/core/files/test_clients.py +1 -0
- pygeai/tests/core/llm/test_clients.py +14 -9
- pygeai/tests/core/plugins/test_clients.py +5 -3
- pygeai/tests/core/rerank/test_clients.py +1 -0
- pygeai/tests/core/secrets/test_clients.py +19 -13
- pygeai/tests/dbg/test_debugger.py +453 -75
- pygeai/tests/evaluation/dataset/test_clients.py +3 -1
- pygeai/tests/evaluation/plan/test_clients.py +4 -2
- pygeai/tests/evaluation/result/test_clients.py +7 -5
- pygeai/tests/gam/test_clients.py +1 -1
- pygeai/tests/health/test_clients.py +1 -0
- pygeai/tests/lab/agents/test_clients.py +9 -0
- pygeai/tests/lab/processes/test_clients.py +36 -0
- pygeai/tests/lab/processes/test_mappers.py +3 -0
- pygeai/tests/lab/strategies/test_clients.py +14 -9
- pygeai/tests/migration/test_strategies.py +45 -218
- pygeai/tests/migration/test_tools.py +133 -9
- pygeai/tests/organization/limits/test_clients.py +17 -0
- pygeai/tests/organization/test_clients.py +206 -1
- pygeai/tests/organization/test_managers.py +122 -1
- pygeai/tests/proxy/test_clients.py +2 -0
- pygeai/tests/proxy/test_integration.py +1 -0
- pygeai/tests/snippets/auth/__init__.py +0 -0
- pygeai/tests/snippets/chat/chat_completion_with_reasoning_effort.py +18 -0
- pygeai/tests/snippets/chat/get_response.py +15 -0
- pygeai/tests/snippets/chat/get_response_streaming.py +20 -0
- pygeai/tests/snippets/chat/get_response_with_files.py +16 -0
- pygeai/tests/snippets/chat/get_response_with_tools.py +36 -0
- pygeai/tests/snippets/dbg/__init__.py +0 -0
- pygeai/tests/snippets/dbg/basic_debugging.py +32 -0
- pygeai/tests/snippets/dbg/breakpoint_management.py +48 -0
- pygeai/tests/snippets/dbg/stack_navigation.py +45 -0
- pygeai/tests/snippets/dbg/stepping_example.py +40 -0
- pygeai/tests/snippets/embeddings/cache_example.py +31 -0
- pygeai/tests/snippets/embeddings/cohere_example.py +41 -0
- pygeai/tests/snippets/embeddings/openai_base64_example.py +27 -0
- pygeai/tests/snippets/embeddings/openai_example.py +30 -0
- pygeai/tests/snippets/embeddings/similarity_example.py +42 -0
- pygeai/tests/snippets/evaluation/dataset/__init__.py +0 -0
- pygeai/tests/snippets/evaluation/dataset/complete_workflow_example.py +195 -0
- pygeai/tests/snippets/evaluation/dataset/create_dataset.py +26 -0
- pygeai/tests/snippets/evaluation/dataset/create_dataset_from_file.py +11 -0
- pygeai/tests/snippets/evaluation/dataset/create_dataset_row.py +17 -0
- pygeai/tests/snippets/evaluation/dataset/create_expected_source.py +18 -0
- pygeai/tests/snippets/evaluation/dataset/create_filter_variable.py +19 -0
- pygeai/tests/snippets/evaluation/dataset/delete_dataset.py +9 -0
- pygeai/tests/snippets/evaluation/dataset/delete_dataset_row.py +10 -0
- pygeai/tests/snippets/evaluation/dataset/delete_expected_source.py +15 -0
- pygeai/tests/snippets/evaluation/dataset/delete_filter_variable.py +15 -0
- pygeai/tests/snippets/evaluation/dataset/get_dataset.py +9 -0
- pygeai/tests/snippets/evaluation/dataset/get_dataset_row.py +10 -0
- pygeai/tests/snippets/evaluation/dataset/get_expected_source.py +15 -0
- pygeai/tests/snippets/evaluation/dataset/get_filter_variable.py +15 -0
- pygeai/tests/snippets/evaluation/dataset/list_dataset_rows.py +9 -0
- pygeai/tests/snippets/evaluation/dataset/list_datasets.py +6 -0
- pygeai/tests/snippets/evaluation/dataset/list_expected_sources.py +10 -0
- pygeai/tests/snippets/evaluation/dataset/list_filter_variables.py +10 -0
- pygeai/tests/snippets/evaluation/dataset/update_dataset.py +15 -0
- pygeai/tests/snippets/evaluation/dataset/update_dataset_row.py +20 -0
- pygeai/tests/snippets/evaluation/dataset/update_expected_source.py +18 -0
- pygeai/tests/snippets/evaluation/dataset/update_filter_variable.py +19 -0
- pygeai/tests/snippets/evaluation/dataset/upload_dataset_rows_file.py +10 -0
- pygeai/tests/snippets/evaluation/plan/__init__.py +0 -0
- pygeai/tests/snippets/evaluation/plan/add_plan_system_metric.py +13 -0
- pygeai/tests/snippets/evaluation/plan/complete_workflow_example.py +136 -0
- pygeai/tests/snippets/evaluation/plan/create_evaluation_plan.py +24 -0
- pygeai/tests/snippets/evaluation/plan/create_rag_evaluation_plan.py +22 -0
- pygeai/tests/snippets/evaluation/plan/delete_evaluation_plan.py +9 -0
- pygeai/tests/snippets/evaluation/plan/delete_plan_system_metric.py +13 -0
- pygeai/tests/snippets/evaluation/plan/execute_evaluation_plan.py +11 -0
- pygeai/tests/snippets/evaluation/plan/get_evaluation_plan.py +9 -0
- pygeai/tests/snippets/evaluation/plan/get_plan_system_metric.py +13 -0
- pygeai/tests/snippets/evaluation/plan/get_system_metric.py +9 -0
- pygeai/tests/snippets/evaluation/plan/list_evaluation_plans.py +7 -0
- pygeai/tests/snippets/evaluation/plan/list_plan_system_metrics.py +9 -0
- pygeai/tests/snippets/evaluation/plan/list_system_metrics.py +7 -0
- pygeai/tests/snippets/evaluation/plan/update_evaluation_plan.py +22 -0
- pygeai/tests/snippets/evaluation/plan/update_plan_system_metric.py +14 -0
- pygeai/tests/snippets/evaluation/result/__init__.py +0 -0
- pygeai/tests/snippets/evaluation/result/complete_workflow_example.py +150 -0
- pygeai/tests/snippets/evaluation/result/get_evaluation_result.py +26 -0
- pygeai/tests/snippets/evaluation/result/list_evaluation_results.py +17 -0
- pygeai/tests/snippets/migrate/__init__.py +45 -0
- pygeai/tests/snippets/migrate/agent_migration.py +110 -0
- pygeai/tests/snippets/migrate/assistant_migration.py +64 -0
- pygeai/tests/snippets/migrate/orchestrator_examples.py +179 -0
- pygeai/tests/snippets/migrate/process_migration.py +64 -0
- pygeai/tests/snippets/migrate/project_migration.py +42 -0
- pygeai/tests/snippets/migrate/tool_migration.py +64 -0
- pygeai/tests/snippets/organization/create_project.py +2 -2
- pygeai/tests/snippets/organization/get_memberships.py +12 -0
- pygeai/tests/snippets/organization/get_organization_members.py +6 -0
- pygeai/tests/snippets/organization/get_project_members.py +6 -0
- pygeai/tests/snippets/organization/get_project_memberships.py +12 -0
- pygeai/tests/snippets/organization/get_project_roles.py +6 -0
- {pygeai-0.6.0b6.dist-info → pygeai-0.6.0b10.dist-info}/METADATA +1 -1
- {pygeai-0.6.0b6.dist-info → pygeai-0.6.0b10.dist-info}/RECORD +227 -124
- {pygeai-0.6.0b6.dist-info → pygeai-0.6.0b10.dist-info}/WHEEL +0 -0
- {pygeai-0.6.0b6.dist-info → pygeai-0.6.0b10.dist-info}/entry_points.txt +0 -0
- {pygeai-0.6.0b6.dist-info → pygeai-0.6.0b10.dist-info}/licenses/LICENSE +0 -0
- {pygeai-0.6.0b6.dist-info → pygeai-0.6.0b10.dist-info}/top_level.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import unittest
|
|
2
2
|
from unittest.mock import Mock
|
|
3
3
|
|
|
4
|
-
from pygeai.migration.tools import MigrationTool
|
|
4
|
+
from pygeai.migration.tools import MigrationTool, MigrationPlan, MigrationOrchestrator
|
|
5
5
|
from pygeai.migration.strategies import MigrationStrategy
|
|
6
6
|
|
|
7
7
|
|
|
@@ -11,25 +11,149 @@ class TestMigrationTool(unittest.TestCase):
|
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
13
|
def setUp(self):
|
|
14
|
-
"""Set up test fixtures"""
|
|
15
14
|
self.mock_strategy = Mock(spec=MigrationStrategy)
|
|
16
15
|
|
|
17
16
|
def test_migration_tool_initialization(self):
|
|
18
|
-
"""Test MigrationTool initialization with a strategy"""
|
|
19
17
|
tool = MigrationTool(self.mock_strategy)
|
|
20
|
-
self.assertEqual(tool.
|
|
18
|
+
self.assertEqual(tool._strategy, self.mock_strategy)
|
|
21
19
|
|
|
22
20
|
def test_migration_tool_set_strategy(self):
|
|
23
|
-
"""Test setting a new strategy on MigrationTool"""
|
|
24
21
|
tool = MigrationTool(self.mock_strategy)
|
|
25
22
|
new_strategy = Mock(spec=MigrationStrategy)
|
|
26
23
|
tool.set_strategy(new_strategy)
|
|
27
|
-
|
|
28
|
-
self.assertEqual(tool.strategy, new_strategy)
|
|
24
|
+
self.assertEqual(tool._strategy, new_strategy)
|
|
29
25
|
|
|
30
26
|
def test_migration_tool_run_migration(self):
|
|
31
|
-
"""Test running migration with the strategy"""
|
|
32
27
|
tool = MigrationTool(self.mock_strategy)
|
|
33
28
|
tool.run_migration()
|
|
29
|
+
self.mock_strategy.migrate.assert_called_once()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class TestMigrationPlan(unittest.TestCase):
|
|
33
|
+
"""
|
|
34
|
+
python -m unittest pygeai.tests.migration.test_tools.TestMigrationPlan
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def test_migration_plan_initialization(self):
|
|
38
|
+
strategies = [Mock(spec=MigrationStrategy), Mock(spec=MigrationStrategy)]
|
|
39
|
+
plan = MigrationPlan(strategies=strategies)
|
|
40
|
+
self.assertEqual(len(plan.strategies), 2)
|
|
41
|
+
self.assertEqual(plan.dependencies, {})
|
|
42
|
+
self.assertFalse(plan.dry_run)
|
|
43
|
+
self.assertTrue(plan.stop_on_error)
|
|
44
|
+
|
|
45
|
+
def test_migration_plan_with_dependencies(self):
|
|
46
|
+
strategies = [Mock(spec=MigrationStrategy), Mock(spec=MigrationStrategy)]
|
|
47
|
+
dependencies = {1: [0]}
|
|
48
|
+
plan = MigrationPlan(strategies=strategies, dependencies=dependencies)
|
|
49
|
+
self.assertEqual(plan.dependencies, {1: [0]})
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class TestMigrationOrchestrator(unittest.TestCase):
|
|
53
|
+
"""
|
|
54
|
+
python -m unittest pygeai.tests.migration.test_tools.TestMigrationOrchestrator
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
def setUp(self):
|
|
58
|
+
self.strategy1 = Mock(spec=MigrationStrategy)
|
|
59
|
+
self.strategy2 = Mock(spec=MigrationStrategy)
|
|
60
|
+
self.strategy3 = Mock(spec=MigrationStrategy)
|
|
61
|
+
|
|
62
|
+
def test_orchestrator_simple_execution(self):
|
|
63
|
+
plan = MigrationPlan(strategies=[self.strategy1, self.strategy2])
|
|
64
|
+
orchestrator = MigrationOrchestrator(plan)
|
|
65
|
+
|
|
66
|
+
result = orchestrator.execute()
|
|
67
|
+
|
|
68
|
+
self.strategy1.migrate.assert_called_once()
|
|
69
|
+
self.strategy2.migrate.assert_called_once()
|
|
70
|
+
self.assertEqual(result['total'], 2)
|
|
71
|
+
self.assertEqual(result['completed'], 2)
|
|
72
|
+
self.assertEqual(result['failed'], 0)
|
|
73
|
+
self.assertEqual(result['success_rate'], 1.0)
|
|
74
|
+
|
|
75
|
+
def test_orchestrator_with_dependencies(self):
|
|
76
|
+
plan = MigrationPlan(
|
|
77
|
+
strategies=[self.strategy1, self.strategy2, self.strategy3],
|
|
78
|
+
dependencies={1: [0], 2: [1]}
|
|
79
|
+
)
|
|
80
|
+
orchestrator = MigrationOrchestrator(plan)
|
|
81
|
+
|
|
82
|
+
result = orchestrator.execute()
|
|
83
|
+
|
|
84
|
+
self.assertEqual(result['completed'], 3)
|
|
85
|
+
self.assertEqual(result['completed_indices'], [0, 1, 2])
|
|
86
|
+
|
|
87
|
+
def test_orchestrator_stop_on_error(self):
|
|
88
|
+
self.strategy1.migrate.side_effect = Exception("Migration failed")
|
|
89
|
+
plan = MigrationPlan(
|
|
90
|
+
strategies=[self.strategy1, self.strategy2],
|
|
91
|
+
stop_on_error=True
|
|
92
|
+
)
|
|
93
|
+
orchestrator = MigrationOrchestrator(plan)
|
|
94
|
+
|
|
95
|
+
with self.assertRaises(ValueError) as context:
|
|
96
|
+
orchestrator.execute()
|
|
97
|
+
|
|
98
|
+
self.assertIn("Migration failed at strategy", str(context.exception))
|
|
99
|
+
self.strategy1.migrate.assert_called_once()
|
|
100
|
+
self.strategy2.migrate.assert_not_called()
|
|
101
|
+
|
|
102
|
+
def test_orchestrator_continue_on_error(self):
|
|
103
|
+
self.strategy1.migrate.side_effect = Exception("Migration failed")
|
|
104
|
+
plan = MigrationPlan(
|
|
105
|
+
strategies=[self.strategy1, self.strategy2],
|
|
106
|
+
stop_on_error=False
|
|
107
|
+
)
|
|
108
|
+
orchestrator = MigrationOrchestrator(plan)
|
|
109
|
+
|
|
110
|
+
result = orchestrator.execute()
|
|
111
|
+
|
|
112
|
+
self.strategy1.migrate.assert_called_once()
|
|
113
|
+
self.strategy2.migrate.assert_called_once()
|
|
114
|
+
self.assertEqual(result['completed'], 1)
|
|
115
|
+
self.assertEqual(result['failed'], 1)
|
|
116
|
+
self.assertEqual(result['success_rate'], 0.5)
|
|
117
|
+
|
|
118
|
+
def test_orchestrator_dry_run(self):
|
|
119
|
+
plan = MigrationPlan(
|
|
120
|
+
strategies=[self.strategy1, self.strategy2],
|
|
121
|
+
dry_run=True
|
|
122
|
+
)
|
|
123
|
+
orchestrator = MigrationOrchestrator(plan)
|
|
124
|
+
|
|
125
|
+
result = orchestrator.execute()
|
|
126
|
+
|
|
127
|
+
self.strategy1.migrate.assert_not_called()
|
|
128
|
+
self.strategy2.migrate.assert_not_called()
|
|
129
|
+
self.assertTrue(result['valid'])
|
|
130
|
+
self.assertEqual(result['execution_order'], [0, 1])
|
|
131
|
+
|
|
132
|
+
def test_orchestrator_circular_dependency(self):
|
|
133
|
+
plan = MigrationPlan(
|
|
134
|
+
strategies=[self.strategy1, self.strategy2],
|
|
135
|
+
dependencies={0: [1], 1: [0]}
|
|
136
|
+
)
|
|
137
|
+
orchestrator = MigrationOrchestrator(plan)
|
|
138
|
+
|
|
139
|
+
with self.assertRaises(ValueError) as context:
|
|
140
|
+
orchestrator.execute()
|
|
141
|
+
|
|
142
|
+
self.assertIn("Circular dependency", str(context.exception))
|
|
143
|
+
|
|
144
|
+
def test_orchestrator_dry_run_circular_dependency(self):
|
|
145
|
+
plan = MigrationPlan(
|
|
146
|
+
strategies=[self.strategy1, self.strategy2],
|
|
147
|
+
dependencies={0: [1], 1: [0]},
|
|
148
|
+
dry_run=True
|
|
149
|
+
)
|
|
150
|
+
orchestrator = MigrationOrchestrator(plan)
|
|
151
|
+
|
|
152
|
+
result = orchestrator.execute()
|
|
153
|
+
|
|
154
|
+
self.assertFalse(result['valid'])
|
|
155
|
+
self.assertIn("Circular dependency", result['error'])
|
|
156
|
+
|
|
34
157
|
|
|
35
|
-
|
|
158
|
+
if __name__ == '__main__':
|
|
159
|
+
unittest.main()
|
|
@@ -47,6 +47,7 @@ class TestUsageLimitClient(unittest.TestCase):
|
|
|
47
47
|
def test_set_organization_usage_limit_success(self, mock_post):
|
|
48
48
|
mock_response = mock_post.return_value
|
|
49
49
|
mock_response.json.return_value = self.valid_response
|
|
50
|
+
mock_response.status_code = 200
|
|
50
51
|
|
|
51
52
|
result = self.client.set_organization_usage_limit(self.organization, self.valid_usage_limit)
|
|
52
53
|
|
|
@@ -79,6 +80,7 @@ class TestUsageLimitClient(unittest.TestCase):
|
|
|
79
80
|
invalid_usage_limit["renewalStatus"] = "InvalidStatus"
|
|
80
81
|
mock_response = mock_post.return_value
|
|
81
82
|
mock_response.json.return_value = {"error": "Invalid renewal status"}
|
|
83
|
+
mock_response.status_code = 200
|
|
82
84
|
|
|
83
85
|
result = self.client.set_organization_usage_limit(self.organization, invalid_usage_limit)
|
|
84
86
|
|
|
@@ -94,6 +96,7 @@ class TestUsageLimitClient(unittest.TestCase):
|
|
|
94
96
|
def test_get_organization_latest_usage_limit_success(self, mock_get):
|
|
95
97
|
mock_response = mock_get.return_value
|
|
96
98
|
mock_response.json.return_value = self.valid_response
|
|
99
|
+
mock_response.status_code = 200
|
|
97
100
|
|
|
98
101
|
result = self.client.get_organization_latest_usage_limit(self.organization)
|
|
99
102
|
|
|
@@ -123,6 +126,7 @@ class TestUsageLimitClient(unittest.TestCase):
|
|
|
123
126
|
response = {"limits": [self.valid_response]}
|
|
124
127
|
mock_response = mock_get.return_value
|
|
125
128
|
mock_response.json.return_value = response
|
|
129
|
+
mock_response.status_code = 200
|
|
126
130
|
|
|
127
131
|
result = self.client.get_all_usage_limits_from_organization(self.organization)
|
|
128
132
|
|
|
@@ -153,6 +157,7 @@ class TestUsageLimitClient(unittest.TestCase):
|
|
|
153
157
|
response = {"status": "deleted"}
|
|
154
158
|
mock_response = mock_delete.return_value
|
|
155
159
|
mock_response.json.return_value = response
|
|
160
|
+
mock_response.status_code = 200
|
|
156
161
|
|
|
157
162
|
result = self.client.delete_usage_limit_from_organization(self.organization, self.limit_id)
|
|
158
163
|
|
|
@@ -183,6 +188,7 @@ class TestUsageLimitClient(unittest.TestCase):
|
|
|
183
188
|
response["hardLimit"] = hard_limit
|
|
184
189
|
mock_response = mock_put.return_value
|
|
185
190
|
mock_response.json.return_value = response
|
|
191
|
+
mock_response.status_code = 200
|
|
186
192
|
|
|
187
193
|
result = self.client.set_organization_hard_limit(self.organization, self.limit_id, hard_limit)
|
|
188
194
|
|
|
@@ -217,6 +223,7 @@ class TestUsageLimitClient(unittest.TestCase):
|
|
|
217
223
|
response["softLimit"] = soft_limit
|
|
218
224
|
mock_response = mock_put.return_value
|
|
219
225
|
mock_response.json.return_value = response
|
|
226
|
+
mock_response.status_code = 200
|
|
220
227
|
|
|
221
228
|
result = self.client.set_organization_soft_limit(self.organization, self.limit_id, soft_limit)
|
|
222
229
|
|
|
@@ -251,6 +258,7 @@ class TestUsageLimitClient(unittest.TestCase):
|
|
|
251
258
|
response["renewalStatus"] = renewal_status
|
|
252
259
|
mock_response = mock_put.return_value
|
|
253
260
|
mock_response.json.return_value = response
|
|
261
|
+
mock_response.status_code = 200
|
|
254
262
|
|
|
255
263
|
result = self.client.set_organization_renewal_status(self.organization, self.limit_id, renewal_status)
|
|
256
264
|
|
|
@@ -282,6 +290,7 @@ class TestUsageLimitClient(unittest.TestCase):
|
|
|
282
290
|
def test_set_project_usage_limit_success(self, mock_post):
|
|
283
291
|
mock_response = mock_post.return_value
|
|
284
292
|
mock_response.json.return_value = self.valid_response
|
|
293
|
+
mock_response.status_code = 200
|
|
285
294
|
|
|
286
295
|
result = self.client.set_project_usage_limit(self.organization, self.project, self.valid_usage_limit)
|
|
287
296
|
|
|
@@ -314,6 +323,7 @@ class TestUsageLimitClient(unittest.TestCase):
|
|
|
314
323
|
invalid_usage_limit["subscriptionType"] = "InvalidType"
|
|
315
324
|
mock_response = mock_post.return_value
|
|
316
325
|
mock_response.json.return_value = {"error": "Invalid subscription type"}
|
|
326
|
+
mock_response.status_code = 200
|
|
317
327
|
|
|
318
328
|
result = self.client.set_project_usage_limit(self.organization, self.project, invalid_usage_limit)
|
|
319
329
|
|
|
@@ -330,6 +340,7 @@ class TestUsageLimitClient(unittest.TestCase):
|
|
|
330
340
|
response = {"limits": [self.valid_response]}
|
|
331
341
|
mock_response = mock_get.return_value
|
|
332
342
|
mock_response.json.return_value = response
|
|
343
|
+
mock_response.status_code = 200
|
|
333
344
|
|
|
334
345
|
result = self.client.get_all_usage_limits_from_project(self.organization, self.project)
|
|
335
346
|
|
|
@@ -359,6 +370,7 @@ class TestUsageLimitClient(unittest.TestCase):
|
|
|
359
370
|
def test_get_latest_usage_limit_from_project_success(self, mock_get):
|
|
360
371
|
mock_response = mock_get.return_value
|
|
361
372
|
mock_response.json.return_value = self.valid_response
|
|
373
|
+
mock_response.status_code = 200
|
|
362
374
|
|
|
363
375
|
result = self.client.get_latest_usage_limit_from_project(self.organization, self.project)
|
|
364
376
|
|
|
@@ -387,6 +399,7 @@ class TestUsageLimitClient(unittest.TestCase):
|
|
|
387
399
|
def test_get_active_usage_limit_from_project_success(self, mock_get):
|
|
388
400
|
mock_response = mock_get.return_value
|
|
389
401
|
mock_response.json.return_value = self.valid_response
|
|
402
|
+
mock_response.status_code = 200
|
|
390
403
|
|
|
391
404
|
result = self.client.get_active_usage_limit_from_project(self.organization, self.project)
|
|
392
405
|
|
|
@@ -416,6 +429,7 @@ class TestUsageLimitClient(unittest.TestCase):
|
|
|
416
429
|
response = {"status": "deleted"}
|
|
417
430
|
mock_response = mock_delete.return_value
|
|
418
431
|
mock_response.json.return_value = response
|
|
432
|
+
mock_response.status_code = 200
|
|
419
433
|
|
|
420
434
|
result = self.client.delete_usage_limit_from_project(self.organization, self.project, self.limit_id)
|
|
421
435
|
|
|
@@ -446,6 +460,7 @@ class TestUsageLimitClient(unittest.TestCase):
|
|
|
446
460
|
response["hardLimit"] = hard_limit
|
|
447
461
|
mock_response = mock_put.return_value
|
|
448
462
|
mock_response.json.return_value = response
|
|
463
|
+
mock_response.status_code = 200
|
|
449
464
|
|
|
450
465
|
result = self.client.set_hard_limit_for_active_usage_limit_from_project(
|
|
451
466
|
self.organization, self.project, self.limit_id, hard_limit
|
|
@@ -484,6 +499,7 @@ class TestUsageLimitClient(unittest.TestCase):
|
|
|
484
499
|
response["softLimit"] = soft_limit
|
|
485
500
|
mock_response = mock_put.return_value
|
|
486
501
|
mock_response.json.return_value = response
|
|
502
|
+
mock_response.status_code = 200
|
|
487
503
|
|
|
488
504
|
result = self.client.set_soft_limit_for_active_usage_limit_from_project(
|
|
489
505
|
self.organization, self.project, self.limit_id, soft_limit
|
|
@@ -522,6 +538,7 @@ class TestUsageLimitClient(unittest.TestCase):
|
|
|
522
538
|
response["renewalStatus"] = renewal_status
|
|
523
539
|
mock_response = mock_put.return_value
|
|
524
540
|
mock_response.json.return_value = response
|
|
541
|
+
mock_response.status_code = 200
|
|
525
542
|
|
|
526
543
|
result = self.client.set_project_renewal_status(self.organization, self.project, self.limit_id, renewal_status)
|
|
527
544
|
|
|
@@ -5,7 +5,8 @@ from unittest.mock import patch
|
|
|
5
5
|
from pygeai.organization.clients import OrganizationClient
|
|
6
6
|
from pygeai.core.common.exceptions import InvalidAPIResponseException
|
|
7
7
|
from pygeai.organization.endpoints import GET_ASSISTANT_LIST_V1, GET_PROJECT_LIST_V1, GET_PROJECT_V1, CREATE_PROJECT_V1, \
|
|
8
|
-
UPDATE_PROJECT_V1, DELETE_PROJECT_V1, GET_PROJECT_TOKENS_V1, GET_REQUEST_DATA_V1
|
|
8
|
+
UPDATE_PROJECT_V1, DELETE_PROJECT_V1, GET_PROJECT_TOKENS_V1, GET_REQUEST_DATA_V1, GET_MEMBERSHIPS_V2, \
|
|
9
|
+
GET_PROJECT_MEMBERSHIPS_V2, GET_PROJECT_ROLES_V2, GET_PROJECT_MEMBERS_V2, GET_ORGANIZATION_MEMBERS_V2
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
class TestOrganizationClient(unittest.TestCase):
|
|
@@ -20,6 +21,7 @@ class TestOrganizationClient(unittest.TestCase):
|
|
|
20
21
|
def test_get_assistant_list_success(self, mock_get):
|
|
21
22
|
mock_response = mock_get.return_value
|
|
22
23
|
mock_response.json.return_value = {"assistants": [{"name": "assistant1"}, {"name": "assistant2"}]}
|
|
24
|
+
mock_response.status_code = 200
|
|
23
25
|
|
|
24
26
|
result = self.client.get_assistant_list(detail="summary")
|
|
25
27
|
|
|
@@ -46,6 +48,7 @@ class TestOrganizationClient(unittest.TestCase):
|
|
|
46
48
|
def test_get_project_list_success(self, mock_get):
|
|
47
49
|
mock_response = mock_get.return_value
|
|
48
50
|
mock_response.json.return_value = {"projects": [{"name": "project1"}, {"name": "project2"}]}
|
|
51
|
+
mock_response.status_code = 200
|
|
49
52
|
|
|
50
53
|
result = self.client.get_project_list(detail="summary")
|
|
51
54
|
|
|
@@ -59,6 +62,7 @@ class TestOrganizationClient(unittest.TestCase):
|
|
|
59
62
|
def test_get_project_list_with_name(self, mock_get):
|
|
60
63
|
mock_response = mock_get.return_value
|
|
61
64
|
mock_response.json.return_value = {"projects": [{"name": "specific_project"}]}
|
|
65
|
+
mock_response.status_code = 200
|
|
62
66
|
|
|
63
67
|
result = self.client.get_project_list(detail="full", name="specific_project")
|
|
64
68
|
|
|
@@ -87,6 +91,7 @@ class TestOrganizationClient(unittest.TestCase):
|
|
|
87
91
|
def test_get_project_data_success(self, mock_get):
|
|
88
92
|
mock_response = mock_get.return_value
|
|
89
93
|
mock_response.json.return_value = {"project": {"id": "123", "name": "project1"}}
|
|
94
|
+
mock_response.status_code = 200
|
|
90
95
|
|
|
91
96
|
result = self.client.get_project_data(project_id="123")
|
|
92
97
|
|
|
@@ -111,6 +116,7 @@ class TestOrganizationClient(unittest.TestCase):
|
|
|
111
116
|
def test_create_project_success(self, mock_post):
|
|
112
117
|
mock_response = mock_post.return_value
|
|
113
118
|
mock_response.json.return_value = {"project": {"id": "123", "name": "project1"}}
|
|
119
|
+
mock_response.status_code = 200
|
|
114
120
|
|
|
115
121
|
result = self.client.create_project(name="project1", email="admin@example.com", description="A test project")
|
|
116
122
|
|
|
@@ -129,6 +135,7 @@ class TestOrganizationClient(unittest.TestCase):
|
|
|
129
135
|
def test_create_project_with_usage_limit(self, mock_post):
|
|
130
136
|
mock_response = mock_post.return_value
|
|
131
137
|
mock_response.json.return_value = {"project": {"id": "123", "name": "project1"}}
|
|
138
|
+
mock_response.status_code = 200
|
|
132
139
|
|
|
133
140
|
usage_limit = {"type": "Requests", "threshold": 1000}
|
|
134
141
|
result = self.client.create_project(
|
|
@@ -171,6 +178,7 @@ class TestOrganizationClient(unittest.TestCase):
|
|
|
171
178
|
def test_update_project_success(self, mock_put):
|
|
172
179
|
mock_response = mock_put.return_value
|
|
173
180
|
mock_response.json.return_value = {"project": {"id": "123", "name": "updated_project"}}
|
|
181
|
+
mock_response.status_code = 200
|
|
174
182
|
|
|
175
183
|
result = self.client.update_project(project_id="123", name="updated_project", description="Updated description")
|
|
176
184
|
|
|
@@ -201,6 +209,7 @@ class TestOrganizationClient(unittest.TestCase):
|
|
|
201
209
|
def test_delete_project_success(self, mock_delete):
|
|
202
210
|
mock_response = mock_delete.return_value
|
|
203
211
|
mock_response.json.return_value = {"status": "deleted"}
|
|
212
|
+
mock_response.status_code = 200
|
|
204
213
|
|
|
205
214
|
result = self.client.delete_project(project_id="123")
|
|
206
215
|
|
|
@@ -225,6 +234,7 @@ class TestOrganizationClient(unittest.TestCase):
|
|
|
225
234
|
def test_get_project_tokens_success(self, mock_get):
|
|
226
235
|
mock_response = mock_get.return_value
|
|
227
236
|
mock_response.json.return_value = {"tokens": ["token1", "token2"]}
|
|
237
|
+
mock_response.status_code = 200
|
|
228
238
|
|
|
229
239
|
result = self.client.get_project_tokens(project_id="123")
|
|
230
240
|
|
|
@@ -251,6 +261,7 @@ class TestOrganizationClient(unittest.TestCase):
|
|
|
251
261
|
def test_export_request_data_success(self, mock_get):
|
|
252
262
|
mock_response = mock_get.return_value
|
|
253
263
|
mock_response.json.return_value = {"requests": [{"id": "1", "status": "pending"}]}
|
|
264
|
+
mock_response.status_code = 200
|
|
254
265
|
|
|
255
266
|
result = self.client.export_request_data()
|
|
256
267
|
|
|
@@ -266,6 +277,7 @@ class TestOrganizationClient(unittest.TestCase):
|
|
|
266
277
|
def test_export_request_data_with_params(self, mock_get):
|
|
267
278
|
mock_response = mock_get.return_value
|
|
268
279
|
mock_response.json.return_value = {"requests": [{"id": "1", "status": "completed"}]}
|
|
280
|
+
mock_response.status_code = 200
|
|
269
281
|
|
|
270
282
|
result = self.client.export_request_data(assistant_name="assistant1", status="completed", skip=10, count=5)
|
|
271
283
|
|
|
@@ -293,3 +305,196 @@ class TestOrganizationClient(unittest.TestCase):
|
|
|
293
305
|
)
|
|
294
306
|
self.assertEqual(str(context.exception), "Unable to export request data: Invalid JSON response")
|
|
295
307
|
|
|
308
|
+
@patch("pygeai.core.services.rest.ApiService.get")
|
|
309
|
+
def test_get_memberships_success(self, mock_get):
|
|
310
|
+
mock_response = mock_get.return_value
|
|
311
|
+
mock_response.json.return_value = {
|
|
312
|
+
|
|
313
|
+
"count": 1,
|
|
314
|
+
"pages": 1,
|
|
315
|
+
"organizationsMemberships": [
|
|
316
|
+
{
|
|
317
|
+
"organizationId": "org-123",
|
|
318
|
+
"organizationName": "Test Org",
|
|
319
|
+
"projectsMemberships": []
|
|
320
|
+
}
|
|
321
|
+
]
|
|
322
|
+
}
|
|
323
|
+
mock_response.status_code = 200
|
|
324
|
+
|
|
325
|
+
result = self.client.get_memberships()
|
|
326
|
+
|
|
327
|
+
mock_get.assert_called_once_with(
|
|
328
|
+
endpoint=GET_MEMBERSHIPS_V2,
|
|
329
|
+
params={"startPage": 1, "pageSize": 20, "orderDirection": "desc"}
|
|
330
|
+
)
|
|
331
|
+
self.assertIsNotNone(result)
|
|
332
|
+
self.assertEqual(result['count'], 1)
|
|
333
|
+
|
|
334
|
+
@patch("pygeai.core.services.rest.ApiService.get")
|
|
335
|
+
def test_get_memberships_with_params(self, mock_get):
|
|
336
|
+
mock_response = mock_get.return_value
|
|
337
|
+
mock_response.json.return_value = {"count": 0, "pages": 0, "organizationsMemberships": []}
|
|
338
|
+
mock_response.status_code = 200
|
|
339
|
+
|
|
340
|
+
result = self.client.get_memberships(
|
|
341
|
+
email="test@example.com",
|
|
342
|
+
start_page=2,
|
|
343
|
+
page_size=10,
|
|
344
|
+
order_key="organizationName",
|
|
345
|
+
order_direction="asc",
|
|
346
|
+
role_types="backend,frontend"
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
mock_get.assert_called_once_with(
|
|
350
|
+
endpoint=GET_MEMBERSHIPS_V2,
|
|
351
|
+
params={
|
|
352
|
+
"email": "test@example.com",
|
|
353
|
+
"startPage": 2,
|
|
354
|
+
"pageSize": 10,
|
|
355
|
+
"orderKey": "organizationName",
|
|
356
|
+
"orderDirection": "asc",
|
|
357
|
+
"roleTypes": "backend,frontend"
|
|
358
|
+
}
|
|
359
|
+
)
|
|
360
|
+
self.assertIsNotNone(result)
|
|
361
|
+
|
|
362
|
+
@patch("pygeai.core.services.rest.ApiService.get")
|
|
363
|
+
def test_get_memberships_json_decode_error(self, mock_get):
|
|
364
|
+
mock_response = mock_get.return_value
|
|
365
|
+
mock_response.status_code = 200
|
|
366
|
+
mock_response.json.side_effect = JSONDecodeError("Invalid JSON", "", 0)
|
|
367
|
+
mock_response.text = "Invalid JSON response"
|
|
368
|
+
|
|
369
|
+
with self.assertRaises(InvalidAPIResponseException) as context:
|
|
370
|
+
self.client.get_memberships()
|
|
371
|
+
|
|
372
|
+
self.assertEqual(str(context.exception), "Unable to get memberships: Invalid JSON response")
|
|
373
|
+
|
|
374
|
+
@patch("pygeai.core.services.rest.ApiService.get")
|
|
375
|
+
def test_get_project_memberships_success(self, mock_get):
|
|
376
|
+
mock_response = mock_get.return_value
|
|
377
|
+
mock_response.json.return_value = {
|
|
378
|
+
|
|
379
|
+
"count": 1,
|
|
380
|
+
"pages": 1,
|
|
381
|
+
"projectsMemberships": [
|
|
382
|
+
{
|
|
383
|
+
"organizationId": "org-123",
|
|
384
|
+
"organizationName": "Test Org",
|
|
385
|
+
"projectId": "proj-456",
|
|
386
|
+
"projectName": "Test Project",
|
|
387
|
+
"roles": []
|
|
388
|
+
}
|
|
389
|
+
]
|
|
390
|
+
}
|
|
391
|
+
mock_response.status_code = 200
|
|
392
|
+
|
|
393
|
+
result = self.client.get_project_memberships()
|
|
394
|
+
|
|
395
|
+
mock_get.assert_called_once_with(
|
|
396
|
+
endpoint=GET_PROJECT_MEMBERSHIPS_V2,
|
|
397
|
+
params={"startPage": 1, "pageSize": 20, "orderDirection": "desc"}
|
|
398
|
+
)
|
|
399
|
+
self.assertIsNotNone(result)
|
|
400
|
+
self.assertEqual(result['count'], 1)
|
|
401
|
+
|
|
402
|
+
@patch("pygeai.core.services.rest.ApiService.get")
|
|
403
|
+
def test_get_project_memberships_json_decode_error(self, mock_get):
|
|
404
|
+
mock_response = mock_get.return_value
|
|
405
|
+
mock_response.status_code = 200
|
|
406
|
+
mock_response.json.side_effect = JSONDecodeError("Invalid JSON", "", 0)
|
|
407
|
+
mock_response.text = "Invalid JSON response"
|
|
408
|
+
|
|
409
|
+
with self.assertRaises(InvalidAPIResponseException) as context:
|
|
410
|
+
self.client.get_project_memberships()
|
|
411
|
+
|
|
412
|
+
self.assertEqual(str(context.exception), "Unable to get project memberships: Invalid JSON response")
|
|
413
|
+
|
|
414
|
+
@patch("pygeai.core.services.rest.ApiService.get")
|
|
415
|
+
def test_get_project_roles_success(self, mock_get):
|
|
416
|
+
mock_response = mock_get.return_value
|
|
417
|
+
mock_response.json.return_value = {
|
|
418
|
+
|
|
419
|
+
"roles": [
|
|
420
|
+
{"id": "role-1", "name": "Admin", "externalId": "admin-ext", "type": "backend", "origin": "system"}
|
|
421
|
+
]
|
|
422
|
+
}
|
|
423
|
+
mock_response.status_code = 200
|
|
424
|
+
|
|
425
|
+
result = self.client.get_project_roles(project_id="proj-123")
|
|
426
|
+
|
|
427
|
+
mock_get.assert_called_once_with(endpoint=GET_PROJECT_ROLES_V2, params={"projectId": "proj-123"})
|
|
428
|
+
self.assertIsNotNone(result)
|
|
429
|
+
self.assertEqual(len(result['roles']), 1)
|
|
430
|
+
|
|
431
|
+
@patch("pygeai.core.services.rest.ApiService.get")
|
|
432
|
+
def test_get_project_roles_json_decode_error(self, mock_get):
|
|
433
|
+
mock_response = mock_get.return_value
|
|
434
|
+
mock_response.status_code = 200
|
|
435
|
+
mock_response.json.side_effect = JSONDecodeError("Invalid JSON", "", 0)
|
|
436
|
+
mock_response.text = "Invalid JSON response"
|
|
437
|
+
|
|
438
|
+
with self.assertRaises(InvalidAPIResponseException) as context:
|
|
439
|
+
self.client.get_project_roles(project_id="proj-123")
|
|
440
|
+
|
|
441
|
+
self.assertEqual(str(context.exception), "Unable to get project roles for project 'proj-123': Invalid JSON response")
|
|
442
|
+
|
|
443
|
+
@patch("pygeai.core.services.rest.ApiService.get")
|
|
444
|
+
def test_get_project_members_success(self, mock_get):
|
|
445
|
+
mock_response = mock_get.return_value
|
|
446
|
+
mock_response.json.return_value = {
|
|
447
|
+
|
|
448
|
+
"members": [
|
|
449
|
+
{"email": "user@example.com", "roles": []}
|
|
450
|
+
]
|
|
451
|
+
}
|
|
452
|
+
mock_response.status_code = 200
|
|
453
|
+
|
|
454
|
+
result = self.client.get_project_members(project_id="proj-123")
|
|
455
|
+
|
|
456
|
+
mock_get.assert_called_once_with(endpoint=GET_PROJECT_MEMBERS_V2, params={"projectId": "proj-123"})
|
|
457
|
+
self.assertIsNotNone(result)
|
|
458
|
+
self.assertEqual(len(result['members']), 1)
|
|
459
|
+
|
|
460
|
+
@patch("pygeai.core.services.rest.ApiService.get")
|
|
461
|
+
def test_get_project_members_json_decode_error(self, mock_get):
|
|
462
|
+
mock_response = mock_get.return_value
|
|
463
|
+
mock_response.status_code = 200
|
|
464
|
+
mock_response.json.side_effect = JSONDecodeError("Invalid JSON", "", 0)
|
|
465
|
+
mock_response.text = "Invalid JSON response"
|
|
466
|
+
|
|
467
|
+
with self.assertRaises(InvalidAPIResponseException) as context:
|
|
468
|
+
self.client.get_project_members(project_id="proj-123")
|
|
469
|
+
|
|
470
|
+
self.assertEqual(str(context.exception), "Unable to get project members for project 'proj-123': Invalid JSON response")
|
|
471
|
+
|
|
472
|
+
@patch("pygeai.core.services.rest.ApiService.get")
|
|
473
|
+
def test_get_organization_members_success(self, mock_get):
|
|
474
|
+
mock_response = mock_get.return_value
|
|
475
|
+
mock_response.json.return_value = {
|
|
476
|
+
|
|
477
|
+
"members": [
|
|
478
|
+
{"email": "user@example.com", "roles": []}
|
|
479
|
+
]
|
|
480
|
+
}
|
|
481
|
+
mock_response.status_code = 200
|
|
482
|
+
|
|
483
|
+
result = self.client.get_organization_members(organization_id="org-123")
|
|
484
|
+
|
|
485
|
+
mock_get.assert_called_once_with(endpoint=GET_ORGANIZATION_MEMBERS_V2, params={"organizationId": "org-123"})
|
|
486
|
+
self.assertIsNotNone(result)
|
|
487
|
+
self.assertEqual(len(result['members']), 1)
|
|
488
|
+
|
|
489
|
+
@patch("pygeai.core.services.rest.ApiService.get")
|
|
490
|
+
def test_get_organization_members_json_decode_error(self, mock_get):
|
|
491
|
+
mock_response = mock_get.return_value
|
|
492
|
+
mock_response.status_code = 200
|
|
493
|
+
mock_response.json.side_effect = JSONDecodeError("Invalid JSON", "", 0)
|
|
494
|
+
mock_response.text = "Invalid JSON response"
|
|
495
|
+
|
|
496
|
+
with self.assertRaises(InvalidAPIResponseException) as context:
|
|
497
|
+
self.client.get_organization_members(organization_id="org-123")
|
|
498
|
+
|
|
499
|
+
self.assertEqual(str(context.exception), "Unable to get organization members for organization 'org-123': Invalid JSON response")
|
|
500
|
+
|