pygeai 0.6.0b10__py3-none-any.whl → 0.6.0b12__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/content/ai_lab/cli.rst +4 -4
- pygeai/_docs/source/content/ai_lab/models.rst +169 -35
- pygeai/_docs/source/content/ai_lab/runner.rst +2 -2
- pygeai/_docs/source/content/ai_lab/spec.rst +9 -9
- pygeai/_docs/source/content/ai_lab/usage.rst +34 -34
- pygeai/_docs/source/content/ai_lab.rst +1 -1
- pygeai/_docs/source/content/analytics.rst +598 -0
- pygeai/_docs/source/content/api_reference/chat.rst +428 -2
- pygeai/_docs/source/content/api_reference/embeddings.rst +1 -1
- pygeai/_docs/source/content/api_reference/project.rst +184 -0
- pygeai/_docs/source/content/api_reference/rag.rst +2 -2
- pygeai/_docs/source/content/authentication.rst +295 -0
- pygeai/_docs/source/content/cli.rst +79 -2
- pygeai/_docs/source/content/debugger.rst +1 -1
- pygeai/_docs/source/content/migration.rst +19 -2
- pygeai/_docs/source/index.rst +2 -0
- pygeai/_docs/source/pygeai.analytics.rst +53 -0
- pygeai/_docs/source/pygeai.cli.commands.rst +8 -0
- pygeai/_docs/source/pygeai.rst +1 -0
- pygeai/_docs/source/pygeai.tests.analytics.rst +45 -0
- pygeai/_docs/source/pygeai.tests.auth.rst +8 -0
- pygeai/_docs/source/pygeai.tests.rst +1 -1
- pygeai/analytics/__init__.py +0 -0
- pygeai/analytics/clients.py +505 -0
- pygeai/analytics/endpoints.py +35 -0
- pygeai/analytics/managers.py +606 -0
- pygeai/analytics/mappers.py +207 -0
- pygeai/analytics/responses.py +240 -0
- pygeai/chat/clients.py +46 -1
- pygeai/chat/endpoints.py +1 -0
- pygeai/cli/commands/analytics.py +525 -0
- pygeai/cli/commands/base.py +16 -0
- pygeai/cli/commands/chat.py +95 -0
- pygeai/cli/commands/common.py +28 -24
- pygeai/cli/commands/migrate.py +75 -6
- pygeai/cli/commands/organization.py +265 -0
- pygeai/cli/commands/validators.py +144 -1
- pygeai/cli/error_handler.py +41 -6
- pygeai/cli/geai.py +99 -16
- pygeai/cli/parsers.py +75 -31
- pygeai/cli/texts/help.py +75 -6
- pygeai/core/base/clients.py +18 -4
- pygeai/core/base/session.py +46 -7
- pygeai/core/common/config.py +25 -2
- pygeai/core/common/exceptions.py +64 -1
- pygeai/core/services/rest.py +20 -2
- pygeai/evaluation/clients.py +5 -3
- pygeai/lab/agents/clients.py +3 -3
- pygeai/lab/agents/endpoints.py +2 -2
- pygeai/lab/agents/mappers.py +50 -2
- pygeai/lab/clients.py +5 -2
- pygeai/lab/managers.py +7 -9
- pygeai/lab/models.py +70 -2
- pygeai/lab/tools/clients.py +1 -59
- pygeai/migration/__init__.py +3 -1
- pygeai/migration/strategies.py +72 -3
- pygeai/organization/clients.py +110 -1
- pygeai/organization/endpoints.py +11 -7
- pygeai/organization/managers.py +134 -2
- pygeai/organization/mappers.py +28 -2
- pygeai/organization/responses.py +11 -1
- pygeai/tests/analytics/__init__.py +0 -0
- pygeai/tests/analytics/test_clients.py +86 -0
- pygeai/tests/analytics/test_managers.py +94 -0
- pygeai/tests/analytics/test_mappers.py +84 -0
- pygeai/tests/analytics/test_responses.py +73 -0
- pygeai/tests/auth/test_oauth.py +172 -0
- pygeai/tests/cli/commands/test_migrate.py +14 -1
- pygeai/tests/cli/commands/test_organization.py +69 -1
- pygeai/tests/cli/test_error_handler.py +4 -4
- pygeai/tests/cli/test_geai_driver.py +1 -1
- pygeai/tests/lab/agents/test_mappers.py +128 -1
- pygeai/tests/lab/test_models.py +2 -0
- pygeai/tests/lab/tools/test_clients.py +2 -31
- pygeai/tests/organization/test_clients.py +180 -1
- pygeai/tests/organization/test_managers.py +40 -0
- pygeai/tests/snippets/analytics/__init__.py +0 -0
- pygeai/tests/snippets/analytics/get_agent_usage_per_user.py +16 -0
- pygeai/tests/snippets/analytics/get_agents_created_and_modified.py +11 -0
- pygeai/tests/snippets/analytics/get_average_cost_per_request.py +10 -0
- pygeai/tests/snippets/analytics/get_overall_error_rate.py +10 -0
- pygeai/tests/snippets/analytics/get_top_10_agents_by_requests.py +12 -0
- pygeai/tests/snippets/analytics/get_total_active_users.py +10 -0
- pygeai/tests/snippets/analytics/get_total_cost.py +10 -0
- pygeai/tests/snippets/analytics/get_total_requests_per_day.py +12 -0
- pygeai/tests/snippets/analytics/get_total_tokens.py +12 -0
- pygeai/tests/snippets/chat/get_response_complete_example.py +67 -0
- pygeai/tests/snippets/chat/get_response_with_instructions.py +19 -0
- pygeai/tests/snippets/chat/get_response_with_metadata.py +24 -0
- pygeai/tests/snippets/chat/get_response_with_parallel_tools.py +58 -0
- pygeai/tests/snippets/chat/get_response_with_reasoning.py +21 -0
- pygeai/tests/snippets/chat/get_response_with_store.py +38 -0
- pygeai/tests/snippets/chat/get_response_with_truncation.py +24 -0
- pygeai/tests/snippets/lab/agents/create_agent_with_permissions.py +39 -0
- pygeai/tests/snippets/lab/agents/create_agent_with_properties.py +46 -0
- pygeai/tests/snippets/lab/agents/get_agent_with_new_fields.py +62 -0
- pygeai/tests/snippets/lab/agents/update_agent_properties.py +50 -0
- pygeai/tests/snippets/organization/add_project_member.py +10 -0
- pygeai/tests/snippets/organization/add_project_member_batch.py +44 -0
- {pygeai-0.6.0b10.dist-info → pygeai-0.6.0b12.dist-info}/METADATA +1 -1
- {pygeai-0.6.0b10.dist-info → pygeai-0.6.0b12.dist-info}/RECORD +105 -95
- pygeai/_docs/source/pygeai.tests.snippets.assistants.data_analyst.rst +0 -37
- pygeai/_docs/source/pygeai.tests.snippets.assistants.rag.rst +0 -85
- pygeai/_docs/source/pygeai.tests.snippets.assistants.rst +0 -78
- pygeai/_docs/source/pygeai.tests.snippets.auth.rst +0 -10
- pygeai/_docs/source/pygeai.tests.snippets.chat.rst +0 -125
- pygeai/_docs/source/pygeai.tests.snippets.dbg.rst +0 -45
- pygeai/_docs/source/pygeai.tests.snippets.embeddings.rst +0 -61
- pygeai/_docs/source/pygeai.tests.snippets.evaluation.dataset.rst +0 -197
- pygeai/_docs/source/pygeai.tests.snippets.evaluation.plan.rst +0 -133
- pygeai/_docs/source/pygeai.tests.snippets.evaluation.result.rst +0 -37
- pygeai/_docs/source/pygeai.tests.snippets.evaluation.rst +0 -20
- pygeai/_docs/source/pygeai.tests.snippets.extras.rst +0 -37
- pygeai/_docs/source/pygeai.tests.snippets.files.rst +0 -53
- pygeai/_docs/source/pygeai.tests.snippets.gam.rst +0 -21
- pygeai/_docs/source/pygeai.tests.snippets.lab.agents.rst +0 -93
- pygeai/_docs/source/pygeai.tests.snippets.lab.processes.jobs.rst +0 -21
- pygeai/_docs/source/pygeai.tests.snippets.lab.processes.kbs.rst +0 -45
- pygeai/_docs/source/pygeai.tests.snippets.lab.processes.rst +0 -46
- pygeai/_docs/source/pygeai.tests.snippets.lab.rst +0 -82
- pygeai/_docs/source/pygeai.tests.snippets.lab.samples.rst +0 -21
- pygeai/_docs/source/pygeai.tests.snippets.lab.strategies.rst +0 -45
- pygeai/_docs/source/pygeai.tests.snippets.lab.tools.rst +0 -85
- pygeai/_docs/source/pygeai.tests.snippets.lab.use_cases.rst +0 -117
- pygeai/_docs/source/pygeai.tests.snippets.migrate.rst +0 -10
- pygeai/_docs/source/pygeai.tests.snippets.organization.rst +0 -109
- pygeai/_docs/source/pygeai.tests.snippets.rag.rst +0 -85
- pygeai/_docs/source/pygeai.tests.snippets.rerank.rst +0 -21
- pygeai/_docs/source/pygeai.tests.snippets.rst +0 -32
- pygeai/_docs/source/pygeai.tests.snippets.secrets.rst +0 -10
- pygeai/_docs/source/pygeai.tests.snippets.usage_limit.rst +0 -77
- {pygeai-0.6.0b10.dist-info → pygeai-0.6.0b12.dist-info}/WHEEL +0 -0
- {pygeai-0.6.0b10.dist-info → pygeai-0.6.0b12.dist-info}/entry_points.txt +0 -0
- {pygeai-0.6.0b10.dist-info → pygeai-0.6.0b12.dist-info}/licenses/LICENSE +0 -0
- {pygeai-0.6.0b10.dist-info → pygeai-0.6.0b12.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from unittest.mock import patch, MagicMock
|
|
3
|
+
from pygeai.analytics.clients import AnalyticsClient
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TestAnalyticsClient(unittest.TestCase):
|
|
7
|
+
|
|
8
|
+
def setUp(self):
|
|
9
|
+
self.client = AnalyticsClient()
|
|
10
|
+
|
|
11
|
+
@patch('pygeai.analytics.clients.AnalyticsClient.api_service')
|
|
12
|
+
def test_get_agents_created_and_modified(self, mock_api_service):
|
|
13
|
+
mock_response = MagicMock()
|
|
14
|
+
mock_response.status_code = 200
|
|
15
|
+
mock_response.json.return_value = {"createdAgents": 10, "modifiedAgents": 5}
|
|
16
|
+
mock_api_service.get.return_value = mock_response
|
|
17
|
+
|
|
18
|
+
result = self.client.get_agents_created_and_modified("2024-01-01", "2024-01-31")
|
|
19
|
+
|
|
20
|
+
self.assertEqual(result["createdAgents"], 10)
|
|
21
|
+
self.assertEqual(result["modifiedAgents"], 5)
|
|
22
|
+
mock_api_service.get.assert_called_once()
|
|
23
|
+
|
|
24
|
+
@patch('pygeai.analytics.clients.AnalyticsClient.api_service')
|
|
25
|
+
def test_get_total_requests_per_day(self, mock_api_service):
|
|
26
|
+
mock_response = MagicMock()
|
|
27
|
+
mock_response.status_code = 200
|
|
28
|
+
mock_response.json.return_value = {
|
|
29
|
+
"requestsPerDay": [
|
|
30
|
+
{"date": "2024-01-01", "totalRequests": 100, "totalRequestsWithError": 5}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
mock_api_service.get.return_value = mock_response
|
|
34
|
+
|
|
35
|
+
result = self.client.get_total_requests_per_day("2024-01-01", "2024-01-31")
|
|
36
|
+
|
|
37
|
+
self.assertIn("requestsPerDay", result)
|
|
38
|
+
mock_api_service.get.assert_called_once()
|
|
39
|
+
|
|
40
|
+
@patch('pygeai.analytics.clients.AnalyticsClient.api_service')
|
|
41
|
+
def test_get_total_requests_per_day_with_agent_name(self, mock_api_service):
|
|
42
|
+
mock_response = MagicMock()
|
|
43
|
+
mock_response.status_code = 200
|
|
44
|
+
mock_response.json.return_value = {
|
|
45
|
+
"requestsPerDay": [
|
|
46
|
+
{"date": "2024-01-01", "totalRequests": 50, "totalRequestsWithError": 2}
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
mock_api_service.get.return_value = mock_response
|
|
50
|
+
|
|
51
|
+
result = self.client.get_total_requests_per_day("2024-01-01", "2024-01-31", agent_name="TestAgent")
|
|
52
|
+
|
|
53
|
+
self.assertIn("requestsPerDay", result)
|
|
54
|
+
mock_api_service.get.assert_called_once()
|
|
55
|
+
|
|
56
|
+
@patch('pygeai.analytics.clients.AnalyticsClient.api_service')
|
|
57
|
+
def test_get_average_cost_per_request(self, mock_api_service):
|
|
58
|
+
mock_response = MagicMock()
|
|
59
|
+
mock_response.status_code = 200
|
|
60
|
+
mock_response.json.return_value = {"averageCost": 2.50}
|
|
61
|
+
mock_api_service.get.return_value = mock_response
|
|
62
|
+
|
|
63
|
+
result = self.client.get_average_cost_per_request("2024-01-01", "2024-01-31")
|
|
64
|
+
|
|
65
|
+
self.assertEqual(result["averageCost"], 2.50)
|
|
66
|
+
mock_api_service.get.assert_called_once()
|
|
67
|
+
|
|
68
|
+
@patch('pygeai.analytics.clients.AnalyticsClient.api_service')
|
|
69
|
+
def test_get_total_tokens(self, mock_api_service):
|
|
70
|
+
mock_response = MagicMock()
|
|
71
|
+
mock_response.status_code = 200
|
|
72
|
+
mock_response.json.return_value = {
|
|
73
|
+
"totalInputTokens": 5000,
|
|
74
|
+
"totalOutputTokens": 3000,
|
|
75
|
+
"totalTokens": 8000
|
|
76
|
+
}
|
|
77
|
+
mock_api_service.get.return_value = mock_response
|
|
78
|
+
|
|
79
|
+
result = self.client.get_total_tokens("2024-01-01", "2024-01-31")
|
|
80
|
+
|
|
81
|
+
self.assertEqual(result["totalTokens"], 8000)
|
|
82
|
+
mock_api_service.get.assert_called_once()
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
if __name__ == '__main__':
|
|
86
|
+
unittest.main()
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from unittest.mock import patch
|
|
3
|
+
from pygeai.core.common.exceptions import APIError
|
|
4
|
+
from pygeai.core.handlers import ErrorHandler
|
|
5
|
+
from pygeai.analytics.managers import AnalyticsManager
|
|
6
|
+
from pygeai.analytics.mappers import AnalyticsResponseMapper
|
|
7
|
+
from pygeai.analytics.responses import (
|
|
8
|
+
AgentsCreatedAndModifiedResponse, TotalRequestsPerDayResponse,
|
|
9
|
+
AverageCostPerRequestResponse, NumberOfTokensResponse
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TestAnalyticsManager(unittest.TestCase):
|
|
14
|
+
|
|
15
|
+
def setUp(self):
|
|
16
|
+
self.manager = AnalyticsManager()
|
|
17
|
+
self.error_response = {"errors": [{"id": 404, "description": "Not found"}]}
|
|
18
|
+
|
|
19
|
+
@patch("pygeai.analytics.clients.AnalyticsClient.get_agents_created_and_modified")
|
|
20
|
+
def test_get_agents_created_and_modified(self, mock_get_agents):
|
|
21
|
+
mock_response = AgentsCreatedAndModifiedResponse(createdAgents=10, modifiedAgents=5)
|
|
22
|
+
mock_get_agents.return_value = {"createdAgents": 10, "modifiedAgents": 5}
|
|
23
|
+
|
|
24
|
+
with patch.object(AnalyticsResponseMapper, 'map_to_agents_created_and_modified_response', return_value=mock_response):
|
|
25
|
+
response = self.manager.get_agents_created_and_modified("2024-01-01", "2024-01-31")
|
|
26
|
+
|
|
27
|
+
self.assertIsInstance(response, AgentsCreatedAndModifiedResponse)
|
|
28
|
+
self.assertEqual(response.createdAgents, 10)
|
|
29
|
+
self.assertEqual(response.modifiedAgents, 5)
|
|
30
|
+
mock_get_agents.assert_called_once_with(start_date="2024-01-01", end_date="2024-01-31")
|
|
31
|
+
|
|
32
|
+
@patch("pygeai.analytics.clients.AnalyticsClient.get_agents_created_and_modified")
|
|
33
|
+
def test_get_agents_created_and_modified_error(self, mock_get_agents):
|
|
34
|
+
mock_get_agents.return_value = self.error_response
|
|
35
|
+
|
|
36
|
+
with patch.object(ErrorHandler, 'has_errors', return_value=True):
|
|
37
|
+
with patch.object(ErrorHandler, 'extract_error', return_value="Not found"):
|
|
38
|
+
with self.assertRaises(APIError) as context:
|
|
39
|
+
self.manager.get_agents_created_and_modified("2024-01-01", "2024-01-31")
|
|
40
|
+
|
|
41
|
+
self.assertIn("Error received while retrieving agents created and modified", str(context.exception))
|
|
42
|
+
mock_get_agents.assert_called_once_with(start_date="2024-01-01", end_date="2024-01-31")
|
|
43
|
+
|
|
44
|
+
@patch("pygeai.analytics.clients.AnalyticsClient.get_total_requests_per_day")
|
|
45
|
+
def test_get_total_requests_per_day(self, mock_get_requests):
|
|
46
|
+
mock_response = TotalRequestsPerDayResponse(requestsPerDay=[])
|
|
47
|
+
mock_get_requests.return_value = {"requestsPerDay": []}
|
|
48
|
+
|
|
49
|
+
with patch.object(AnalyticsResponseMapper, 'map_to_total_requests_per_day_response', return_value=mock_response):
|
|
50
|
+
response = self.manager.get_total_requests_per_day("2024-01-01", "2024-01-31")
|
|
51
|
+
|
|
52
|
+
self.assertIsInstance(response, TotalRequestsPerDayResponse)
|
|
53
|
+
mock_get_requests.assert_called_once_with(start_date="2024-01-01", end_date="2024-01-31", agent_name=None)
|
|
54
|
+
|
|
55
|
+
@patch("pygeai.analytics.clients.AnalyticsClient.get_total_requests_per_day")
|
|
56
|
+
def test_get_total_requests_per_day_with_agent_name(self, mock_get_requests):
|
|
57
|
+
mock_response = TotalRequestsPerDayResponse(requestsPerDay=[])
|
|
58
|
+
mock_get_requests.return_value = {"requestsPerDay": []}
|
|
59
|
+
|
|
60
|
+
with patch.object(AnalyticsResponseMapper, 'map_to_total_requests_per_day_response', return_value=mock_response):
|
|
61
|
+
response = self.manager.get_total_requests_per_day("2024-01-01", "2024-01-31", agent_name="TestAgent")
|
|
62
|
+
|
|
63
|
+
self.assertIsInstance(response, TotalRequestsPerDayResponse)
|
|
64
|
+
mock_get_requests.assert_called_once_with(start_date="2024-01-01", end_date="2024-01-31", agent_name="TestAgent")
|
|
65
|
+
|
|
66
|
+
@patch("pygeai.analytics.clients.AnalyticsClient.get_average_cost_per_request")
|
|
67
|
+
def test_get_average_cost_per_request(self, mock_get_cost):
|
|
68
|
+
mock_response = AverageCostPerRequestResponse(averageCost=2.50)
|
|
69
|
+
mock_get_cost.return_value = {"averageCost": 2.50}
|
|
70
|
+
|
|
71
|
+
with patch.object(AnalyticsResponseMapper, 'map_to_average_cost_per_request_response', return_value=mock_response):
|
|
72
|
+
response = self.manager.get_average_cost_per_request("2024-01-01", "2024-01-31")
|
|
73
|
+
|
|
74
|
+
self.assertIsInstance(response, AverageCostPerRequestResponse)
|
|
75
|
+
self.assertEqual(response.averageCost, 2.50)
|
|
76
|
+
|
|
77
|
+
@patch("pygeai.analytics.clients.AnalyticsClient.get_total_tokens")
|
|
78
|
+
def test_get_total_tokens(self, mock_get_tokens):
|
|
79
|
+
mock_response = NumberOfTokensResponse(totalInputTokens=5000, totalOutputTokens=3000, totalTokens=8000)
|
|
80
|
+
mock_get_tokens.return_value = {
|
|
81
|
+
"totalInputTokens": 5000,
|
|
82
|
+
"totalOutputTokens": 3000,
|
|
83
|
+
"totalTokens": 8000
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
with patch.object(AnalyticsResponseMapper, 'map_to_total_tokens_response', return_value=mock_response):
|
|
87
|
+
response = self.manager.get_total_tokens("2024-01-01", "2024-01-31")
|
|
88
|
+
|
|
89
|
+
self.assertIsInstance(response, NumberOfTokensResponse)
|
|
90
|
+
self.assertEqual(response.totalTokens, 8000)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
if __name__ == '__main__':
|
|
94
|
+
unittest.main()
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from pygeai.analytics.mappers import AnalyticsResponseMapper
|
|
3
|
+
from pygeai.analytics.responses import (
|
|
4
|
+
AgentsCreatedAndModifiedResponse, AgentsCreatedAndModifiedPerDayResponse,
|
|
5
|
+
FlowsCreatedAndModifiedResponse, AverageCostPerRequestResponse,
|
|
6
|
+
TotalRequestsPerDayResponse, NumberOfTokensResponse
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TestAnalyticsResponseMapper(unittest.TestCase):
|
|
11
|
+
|
|
12
|
+
def test_map_to_agents_created_and_modified_response(self):
|
|
13
|
+
data = {
|
|
14
|
+
"createdAgents": 15,
|
|
15
|
+
"modifiedAgents": 8
|
|
16
|
+
}
|
|
17
|
+
response = AnalyticsResponseMapper.map_to_agents_created_and_modified_response(data)
|
|
18
|
+
self.assertIsInstance(response, AgentsCreatedAndModifiedResponse)
|
|
19
|
+
self.assertEqual(response.createdAgents, 15)
|
|
20
|
+
self.assertEqual(response.modifiedAgents, 8)
|
|
21
|
+
|
|
22
|
+
def test_map_to_agents_created_and_modified_per_day_response(self):
|
|
23
|
+
data = {
|
|
24
|
+
"agentsCreatedAndModifiedPerDay": [
|
|
25
|
+
{"date": "2024-01-01", "createdAgents": 5, "modifiedAgents": 2},
|
|
26
|
+
{"date": "2024-01-02", "createdAgents": 3, "modifiedAgents": 1}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
response = AnalyticsResponseMapper.map_to_agents_created_and_modified_per_day_response(data)
|
|
30
|
+
self.assertIsInstance(response, AgentsCreatedAndModifiedPerDayResponse)
|
|
31
|
+
self.assertEqual(len(response.agentsCreatedAndModifiedPerDay), 2)
|
|
32
|
+
self.assertEqual(response.agentsCreatedAndModifiedPerDay[0].date, "2024-01-01")
|
|
33
|
+
|
|
34
|
+
def test_map_to_flows_created_and_modified_response(self):
|
|
35
|
+
data = {
|
|
36
|
+
"createdFlows": 10,
|
|
37
|
+
"modifiedFlows": 5
|
|
38
|
+
}
|
|
39
|
+
response = AnalyticsResponseMapper.map_to_flows_created_and_modified_response(data)
|
|
40
|
+
self.assertIsInstance(response, FlowsCreatedAndModifiedResponse)
|
|
41
|
+
self.assertEqual(response.createdFlows, 10)
|
|
42
|
+
self.assertEqual(response.modifiedFlows, 5)
|
|
43
|
+
|
|
44
|
+
def test_map_to_average_cost_per_request_response(self):
|
|
45
|
+
data = {
|
|
46
|
+
"averageCost": 3.25
|
|
47
|
+
}
|
|
48
|
+
response = AnalyticsResponseMapper.map_to_average_cost_per_request_response(data)
|
|
49
|
+
self.assertIsInstance(response, AverageCostPerRequestResponse)
|
|
50
|
+
self.assertEqual(response.averageCost, 3.25)
|
|
51
|
+
|
|
52
|
+
def test_map_to_total_requests_per_day_response(self):
|
|
53
|
+
data = {
|
|
54
|
+
"requestsPerDay": [
|
|
55
|
+
{"date": "2024-01-01", "totalRequests": 150, "totalRequestsWithError": 10},
|
|
56
|
+
{"date": "2024-01-02", "totalRequests": 200, "totalRequestsWithError": 5}
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
response = AnalyticsResponseMapper.map_to_total_requests_per_day_response(data)
|
|
60
|
+
self.assertIsInstance(response, TotalRequestsPerDayResponse)
|
|
61
|
+
self.assertEqual(len(response.requestsPerDay), 2)
|
|
62
|
+
self.assertEqual(response.requestsPerDay[0].totalRequests, 150)
|
|
63
|
+
|
|
64
|
+
def test_map_to_number_of_tokens_response(self):
|
|
65
|
+
data = {
|
|
66
|
+
"totalInputTokens": 5000,
|
|
67
|
+
"totalOutputTokens": 3000,
|
|
68
|
+
"totalTokens": 8000
|
|
69
|
+
}
|
|
70
|
+
response = AnalyticsResponseMapper.map_to_number_of_tokens_response(data)
|
|
71
|
+
self.assertIsInstance(response, NumberOfTokensResponse)
|
|
72
|
+
self.assertEqual(response.totalInputTokens, 5000)
|
|
73
|
+
self.assertEqual(response.totalOutputTokens, 3000)
|
|
74
|
+
self.assertEqual(response.totalTokens, 8000)
|
|
75
|
+
|
|
76
|
+
def test_map_with_missing_fields(self):
|
|
77
|
+
data = {}
|
|
78
|
+
response = AnalyticsResponseMapper.map_to_agents_created_and_modified_response(data)
|
|
79
|
+
self.assertEqual(response.createdAgents, 0)
|
|
80
|
+
self.assertEqual(response.modifiedAgents, 0)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
if __name__ == '__main__':
|
|
84
|
+
unittest.main()
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from pygeai.analytics.responses import (
|
|
3
|
+
AgentsCreatedAndModifiedResponse, AgentActivityPerDayItem, AgentsCreatedAndModifiedPerDayResponse,
|
|
4
|
+
FlowsCreatedAndModifiedResponse, FlowActivityPerDayItem, FlowsCreatedAndModifiedPerDayResponse,
|
|
5
|
+
ProcessesCreatedAndModifiedResponse, AgentUsagePerUserItem, AgentUsagePerUserResponse,
|
|
6
|
+
AverageCostPerRequestResponse, AverageCostPerUserResponse, TotalRequestsPerDayResponse,
|
|
7
|
+
RequestsPerDayItem
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TestAnalyticsResponses(unittest.TestCase):
|
|
12
|
+
|
|
13
|
+
def test_agents_created_and_modified_response(self):
|
|
14
|
+
response = AgentsCreatedAndModifiedResponse(createdAgents=10, modifiedAgents=5)
|
|
15
|
+
self.assertEqual(response.createdAgents, 10)
|
|
16
|
+
self.assertEqual(response.modifiedAgents, 5)
|
|
17
|
+
|
|
18
|
+
def test_agents_created_and_modified_per_day_response(self):
|
|
19
|
+
items = [
|
|
20
|
+
AgentActivityPerDayItem(date="2024-01-01", createdAgents=5, modifiedAgents=2),
|
|
21
|
+
AgentActivityPerDayItem(date="2024-01-02", createdAgents=3, modifiedAgents=1)
|
|
22
|
+
]
|
|
23
|
+
response = AgentsCreatedAndModifiedPerDayResponse(agentsCreatedAndModifiedPerDay=items)
|
|
24
|
+
self.assertEqual(len(response.agentsCreatedAndModifiedPerDay), 2)
|
|
25
|
+
self.assertEqual(response.agentsCreatedAndModifiedPerDay[0].date, "2024-01-01")
|
|
26
|
+
|
|
27
|
+
def test_flows_created_and_modified_response(self):
|
|
28
|
+
response = FlowsCreatedAndModifiedResponse(createdFlows=8, modifiedFlows=3)
|
|
29
|
+
self.assertEqual(response.createdFlows, 8)
|
|
30
|
+
self.assertEqual(response.modifiedFlows, 3)
|
|
31
|
+
|
|
32
|
+
def test_flows_created_and_modified_per_day_response(self):
|
|
33
|
+
items = [
|
|
34
|
+
FlowActivityPerDayItem(date="2024-01-01", createdFlows=4, modifiedFlows=1)
|
|
35
|
+
]
|
|
36
|
+
response = FlowsCreatedAndModifiedPerDayResponse(flowsCreatedAndModifiedPerDay=items)
|
|
37
|
+
self.assertEqual(len(response.flowsCreatedAndModifiedPerDay), 1)
|
|
38
|
+
|
|
39
|
+
def test_processes_created_and_modified_response(self):
|
|
40
|
+
response = ProcessesCreatedAndModifiedResponse(createdProcesses=12, modifiedProcesses=6)
|
|
41
|
+
self.assertEqual(response.createdProcesses, 12)
|
|
42
|
+
self.assertEqual(response.modifiedProcesses, 6)
|
|
43
|
+
|
|
44
|
+
def test_agent_usage_per_user_response(self):
|
|
45
|
+
items = [
|
|
46
|
+
AgentUsagePerUserItem(userId="user1", userName="John Doe", totalCost=100.50, totalRequests=50, totalTokens=1000)
|
|
47
|
+
]
|
|
48
|
+
response = AgentUsagePerUserResponse(agentUsagePerUser=items)
|
|
49
|
+
self.assertEqual(len(response.agentUsagePerUser), 1)
|
|
50
|
+
self.assertEqual(response.agentUsagePerUser[0].userId, "user1")
|
|
51
|
+
self.assertEqual(response.agentUsagePerUser[0].totalCost, 100.50)
|
|
52
|
+
|
|
53
|
+
def test_average_cost_per_request_response(self):
|
|
54
|
+
response = AverageCostPerRequestResponse(averageCost=2.50)
|
|
55
|
+
self.assertEqual(response.averageCost, 2.50)
|
|
56
|
+
|
|
57
|
+
def test_average_cost_per_user_response(self):
|
|
58
|
+
response = AverageCostPerUserResponse(averageCost=150.75)
|
|
59
|
+
self.assertEqual(response.averageCost, 150.75)
|
|
60
|
+
|
|
61
|
+
def test_total_requests_per_day_response(self):
|
|
62
|
+
items = [
|
|
63
|
+
RequestsPerDayItem(date="2024-01-01", totalRequests=100, totalRequestsWithError=5),
|
|
64
|
+
RequestsPerDayItem(date="2024-01-02", totalRequests=120, totalRequestsWithError=3)
|
|
65
|
+
]
|
|
66
|
+
response = TotalRequestsPerDayResponse(requestsPerDay=items)
|
|
67
|
+
self.assertEqual(len(response.requestsPerDay), 2)
|
|
68
|
+
self.assertEqual(response.requestsPerDay[0].totalRequests, 100)
|
|
69
|
+
self.assertEqual(response.requestsPerDay[1].totalRequestsWithError, 3)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
if __name__ == '__main__':
|
|
73
|
+
unittest.main()
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from unittest.mock import patch, MagicMock
|
|
3
|
+
|
|
4
|
+
from pygeai.lab.clients import AILabClient
|
|
5
|
+
from pygeai.evaluation.clients import EvaluationClient
|
|
6
|
+
from pygeai.core.secrets.clients import SecretClient
|
|
7
|
+
from pygeai.core.base.clients import BaseClient
|
|
8
|
+
from pygeai.core.common.exceptions import MissingRequirementException
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TestOAuthAuthentication(unittest.TestCase):
|
|
12
|
+
"""
|
|
13
|
+
Tests for OAuth authentication support across all clients.
|
|
14
|
+
|
|
15
|
+
python -m unittest pygeai.tests.auth.test_oauth.TestOAuthAuthentication
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def test_base_client_oauth_initialization(self):
|
|
19
|
+
"""Test BaseClient accepts OAuth parameters"""
|
|
20
|
+
client = BaseClient(
|
|
21
|
+
base_url="https://api.test.com",
|
|
22
|
+
access_token="oauth_token_123",
|
|
23
|
+
project_id="project-456"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
self.assertEqual(client.api_service.token, "oauth_token_123")
|
|
27
|
+
self.assertEqual(client.api_service.project_id, "project-456")
|
|
28
|
+
|
|
29
|
+
def test_base_client_api_key_initialization(self):
|
|
30
|
+
"""Test BaseClient backward compatibility with API key"""
|
|
31
|
+
client = BaseClient(
|
|
32
|
+
api_key="api_key_123",
|
|
33
|
+
base_url="https://api.test.com"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
self.assertEqual(client.api_service.token, "api_key_123")
|
|
37
|
+
self.assertIsNone(client.api_service.project_id)
|
|
38
|
+
|
|
39
|
+
def test_base_client_incomplete_oauth_raises_error(self):
|
|
40
|
+
"""Test that providing only access_token without project_id raises error"""
|
|
41
|
+
with self.assertRaises(MissingRequirementException) as context:
|
|
42
|
+
BaseClient(
|
|
43
|
+
base_url="https://api.test.com",
|
|
44
|
+
access_token="oauth_token_123"
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
self.assertIn("project_id is required when using access_token", str(context.exception))
|
|
48
|
+
|
|
49
|
+
def test_oauth_headers_are_added_correctly(self):
|
|
50
|
+
"""Test that OAuth headers are added correctly by _add_token_to_headers"""
|
|
51
|
+
client = BaseClient(
|
|
52
|
+
base_url="https://api.test.com",
|
|
53
|
+
access_token="oauth_token_123",
|
|
54
|
+
project_id="project-456"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
headers = client.api_service._add_token_to_headers({})
|
|
58
|
+
|
|
59
|
+
self.assertEqual(headers.get('Authorization'), 'Bearer oauth_token_123')
|
|
60
|
+
self.assertEqual(headers.get('ProjectId'), 'project-456')
|
|
61
|
+
|
|
62
|
+
def test_api_key_headers_use_bearer(self):
|
|
63
|
+
"""Test that API key also uses Bearer format in Authorization header"""
|
|
64
|
+
client = BaseClient(
|
|
65
|
+
api_key="api_key_123",
|
|
66
|
+
base_url="https://api.test.com"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
headers = client.api_service._add_token_to_headers({})
|
|
70
|
+
|
|
71
|
+
self.assertEqual(headers.get('Authorization'), 'Bearer api_key_123')
|
|
72
|
+
self.assertNotIn('ProjectId', headers)
|
|
73
|
+
|
|
74
|
+
def test_oauth_headers_preserve_existing_headers(self):
|
|
75
|
+
"""Test that OAuth headers are added while preserving existing headers"""
|
|
76
|
+
client = BaseClient(
|
|
77
|
+
base_url="https://api.test.com",
|
|
78
|
+
access_token="oauth_token_123",
|
|
79
|
+
project_id="project-456"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
existing_headers = {'Content-Type': 'application/json', 'Custom-Header': 'value'}
|
|
83
|
+
headers = client.api_service._add_token_to_headers(existing_headers.copy())
|
|
84
|
+
|
|
85
|
+
self.assertEqual(headers.get('Authorization'), 'Bearer oauth_token_123')
|
|
86
|
+
self.assertEqual(headers.get('ProjectId'), 'project-456')
|
|
87
|
+
self.assertEqual(headers.get('Content-Type'), 'application/json')
|
|
88
|
+
self.assertEqual(headers.get('Custom-Header'), 'value')
|
|
89
|
+
|
|
90
|
+
def test_ailab_client_oauth_initialization(self):
|
|
91
|
+
"""Test AILabClient accepts OAuth parameters with project_id keyword-only"""
|
|
92
|
+
client = AILabClient(
|
|
93
|
+
base_url="https://api.test.com",
|
|
94
|
+
access_token="oauth_token_123",
|
|
95
|
+
project_id="project-456"
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
self.assertEqual(client.api_service.token, "oauth_token_123")
|
|
99
|
+
self.assertEqual(client.api_service.project_id, "project-456")
|
|
100
|
+
self.assertEqual(client.project_id, "project-456")
|
|
101
|
+
|
|
102
|
+
def test_ailab_client_api_key_with_project_id(self):
|
|
103
|
+
"""Test AILabClient with API key and project_id"""
|
|
104
|
+
client = AILabClient(
|
|
105
|
+
api_key="api_key_123",
|
|
106
|
+
base_url="https://api.test.com",
|
|
107
|
+
project_id="project-456"
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
self.assertEqual(client.api_service.token, "api_key_123")
|
|
111
|
+
self.assertEqual(client.project_id, "project-456")
|
|
112
|
+
|
|
113
|
+
def test_evaluation_client_oauth_initialization(self):
|
|
114
|
+
"""Test EvaluationClient accepts OAuth parameters"""
|
|
115
|
+
client = EvaluationClient(
|
|
116
|
+
base_url="https://api.test.com",
|
|
117
|
+
eval_url="https://eval.test.com",
|
|
118
|
+
access_token="oauth_token_123",
|
|
119
|
+
project_id="project-456"
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
self.assertEqual(client.api_service.token, "oauth_token_123")
|
|
123
|
+
self.assertEqual(client.api_service.project_id, "project-456")
|
|
124
|
+
|
|
125
|
+
def test_evaluation_client_api_key_backward_compatibility(self):
|
|
126
|
+
"""Test EvaluationClient backward compatibility with API key"""
|
|
127
|
+
client = EvaluationClient(
|
|
128
|
+
api_key="api_key_123",
|
|
129
|
+
base_url="https://api.test.com",
|
|
130
|
+
eval_url="https://eval.test.com"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
self.assertEqual(client.api_service.token, "api_key_123")
|
|
134
|
+
|
|
135
|
+
def test_secret_client_oauth_initialization(self):
|
|
136
|
+
"""Test SecretClient accepts OAuth parameters"""
|
|
137
|
+
client = SecretClient(
|
|
138
|
+
base_url="https://api.test.com",
|
|
139
|
+
access_token="oauth_token_123",
|
|
140
|
+
project_id="project-456"
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
self.assertEqual(client.api_service.token, "oauth_token_123")
|
|
144
|
+
self.assertEqual(client.api_service.project_id, "project-456")
|
|
145
|
+
|
|
146
|
+
def test_secret_client_api_key_backward_compatibility(self):
|
|
147
|
+
"""Test SecretClient backward compatibility with API key"""
|
|
148
|
+
client = SecretClient(
|
|
149
|
+
api_key="api_key_123",
|
|
150
|
+
base_url="https://api.test.com"
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
self.assertEqual(client.api_service.token, "api_key_123")
|
|
154
|
+
|
|
155
|
+
def test_oauth_token_used_over_api_key_in_headers(self):
|
|
156
|
+
"""Test that when OAuth token is used, it's properly formatted"""
|
|
157
|
+
client = BaseClient(
|
|
158
|
+
base_url="https://api.test.com",
|
|
159
|
+
access_token="oauth_token_wins",
|
|
160
|
+
project_id="project-wins"
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
self.assertEqual(client.api_service.token, "oauth_token_wins")
|
|
164
|
+
self.assertEqual(client.api_service.project_id, "project-wins")
|
|
165
|
+
|
|
166
|
+
headers = client.api_service._add_token_to_headers({})
|
|
167
|
+
self.assertEqual(headers['Authorization'], 'Bearer oauth_token_wins')
|
|
168
|
+
self.assertEqual(headers['ProjectId'], 'project-wins')
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
if __name__ == '__main__':
|
|
172
|
+
unittest.main()
|
|
@@ -19,9 +19,10 @@ class TestMigrateCommands(unittest.TestCase):
|
|
|
19
19
|
@patch('pygeai.cli.commands.migrate.MigrationOrchestrator')
|
|
20
20
|
@patch('pygeai.cli.commands.migrate.RAGAssistantClient')
|
|
21
21
|
@patch('pygeai.cli.commands.migrate.FileManager')
|
|
22
|
+
@patch('pygeai.cli.commands.migrate.SecretClient')
|
|
22
23
|
@patch('pygeai.cli.commands.migrate.AILabManager')
|
|
23
24
|
@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
|
+
def test_clone_project_with_all_flag(self, mock_base_client, mock_lab_mgr, mock_secret_client, mock_file_mgr, mock_rag_client, mock_orchestrator, mock_migration_tool, mock_stdout, mock_auth_client):
|
|
25
26
|
mock_lab_instance = Mock()
|
|
26
27
|
mock_lab_mgr.return_value = mock_lab_instance
|
|
27
28
|
|
|
@@ -48,6 +49,17 @@ class TestMigrateCommands(unittest.TestCase):
|
|
|
48
49
|
mock_file_mgr.return_value = mock_file_instance
|
|
49
50
|
mock_file_instance.get_file_list.return_value = Mock(files=[Mock(id="file1")])
|
|
50
51
|
|
|
52
|
+
mock_secret_instance = Mock()
|
|
53
|
+
mock_secret_client.return_value = mock_secret_instance
|
|
54
|
+
mock_secret_instance.list_secrets.return_value = {
|
|
55
|
+
"secrets": [
|
|
56
|
+
{
|
|
57
|
+
"id": "secret1",
|
|
58
|
+
"name": "Test Secret"
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
|
|
51
63
|
mock_migration_tool_instance = Mock()
|
|
52
64
|
mock_migration_tool.return_value = mock_migration_tool_instance
|
|
53
65
|
mock_migration_tool_instance.run_migration.return_value = "proj456"
|
|
@@ -83,6 +95,7 @@ class TestMigrateCommands(unittest.TestCase):
|
|
|
83
95
|
mock_lab_instance.list_tasks.assert_called_once()
|
|
84
96
|
mock_rag_instance.get_assistants_from_project.assert_called_once()
|
|
85
97
|
mock_file_instance.get_file_list.assert_called_once()
|
|
98
|
+
mock_secret_instance.list_secrets.assert_called_once()
|
|
86
99
|
mock_orch_instance.execute.assert_called_once()
|
|
87
100
|
|
|
88
101
|
@patch('pygeai.cli.commands.migrate.Console.write_stdout')
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import unittest
|
|
2
|
-
from unittest.mock import patch, Mock
|
|
2
|
+
from unittest.mock import patch, Mock, mock_open
|
|
3
3
|
from pygeai.cli.commands.organization import (
|
|
4
4
|
show_help,
|
|
5
5
|
list_assistants,
|
|
@@ -10,6 +10,8 @@ from pygeai.cli.commands.organization import (
|
|
|
10
10
|
delete_project,
|
|
11
11
|
get_project_tokens,
|
|
12
12
|
export_request_data,
|
|
13
|
+
add_project_member,
|
|
14
|
+
add_project_member_in_batch,
|
|
13
15
|
Option
|
|
14
16
|
)
|
|
15
17
|
from pygeai.core.common.exceptions import MissingRequirementException
|
|
@@ -206,3 +208,69 @@ class TestOrganizationCommands(unittest.TestCase):
|
|
|
206
208
|
mock_instance.export_request_data.assert_called_once_with("assistant1", "completed", "10", "20")
|
|
207
209
|
mock_write_stdout.assert_called_once_with("Request data: \n{'data': ['request1', 'request2']}")
|
|
208
210
|
|
|
211
|
+
@patch('pygeai.cli.commands.organization.Console.write_stdout')
|
|
212
|
+
@patch('pygeai.cli.commands.organization.OrganizationClient')
|
|
213
|
+
def test_add_project_member_success(self, mock_client, mock_write_stdout):
|
|
214
|
+
mock_instance = Mock()
|
|
215
|
+
mock_client.return_value = mock_instance
|
|
216
|
+
mock_instance.add_project_member.return_value = {"status": "invitation sent"}
|
|
217
|
+
option_list = [
|
|
218
|
+
self.mock_option("project_id", "proj123"),
|
|
219
|
+
self.mock_option("user_email", "user@example.com"),
|
|
220
|
+
self.mock_option("roles", "Project member,Project administrator")
|
|
221
|
+
]
|
|
222
|
+
|
|
223
|
+
add_project_member(option_list)
|
|
224
|
+
|
|
225
|
+
mock_instance.add_project_member.assert_called_once_with("proj123", "user@example.com", ["Project member", "Project administrator"])
|
|
226
|
+
mock_write_stdout.assert_called_once_with("User invitation sent: \n{'status': 'invitation sent'}")
|
|
227
|
+
|
|
228
|
+
def test_add_project_member_missing_fields(self):
|
|
229
|
+
option_list = [
|
|
230
|
+
self.mock_option("project_id", "proj123")
|
|
231
|
+
]
|
|
232
|
+
|
|
233
|
+
with self.assertRaises(MissingRequirementException) as context:
|
|
234
|
+
add_project_member(option_list)
|
|
235
|
+
|
|
236
|
+
self.assertEqual(str(context.exception), "Cannot add project member without project-id, user email, and roles")
|
|
237
|
+
|
|
238
|
+
@patch('pygeai.cli.commands.organization.Console.write_stdout')
|
|
239
|
+
@patch('pygeai.cli.commands.organization.OrganizationClient')
|
|
240
|
+
@patch('builtins.open', new_callable=mock_open, read_data='proj1,user1@example.com,Project member\nproj2,user2@example.com,Project member,Project administrator\n')
|
|
241
|
+
@patch('os.path.exists', return_value=True)
|
|
242
|
+
def test_add_project_member_batch_success(self, mock_exists, mock_file, mock_client, mock_write_stdout):
|
|
243
|
+
mock_instance = Mock()
|
|
244
|
+
mock_client.return_value = mock_instance
|
|
245
|
+
mock_instance.add_project_member.return_value = {"status": "invitation sent"}
|
|
246
|
+
option_list = [
|
|
247
|
+
self.mock_option("batch_file", "test.csv")
|
|
248
|
+
]
|
|
249
|
+
|
|
250
|
+
add_project_member(option_list)
|
|
251
|
+
|
|
252
|
+
self.assertEqual(mock_instance.add_project_member.call_count, 2)
|
|
253
|
+
mock_instance.add_project_member.assert_any_call("proj1", "user1@example.com", ["Project member"])
|
|
254
|
+
mock_instance.add_project_member.assert_any_call("proj2", "user2@example.com", ["Project member", "Project administrator"])
|
|
255
|
+
|
|
256
|
+
@patch('os.path.exists', return_value=False)
|
|
257
|
+
def test_add_project_member_batch_file_not_found(self, mock_exists):
|
|
258
|
+
mock_client = Mock()
|
|
259
|
+
|
|
260
|
+
with self.assertRaises(MissingRequirementException) as context:
|
|
261
|
+
add_project_member_in_batch(mock_client, "nonexistent.csv")
|
|
262
|
+
|
|
263
|
+
self.assertIn("Batch file not found", str(context.exception))
|
|
264
|
+
|
|
265
|
+
@patch('pygeai.cli.commands.organization.Console.write_stdout')
|
|
266
|
+
@patch('builtins.open', new_callable=mock_open, read_data='proj1,user1@example.com\n')
|
|
267
|
+
@patch('os.path.exists', return_value=True)
|
|
268
|
+
def test_add_project_member_batch_invalid_format(self, mock_exists, mock_file, mock_write_stdout):
|
|
269
|
+
mock_client = Mock()
|
|
270
|
+
mock_client.add_project_member.return_value = {"status": "invitation sent"}
|
|
271
|
+
|
|
272
|
+
add_project_member_in_batch(mock_client, "test.csv")
|
|
273
|
+
|
|
274
|
+
calls = [call[0][0] for call in mock_write_stdout.call_args_list]
|
|
275
|
+
self.assertTrue(any("0 successful, 1 failed" in call for call in calls))
|
|
276
|
+
self.assertTrue(any("Invalid format" in call for call in calls))
|
|
@@ -70,7 +70,7 @@ class TestErrorHandler(TestCase):
|
|
|
70
70
|
def test_format_error_basic(self):
|
|
71
71
|
"""Test basic error formatting"""
|
|
72
72
|
result = ErrorHandler.format_error("Test Error", "Something went wrong")
|
|
73
|
-
self.assertIn("ERROR: Something went wrong", result)
|
|
73
|
+
self.assertIn("ERROR [Test Error]: Something went wrong", result)
|
|
74
74
|
self.assertIn("Run 'geai help' for usage information.", result)
|
|
75
75
|
|
|
76
76
|
def test_format_error_with_suggestion(self):
|
|
@@ -80,7 +80,7 @@ class TestErrorHandler(TestCase):
|
|
|
80
80
|
"Command not found",
|
|
81
81
|
suggestion="Try using 'help' command"
|
|
82
82
|
)
|
|
83
|
-
self.assertIn("ERROR: Command not found", result)
|
|
83
|
+
self.assertIn("ERROR [Test Error]: Command not found", result)
|
|
84
84
|
self.assertIn("→ Try using 'help' command", result)
|
|
85
85
|
|
|
86
86
|
def test_format_error_without_help(self):
|
|
@@ -90,7 +90,7 @@ class TestErrorHandler(TestCase):
|
|
|
90
90
|
"Critical error",
|
|
91
91
|
show_help=False
|
|
92
92
|
)
|
|
93
|
-
self.assertIn("ERROR: Critical error", result)
|
|
93
|
+
self.assertIn("ERROR [Test Error]: Critical error", result)
|
|
94
94
|
self.assertNotIn("Run 'geai help'", result)
|
|
95
95
|
|
|
96
96
|
def test_find_similar_items_exact_match(self):
|
|
@@ -217,7 +217,7 @@ class TestErrorHandler(TestCase):
|
|
|
217
217
|
]
|
|
218
218
|
|
|
219
219
|
for result in results:
|
|
220
|
-
self.assertIn("ERROR
|
|
220
|
+
self.assertIn("ERROR [", result)
|
|
221
221
|
self.assertIn("→", result)
|
|
222
222
|
|
|
223
223
|
|