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,294 +1,163 @@
|
|
|
1
1
|
import unittest
|
|
2
2
|
from unittest.mock import patch, Mock
|
|
3
|
-
from pygeai.cli.commands.migrate import
|
|
4
|
-
show_help,
|
|
5
|
-
migrate_base,
|
|
6
|
-
clone_project,
|
|
7
|
-
clone_agent,
|
|
8
|
-
clone_tool,
|
|
9
|
-
clone_process,
|
|
10
|
-
clone_task,
|
|
11
|
-
Option
|
|
12
|
-
)
|
|
13
|
-
from pygeai.core.common.exceptions import MissingRequirementException
|
|
3
|
+
from pygeai.cli.commands.migrate import clone_project, show_help, migrate_commands
|
|
14
4
|
|
|
15
5
|
|
|
16
6
|
class TestMigrateCommands(unittest.TestCase):
|
|
17
|
-
"""
|
|
18
|
-
python -m unittest pygeai.tests.cli.commands.test_migrate.TestMigrateCommands
|
|
19
|
-
"""
|
|
20
|
-
def setUp(self):
|
|
21
|
-
# Helper to create Option objects for testing
|
|
22
|
-
self.mock_option = lambda name, value: (Option(name, [f"--{name}"], f"Description for {name}", True), value)
|
|
23
7
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
mock_build_help.return_value = mock_help_text
|
|
8
|
+
def mock_option(self, name, value=None):
|
|
9
|
+
option = Mock()
|
|
10
|
+
option.name = name
|
|
11
|
+
return (option, value)
|
|
29
12
|
|
|
13
|
+
def test_show_help(self):
|
|
30
14
|
show_help()
|
|
31
15
|
|
|
32
|
-
|
|
33
|
-
mock_write_stdout.assert_called_once_with(mock_help_text)
|
|
34
|
-
|
|
35
|
-
def test_migrate_base_success(self):
|
|
36
|
-
option_list = [
|
|
37
|
-
self.mock_option("from_api_key", "from_key"),
|
|
38
|
-
self.mock_option("from_instance", "from_instance"),
|
|
39
|
-
self.mock_option("to_api_key", "to_key"),
|
|
40
|
-
self.mock_option("to_instance", "to_instance")
|
|
41
|
-
]
|
|
42
|
-
|
|
43
|
-
result = migrate_base(option_list)
|
|
44
|
-
|
|
45
|
-
self.assertEqual(result, ("from_key", "from_instance", "to_key", "to_instance"))
|
|
46
|
-
|
|
47
|
-
def test_migrate_base_missing_source(self):
|
|
48
|
-
option_list = [
|
|
49
|
-
self.mock_option("to_api_key", "to_key"),
|
|
50
|
-
self.mock_option("to_instance", "to_instance")
|
|
51
|
-
]
|
|
52
|
-
|
|
53
|
-
with self.assertRaises(MissingRequirementException) as context:
|
|
54
|
-
migrate_base(option_list)
|
|
55
|
-
|
|
56
|
-
self.assertEqual(str(context.exception), "Cannot migrate resources without indicating source: API key and instance")
|
|
57
|
-
|
|
16
|
+
@patch('pygeai.auth.clients.AuthClient')
|
|
58
17
|
@patch('pygeai.cli.commands.migrate.Console.write_stdout')
|
|
59
18
|
@patch('pygeai.cli.commands.migrate.MigrationTool')
|
|
60
|
-
@patch('pygeai.cli.commands.migrate.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
19
|
+
@patch('pygeai.cli.commands.migrate.MigrationOrchestrator')
|
|
20
|
+
@patch('pygeai.cli.commands.migrate.RAGAssistantClient')
|
|
21
|
+
@patch('pygeai.cli.commands.migrate.FileManager')
|
|
22
|
+
@patch('pygeai.cli.commands.migrate.AILabManager')
|
|
23
|
+
@patch("pygeai.admin.clients.AdminClient.validate_api_token", return_value={"projectId": "test_project"})
|
|
24
|
+
def test_clone_project_with_all_flag(self, mock_base_client, mock_lab_mgr, mock_file_mgr, mock_rag_client, mock_orchestrator, mock_migration_tool, mock_stdout, mock_auth_client):
|
|
25
|
+
mock_lab_instance = Mock()
|
|
26
|
+
mock_lab_mgr.return_value = mock_lab_instance
|
|
27
|
+
|
|
28
|
+
mock_lab_instance.get_agent_list.return_value = Mock(agents=[Mock(id="agent1")])
|
|
29
|
+
mock_lab_instance.list_tools.return_value = Mock(tools=[Mock(id="tool1")])
|
|
30
|
+
mock_lab_instance.list_processes.return_value = Mock(processes=[Mock(id="proc1")])
|
|
31
|
+
mock_lab_instance.list_tasks.return_value = Mock(tasks=[Mock(id="task1")])
|
|
32
|
+
|
|
33
|
+
mock_rag_instance = Mock()
|
|
34
|
+
mock_rag_client.return_value = mock_rag_instance
|
|
35
|
+
mock_rag_instance.get_assistants_from_project.return_value = {
|
|
36
|
+
"assistants": [
|
|
37
|
+
{
|
|
38
|
+
"name": "asst1",
|
|
39
|
+
"searchOptions": {},
|
|
40
|
+
"indexOptions": {},
|
|
41
|
+
"welcomeData": None,
|
|
42
|
+
"llmSettings": None
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
mock_file_instance = Mock()
|
|
48
|
+
mock_file_mgr.return_value = mock_file_instance
|
|
49
|
+
mock_file_instance.get_file_list.return_value = Mock(files=[Mock(id="file1")])
|
|
50
|
+
|
|
51
|
+
mock_migration_tool_instance = Mock()
|
|
52
|
+
mock_migration_tool.return_value = mock_migration_tool_instance
|
|
53
|
+
mock_migration_tool_instance.run_migration.return_value = "proj456"
|
|
54
|
+
|
|
55
|
+
mock_auth_instance = Mock()
|
|
56
|
+
mock_auth_client.return_value = mock_auth_instance
|
|
57
|
+
mock_auth_instance.create_project_api_token.return_value = {"id": "new_project_api_key", "name": "Migration API Key"}
|
|
58
|
+
|
|
59
|
+
mock_orch_instance = Mock()
|
|
60
|
+
mock_orchestrator.return_value = mock_orch_instance
|
|
61
|
+
mock_orch_instance.execute.return_value = {"completed": 1, "total": 1, "failed": 0}
|
|
62
|
+
|
|
67
63
|
option_list = [
|
|
68
64
|
self.mock_option("from_api_key", "from_key"),
|
|
65
|
+
self.mock_option("from_organization_api_key", "from_org_key"),
|
|
69
66
|
self.mock_option("from_project_id", "proj123"),
|
|
70
67
|
self.mock_option("from_instance", "from_instance"),
|
|
71
|
-
self.mock_option("
|
|
68
|
+
self.mock_option("from_organization_id", "org123"),
|
|
69
|
+
self.mock_option("to_organization_api_key", "to_org_key"),
|
|
72
70
|
self.mock_option("to_project_name", "new_project"),
|
|
73
|
-
self.mock_option("
|
|
74
|
-
self.mock_option("admin_email", "admin@example.com")
|
|
71
|
+
self.mock_option("to_organization_id", "org456"),
|
|
72
|
+
self.mock_option("admin_email", "admin@example.com"),
|
|
73
|
+
self.mock_option("all", True)
|
|
75
74
|
]
|
|
76
75
|
|
|
77
76
|
clone_project(option_list)
|
|
78
77
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
)
|
|
88
|
-
mock_tool.assert_called_once_with(mock_strategy_instance)
|
|
89
|
-
mock_tool_instance.run_migration.assert_called_once()
|
|
90
|
-
mock_write_stdout.assert_called_once_with("Migration result: \n{'status': 'success'}")
|
|
78
|
+
mock_migration_tool_instance.run_migration.assert_called_once()
|
|
79
|
+
mock_auth_instance.create_project_api_token.assert_called_once()
|
|
80
|
+
mock_lab_instance.get_agent_list.assert_called_once()
|
|
81
|
+
mock_lab_instance.list_tools.assert_called_once()
|
|
82
|
+
mock_lab_instance.list_processes.assert_called_once()
|
|
83
|
+
mock_lab_instance.list_tasks.assert_called_once()
|
|
84
|
+
mock_rag_instance.get_assistants_from_project.assert_called_once()
|
|
85
|
+
mock_file_instance.get_file_list.assert_called_once()
|
|
86
|
+
mock_orch_instance.execute.assert_called_once()
|
|
91
87
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
self.mock_option("from_api_key", "from_key"),
|
|
108
|
-
self.mock_option("from_project_id", "proj123"),
|
|
109
|
-
self.mock_option("from_instance", "from_instance"),
|
|
110
|
-
self.mock_option("to_api_key", "to_key"),
|
|
111
|
-
self.mock_option("to_project_name", "new_project"),
|
|
112
|
-
self.mock_option("to_instance", "to_instance")
|
|
113
|
-
]
|
|
114
|
-
|
|
115
|
-
with self.assertRaises(MissingRequirementException) as context:
|
|
116
|
-
clone_project(option_list)
|
|
117
|
-
|
|
118
|
-
self.assertEqual(str(context.exception), "Admin email for new project must be defined.")
|
|
88
|
+
@patch('pygeai.cli.commands.migrate.Console.write_stdout')
|
|
89
|
+
@patch('pygeai.cli.commands.migrate.MigrationOrchestrator')
|
|
90
|
+
@patch('pygeai.cli.commands.migrate.AILabManager')
|
|
91
|
+
@patch("pygeai.admin.clients.AdminClient.validate_api_token", return_value={"projectId": "test_project"})
|
|
92
|
+
def test_clone_project_without_project_creation_no_org_keys_needed(self, mock_base_client, mock_lab_mgr,
|
|
93
|
+
mock_orchestrator, mock_stdout):
|
|
94
|
+
"""Test that migration to existing project works without org keys but requires to_api_key"""
|
|
95
|
+
mock_lab_instance = Mock()
|
|
96
|
+
mock_lab_mgr.return_value = mock_lab_instance
|
|
97
|
+
|
|
98
|
+
mock_lab_instance.get_agent_list.return_value = Mock(agents=[Mock(id="agent1")])
|
|
99
|
+
|
|
100
|
+
mock_orch_instance = Mock()
|
|
101
|
+
mock_orchestrator.return_value = mock_orch_instance
|
|
102
|
+
mock_orch_instance.execute.return_value = {"completed": 1, "total": 1, "failed": 0}
|
|
119
103
|
|
|
120
|
-
@patch('pygeai.cli.commands.migrate.MigrationTool')
|
|
121
|
-
@patch('pygeai.cli.commands.migrate.AgentMigrationStrategy')
|
|
122
|
-
def test_clone_agent_success(self, mock_strategy, mock_tool):
|
|
123
|
-
mock_strategy_instance = Mock()
|
|
124
|
-
mock_strategy.return_value = mock_strategy_instance
|
|
125
|
-
mock_tool_instance = Mock()
|
|
126
|
-
mock_tool.return_value = mock_tool_instance
|
|
127
104
|
option_list = [
|
|
128
105
|
self.mock_option("from_api_key", "from_key"),
|
|
129
106
|
self.mock_option("from_project_id", "proj123"),
|
|
130
107
|
self.mock_option("from_instance", "from_instance"),
|
|
108
|
+
self.mock_option("to_project_id", "proj456"),
|
|
131
109
|
self.mock_option("to_api_key", "to_key"),
|
|
132
|
-
self.mock_option("
|
|
133
|
-
self.mock_option("to_instance", "to_instance"),
|
|
134
|
-
self.mock_option("agent_id", "agent456")
|
|
135
|
-
]
|
|
136
|
-
|
|
137
|
-
clone_agent(option_list)
|
|
138
|
-
|
|
139
|
-
mock_strategy.assert_called_once_with(
|
|
140
|
-
from_api_key="from_key",
|
|
141
|
-
from_project_id="proj123",
|
|
142
|
-
from_instance="from_instance",
|
|
143
|
-
to_api_key="to_key",
|
|
144
|
-
to_project_id="to_proj123",
|
|
145
|
-
to_instance="to_instance",
|
|
146
|
-
agent_id="agent456"
|
|
147
|
-
)
|
|
148
|
-
mock_tool.assert_called_once_with(mock_strategy_instance)
|
|
149
|
-
mock_tool_instance.run_migration.assert_called_once()
|
|
150
|
-
|
|
151
|
-
def test_clone_agent_missing_source(self):
|
|
152
|
-
option_list = [
|
|
153
|
-
self.mock_option("to_api_key", "to_key"),
|
|
154
|
-
self.mock_option("to_project_id", "to_proj123"),
|
|
155
|
-
self.mock_option("to_instance", "to_instance")
|
|
110
|
+
self.mock_option("agents", "all")
|
|
156
111
|
]
|
|
157
112
|
|
|
158
|
-
|
|
159
|
-
clone_agent(option_list)
|
|
113
|
+
clone_project(option_list)
|
|
160
114
|
|
|
161
|
-
|
|
115
|
+
mock_lab_instance.get_agent_list.assert_called_once()
|
|
116
|
+
mock_orch_instance.execute.assert_called_once()
|
|
162
117
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
mock_strategy_instance = Mock()
|
|
167
|
-
mock_strategy.return_value = mock_strategy_instance
|
|
168
|
-
mock_tool_instance = Mock()
|
|
169
|
-
mock_tool.return_value = mock_tool_instance
|
|
118
|
+
def test_clone_project_missing_required_params(self):
|
|
119
|
+
from pygeai.core.common.exceptions import MissingRequirementException
|
|
120
|
+
|
|
170
121
|
option_list = [
|
|
171
122
|
self.mock_option("from_api_key", "from_key"),
|
|
172
|
-
self.mock_option("from_project_id", "proj123"),
|
|
173
|
-
self.mock_option("from_instance", "from_instance"),
|
|
174
|
-
self.mock_option("to_api_key", "to_key"),
|
|
175
|
-
self.mock_option("to_project_id", "to_proj123"),
|
|
176
|
-
self.mock_option("to_instance", "to_instance"),
|
|
177
|
-
self.mock_option("tool_id", "tool789")
|
|
178
123
|
]
|
|
179
124
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
mock_strategy.assert_called_once_with(
|
|
183
|
-
from_api_key="from_key",
|
|
184
|
-
from_project_id="proj123",
|
|
185
|
-
from_instance="from_instance",
|
|
186
|
-
to_api_key="to_key",
|
|
187
|
-
to_project_id="to_proj123",
|
|
188
|
-
to_instance="to_instance",
|
|
189
|
-
tool_id="tool789"
|
|
190
|
-
)
|
|
191
|
-
mock_tool.assert_called_once_with(mock_strategy_instance)
|
|
192
|
-
mock_tool_instance.run_migration.assert_called_once()
|
|
193
|
-
|
|
194
|
-
def test_clone_tool_missing_source(self):
|
|
195
|
-
option_list = [
|
|
196
|
-
self.mock_option("to_api_key", "to_key"),
|
|
197
|
-
self.mock_option("to_project_id", "to_proj123"),
|
|
198
|
-
self.mock_option("to_instance", "to_instance")
|
|
199
|
-
]
|
|
200
|
-
|
|
201
|
-
with self.assertRaises(MissingRequirementException) as context:
|
|
202
|
-
clone_tool(option_list)
|
|
203
|
-
|
|
204
|
-
self.assertEqual(str(context.exception), "Cannot migrate resources without indicating source: project, instance, and tool ID")
|
|
125
|
+
with self.assertRaises(MissingRequirementException):
|
|
126
|
+
clone_project(option_list)
|
|
205
127
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
mock_strategy_instance = Mock()
|
|
210
|
-
mock_strategy.return_value = mock_strategy_instance
|
|
211
|
-
mock_tool_instance = Mock()
|
|
212
|
-
mock_tool.return_value = mock_tool_instance
|
|
128
|
+
def test_clone_project_missing_org_key_when_creating_project(self):
|
|
129
|
+
from pygeai.core.common.exceptions import MissingRequirementException
|
|
130
|
+
|
|
213
131
|
option_list = [
|
|
214
132
|
self.mock_option("from_api_key", "from_key"),
|
|
215
133
|
self.mock_option("from_project_id", "proj123"),
|
|
216
134
|
self.mock_option("from_instance", "from_instance"),
|
|
217
|
-
self.mock_option("
|
|
218
|
-
self.mock_option("
|
|
219
|
-
self.mock_option("
|
|
220
|
-
self.mock_option("process_id", "proc101")
|
|
135
|
+
self.mock_option("to_project_name", "new_project"),
|
|
136
|
+
self.mock_option("admin_email", "admin@example.com"),
|
|
137
|
+
self.mock_option("agents", "all")
|
|
221
138
|
]
|
|
222
139
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
from_project_id="proj123",
|
|
228
|
-
from_instance="from_instance",
|
|
229
|
-
to_api_key="to_key",
|
|
230
|
-
to_project_id="to_proj123",
|
|
231
|
-
to_instance="to_instance",
|
|
232
|
-
process_id="proc101"
|
|
233
|
-
)
|
|
234
|
-
mock_tool.assert_called_once_with(mock_strategy_instance)
|
|
235
|
-
mock_tool_instance.run_migration.assert_called_once()
|
|
140
|
+
with self.assertRaises(MissingRequirementException) as cm:
|
|
141
|
+
clone_project(option_list)
|
|
142
|
+
|
|
143
|
+
self.assertIn("organization scope API key", str(cm.exception))
|
|
236
144
|
|
|
237
|
-
def
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
self.mock_option("to_project_id", "to_proj123"),
|
|
241
|
-
self.mock_option("to_instance", "to_instance")
|
|
242
|
-
]
|
|
243
|
-
|
|
244
|
-
with self.assertRaises(MissingRequirementException) as context:
|
|
245
|
-
clone_process(option_list)
|
|
246
|
-
|
|
247
|
-
self.assertEqual(str(context.exception), "Cannot migrate resources without indicating source: project, instance, and process ID")
|
|
248
|
-
|
|
249
|
-
@patch('pygeai.cli.commands.migrate.Console.write_stdout')
|
|
250
|
-
@patch('pygeai.cli.commands.migrate.MigrationTool')
|
|
251
|
-
@patch('pygeai.cli.commands.migrate.TaskMigrationStrategy')
|
|
252
|
-
def test_clone_task_success(self, mock_strategy, mock_tool, mock_write_stdout):
|
|
253
|
-
mock_strategy_instance = Mock()
|
|
254
|
-
mock_strategy.return_value = mock_strategy_instance
|
|
255
|
-
mock_tool_instance = Mock()
|
|
256
|
-
mock_tool.return_value = mock_tool_instance
|
|
257
|
-
mock_tool_instance.run_migration.return_value = {"status": "success"}
|
|
145
|
+
def test_clone_project_missing_to_api_key_when_using_existing_project(self):
|
|
146
|
+
from pygeai.core.common.exceptions import MissingRequirementException
|
|
147
|
+
|
|
258
148
|
option_list = [
|
|
259
149
|
self.mock_option("from_api_key", "from_key"),
|
|
260
150
|
self.mock_option("from_project_id", "proj123"),
|
|
261
151
|
self.mock_option("from_instance", "from_instance"),
|
|
262
|
-
self.mock_option("
|
|
263
|
-
self.mock_option("
|
|
264
|
-
self.mock_option("to_instance", "to_instance"),
|
|
265
|
-
self.mock_option("task_id", "task202")
|
|
152
|
+
self.mock_option("to_project_id", "proj456"),
|
|
153
|
+
self.mock_option("agents", "all")
|
|
266
154
|
]
|
|
267
155
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
from_project_id="proj123",
|
|
273
|
-
from_instance="from_instance",
|
|
274
|
-
to_api_key="to_key",
|
|
275
|
-
to_project_id="to_proj123",
|
|
276
|
-
to_instance="to_instance",
|
|
277
|
-
task_id="task202"
|
|
278
|
-
)
|
|
279
|
-
mock_tool.assert_called_once_with(mock_strategy_instance)
|
|
280
|
-
mock_tool_instance.run_migration.assert_called_once()
|
|
281
|
-
mock_write_stdout.assert_called_once_with("Migration result: \n{'status': 'success'}")
|
|
282
|
-
|
|
283
|
-
def test_clone_task_missing_source(self):
|
|
284
|
-
option_list = [
|
|
285
|
-
self.mock_option("to_api_key", "to_key"),
|
|
286
|
-
self.mock_option("to_project_id", "to_proj123"),
|
|
287
|
-
self.mock_option("to_instance", "to_instance")
|
|
288
|
-
]
|
|
289
|
-
|
|
290
|
-
with self.assertRaises(MissingRequirementException) as context:
|
|
291
|
-
clone_task(option_list)
|
|
156
|
+
with self.assertRaises(MissingRequirementException) as cm:
|
|
157
|
+
clone_project(option_list)
|
|
158
|
+
|
|
159
|
+
self.assertIn("Destination project API key", str(cm.exception))
|
|
292
160
|
|
|
293
|
-
self.assertEqual(str(context.exception), "Cannot migrate resources without indicating source: project, instance, and task ID")
|
|
294
161
|
|
|
162
|
+
if __name__ == '__main__':
|
|
163
|
+
unittest.main()
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from unittest import TestCase
|
|
3
|
+
from unittest.mock import MagicMock
|
|
4
|
+
|
|
5
|
+
from pygeai.cli.commands import Command, Option, ArgumentsEnum
|
|
6
|
+
from pygeai.cli.error_handler import ErrorHandler, ExitCode
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TestErrorHandler(TestCase):
|
|
10
|
+
"""
|
|
11
|
+
Test suite for the ErrorHandler class.
|
|
12
|
+
Run with: python -m unittest pygeai.tests.cli.test_error_handler.TestErrorHandler
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def setUp(self):
|
|
16
|
+
self.commands = [
|
|
17
|
+
Command(
|
|
18
|
+
name='help',
|
|
19
|
+
identifiers=['help', 'h'],
|
|
20
|
+
description='Display help',
|
|
21
|
+
action=None,
|
|
22
|
+
additional_args=ArgumentsEnum.NOT_AVAILABLE,
|
|
23
|
+
subcommands=[],
|
|
24
|
+
options=[]
|
|
25
|
+
),
|
|
26
|
+
Command(
|
|
27
|
+
name='version',
|
|
28
|
+
identifiers=['version', 'v'],
|
|
29
|
+
description='Display version',
|
|
30
|
+
action=None,
|
|
31
|
+
additional_args=ArgumentsEnum.NOT_AVAILABLE,
|
|
32
|
+
subcommands=[],
|
|
33
|
+
options=[]
|
|
34
|
+
),
|
|
35
|
+
Command(
|
|
36
|
+
name='configure',
|
|
37
|
+
identifiers=['configure', 'config', 'c'],
|
|
38
|
+
description='Configure settings',
|
|
39
|
+
action=None,
|
|
40
|
+
additional_args=ArgumentsEnum.OPTIONAL,
|
|
41
|
+
subcommands=[],
|
|
42
|
+
options=[]
|
|
43
|
+
),
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
self.options = [
|
|
47
|
+
Option(
|
|
48
|
+
name='key',
|
|
49
|
+
identifiers=['--key', '-k'],
|
|
50
|
+
description='API key',
|
|
51
|
+
requires_args=True
|
|
52
|
+
),
|
|
53
|
+
Option(
|
|
54
|
+
name='url',
|
|
55
|
+
identifiers=['--url', '-u'],
|
|
56
|
+
description='API URL',
|
|
57
|
+
requires_args=True
|
|
58
|
+
),
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
def test_exit_codes_defined(self):
|
|
62
|
+
"""Test that all exit codes are properly defined"""
|
|
63
|
+
self.assertEqual(ExitCode.SUCCESS, 0)
|
|
64
|
+
self.assertEqual(ExitCode.USER_INPUT_ERROR, 1)
|
|
65
|
+
self.assertEqual(ExitCode.MISSING_REQUIREMENT, 2)
|
|
66
|
+
self.assertEqual(ExitCode.SERVICE_ERROR, 3)
|
|
67
|
+
self.assertEqual(ExitCode.KEYBOARD_INTERRUPT, 130)
|
|
68
|
+
self.assertEqual(ExitCode.UNEXPECTED_ERROR, 255)
|
|
69
|
+
|
|
70
|
+
def test_format_error_basic(self):
|
|
71
|
+
"""Test basic error formatting"""
|
|
72
|
+
result = ErrorHandler.format_error("Test Error", "Something went wrong")
|
|
73
|
+
self.assertIn("ERROR: Something went wrong", result)
|
|
74
|
+
self.assertIn("Run 'geai help' for usage information.", result)
|
|
75
|
+
|
|
76
|
+
def test_format_error_with_suggestion(self):
|
|
77
|
+
"""Test error formatting with suggestion"""
|
|
78
|
+
result = ErrorHandler.format_error(
|
|
79
|
+
"Test Error",
|
|
80
|
+
"Command not found",
|
|
81
|
+
suggestion="Try using 'help' command"
|
|
82
|
+
)
|
|
83
|
+
self.assertIn("ERROR: Command not found", result)
|
|
84
|
+
self.assertIn("→ Try using 'help' command", result)
|
|
85
|
+
|
|
86
|
+
def test_format_error_without_help(self):
|
|
87
|
+
"""Test error formatting without help text"""
|
|
88
|
+
result = ErrorHandler.format_error(
|
|
89
|
+
"Test Error",
|
|
90
|
+
"Critical error",
|
|
91
|
+
show_help=False
|
|
92
|
+
)
|
|
93
|
+
self.assertIn("ERROR: Critical error", result)
|
|
94
|
+
self.assertNotIn("Run 'geai help'", result)
|
|
95
|
+
|
|
96
|
+
def test_find_similar_items_exact_match(self):
|
|
97
|
+
"""Test fuzzy matching with high similarity"""
|
|
98
|
+
items = ['help', 'version', 'configure']
|
|
99
|
+
similar = ErrorHandler.find_similar_items('halp', items)
|
|
100
|
+
self.assertIn('help', similar)
|
|
101
|
+
|
|
102
|
+
def test_find_similar_items_no_match(self):
|
|
103
|
+
"""Test fuzzy matching with no similar items"""
|
|
104
|
+
items = ['help', 'version', 'configure']
|
|
105
|
+
similar = ErrorHandler.find_similar_items('xyz123', items, threshold=0.6)
|
|
106
|
+
self.assertEqual(len(similar), 0)
|
|
107
|
+
|
|
108
|
+
def test_find_similar_items_multiple_matches(self):
|
|
109
|
+
"""Test fuzzy matching returns top matches"""
|
|
110
|
+
items = ['configure', 'config', 'configuration', 'help']
|
|
111
|
+
similar = ErrorHandler.find_similar_items('config', items)
|
|
112
|
+
self.assertGreater(len(similar), 0)
|
|
113
|
+
self.assertLessEqual(len(similar), 3)
|
|
114
|
+
|
|
115
|
+
def test_get_available_commands(self):
|
|
116
|
+
"""Test extraction of available command identifiers"""
|
|
117
|
+
identifiers = ErrorHandler.get_available_commands(self.commands)
|
|
118
|
+
self.assertIn('help', identifiers)
|
|
119
|
+
self.assertIn('h', identifiers)
|
|
120
|
+
self.assertIn('version', identifiers)
|
|
121
|
+
self.assertIn('v', identifiers)
|
|
122
|
+
self.assertIn('configure', identifiers)
|
|
123
|
+
self.assertIn('config', identifiers)
|
|
124
|
+
self.assertIn('c', identifiers)
|
|
125
|
+
|
|
126
|
+
def test_get_available_options(self):
|
|
127
|
+
"""Test extraction of available option identifiers"""
|
|
128
|
+
identifiers = ErrorHandler.get_available_options(self.options)
|
|
129
|
+
self.assertIn('--key', identifiers)
|
|
130
|
+
self.assertIn('-k', identifiers)
|
|
131
|
+
self.assertIn('--url', identifiers)
|
|
132
|
+
self.assertIn('-u', identifiers)
|
|
133
|
+
|
|
134
|
+
def test_handle_unknown_command_with_fuzzy_match(self):
|
|
135
|
+
"""Test unknown command error with fuzzy matching suggestion"""
|
|
136
|
+
result = ErrorHandler.handle_unknown_command('halp', self.commands)
|
|
137
|
+
self.assertIn("'halp' is not a valid command", result)
|
|
138
|
+
self.assertIn("Did you mean", result)
|
|
139
|
+
self.assertIn("help", result)
|
|
140
|
+
|
|
141
|
+
def test_handle_unknown_command_no_match(self):
|
|
142
|
+
"""Test unknown command error without fuzzy match"""
|
|
143
|
+
result = ErrorHandler.handle_unknown_command('xyz123', self.commands)
|
|
144
|
+
self.assertIn("'xyz123' is not a valid command", result)
|
|
145
|
+
self.assertIn("Available commands:", result)
|
|
146
|
+
|
|
147
|
+
def test_handle_unknown_option_with_fuzzy_match(self):
|
|
148
|
+
"""Test unknown option error with fuzzy matching"""
|
|
149
|
+
result = ErrorHandler.handle_unknown_option('--kee', self.options)
|
|
150
|
+
self.assertIn("'--kee' is not a valid option", result)
|
|
151
|
+
self.assertIn("Did you mean", result)
|
|
152
|
+
|
|
153
|
+
def test_handle_unknown_option_no_match(self):
|
|
154
|
+
"""Test unknown option error without fuzzy match"""
|
|
155
|
+
result = ErrorHandler.handle_unknown_option('--completely-different-option-xyz123', self.options)
|
|
156
|
+
self.assertIn("'--completely-different-option-xyz123' is not a valid option", result)
|
|
157
|
+
self.assertIn("Available options:", result)
|
|
158
|
+
|
|
159
|
+
def test_handle_missing_requirement(self):
|
|
160
|
+
"""Test missing requirement error formatting"""
|
|
161
|
+
result = ErrorHandler.handle_missing_requirement("API key is required")
|
|
162
|
+
self.assertIn("API key is required", result)
|
|
163
|
+
self.assertIn("Please provide all required parameters", result)
|
|
164
|
+
|
|
165
|
+
def test_handle_invalid_agent(self):
|
|
166
|
+
"""Test invalid agent error formatting"""
|
|
167
|
+
result = ErrorHandler.handle_invalid_agent("Agent 'test-agent' not found")
|
|
168
|
+
self.assertIn("Failed to retrieve or validate the agent", result)
|
|
169
|
+
self.assertIn("Agent 'test-agent' not found", result)
|
|
170
|
+
self.assertIn("Check your agent configuration", result)
|
|
171
|
+
|
|
172
|
+
def test_handle_wrong_argument(self):
|
|
173
|
+
"""Test wrong argument error formatting"""
|
|
174
|
+
usage = "geai <command> [options]"
|
|
175
|
+
result = ErrorHandler.handle_wrong_argument("Invalid format", usage)
|
|
176
|
+
self.assertIn("Invalid format", result)
|
|
177
|
+
self.assertIn("Check the command syntax", result)
|
|
178
|
+
|
|
179
|
+
def test_handle_keyboard_interrupt(self):
|
|
180
|
+
"""Test keyboard interrupt message"""
|
|
181
|
+
result = ErrorHandler.handle_keyboard_interrupt()
|
|
182
|
+
self.assertIn("Operation cancelled by user", result)
|
|
183
|
+
|
|
184
|
+
def test_handle_unexpected_error(self):
|
|
185
|
+
"""Test unexpected error formatting"""
|
|
186
|
+
exception = ValueError("Test error")
|
|
187
|
+
result = ErrorHandler.handle_unexpected_error(exception)
|
|
188
|
+
self.assertIn("unexpected error occurred", result)
|
|
189
|
+
self.assertIn("Test error", result)
|
|
190
|
+
self.assertIn("geai-sdk@globant.com", result)
|
|
191
|
+
|
|
192
|
+
def test_fuzzy_matching_threshold(self):
|
|
193
|
+
"""Test that threshold parameter works correctly"""
|
|
194
|
+
items = ['configure', 'help', 'version']
|
|
195
|
+
|
|
196
|
+
# With high threshold, should not match
|
|
197
|
+
similar_high = ErrorHandler.find_similar_items('xyz', items, threshold=0.9)
|
|
198
|
+
self.assertEqual(len(similar_high), 0)
|
|
199
|
+
|
|
200
|
+
# With low threshold, might match
|
|
201
|
+
similar_low = ErrorHandler.find_similar_items('c', items, threshold=0.3)
|
|
202
|
+
self.assertGreaterEqual(len(similar_low), 0)
|
|
203
|
+
|
|
204
|
+
def test_multiple_command_identifiers_in_suggestions(self):
|
|
205
|
+
"""Test that fuzzy matching works with multiple identifiers"""
|
|
206
|
+
result = ErrorHandler.handle_unknown_command('configurr', self.commands)
|
|
207
|
+
# Should suggest 'configure' or 'config'
|
|
208
|
+
self.assertTrue('configure' in result or 'config' in result)
|
|
209
|
+
|
|
210
|
+
def test_error_format_consistency(self):
|
|
211
|
+
"""Test that all error handlers produce consistent format"""
|
|
212
|
+
results = [
|
|
213
|
+
ErrorHandler.handle_unknown_command('test', self.commands),
|
|
214
|
+
ErrorHandler.handle_unknown_option('--test', self.options),
|
|
215
|
+
ErrorHandler.handle_missing_requirement("test requirement"),
|
|
216
|
+
ErrorHandler.handle_invalid_agent("test agent"),
|
|
217
|
+
]
|
|
218
|
+
|
|
219
|
+
for result in results:
|
|
220
|
+
self.assertIn("ERROR:", result)
|
|
221
|
+
self.assertIn("→", result)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
if __name__ == '__main__':
|
|
225
|
+
unittest.main()
|