mito-ai 0.1.50__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.
- mito_ai/__init__.py +114 -0
- mito_ai/_version.py +4 -0
- mito_ai/anthropic_client.py +334 -0
- mito_ai/app_deploy/__init__.py +6 -0
- mito_ai/app_deploy/app_deploy_utils.py +44 -0
- mito_ai/app_deploy/handlers.py +345 -0
- mito_ai/app_deploy/models.py +98 -0
- mito_ai/app_manager/__init__.py +4 -0
- mito_ai/app_manager/handlers.py +167 -0
- mito_ai/app_manager/models.py +71 -0
- mito_ai/app_manager/utils.py +24 -0
- mito_ai/auth/README.md +18 -0
- mito_ai/auth/__init__.py +6 -0
- mito_ai/auth/handlers.py +96 -0
- mito_ai/auth/urls.py +13 -0
- mito_ai/chat_history/handlers.py +63 -0
- mito_ai/chat_history/urls.py +32 -0
- mito_ai/completions/completion_handlers/__init__.py +3 -0
- mito_ai/completions/completion_handlers/agent_auto_error_fixup_handler.py +59 -0
- mito_ai/completions/completion_handlers/agent_execution_handler.py +66 -0
- mito_ai/completions/completion_handlers/chat_completion_handler.py +141 -0
- mito_ai/completions/completion_handlers/code_explain_handler.py +113 -0
- mito_ai/completions/completion_handlers/completion_handler.py +42 -0
- mito_ai/completions/completion_handlers/inline_completer_handler.py +48 -0
- mito_ai/completions/completion_handlers/smart_debug_handler.py +160 -0
- mito_ai/completions/completion_handlers/utils.py +147 -0
- mito_ai/completions/handlers.py +415 -0
- mito_ai/completions/message_history.py +401 -0
- mito_ai/completions/models.py +404 -0
- mito_ai/completions/prompt_builders/__init__.py +3 -0
- mito_ai/completions/prompt_builders/agent_execution_prompt.py +57 -0
- mito_ai/completions/prompt_builders/agent_smart_debug_prompt.py +160 -0
- mito_ai/completions/prompt_builders/agent_system_message.py +472 -0
- mito_ai/completions/prompt_builders/chat_name_prompt.py +15 -0
- mito_ai/completions/prompt_builders/chat_prompt.py +116 -0
- mito_ai/completions/prompt_builders/chat_system_message.py +92 -0
- mito_ai/completions/prompt_builders/explain_code_prompt.py +32 -0
- mito_ai/completions/prompt_builders/inline_completer_prompt.py +197 -0
- mito_ai/completions/prompt_builders/prompt_constants.py +170 -0
- mito_ai/completions/prompt_builders/smart_debug_prompt.py +199 -0
- mito_ai/completions/prompt_builders/utils.py +84 -0
- mito_ai/completions/providers.py +284 -0
- mito_ai/constants.py +63 -0
- mito_ai/db/__init__.py +3 -0
- mito_ai/db/crawlers/__init__.py +6 -0
- mito_ai/db/crawlers/base_crawler.py +61 -0
- mito_ai/db/crawlers/constants.py +43 -0
- mito_ai/db/crawlers/snowflake.py +71 -0
- mito_ai/db/handlers.py +168 -0
- mito_ai/db/models.py +31 -0
- mito_ai/db/urls.py +34 -0
- mito_ai/db/utils.py +185 -0
- mito_ai/docker/mssql/compose.yml +37 -0
- mito_ai/docker/mssql/init/setup.sql +21 -0
- mito_ai/docker/mysql/compose.yml +18 -0
- mito_ai/docker/mysql/init/setup.sql +13 -0
- mito_ai/docker/oracle/compose.yml +17 -0
- mito_ai/docker/oracle/init/setup.sql +20 -0
- mito_ai/docker/postgres/compose.yml +17 -0
- mito_ai/docker/postgres/init/setup.sql +13 -0
- mito_ai/enterprise/__init__.py +3 -0
- mito_ai/enterprise/utils.py +15 -0
- mito_ai/file_uploads/__init__.py +3 -0
- mito_ai/file_uploads/handlers.py +248 -0
- mito_ai/file_uploads/urls.py +21 -0
- mito_ai/gemini_client.py +232 -0
- mito_ai/log/handlers.py +38 -0
- mito_ai/log/urls.py +21 -0
- mito_ai/logger.py +37 -0
- mito_ai/openai_client.py +382 -0
- mito_ai/path_utils.py +70 -0
- mito_ai/rules/handlers.py +44 -0
- mito_ai/rules/urls.py +22 -0
- mito_ai/rules/utils.py +56 -0
- mito_ai/settings/handlers.py +41 -0
- mito_ai/settings/urls.py +20 -0
- mito_ai/settings/utils.py +42 -0
- mito_ai/streamlit_conversion/agent_utils.py +37 -0
- mito_ai/streamlit_conversion/prompts/prompt_constants.py +172 -0
- mito_ai/streamlit_conversion/prompts/prompt_utils.py +10 -0
- mito_ai/streamlit_conversion/prompts/streamlit_app_creation_prompt.py +46 -0
- mito_ai/streamlit_conversion/prompts/streamlit_error_correction_prompt.py +28 -0
- mito_ai/streamlit_conversion/prompts/streamlit_finish_todo_prompt.py +45 -0
- mito_ai/streamlit_conversion/prompts/streamlit_system_prompt.py +56 -0
- mito_ai/streamlit_conversion/prompts/update_existing_app_prompt.py +50 -0
- mito_ai/streamlit_conversion/search_replace_utils.py +94 -0
- mito_ai/streamlit_conversion/streamlit_agent_handler.py +144 -0
- mito_ai/streamlit_conversion/streamlit_utils.py +85 -0
- mito_ai/streamlit_conversion/validate_streamlit_app.py +105 -0
- mito_ai/streamlit_preview/__init__.py +6 -0
- mito_ai/streamlit_preview/handlers.py +111 -0
- mito_ai/streamlit_preview/manager.py +152 -0
- mito_ai/streamlit_preview/urls.py +22 -0
- mito_ai/streamlit_preview/utils.py +29 -0
- mito_ai/tests/__init__.py +3 -0
- mito_ai/tests/chat_history/test_chat_history.py +211 -0
- mito_ai/tests/completions/completion_handlers_utils_test.py +190 -0
- mito_ai/tests/conftest.py +53 -0
- mito_ai/tests/create_agent_system_message_prompt_test.py +22 -0
- mito_ai/tests/data/prompt_lg.py +69 -0
- mito_ai/tests/data/prompt_sm.py +6 -0
- mito_ai/tests/data/prompt_xl.py +13 -0
- mito_ai/tests/data/stock_data.sqlite3 +0 -0
- mito_ai/tests/db/conftest.py +39 -0
- mito_ai/tests/db/connections_test.py +102 -0
- mito_ai/tests/db/mssql_test.py +29 -0
- mito_ai/tests/db/mysql_test.py +29 -0
- mito_ai/tests/db/oracle_test.py +29 -0
- mito_ai/tests/db/postgres_test.py +29 -0
- mito_ai/tests/db/schema_test.py +93 -0
- mito_ai/tests/db/sqlite_test.py +31 -0
- mito_ai/tests/db/test_db_constants.py +61 -0
- mito_ai/tests/deploy_app/test_app_deploy_utils.py +89 -0
- mito_ai/tests/file_uploads/__init__.py +2 -0
- mito_ai/tests/file_uploads/test_handlers.py +282 -0
- mito_ai/tests/message_history/test_generate_short_chat_name.py +120 -0
- mito_ai/tests/message_history/test_message_history_utils.py +469 -0
- mito_ai/tests/open_ai_utils_test.py +152 -0
- mito_ai/tests/performance_test.py +329 -0
- mito_ai/tests/providers/test_anthropic_client.py +447 -0
- mito_ai/tests/providers/test_azure.py +631 -0
- mito_ai/tests/providers/test_capabilities.py +120 -0
- mito_ai/tests/providers/test_gemini_client.py +195 -0
- mito_ai/tests/providers/test_mito_server_utils.py +448 -0
- mito_ai/tests/providers/test_model_resolution.py +130 -0
- mito_ai/tests/providers/test_openai_client.py +57 -0
- mito_ai/tests/providers/test_provider_completion_exception.py +66 -0
- mito_ai/tests/providers/test_provider_limits.py +42 -0
- mito_ai/tests/providers/test_providers.py +382 -0
- mito_ai/tests/providers/test_retry_logic.py +389 -0
- mito_ai/tests/providers/test_stream_mito_server_utils.py +140 -0
- mito_ai/tests/providers/utils.py +85 -0
- mito_ai/tests/rules/conftest.py +26 -0
- mito_ai/tests/rules/rules_test.py +117 -0
- mito_ai/tests/server_limits_test.py +406 -0
- mito_ai/tests/settings/conftest.py +26 -0
- mito_ai/tests/settings/settings_test.py +70 -0
- mito_ai/tests/settings/test_settings_constants.py +9 -0
- mito_ai/tests/streamlit_conversion/__init__.py +3 -0
- mito_ai/tests/streamlit_conversion/test_apply_search_replace.py +240 -0
- mito_ai/tests/streamlit_conversion/test_streamlit_agent_handler.py +246 -0
- mito_ai/tests/streamlit_conversion/test_streamlit_utils.py +193 -0
- mito_ai/tests/streamlit_conversion/test_validate_streamlit_app.py +112 -0
- mito_ai/tests/streamlit_preview/test_streamlit_preview_handler.py +118 -0
- mito_ai/tests/streamlit_preview/test_streamlit_preview_manager.py +292 -0
- mito_ai/tests/test_constants.py +47 -0
- mito_ai/tests/test_telemetry.py +12 -0
- mito_ai/tests/user/__init__.py +2 -0
- mito_ai/tests/user/test_user.py +120 -0
- mito_ai/tests/utils/__init__.py +3 -0
- mito_ai/tests/utils/test_anthropic_utils.py +162 -0
- mito_ai/tests/utils/test_gemini_utils.py +98 -0
- mito_ai/tests/version_check_test.py +169 -0
- mito_ai/user/handlers.py +45 -0
- mito_ai/user/urls.py +21 -0
- mito_ai/utils/__init__.py +3 -0
- mito_ai/utils/anthropic_utils.py +168 -0
- mito_ai/utils/create.py +94 -0
- mito_ai/utils/db.py +74 -0
- mito_ai/utils/error_classes.py +42 -0
- mito_ai/utils/gemini_utils.py +133 -0
- mito_ai/utils/message_history_utils.py +87 -0
- mito_ai/utils/mito_server_utils.py +242 -0
- mito_ai/utils/open_ai_utils.py +200 -0
- mito_ai/utils/provider_utils.py +49 -0
- mito_ai/utils/schema.py +86 -0
- mito_ai/utils/server_limits.py +152 -0
- mito_ai/utils/telemetry_utils.py +480 -0
- mito_ai/utils/utils.py +89 -0
- mito_ai/utils/version_utils.py +94 -0
- mito_ai/utils/websocket_base.py +88 -0
- mito_ai/version_check.py +60 -0
- mito_ai-0.1.50.data/data/etc/jupyter/jupyter_server_config.d/mito_ai.json +7 -0
- mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/build_log.json +728 -0
- mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/package.json +243 -0
- mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/package.json.orig +238 -0
- mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/toolbar-buttons.json +37 -0
- mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.8f1845da6bf2b128c049.js +21602 -0
- mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.8f1845da6bf2b128c049.js.map +1 -0
- mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/node_modules_process_browser_js.4b128e94d31a81ebd209.js +198 -0
- mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/node_modules_process_browser_js.4b128e94d31a81ebd209.js.map +1 -0
- mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.78d3ccb73e7ca1da3aae.js +619 -0
- mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.78d3ccb73e7ca1da3aae.js.map +1 -0
- mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/style.js +4 -0
- mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/style_index_js.5876024bb17dbd6a3ee6.js +712 -0
- mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/style_index_js.5876024bb17dbd6a3ee6.js.map +1 -0
- mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_auth_dist_esm_providers_cognito_apis_signOut_mjs-node_module-75790d.688c25857e7b81b1740f.js +533 -0
- mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_auth_dist_esm_providers_cognito_apis_signOut_mjs-node_module-75790d.688c25857e7b81b1740f.js.map +1 -0
- mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_auth_dist_esm_providers_cognito_tokenProvider_tokenProvider_-72f1c8.a917210f057fcfe224ad.js +6941 -0
- mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_auth_dist_esm_providers_cognito_tokenProvider_tokenProvider_-72f1c8.a917210f057fcfe224ad.js.map +1 -0
- mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_dist_esm_index_mjs.6bac1a8c4cc93f15f6b7.js +1021 -0
- mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_dist_esm_index_mjs.6bac1a8c4cc93f15f6b7.js.map +1 -0
- mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_ui-react_dist_esm_index_mjs.4fcecd65bef9e9847609.js +59698 -0
- mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_ui-react_dist_esm_index_mjs.4fcecd65bef9e9847609.js.map +1 -0
- mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_react-dom_client_js-node_modules_aws-amplify_ui-react_dist_styles_css.b43d4249e4d3dac9ad7b.js +7440 -0
- mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_react-dom_client_js-node_modules_aws-amplify_ui-react_dist_styles_css.b43d4249e4d3dac9ad7b.js.map +1 -0
- mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.3f6754ac5116d47de76b.js +2792 -0
- mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.3f6754ac5116d47de76b.js.map +1 -0
- mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_vscode-diff_dist_index_js.ea55f1f9346638aafbcf.js +4859 -0
- mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_vscode-diff_dist_index_js.ea55f1f9346638aafbcf.js.map +1 -0
- mito_ai-0.1.50.dist-info/METADATA +221 -0
- mito_ai-0.1.50.dist-info/RECORD +205 -0
- mito_ai-0.1.50.dist-info/WHEEL +4 -0
- mito_ai-0.1.50.dist-info/entry_points.txt +2 -0
- mito_ai-0.1.50.dist-info/licenses/LICENSE +3 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Copyright (c) Saga Inc.
|
|
2
|
+
# Distributed under the terms of the GNU Affero General Public License v3.0 License.
|
|
3
|
+
|
|
4
|
+
import requests
|
|
5
|
+
|
|
6
|
+
RULE_NAME = "test_rule"
|
|
7
|
+
RULE_CONTENT = "This is a test rule for data analysis."
|
|
8
|
+
|
|
9
|
+
from mito_ai.tests.conftest import TOKEN
|
|
10
|
+
|
|
11
|
+
# --- PUT RULES ---
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_put_rule_with_auth(jp_base_url):
|
|
15
|
+
response = requests.put(
|
|
16
|
+
jp_base_url + f"/mito-ai/rules/{RULE_NAME}",
|
|
17
|
+
headers={"Authorization": f"token {TOKEN}"},
|
|
18
|
+
json={"content": RULE_CONTENT},
|
|
19
|
+
)
|
|
20
|
+
assert response.status_code == 200
|
|
21
|
+
|
|
22
|
+
response_json = response.json()
|
|
23
|
+
assert response_json["status"] == "updated"
|
|
24
|
+
assert response_json["rules file "] == RULE_NAME
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_put_rule_with_no_auth(jp_base_url):
|
|
28
|
+
response = requests.put(
|
|
29
|
+
jp_base_url + f"/mito-ai/rules/{RULE_NAME}",
|
|
30
|
+
json={"content": RULE_CONTENT},
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
assert response.status_code == 403 # Forbidden
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_put_rule_with_incorrect_auth(jp_base_url):
|
|
37
|
+
response = requests.put(
|
|
38
|
+
jp_base_url + f"/mito-ai/rules/{RULE_NAME}",
|
|
39
|
+
headers={"Authorization": f"token incorrect-token"}, # <- wrong token
|
|
40
|
+
json={"content": RULE_CONTENT},
|
|
41
|
+
)
|
|
42
|
+
assert response.status_code == 403 # Forbidden
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_put_rule_missing_content(jp_base_url):
|
|
46
|
+
response = requests.put(
|
|
47
|
+
jp_base_url + f"/mito-ai/rules/{RULE_NAME}",
|
|
48
|
+
headers={"Authorization": f"token {TOKEN}"},
|
|
49
|
+
json={}, # Missing content field
|
|
50
|
+
)
|
|
51
|
+
assert response.status_code == 400 # Bad Request
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# --- GET SPECIFIC RULE ---
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_get_rule_with_auth(jp_base_url):
|
|
58
|
+
response = requests.get(
|
|
59
|
+
jp_base_url + f"/mito-ai/rules/{RULE_NAME}",
|
|
60
|
+
headers={"Authorization": f"token {TOKEN}"},
|
|
61
|
+
)
|
|
62
|
+
assert response.status_code == 200
|
|
63
|
+
assert response.json() == {"key": RULE_NAME, "content": RULE_CONTENT}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_get_rule_with_no_auth(jp_base_url):
|
|
67
|
+
response = requests.get(
|
|
68
|
+
jp_base_url + f"/mito-ai/rules/{RULE_NAME}",
|
|
69
|
+
)
|
|
70
|
+
assert response.status_code == 403 # Forbidden
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_get_rule_with_incorrect_auth(jp_base_url):
|
|
74
|
+
response = requests.get(
|
|
75
|
+
jp_base_url + f"/mito-ai/rules/{RULE_NAME}",
|
|
76
|
+
headers={"Authorization": f"token incorrect-token"}, # <- wrong token
|
|
77
|
+
)
|
|
78
|
+
assert response.status_code == 403 # Forbidden
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def test_get_nonexistent_rule_with_auth(jp_base_url):
|
|
82
|
+
response = requests.get(
|
|
83
|
+
jp_base_url + f"/mito-ai/rules/nonexistent_rule",
|
|
84
|
+
headers={"Authorization": f"token {TOKEN}"},
|
|
85
|
+
)
|
|
86
|
+
assert response.status_code == 404 # Not Found
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# --- GET ALL RULES ---
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_get_all_rules_with_auth(jp_base_url):
|
|
93
|
+
response = requests.get(
|
|
94
|
+
jp_base_url + f"/mito-ai/rules",
|
|
95
|
+
headers={"Authorization": f"token {TOKEN}"},
|
|
96
|
+
)
|
|
97
|
+
assert response.status_code == 200
|
|
98
|
+
|
|
99
|
+
response_json = response.json()
|
|
100
|
+
assert isinstance(response_json, list)
|
|
101
|
+
# Should contain our test rule (with .md extension)
|
|
102
|
+
assert f"{RULE_NAME}.md" in response_json
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def test_get_all_rules_with_no_auth(jp_base_url):
|
|
106
|
+
response = requests.get(
|
|
107
|
+
jp_base_url + f"/mito-ai/rules",
|
|
108
|
+
)
|
|
109
|
+
assert response.status_code == 403 # Forbidden
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def test_get_all_rules_with_incorrect_auth(jp_base_url):
|
|
113
|
+
response = requests.get(
|
|
114
|
+
jp_base_url + f"/mito-ai/rules",
|
|
115
|
+
headers={"Authorization": f"token incorrect-token"},
|
|
116
|
+
)
|
|
117
|
+
assert response.status_code == 403 # Forbidden
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
# Copyright (c) Saga Inc.
|
|
2
|
+
# Distributed under the terms of the GNU Affero General Public License v3.0 License.
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import Dict, Optional
|
|
7
|
+
import pytest
|
|
8
|
+
from unittest.mock import patch, MagicMock, call
|
|
9
|
+
|
|
10
|
+
from mito_ai.completions.models import MessageType
|
|
11
|
+
from mito_ai.utils.server_limits import (
|
|
12
|
+
OS_MONTHLY_AI_COMPLETIONS_LIMIT,
|
|
13
|
+
OS_MONTHLY_AUTOCOMPLETE_LIMIT,
|
|
14
|
+
check_mito_server_quota,
|
|
15
|
+
update_mito_server_quota,
|
|
16
|
+
UJ_AI_MITO_API_NUM_USAGES,
|
|
17
|
+
UJ_AI_MITO_AUTOCOMPLETE_NUM_USAGES,
|
|
18
|
+
UJ_MITO_AI_LAST_RESET_DATE,
|
|
19
|
+
UJ_MITO_AI_FIRST_USAGE_DATE
|
|
20
|
+
)
|
|
21
|
+
from mito_ai.utils.telemetry_utils import MITO_SERVER_FREE_TIER_LIMIT_REACHED
|
|
22
|
+
|
|
23
|
+
# Constants for testing
|
|
24
|
+
CURRENT_DATE = datetime.now().strftime("%Y-%m-%d")
|
|
25
|
+
OLD_DATE = "2020-01-01"
|
|
26
|
+
FUTURE_DATE = (datetime.now().replace(year=datetime.now().year + 1)).strftime("%Y-%m-%d")
|
|
27
|
+
|
|
28
|
+
# Each test case is a tuple of:
|
|
29
|
+
# (is_pro, chat_completion_count, autocomplete_count, last_reset_date, message_type, should_raise_error)
|
|
30
|
+
|
|
31
|
+
# 1. Open Source Subscription Tests
|
|
32
|
+
OS_SUBSCRIPTION_TESTS = [
|
|
33
|
+
# Below both limits
|
|
34
|
+
(False, OS_MONTHLY_AI_COMPLETIONS_LIMIT - 1, OS_MONTHLY_AUTOCOMPLETE_LIMIT - 1, CURRENT_DATE, MessageType.CHAT, False),
|
|
35
|
+
(False, OS_MONTHLY_AI_COMPLETIONS_LIMIT - 1, OS_MONTHLY_AUTOCOMPLETE_LIMIT - 1, CURRENT_DATE, MessageType.INLINE_COMPLETION, False),
|
|
36
|
+
|
|
37
|
+
# Above AI completions limit only
|
|
38
|
+
(False, OS_MONTHLY_AI_COMPLETIONS_LIMIT + 1, OS_MONTHLY_AUTOCOMPLETE_LIMIT - 1, CURRENT_DATE, MessageType.CHAT, True),
|
|
39
|
+
(False, OS_MONTHLY_AI_COMPLETIONS_LIMIT + 1, OS_MONTHLY_AUTOCOMPLETE_LIMIT - 1, CURRENT_DATE, MessageType.INLINE_COMPLETION, False),
|
|
40
|
+
|
|
41
|
+
# Above autocomplete limit only
|
|
42
|
+
(False, OS_MONTHLY_AI_COMPLETIONS_LIMIT - 1, OS_MONTHLY_AUTOCOMPLETE_LIMIT + 1, CURRENT_DATE, MessageType.CHAT, False),
|
|
43
|
+
(False, OS_MONTHLY_AI_COMPLETIONS_LIMIT - 1, OS_MONTHLY_AUTOCOMPLETE_LIMIT + 1, CURRENT_DATE, MessageType.INLINE_COMPLETION, True),
|
|
44
|
+
|
|
45
|
+
# Above both limits
|
|
46
|
+
(False, OS_MONTHLY_AI_COMPLETIONS_LIMIT + 1, OS_MONTHLY_AUTOCOMPLETE_LIMIT + 1, CURRENT_DATE, MessageType.CHAT, True),
|
|
47
|
+
(False, OS_MONTHLY_AI_COMPLETIONS_LIMIT + 1, OS_MONTHLY_AUTOCOMPLETE_LIMIT + 1, CURRENT_DATE, MessageType.INLINE_COMPLETION, True),
|
|
48
|
+
|
|
49
|
+
# Equal to limits
|
|
50
|
+
(False, OS_MONTHLY_AI_COMPLETIONS_LIMIT, OS_MONTHLY_AUTOCOMPLETE_LIMIT - 1, CURRENT_DATE, MessageType.CHAT, True),
|
|
51
|
+
(False, OS_MONTHLY_AI_COMPLETIONS_LIMIT, OS_MONTHLY_AUTOCOMPLETE_LIMIT - 1, CURRENT_DATE, MessageType.INLINE_COMPLETION, False),
|
|
52
|
+
(False, OS_MONTHLY_AI_COMPLETIONS_LIMIT - 1, OS_MONTHLY_AUTOCOMPLETE_LIMIT, CURRENT_DATE, MessageType.CHAT, False),
|
|
53
|
+
(False, OS_MONTHLY_AI_COMPLETIONS_LIMIT - 1, OS_MONTHLY_AUTOCOMPLETE_LIMIT, CURRENT_DATE, MessageType.INLINE_COMPLETION, True),
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
# 2. Pro Subscription Tests
|
|
57
|
+
PRO_SUBSCRIPTION_TESTS = [
|
|
58
|
+
# Pro user below limits
|
|
59
|
+
(True, OS_MONTHLY_AI_COMPLETIONS_LIMIT - 1, OS_MONTHLY_AUTOCOMPLETE_LIMIT - 1, CURRENT_DATE, MessageType.CHAT, False),
|
|
60
|
+
(True, OS_MONTHLY_AI_COMPLETIONS_LIMIT - 1, OS_MONTHLY_AUTOCOMPLETE_LIMIT - 1, CURRENT_DATE, MessageType.INLINE_COMPLETION, False),
|
|
61
|
+
|
|
62
|
+
# Pro user above limits - should still not raise error
|
|
63
|
+
(True, OS_MONTHLY_AI_COMPLETIONS_LIMIT + 100, OS_MONTHLY_AUTOCOMPLETE_LIMIT + 100, CURRENT_DATE, MessageType.CHAT, False),
|
|
64
|
+
(True, OS_MONTHLY_AI_COMPLETIONS_LIMIT + 100, OS_MONTHLY_AUTOCOMPLETE_LIMIT + 100, CURRENT_DATE, MessageType.INLINE_COMPLETION, False),
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
# 3. Error Handling Tests - Note: None values are replaced in the test function
|
|
68
|
+
ERROR_HANDLING_TESTS = [
|
|
69
|
+
# Missing last reset date - should not raise error and set the date
|
|
70
|
+
(False, OS_MONTHLY_AI_COMPLETIONS_LIMIT - 1, OS_MONTHLY_AUTOCOMPLETE_LIMIT - 1, None, MessageType.CHAT, False),
|
|
71
|
+
(False, OS_MONTHLY_AI_COMPLETIONS_LIMIT - 1, OS_MONTHLY_AUTOCOMPLETE_LIMIT - 1, None, MessageType.INLINE_COMPLETION, False),
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
# 4. Edge Cases
|
|
75
|
+
EDGE_CASE_TESTS = [
|
|
76
|
+
# Negative counts - should be treated as below limit
|
|
77
|
+
(False, -10, -10, CURRENT_DATE, MessageType.CHAT, False),
|
|
78
|
+
(False, -10, -10, CURRENT_DATE, MessageType.INLINE_COMPLETION, False),
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
# Combine all tests
|
|
82
|
+
ALL_TEST_CASES = OS_SUBSCRIPTION_TESTS + PRO_SUBSCRIPTION_TESTS + ERROR_HANDLING_TESTS + EDGE_CASE_TESTS
|
|
83
|
+
|
|
84
|
+
@pytest.mark.parametrize(
|
|
85
|
+
"is_pro, chat_completion_count, autocomplete_count, last_reset_date, message_type, should_raise_error",
|
|
86
|
+
ALL_TEST_CASES
|
|
87
|
+
)
|
|
88
|
+
def test_check_mito_server_quota(
|
|
89
|
+
is_pro: bool,
|
|
90
|
+
chat_completion_count: int,
|
|
91
|
+
autocomplete_count: int,
|
|
92
|
+
last_reset_date: Optional[str],
|
|
93
|
+
message_type: MessageType,
|
|
94
|
+
should_raise_error: bool
|
|
95
|
+
) -> None:
|
|
96
|
+
"""Test the check_mito_server_quota function with various combinations of inputs."""
|
|
97
|
+
|
|
98
|
+
# Create the patch context managers
|
|
99
|
+
patches = [
|
|
100
|
+
patch("mito_ai.utils.server_limits.is_pro", return_value=is_pro),
|
|
101
|
+
patch("mito_ai.utils.server_limits.get_chat_completion_count", return_value=chat_completion_count),
|
|
102
|
+
patch("mito_ai.utils.server_limits.get_autocomplete_count", return_value=autocomplete_count),
|
|
103
|
+
patch("mito_ai.utils.server_limits.get_last_reset_date", return_value=last_reset_date),
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
# Add set_user_field mock if needed for None values
|
|
107
|
+
if last_reset_date is None:
|
|
108
|
+
patches.append(patch("mito_ai.utils.server_limits.set_user_field"))
|
|
109
|
+
|
|
110
|
+
# Apply all patches
|
|
111
|
+
for p in patches:
|
|
112
|
+
p.start()
|
|
113
|
+
|
|
114
|
+
try:
|
|
115
|
+
if should_raise_error:
|
|
116
|
+
# Should raise PermissionError
|
|
117
|
+
with pytest.raises(PermissionError) as exc_info:
|
|
118
|
+
check_mito_server_quota(message_type)
|
|
119
|
+
assert str(exc_info.value) == MITO_SERVER_FREE_TIER_LIMIT_REACHED
|
|
120
|
+
else:
|
|
121
|
+
# Should not raise any error
|
|
122
|
+
check_mito_server_quota(message_type)
|
|
123
|
+
finally:
|
|
124
|
+
# Stop all patches
|
|
125
|
+
for p in patches:
|
|
126
|
+
p.stop()
|
|
127
|
+
|
|
128
|
+
# New dedicated test cases for date-related reset behavior
|
|
129
|
+
# Each test case is a tuple of:
|
|
130
|
+
# (reset_date, is_pro, message_type, should_reset)
|
|
131
|
+
DATE_RESET_TEST_CASES = [
|
|
132
|
+
# Old date should trigger reset for both message types
|
|
133
|
+
(OLD_DATE, False, MessageType.CHAT, True),
|
|
134
|
+
(OLD_DATE, False, MessageType.INLINE_COMPLETION, True),
|
|
135
|
+
|
|
136
|
+
# Pro user with old date should not trigger reset (they have no limits)
|
|
137
|
+
(OLD_DATE, True, MessageType.CHAT, False),
|
|
138
|
+
(OLD_DATE, True, MessageType.INLINE_COMPLETION, False),
|
|
139
|
+
|
|
140
|
+
# Current date should not trigger reset
|
|
141
|
+
(CURRENT_DATE, False, MessageType.CHAT, False),
|
|
142
|
+
(CURRENT_DATE, False, MessageType.INLINE_COMPLETION, False),
|
|
143
|
+
|
|
144
|
+
# Future date should not trigger reset
|
|
145
|
+
(FUTURE_DATE, False, MessageType.CHAT, False),
|
|
146
|
+
(FUTURE_DATE, False, MessageType.INLINE_COMPLETION, False),
|
|
147
|
+
]
|
|
148
|
+
|
|
149
|
+
@pytest.mark.parametrize(
|
|
150
|
+
"reset_date, is_pro, message_type, should_reset",
|
|
151
|
+
DATE_RESET_TEST_CASES
|
|
152
|
+
)
|
|
153
|
+
def test_date_triggers_reset(reset_date: Optional[str], is_pro: bool, message_type: MessageType, should_reset: bool) -> None:
|
|
154
|
+
"""
|
|
155
|
+
Test whether different dates trigger counter reset operations.
|
|
156
|
+
Rather than checking downstream behavior, directly verify the reset operation occurs.
|
|
157
|
+
"""
|
|
158
|
+
# Mock set_user_field to track calls
|
|
159
|
+
mock_set_user_field = MagicMock()
|
|
160
|
+
|
|
161
|
+
with (
|
|
162
|
+
patch("mito_ai.utils.server_limits.is_pro", return_value=is_pro),
|
|
163
|
+
patch("mito_ai.utils.server_limits.get_last_reset_date", return_value=reset_date),
|
|
164
|
+
patch("mito_ai.utils.server_limits.get_chat_completion_count", return_value=1),
|
|
165
|
+
patch("mito_ai.utils.server_limits.get_autocomplete_count", return_value=1),
|
|
166
|
+
patch("mito_ai.utils.server_limits.set_user_field", mock_set_user_field)
|
|
167
|
+
):
|
|
168
|
+
# Call the function
|
|
169
|
+
check_mito_server_quota(message_type)
|
|
170
|
+
|
|
171
|
+
# Verify whether reset was triggered based on expectations
|
|
172
|
+
reset_calls = [
|
|
173
|
+
call(UJ_AI_MITO_API_NUM_USAGES, 0),
|
|
174
|
+
call(UJ_AI_MITO_AUTOCOMPLETE_NUM_USAGES, 0)
|
|
175
|
+
]
|
|
176
|
+
|
|
177
|
+
if should_reset:
|
|
178
|
+
# Check that the API usages and autocomplete usages were reset to 0
|
|
179
|
+
# The format is slightly different because of how we're building the calls list
|
|
180
|
+
for reset_call in reset_calls:
|
|
181
|
+
assert reset_call in mock_set_user_field.call_args_list, f"Expected reset call {reset_call} not found"
|
|
182
|
+
|
|
183
|
+
# Also verify that the reset date was updated
|
|
184
|
+
assert any(call[0][0] == UJ_MITO_AI_LAST_RESET_DATE for call in mock_set_user_field.call_args_list), "Reset date was not updated"
|
|
185
|
+
else:
|
|
186
|
+
# For cases that shouldn't reset, verify the reset calls were NOT made
|
|
187
|
+
# If we're setting a missing date (None), we'll still see calls, but they won't be resets
|
|
188
|
+
if reset_date is not None:
|
|
189
|
+
for reset_call in reset_calls:
|
|
190
|
+
assert reset_call not in mock_set_user_field.call_args_list, f"Unexpected reset call {reset_call} found"
|
|
191
|
+
|
|
192
|
+
# Special test cases that require specific patching of constants
|
|
193
|
+
def test_zero_limits_configured() -> None:
|
|
194
|
+
"""Test when limits are configured as zero."""
|
|
195
|
+
with (
|
|
196
|
+
patch("mito_ai.utils.server_limits.OS_MONTHLY_AI_COMPLETIONS_LIMIT", 0),
|
|
197
|
+
patch("mito_ai.utils.server_limits.OS_MONTHLY_AUTOCOMPLETE_LIMIT", 0),
|
|
198
|
+
patch("mito_ai.utils.server_limits.is_pro", return_value=False),
|
|
199
|
+
patch("mito_ai.utils.server_limits.get_chat_completion_count", return_value=1),
|
|
200
|
+
patch("mito_ai.utils.server_limits.get_autocomplete_count", return_value=1),
|
|
201
|
+
patch("mito_ai.utils.server_limits.get_last_reset_date", return_value=CURRENT_DATE)
|
|
202
|
+
):
|
|
203
|
+
# Test with chat completion - should raise error
|
|
204
|
+
with pytest.raises(PermissionError) as exc_info:
|
|
205
|
+
check_mito_server_quota(MessageType.CHAT)
|
|
206
|
+
assert str(exc_info.value) == MITO_SERVER_FREE_TIER_LIMIT_REACHED
|
|
207
|
+
|
|
208
|
+
# Test with inline completion - should raise error
|
|
209
|
+
with pytest.raises(PermissionError) as exc_info:
|
|
210
|
+
check_mito_server_quota(MessageType.INLINE_COMPLETION)
|
|
211
|
+
assert str(exc_info.value) == MITO_SERVER_FREE_TIER_LIMIT_REACHED
|
|
212
|
+
|
|
213
|
+
# New test cases for update_mito_server_quota function
|
|
214
|
+
# Each case is (first_usage_date, last_reset_date, current_completion_count, current_autocomplete_count, message_type, expected_operations)
|
|
215
|
+
# The expected_operations is a dict specifying what operations should happen: reset_counters, update_first_date, increment_chat, increment_autocomplete
|
|
216
|
+
UPDATE_QUOTA_TEST_CASES = [
|
|
217
|
+
# Basic cases - incrementing counters
|
|
218
|
+
(
|
|
219
|
+
CURRENT_DATE, CURRENT_DATE, 10, 20, MessageType.CHAT,
|
|
220
|
+
{"reset_counters": False, "update_first_date": False, "increment_chat": True, "increment_autocomplete": False}
|
|
221
|
+
),
|
|
222
|
+
(
|
|
223
|
+
CURRENT_DATE, CURRENT_DATE, 10, 20, MessageType.INLINE_COMPLETION,
|
|
224
|
+
{"reset_counters": False, "update_first_date": False, "increment_chat": False, "increment_autocomplete": True}
|
|
225
|
+
),
|
|
226
|
+
|
|
227
|
+
# First usage initialization
|
|
228
|
+
(
|
|
229
|
+
None, CURRENT_DATE, 10, 20, MessageType.CHAT,
|
|
230
|
+
{"reset_counters": False, "update_first_date": True, "increment_chat": True, "increment_autocomplete": False}
|
|
231
|
+
),
|
|
232
|
+
|
|
233
|
+
# Reset date initialization
|
|
234
|
+
(
|
|
235
|
+
CURRENT_DATE, None, 10, 20, MessageType.CHAT,
|
|
236
|
+
{"reset_counters": False, "update_first_date": False, "increment_chat": True, "increment_autocomplete": False}
|
|
237
|
+
),
|
|
238
|
+
|
|
239
|
+
# Both dates missing
|
|
240
|
+
(
|
|
241
|
+
None, None, 10, 20, MessageType.CHAT,
|
|
242
|
+
{"reset_counters": False, "update_first_date": True, "increment_chat": True, "increment_autocomplete": False}
|
|
243
|
+
),
|
|
244
|
+
|
|
245
|
+
# Old reset date - should reset counters before updating
|
|
246
|
+
(
|
|
247
|
+
CURRENT_DATE, OLD_DATE, 10, 20, MessageType.CHAT,
|
|
248
|
+
{"reset_counters": True, "update_first_date": False, "increment_chat": True, "increment_autocomplete": False}
|
|
249
|
+
),
|
|
250
|
+
(
|
|
251
|
+
CURRENT_DATE, OLD_DATE, 10, 20, MessageType.INLINE_COMPLETION,
|
|
252
|
+
{"reset_counters": True, "update_first_date": False, "increment_chat": False, "increment_autocomplete": True}
|
|
253
|
+
),
|
|
254
|
+
|
|
255
|
+
# None counts - should be handled as 0
|
|
256
|
+
(
|
|
257
|
+
CURRENT_DATE, CURRENT_DATE, None, 20, MessageType.CHAT,
|
|
258
|
+
{"reset_counters": False, "update_first_date": False, "increment_chat": True, "increment_autocomplete": False}
|
|
259
|
+
),
|
|
260
|
+
(
|
|
261
|
+
CURRENT_DATE, CURRENT_DATE, 10, None, MessageType.INLINE_COMPLETION,
|
|
262
|
+
{"reset_counters": False, "update_first_date": False, "increment_chat": False, "increment_autocomplete": True}
|
|
263
|
+
),
|
|
264
|
+
]
|
|
265
|
+
|
|
266
|
+
@pytest.mark.parametrize(
|
|
267
|
+
"first_usage_date, last_reset_date, completion_count, autocomplete_count, message_type, expected_ops",
|
|
268
|
+
UPDATE_QUOTA_TEST_CASES
|
|
269
|
+
)
|
|
270
|
+
def test_update_mito_server_quota(
|
|
271
|
+
first_usage_date: Optional[str],
|
|
272
|
+
last_reset_date: Optional[str],
|
|
273
|
+
completion_count: int,
|
|
274
|
+
autocomplete_count: int,
|
|
275
|
+
message_type: MessageType,
|
|
276
|
+
expected_ops: Dict[str, bool]
|
|
277
|
+
) -> None:
|
|
278
|
+
"""Test the update_mito_server_quota function with various input combinations."""
|
|
279
|
+
# Mock set_user_field to track calls
|
|
280
|
+
mock_set_user_field = MagicMock()
|
|
281
|
+
|
|
282
|
+
with (
|
|
283
|
+
patch("mito_ai.utils.server_limits.get_first_completion_date", return_value=first_usage_date),
|
|
284
|
+
patch("mito_ai.utils.server_limits.get_last_reset_date", return_value=last_reset_date),
|
|
285
|
+
patch("mito_ai.utils.server_limits.get_chat_completion_count", return_value=completion_count),
|
|
286
|
+
patch("mito_ai.utils.server_limits.get_autocomplete_count", return_value=autocomplete_count),
|
|
287
|
+
patch("mito_ai.utils.server_limits.set_user_field", mock_set_user_field)
|
|
288
|
+
):
|
|
289
|
+
# Call the function
|
|
290
|
+
update_mito_server_quota(message_type)
|
|
291
|
+
|
|
292
|
+
# Check initialization of first usage date
|
|
293
|
+
if first_usage_date is None or expected_ops["update_first_date"]:
|
|
294
|
+
assert any(call[0][0] == UJ_MITO_AI_FIRST_USAGE_DATE for call in mock_set_user_field.call_args_list), \
|
|
295
|
+
"First usage date should have been initialized"
|
|
296
|
+
|
|
297
|
+
# Check initialization of reset date
|
|
298
|
+
if last_reset_date is None:
|
|
299
|
+
assert any(call[0][0] == UJ_MITO_AI_LAST_RESET_DATE for call in mock_set_user_field.call_args_list), \
|
|
300
|
+
"Reset date should have been initialized"
|
|
301
|
+
|
|
302
|
+
# Check counter reset operations
|
|
303
|
+
if expected_ops["reset_counters"]:
|
|
304
|
+
reset_calls = [
|
|
305
|
+
call(UJ_AI_MITO_API_NUM_USAGES, 0),
|
|
306
|
+
call(UJ_AI_MITO_AUTOCOMPLETE_NUM_USAGES, 0)
|
|
307
|
+
]
|
|
308
|
+
for reset_call in reset_calls:
|
|
309
|
+
assert reset_call in mock_set_user_field.call_args_list, \
|
|
310
|
+
f"Expected reset call {reset_call} not found"
|
|
311
|
+
|
|
312
|
+
# Reset date should also be updated
|
|
313
|
+
assert any(call[0][0] == UJ_MITO_AI_LAST_RESET_DATE for call in mock_set_user_field.call_args_list), \
|
|
314
|
+
"Reset date should have been updated"
|
|
315
|
+
|
|
316
|
+
# Check counter increment operations
|
|
317
|
+
if expected_ops["increment_chat"]:
|
|
318
|
+
expected_count = 1 if completion_count is None else completion_count + 1
|
|
319
|
+
chat_call = call(UJ_AI_MITO_API_NUM_USAGES, expected_count)
|
|
320
|
+
assert chat_call in mock_set_user_field.call_args_list, \
|
|
321
|
+
f"Expected chat completion increment call {chat_call} not found"
|
|
322
|
+
|
|
323
|
+
if expected_ops["increment_autocomplete"]:
|
|
324
|
+
expected_count = 1 if autocomplete_count is None else autocomplete_count + 1
|
|
325
|
+
autocomplete_call = call(UJ_AI_MITO_AUTOCOMPLETE_NUM_USAGES, expected_count)
|
|
326
|
+
assert autocomplete_call in mock_set_user_field.call_args_list, \
|
|
327
|
+
f"Expected autocomplete increment call {autocomplete_call} not found"
|
|
328
|
+
|
|
329
|
+
# Special edge cases for update_mito_server_quota
|
|
330
|
+
def test_update_quota_exception_handling() -> None:
|
|
331
|
+
"""Test that exceptions from set_user_field are properly propagated."""
|
|
332
|
+
|
|
333
|
+
with (
|
|
334
|
+
patch("mito_ai.utils.server_limits.get_first_completion_date", return_value=CURRENT_DATE),
|
|
335
|
+
patch("mito_ai.utils.server_limits.get_last_reset_date", return_value=CURRENT_DATE),
|
|
336
|
+
patch("mito_ai.utils.server_limits.get_chat_completion_count", return_value=10),
|
|
337
|
+
patch("mito_ai.utils.server_limits.get_autocomplete_count", return_value=20),
|
|
338
|
+
patch("mito_ai.utils.server_limits.set_user_field", side_effect=ValueError("Test error"))
|
|
339
|
+
):
|
|
340
|
+
# Test with chat completion
|
|
341
|
+
with pytest.raises(ValueError) as exc_info:
|
|
342
|
+
update_mito_server_quota(MessageType.CHAT)
|
|
343
|
+
assert str(exc_info.value) == "Test error"
|
|
344
|
+
|
|
345
|
+
# Test with inline completion
|
|
346
|
+
with pytest.raises(ValueError) as exc_info:
|
|
347
|
+
update_mito_server_quota(MessageType.INLINE_COMPLETION)
|
|
348
|
+
assert str(exc_info.value) == "Test error"
|
|
349
|
+
|
|
350
|
+
def test_update_quota_future_reset_date() -> None:
|
|
351
|
+
"""Test behavior when reset date is in the future."""
|
|
352
|
+
|
|
353
|
+
mock_set_user_field = MagicMock()
|
|
354
|
+
|
|
355
|
+
with (
|
|
356
|
+
patch("mito_ai.utils.server_limits.get_first_completion_date", return_value=CURRENT_DATE),
|
|
357
|
+
patch("mito_ai.utils.server_limits.get_last_reset_date", return_value=FUTURE_DATE),
|
|
358
|
+
patch("mito_ai.utils.server_limits.get_chat_completion_count", return_value=10),
|
|
359
|
+
patch("mito_ai.utils.server_limits.set_user_field", mock_set_user_field)
|
|
360
|
+
):
|
|
361
|
+
# Call the function
|
|
362
|
+
update_mito_server_quota(MessageType.CHAT)
|
|
363
|
+
|
|
364
|
+
# Check that counters weren't reset
|
|
365
|
+
reset_calls = [
|
|
366
|
+
call(UJ_AI_MITO_API_NUM_USAGES, 0),
|
|
367
|
+
call(UJ_AI_MITO_AUTOCOMPLETE_NUM_USAGES, 0)
|
|
368
|
+
]
|
|
369
|
+
for reset_call in reset_calls:
|
|
370
|
+
assert reset_call not in mock_set_user_field.call_args_list, \
|
|
371
|
+
"Counters should not be reset when reset date is in the future"
|
|
372
|
+
|
|
373
|
+
# Chat count should still be incremented
|
|
374
|
+
chat_call = call(UJ_AI_MITO_API_NUM_USAGES, 11)
|
|
375
|
+
assert chat_call in mock_set_user_field.call_args_list, \
|
|
376
|
+
"Chat completion count should still be incremented"
|
|
377
|
+
|
|
378
|
+
def test_update_quota_chat_name_generation() -> None:
|
|
379
|
+
"""
|
|
380
|
+
Test that when message_type is MessageType.CHAT_NAME_GENERATION,
|
|
381
|
+
neither UJ_AI_MITO_API_NUM_USAGES nor UJ_AI_MITO_AUTOCOMPLETE_NUM_USAGES is incremented.
|
|
382
|
+
"""
|
|
383
|
+
|
|
384
|
+
# Mock set_user_field to track calls
|
|
385
|
+
mock_set_user_field = MagicMock()
|
|
386
|
+
|
|
387
|
+
with (
|
|
388
|
+
patch("mito_ai.utils.server_limits.get_first_completion_date", return_value=CURRENT_DATE),
|
|
389
|
+
patch("mito_ai.utils.server_limits.get_last_reset_date", return_value=CURRENT_DATE),
|
|
390
|
+
patch("mito_ai.utils.server_limits.get_chat_completion_count", return_value=10),
|
|
391
|
+
patch("mito_ai.utils.server_limits.get_autocomplete_count", return_value=20),
|
|
392
|
+
patch("mito_ai.utils.server_limits.set_user_field", mock_set_user_field)
|
|
393
|
+
):
|
|
394
|
+
# Call the function with CHAT_NAME_GENERATION message type
|
|
395
|
+
update_mito_server_quota(MessageType.CHAT_NAME_GENERATION)
|
|
396
|
+
|
|
397
|
+
# Verify that set_user_field was not called at all - no counters should be updated
|
|
398
|
+
mock_set_user_field.assert_not_called()
|
|
399
|
+
|
|
400
|
+
# For comparison, call with a normal message type
|
|
401
|
+
update_mito_server_quota(MessageType.CHAT)
|
|
402
|
+
|
|
403
|
+
# Now verify set_user_field was called to increment the chat counter
|
|
404
|
+
chat_call = call(UJ_AI_MITO_API_NUM_USAGES, 11)
|
|
405
|
+
assert chat_call in mock_set_user_field.call_args_list, \
|
|
406
|
+
"Chat completion count should be incremented for regular chat messages"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Copyright (c) Saga Inc.
|
|
2
|
+
# Distributed under the terms of the GNU Affero General Public License v3.0 License.
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
import pytest
|
|
6
|
+
import shutil
|
|
7
|
+
from mito_ai.tests.settings.test_settings_constants import (
|
|
8
|
+
SETTINGS_PATH,
|
|
9
|
+
BACKUP_SETTINGS_PATH,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@pytest.fixture(scope="module", autouse=True)
|
|
14
|
+
def backup_settings_json():
|
|
15
|
+
"""Backup the settings.json file before tests and restore it after."""
|
|
16
|
+
if os.path.exists(SETTINGS_PATH):
|
|
17
|
+
# Create backup, so we can test with a clean settings.json file
|
|
18
|
+
shutil.move(SETTINGS_PATH, BACKUP_SETTINGS_PATH)
|
|
19
|
+
|
|
20
|
+
yield
|
|
21
|
+
|
|
22
|
+
# Cleanup after tests
|
|
23
|
+
if os.path.exists(SETTINGS_PATH):
|
|
24
|
+
os.remove(SETTINGS_PATH)
|
|
25
|
+
if os.path.exists(BACKUP_SETTINGS_PATH):
|
|
26
|
+
shutil.move(BACKUP_SETTINGS_PATH, SETTINGS_PATH)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Copyright (c) Saga Inc.
|
|
2
|
+
# Distributed under the terms of the GNU Affero General Public License v3.0 License.
|
|
3
|
+
|
|
4
|
+
import requests
|
|
5
|
+
|
|
6
|
+
SETTINGS_KEY = "super_secret_mode"
|
|
7
|
+
SETTINGS_VALUE = "yes"
|
|
8
|
+
|
|
9
|
+
from mito_ai.tests.conftest import TOKEN
|
|
10
|
+
|
|
11
|
+
# --- PUT SETTINGS ---
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_put_settings_with_auth(jp_base_url: str) -> None:
|
|
15
|
+
response = requests.put(
|
|
16
|
+
jp_base_url + f"/mito-ai/settings/{SETTINGS_KEY}",
|
|
17
|
+
headers={"Authorization": f"token {TOKEN}"},
|
|
18
|
+
json={"value": SETTINGS_VALUE},
|
|
19
|
+
)
|
|
20
|
+
assert response.status_code == 200
|
|
21
|
+
|
|
22
|
+
response_json = response.json()
|
|
23
|
+
assert response_json["status"] == "updated"
|
|
24
|
+
assert response_json["key"] == SETTINGS_KEY
|
|
25
|
+
assert response_json["value"] == SETTINGS_VALUE
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_put_settings_with_no_auth(jp_base_url: str) -> None:
|
|
29
|
+
response = requests.put(
|
|
30
|
+
jp_base_url + f"/mito-ai/settings/{SETTINGS_KEY}",
|
|
31
|
+
json={"value": SETTINGS_VALUE},
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
assert response.status_code == 403 # Forbidden
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_put_settings_with_incorrect_auth(jp_base_url: str) -> None:
|
|
38
|
+
response = requests.put(
|
|
39
|
+
jp_base_url + f"/mito-ai/settings/{SETTINGS_KEY}",
|
|
40
|
+
headers={"Authorization": f"token incorrect-token"},
|
|
41
|
+
json={"value": SETTINGS_VALUE},
|
|
42
|
+
)
|
|
43
|
+
assert response.status_code == 403 # Forbidden
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# # --- GET SETTINGS ---
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_get_settings_with_auth(jp_base_url: str) -> None:
|
|
50
|
+
response = requests.get(
|
|
51
|
+
jp_base_url + f"/mito-ai/settings/{SETTINGS_KEY}",
|
|
52
|
+
headers={"Authorization": f"token {TOKEN}"},
|
|
53
|
+
)
|
|
54
|
+
assert response.status_code == 200
|
|
55
|
+
assert response.json() == {"key": SETTINGS_KEY, "value": SETTINGS_VALUE}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_get_settings_with_no_auth(jp_base_url: str) -> None:
|
|
59
|
+
response = requests.get(
|
|
60
|
+
jp_base_url + f"/mito-ai/settings/{SETTINGS_KEY}",
|
|
61
|
+
)
|
|
62
|
+
assert response.status_code == 403 # Forbidden
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_get_settings_with_incorrect_auth(jp_base_url: str) -> None:
|
|
66
|
+
response = requests.get(
|
|
67
|
+
jp_base_url + f"/mito-ai/settings/{SETTINGS_KEY}",
|
|
68
|
+
headers={"Authorization": f"token incorrect-token"},
|
|
69
|
+
)
|
|
70
|
+
assert response.status_code == 403 # Forbidden
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Copyright (c) Saga Inc.
|
|
2
|
+
# Distributed under the terms of the GNU Affero General Public License v3.0 License.
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
from typing import Final
|
|
6
|
+
from mito_ai.utils.schema import MITO_FOLDER
|
|
7
|
+
|
|
8
|
+
SETTINGS_PATH: Final[str] = os.path.join(MITO_FOLDER, "settings.json")
|
|
9
|
+
BACKUP_SETTINGS_PATH: Final[str] = os.path.join(MITO_FOLDER, "settings_backup.json")
|