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.
Files changed (205) hide show
  1. mito_ai/__init__.py +114 -0
  2. mito_ai/_version.py +4 -0
  3. mito_ai/anthropic_client.py +334 -0
  4. mito_ai/app_deploy/__init__.py +6 -0
  5. mito_ai/app_deploy/app_deploy_utils.py +44 -0
  6. mito_ai/app_deploy/handlers.py +345 -0
  7. mito_ai/app_deploy/models.py +98 -0
  8. mito_ai/app_manager/__init__.py +4 -0
  9. mito_ai/app_manager/handlers.py +167 -0
  10. mito_ai/app_manager/models.py +71 -0
  11. mito_ai/app_manager/utils.py +24 -0
  12. mito_ai/auth/README.md +18 -0
  13. mito_ai/auth/__init__.py +6 -0
  14. mito_ai/auth/handlers.py +96 -0
  15. mito_ai/auth/urls.py +13 -0
  16. mito_ai/chat_history/handlers.py +63 -0
  17. mito_ai/chat_history/urls.py +32 -0
  18. mito_ai/completions/completion_handlers/__init__.py +3 -0
  19. mito_ai/completions/completion_handlers/agent_auto_error_fixup_handler.py +59 -0
  20. mito_ai/completions/completion_handlers/agent_execution_handler.py +66 -0
  21. mito_ai/completions/completion_handlers/chat_completion_handler.py +141 -0
  22. mito_ai/completions/completion_handlers/code_explain_handler.py +113 -0
  23. mito_ai/completions/completion_handlers/completion_handler.py +42 -0
  24. mito_ai/completions/completion_handlers/inline_completer_handler.py +48 -0
  25. mito_ai/completions/completion_handlers/smart_debug_handler.py +160 -0
  26. mito_ai/completions/completion_handlers/utils.py +147 -0
  27. mito_ai/completions/handlers.py +415 -0
  28. mito_ai/completions/message_history.py +401 -0
  29. mito_ai/completions/models.py +404 -0
  30. mito_ai/completions/prompt_builders/__init__.py +3 -0
  31. mito_ai/completions/prompt_builders/agent_execution_prompt.py +57 -0
  32. mito_ai/completions/prompt_builders/agent_smart_debug_prompt.py +160 -0
  33. mito_ai/completions/prompt_builders/agent_system_message.py +472 -0
  34. mito_ai/completions/prompt_builders/chat_name_prompt.py +15 -0
  35. mito_ai/completions/prompt_builders/chat_prompt.py +116 -0
  36. mito_ai/completions/prompt_builders/chat_system_message.py +92 -0
  37. mito_ai/completions/prompt_builders/explain_code_prompt.py +32 -0
  38. mito_ai/completions/prompt_builders/inline_completer_prompt.py +197 -0
  39. mito_ai/completions/prompt_builders/prompt_constants.py +170 -0
  40. mito_ai/completions/prompt_builders/smart_debug_prompt.py +199 -0
  41. mito_ai/completions/prompt_builders/utils.py +84 -0
  42. mito_ai/completions/providers.py +284 -0
  43. mito_ai/constants.py +63 -0
  44. mito_ai/db/__init__.py +3 -0
  45. mito_ai/db/crawlers/__init__.py +6 -0
  46. mito_ai/db/crawlers/base_crawler.py +61 -0
  47. mito_ai/db/crawlers/constants.py +43 -0
  48. mito_ai/db/crawlers/snowflake.py +71 -0
  49. mito_ai/db/handlers.py +168 -0
  50. mito_ai/db/models.py +31 -0
  51. mito_ai/db/urls.py +34 -0
  52. mito_ai/db/utils.py +185 -0
  53. mito_ai/docker/mssql/compose.yml +37 -0
  54. mito_ai/docker/mssql/init/setup.sql +21 -0
  55. mito_ai/docker/mysql/compose.yml +18 -0
  56. mito_ai/docker/mysql/init/setup.sql +13 -0
  57. mito_ai/docker/oracle/compose.yml +17 -0
  58. mito_ai/docker/oracle/init/setup.sql +20 -0
  59. mito_ai/docker/postgres/compose.yml +17 -0
  60. mito_ai/docker/postgres/init/setup.sql +13 -0
  61. mito_ai/enterprise/__init__.py +3 -0
  62. mito_ai/enterprise/utils.py +15 -0
  63. mito_ai/file_uploads/__init__.py +3 -0
  64. mito_ai/file_uploads/handlers.py +248 -0
  65. mito_ai/file_uploads/urls.py +21 -0
  66. mito_ai/gemini_client.py +232 -0
  67. mito_ai/log/handlers.py +38 -0
  68. mito_ai/log/urls.py +21 -0
  69. mito_ai/logger.py +37 -0
  70. mito_ai/openai_client.py +382 -0
  71. mito_ai/path_utils.py +70 -0
  72. mito_ai/rules/handlers.py +44 -0
  73. mito_ai/rules/urls.py +22 -0
  74. mito_ai/rules/utils.py +56 -0
  75. mito_ai/settings/handlers.py +41 -0
  76. mito_ai/settings/urls.py +20 -0
  77. mito_ai/settings/utils.py +42 -0
  78. mito_ai/streamlit_conversion/agent_utils.py +37 -0
  79. mito_ai/streamlit_conversion/prompts/prompt_constants.py +172 -0
  80. mito_ai/streamlit_conversion/prompts/prompt_utils.py +10 -0
  81. mito_ai/streamlit_conversion/prompts/streamlit_app_creation_prompt.py +46 -0
  82. mito_ai/streamlit_conversion/prompts/streamlit_error_correction_prompt.py +28 -0
  83. mito_ai/streamlit_conversion/prompts/streamlit_finish_todo_prompt.py +45 -0
  84. mito_ai/streamlit_conversion/prompts/streamlit_system_prompt.py +56 -0
  85. mito_ai/streamlit_conversion/prompts/update_existing_app_prompt.py +50 -0
  86. mito_ai/streamlit_conversion/search_replace_utils.py +94 -0
  87. mito_ai/streamlit_conversion/streamlit_agent_handler.py +144 -0
  88. mito_ai/streamlit_conversion/streamlit_utils.py +85 -0
  89. mito_ai/streamlit_conversion/validate_streamlit_app.py +105 -0
  90. mito_ai/streamlit_preview/__init__.py +6 -0
  91. mito_ai/streamlit_preview/handlers.py +111 -0
  92. mito_ai/streamlit_preview/manager.py +152 -0
  93. mito_ai/streamlit_preview/urls.py +22 -0
  94. mito_ai/streamlit_preview/utils.py +29 -0
  95. mito_ai/tests/__init__.py +3 -0
  96. mito_ai/tests/chat_history/test_chat_history.py +211 -0
  97. mito_ai/tests/completions/completion_handlers_utils_test.py +190 -0
  98. mito_ai/tests/conftest.py +53 -0
  99. mito_ai/tests/create_agent_system_message_prompt_test.py +22 -0
  100. mito_ai/tests/data/prompt_lg.py +69 -0
  101. mito_ai/tests/data/prompt_sm.py +6 -0
  102. mito_ai/tests/data/prompt_xl.py +13 -0
  103. mito_ai/tests/data/stock_data.sqlite3 +0 -0
  104. mito_ai/tests/db/conftest.py +39 -0
  105. mito_ai/tests/db/connections_test.py +102 -0
  106. mito_ai/tests/db/mssql_test.py +29 -0
  107. mito_ai/tests/db/mysql_test.py +29 -0
  108. mito_ai/tests/db/oracle_test.py +29 -0
  109. mito_ai/tests/db/postgres_test.py +29 -0
  110. mito_ai/tests/db/schema_test.py +93 -0
  111. mito_ai/tests/db/sqlite_test.py +31 -0
  112. mito_ai/tests/db/test_db_constants.py +61 -0
  113. mito_ai/tests/deploy_app/test_app_deploy_utils.py +89 -0
  114. mito_ai/tests/file_uploads/__init__.py +2 -0
  115. mito_ai/tests/file_uploads/test_handlers.py +282 -0
  116. mito_ai/tests/message_history/test_generate_short_chat_name.py +120 -0
  117. mito_ai/tests/message_history/test_message_history_utils.py +469 -0
  118. mito_ai/tests/open_ai_utils_test.py +152 -0
  119. mito_ai/tests/performance_test.py +329 -0
  120. mito_ai/tests/providers/test_anthropic_client.py +447 -0
  121. mito_ai/tests/providers/test_azure.py +631 -0
  122. mito_ai/tests/providers/test_capabilities.py +120 -0
  123. mito_ai/tests/providers/test_gemini_client.py +195 -0
  124. mito_ai/tests/providers/test_mito_server_utils.py +448 -0
  125. mito_ai/tests/providers/test_model_resolution.py +130 -0
  126. mito_ai/tests/providers/test_openai_client.py +57 -0
  127. mito_ai/tests/providers/test_provider_completion_exception.py +66 -0
  128. mito_ai/tests/providers/test_provider_limits.py +42 -0
  129. mito_ai/tests/providers/test_providers.py +382 -0
  130. mito_ai/tests/providers/test_retry_logic.py +389 -0
  131. mito_ai/tests/providers/test_stream_mito_server_utils.py +140 -0
  132. mito_ai/tests/providers/utils.py +85 -0
  133. mito_ai/tests/rules/conftest.py +26 -0
  134. mito_ai/tests/rules/rules_test.py +117 -0
  135. mito_ai/tests/server_limits_test.py +406 -0
  136. mito_ai/tests/settings/conftest.py +26 -0
  137. mito_ai/tests/settings/settings_test.py +70 -0
  138. mito_ai/tests/settings/test_settings_constants.py +9 -0
  139. mito_ai/tests/streamlit_conversion/__init__.py +3 -0
  140. mito_ai/tests/streamlit_conversion/test_apply_search_replace.py +240 -0
  141. mito_ai/tests/streamlit_conversion/test_streamlit_agent_handler.py +246 -0
  142. mito_ai/tests/streamlit_conversion/test_streamlit_utils.py +193 -0
  143. mito_ai/tests/streamlit_conversion/test_validate_streamlit_app.py +112 -0
  144. mito_ai/tests/streamlit_preview/test_streamlit_preview_handler.py +118 -0
  145. mito_ai/tests/streamlit_preview/test_streamlit_preview_manager.py +292 -0
  146. mito_ai/tests/test_constants.py +47 -0
  147. mito_ai/tests/test_telemetry.py +12 -0
  148. mito_ai/tests/user/__init__.py +2 -0
  149. mito_ai/tests/user/test_user.py +120 -0
  150. mito_ai/tests/utils/__init__.py +3 -0
  151. mito_ai/tests/utils/test_anthropic_utils.py +162 -0
  152. mito_ai/tests/utils/test_gemini_utils.py +98 -0
  153. mito_ai/tests/version_check_test.py +169 -0
  154. mito_ai/user/handlers.py +45 -0
  155. mito_ai/user/urls.py +21 -0
  156. mito_ai/utils/__init__.py +3 -0
  157. mito_ai/utils/anthropic_utils.py +168 -0
  158. mito_ai/utils/create.py +94 -0
  159. mito_ai/utils/db.py +74 -0
  160. mito_ai/utils/error_classes.py +42 -0
  161. mito_ai/utils/gemini_utils.py +133 -0
  162. mito_ai/utils/message_history_utils.py +87 -0
  163. mito_ai/utils/mito_server_utils.py +242 -0
  164. mito_ai/utils/open_ai_utils.py +200 -0
  165. mito_ai/utils/provider_utils.py +49 -0
  166. mito_ai/utils/schema.py +86 -0
  167. mito_ai/utils/server_limits.py +152 -0
  168. mito_ai/utils/telemetry_utils.py +480 -0
  169. mito_ai/utils/utils.py +89 -0
  170. mito_ai/utils/version_utils.py +94 -0
  171. mito_ai/utils/websocket_base.py +88 -0
  172. mito_ai/version_check.py +60 -0
  173. mito_ai-0.1.50.data/data/etc/jupyter/jupyter_server_config.d/mito_ai.json +7 -0
  174. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/build_log.json +728 -0
  175. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/package.json +243 -0
  176. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/package.json.orig +238 -0
  177. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/toolbar-buttons.json +37 -0
  178. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.8f1845da6bf2b128c049.js +21602 -0
  179. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.8f1845da6bf2b128c049.js.map +1 -0
  180. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/node_modules_process_browser_js.4b128e94d31a81ebd209.js +198 -0
  181. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/node_modules_process_browser_js.4b128e94d31a81ebd209.js.map +1 -0
  182. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.78d3ccb73e7ca1da3aae.js +619 -0
  183. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.78d3ccb73e7ca1da3aae.js.map +1 -0
  184. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/style.js +4 -0
  185. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/style_index_js.5876024bb17dbd6a3ee6.js +712 -0
  186. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/style_index_js.5876024bb17dbd6a3ee6.js.map +1 -0
  187. 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
  188. 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
  189. 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
  190. 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
  191. 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
  192. 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
  193. 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
  194. 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
  195. 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
  196. 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
  197. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.3f6754ac5116d47de76b.js +2792 -0
  198. mito_ai-0.1.50.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.3f6754ac5116d47de76b.js.map +1 -0
  199. 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
  200. 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
  201. mito_ai-0.1.50.dist-info/METADATA +221 -0
  202. mito_ai-0.1.50.dist-info/RECORD +205 -0
  203. mito_ai-0.1.50.dist-info/WHEEL +4 -0
  204. mito_ai-0.1.50.dist-info/entry_points.txt +2 -0
  205. 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")
@@ -0,0 +1,3 @@
1
+ # Copyright (c) Saga Inc.
2
+ # Distributed under the terms of the GNU Affero General Public License v3.0 License.
3
+