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,102 @@
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
+ from mito_ai.tests.db.test_db_constants import SNOWFLAKE
6
+ from mito_ai.tests.conftest import TOKEN
7
+
8
+
9
+ # --- ADD CONNECTION ---
10
+
11
+
12
+ def test_add_connection_with_auth(jp_base_url: str) -> None:
13
+ response = requests.post(
14
+ jp_base_url + "/mito-ai/db/connections",
15
+ headers={"Authorization": f"token {TOKEN}"},
16
+ json=SNOWFLAKE,
17
+ )
18
+ if response.status_code != 200:
19
+ print(f"Error response: {response.text}")
20
+ assert response.status_code == 200
21
+
22
+
23
+ def test_add_connection_with_no_auth(jp_base_url: str) -> None:
24
+ response = requests.post(
25
+ jp_base_url + "/mito-ai/db/connections",
26
+ json=SNOWFLAKE,
27
+ )
28
+ assert response.status_code == 403 # Forbidden
29
+
30
+
31
+ def test_add_connection_with_incorrect_auth(jp_base_url: str) -> None:
32
+ response = requests.post(
33
+ jp_base_url + "/mito-ai/db/connections",
34
+ headers={"Authorization": f"token incorrect-token"},
35
+ json=SNOWFLAKE,
36
+ )
37
+ assert response.status_code == 403 # Forbidden
38
+
39
+
40
+ # --- GET CONNECTIONS ---
41
+
42
+
43
+ def test_get_connections_with_auth(jp_base_url: str, first_connection_id: str) -> None:
44
+ response = requests.get(
45
+ jp_base_url + "/mito-ai/db/connections",
46
+ headers={"Authorization": f"token {TOKEN}"},
47
+ )
48
+ assert response.status_code == 200
49
+ # Ensure the response has the correct number of connections
50
+ assert len(response.json()) == 1
51
+ # Ensure the response has the correct connection details
52
+ assert response.json()[first_connection_id] == SNOWFLAKE
53
+
54
+
55
+ def test_get_connections_with_no_auth(jp_base_url: str) -> None:
56
+ response = requests.get(jp_base_url + "/mito-ai/db/connections")
57
+ assert response.status_code == 403 # Forbidden
58
+
59
+
60
+ def test_get_connections_with_incorrect_auth(jp_base_url: str) -> None:
61
+ response = requests.get(
62
+ jp_base_url + "/mito-ai/db/connections",
63
+ headers={"Authorization": f"token incorrect-token"},
64
+ )
65
+ assert response.status_code == 403 # Forbidden
66
+
67
+
68
+ # --- DELETE CONNECTION ---
69
+
70
+
71
+ def test_delete_connection_with_no_auth(jp_base_url: str, first_connection_id: str) -> None:
72
+ response = requests.delete(
73
+ jp_base_url + f"/mito-ai/db/connections/{first_connection_id}",
74
+ )
75
+ assert response.status_code == 403 # Forbidden
76
+
77
+
78
+ def test_delete_connection_with_incorrect_auth(
79
+ jp_base_url: str, first_connection_id: str
80
+ ) -> None:
81
+ response = requests.delete(
82
+ jp_base_url + f"/mito-ai/db/connections/{first_connection_id}",
83
+ headers={"Authorization": f"token incorrect-token"},
84
+ )
85
+ assert response.status_code == 403 # Forbidden
86
+
87
+
88
+ def test_delete_connection_with_auth(
89
+ jp_base_url: str, first_connection_id: str
90
+ ) -> None:
91
+ response = requests.delete(
92
+ jp_base_url + f"/mito-ai/db/connections/{first_connection_id}",
93
+ headers={"Authorization": f"token {TOKEN}"},
94
+ )
95
+ assert response.status_code == 200
96
+
97
+ # Ensure the connection was deleted
98
+ response = requests.get(
99
+ jp_base_url + f"/mito-ai/db/connections",
100
+ headers={"Authorization": f"token {TOKEN}"},
101
+ )
102
+ assert len(response.json()) == 0
@@ -0,0 +1,29 @@
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
+ from mito_ai.tests.db.test_db_constants import MSSQL_CONNECTION_DETAILS
6
+ from mito_ai.tests.conftest import TOKEN
7
+
8
+ # To create a postgres database, run the following command:
9
+ # docker-compose -f mito_ai/docker/mssql/compose.yml up
10
+ # and then, to delete the database, run the following command:
11
+ # docker-compose -f mito_ai/docker/mssql/compose.yml down -v
12
+
13
+
14
+ def test_add_mssql_connection(jp_base_url: str) -> None:
15
+ response = requests.post(
16
+ jp_base_url + "/mito-ai/db/connections",
17
+ headers={"Authorization": f"token {TOKEN}"},
18
+ json=MSSQL_CONNECTION_DETAILS,
19
+ )
20
+ assert response.status_code == 200
21
+
22
+ # Get the schemas
23
+ response = requests.get(
24
+ jp_base_url + f"/mito-ai/db/schemas",
25
+ headers={"Authorization": f"token {TOKEN}"},
26
+ )
27
+ assert response.status_code == 200
28
+ # Esnure that there is one scema dict in the response
29
+ assert len(response.json()) == 1
@@ -0,0 +1,29 @@
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
+ from mito_ai.tests.db.test_db_constants import MYSQL_CONNECTION_DETAILS
6
+ from mito_ai.tests.conftest import TOKEN
7
+
8
+ # To create a mysql database, run the following command:
9
+ # docker-compose -f mito_ai/docker/mysql/compose.yml up
10
+ # and then, to delete the database, run the following command:
11
+ # docker-compose -f mito_ai/docker/mysql/compose.yml down -v
12
+
13
+
14
+ def test_add_mysql_connection(jp_base_url: str) -> None:
15
+ response = requests.post(
16
+ jp_base_url + "/mito-ai/db/connections",
17
+ headers={"Authorization": f"token {TOKEN}"},
18
+ json=MYSQL_CONNECTION_DETAILS,
19
+ )
20
+ assert response.status_code == 200
21
+
22
+ # Get the schemas
23
+ response = requests.get(
24
+ jp_base_url + f"/mito-ai/db/schemas",
25
+ headers={"Authorization": f"token {TOKEN}"},
26
+ )
27
+ assert response.status_code == 200
28
+ # Esnure that there is one scema dict in the response
29
+ assert len(response.json()) == 1
@@ -0,0 +1,29 @@
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
+ from mito_ai.tests.db.test_db_constants import ORACLE_CONNECTION_DETAILS
6
+ from mito_ai.tests.conftest import TOKEN
7
+
8
+ # To create a postgres database, run the following command:
9
+ # docker-compose -f mito_ai/docker/postgres/compose.yml up
10
+ # and then, to delete the database, run the following command:
11
+ # docker-compose -f mito_ai/docker/postgres/compose.yml down -v
12
+
13
+
14
+ def test_add_oracle_connection(jp_base_url: str) -> None:
15
+ response = requests.post(
16
+ jp_base_url + "/mito-ai/db/connections",
17
+ headers={"Authorization": f"token {TOKEN}"},
18
+ json=ORACLE_CONNECTION_DETAILS,
19
+ )
20
+ assert response.status_code == 200
21
+
22
+ # Get the schemas
23
+ response = requests.get(
24
+ jp_base_url + f"/mito-ai/db/schemas",
25
+ headers={"Authorization": f"token {TOKEN}"},
26
+ )
27
+ assert response.status_code == 200
28
+ # Esnure that there is one scema dict in the response
29
+ assert len(response.json()) == 1
@@ -0,0 +1,29 @@
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
+ from mito_ai.tests.db.test_db_constants import POSTGRES_CONNECTION_DETAILS
6
+ from mito_ai.tests.conftest import TOKEN
7
+
8
+ # To create a postgres database, run the following command:
9
+ # docker-compose -f mito_ai/docker/postgres/compose.yml up
10
+ # and then, to delete the database, run the following command:
11
+ # docker-compose -f mito_ai/docker/postgres/compose.yml down -v
12
+
13
+
14
+ def test_add_postgres_connection(jp_base_url: str) -> None:
15
+ response = requests.post(
16
+ jp_base_url + "/mito-ai/db/connections",
17
+ headers={"Authorization": f"token {TOKEN}"},
18
+ json=POSTGRES_CONNECTION_DETAILS,
19
+ )
20
+ assert response.status_code == 200
21
+
22
+ # Get the schemas
23
+ response = requests.get(
24
+ jp_base_url + f"/mito-ai/db/schemas",
25
+ headers={"Authorization": f"token {TOKEN}"},
26
+ )
27
+ assert response.status_code == 200
28
+ # Esnure that there is one scema dict in the response
29
+ assert len(response.json()) == 1
@@ -0,0 +1,93 @@
1
+ # Copyright (c) Saga Inc.
2
+ # Distributed under the terms of the GNU Affero General Public License v3.0 License.
3
+
4
+ import pytest
5
+ import requests
6
+ from mito_ai.tests.db.test_db_constants import SNOWFLAKE
7
+ from mito_ai.tests.conftest import TOKEN
8
+
9
+ # --- GET SCHEMAS ---
10
+
11
+
12
+ def test_get_schemas_with_auth(jp_base_url: str) -> None:
13
+ # Add a connection to get a schema
14
+ response = requests.post(
15
+ jp_base_url + f"/mito-ai/db/connections",
16
+ headers={"Authorization": f"token {TOKEN}"},
17
+ json=SNOWFLAKE,
18
+ )
19
+ assert response.status_code == 200
20
+
21
+ # Get the schemas
22
+ response = requests.get(
23
+ jp_base_url + f"/mito-ai/db/schemas",
24
+ headers={"Authorization": f"token {TOKEN}"},
25
+ )
26
+ assert response.status_code == 200
27
+ # Esnure that there is one scema dict in the response
28
+ assert len(response.json()) == 1
29
+
30
+
31
+ def test_ensure_no_application_databases(jp_base_url: str, first_connection_id: str) -> None:
32
+ # We currently don't support Snowflake application databases.
33
+ # This test ensures that we don't crawl application databases.
34
+
35
+ response = requests.get(
36
+ jp_base_url + f"/mito-ai/db/schemas",
37
+ headers={"Authorization": f"token {TOKEN}"},
38
+ )
39
+
40
+ crawled_databases = response.json()[first_connection_id]["databases"].keys()
41
+ assert len(crawled_databases) > 0
42
+ assert "SNOWFLAKE" not in crawled_databases
43
+
44
+
45
+ def test_get_schemas_with_no_auth(jp_base_url: str) -> None:
46
+ response = requests.get(
47
+ jp_base_url + f"/mito-ai/db/schemas",
48
+ )
49
+ assert response.status_code == 403 # Forbidden
50
+
51
+
52
+ def test_get_schemas_with_incorrect_auth(jp_base_url: str) -> None:
53
+ response = requests.get(
54
+ jp_base_url + f"/mito-ai/db/schemas",
55
+ headers={"Authorization": f"token incorrect-token"},
56
+ )
57
+ assert response.status_code == 403 # Forbidden
58
+
59
+
60
+ # --- DELETE SCHEMA ---
61
+
62
+
63
+ def test_delete_schema_with_auth(jp_base_url: str, first_connection_id: str) -> None:
64
+ response = requests.delete(
65
+ jp_base_url + f"/mito-ai/db/schemas/{first_connection_id}",
66
+ headers={"Authorization": f"token {TOKEN}"},
67
+ )
68
+ assert response.status_code == 200
69
+
70
+ # Ensure the schema was deleted
71
+ response = requests.get(
72
+ jp_base_url + f"/mito-ai/db/schemas",
73
+ headers={"Authorization": f"token {TOKEN}"},
74
+ )
75
+ assert response.status_code == 200
76
+ assert len(response.json()) == 0
77
+
78
+
79
+ def test_delete_schema_with_no_auth(jp_base_url: str, first_connection_id: str) -> None:
80
+ response = requests.delete(
81
+ jp_base_url + f"/mito-ai/db/schemas/{first_connection_id}",
82
+ )
83
+ assert response.status_code == 403 # Forbidden
84
+
85
+
86
+ def test_delete_schema_with_incorrect_auth(
87
+ jp_base_url: str, first_connection_id: str
88
+ ) -> None:
89
+ response = requests.delete(
90
+ jp_base_url + f"/mito-ai/db/schemas/{first_connection_id}",
91
+ headers={"Authorization": f"token incorrect-token"},
92
+ )
93
+ assert response.status_code == 403 # Forbidden
@@ -0,0 +1,31 @@
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 requests
6
+ from mito_ai.tests.db.test_db_constants import (
7
+ SQLITE_TEST_DB_PATH,
8
+ SQLITE_CONNECTION_DETAILS,
9
+ )
10
+ from mito_ai.tests.conftest import TOKEN
11
+
12
+
13
+ def test_add_sqlite_connection(jp_base_url: str) -> None:
14
+ # Check for the sqlite database
15
+ assert os.path.exists(SQLITE_TEST_DB_PATH)
16
+
17
+ response = requests.post(
18
+ jp_base_url + "/mito-ai/db/connections",
19
+ headers={"Authorization": f"token {TOKEN}"},
20
+ json=SQLITE_CONNECTION_DETAILS,
21
+ )
22
+ assert response.status_code == 200
23
+
24
+ # Get the schemas
25
+ response = requests.get(
26
+ jp_base_url + f"/mito-ai/db/schemas",
27
+ headers={"Authorization": f"token {TOKEN}"},
28
+ )
29
+ assert response.status_code == 200
30
+ # Esnure that there is one scema dict in the response
31
+ assert len(response.json()) == 1
@@ -0,0 +1,61 @@
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
+ DB_DIR_PATH: Final[str] = os.path.join(MITO_FOLDER, "db")
9
+ CONNECTIONS_PATH: Final[str] = os.path.join(DB_DIR_PATH, "connections.json")
10
+ SCHEMAS_PATH: Final[str] = os.path.join(DB_DIR_PATH, "schemas.json")
11
+ BACKUP_DB_DIR_PATH: Final[str] = os.path.join(MITO_FOLDER, "db_backup")
12
+ MSSQL_CONNECTION_DETAILS = {
13
+ "type": "mssql",
14
+ "username": os.environ.get("MSSQL_USERNAME"),
15
+ "password": os.environ.get("MSSQL_PASSWORD"),
16
+ "host": os.environ.get("MSSQL_HOST"),
17
+ "port": "1433",
18
+ "database": "Northwind",
19
+ "odbc_driver_version": "18",
20
+ }
21
+ MYSQL_CONNECTION_DETAILS = {
22
+ "type": "mysql",
23
+ "username": os.environ.get("MYSQL_USERNAME"),
24
+ "password": os.environ.get("MYSQL_PASSWORD"),
25
+ "host": os.environ.get("MYSQL_HOST"),
26
+ "port": "3306",
27
+ "database": "Northwind",
28
+ }
29
+ ORACLE_CONNECTION_DETAILS = {
30
+ "type": "oracle",
31
+ "username": os.environ.get("ORACLE_USERNAME"),
32
+ "password": os.environ.get("ORACLE_PASSWORD"),
33
+ "host": os.environ.get("ORACLE_HOST"),
34
+ "port": "1521",
35
+ "service_name": "ORCL",
36
+ }
37
+ POSTGRES_CONNECTION_DETAILS = {
38
+ "type": "postgres",
39
+ "username": os.environ.get("POSTGRES_USERNAME"),
40
+ "password": os.environ.get("POSTGRES_PASSWORD"),
41
+ "host": os.environ.get("POSTGRES_HOST"),
42
+ "port": "5432",
43
+ "database": "postgres",
44
+ }
45
+ SNOWFLAKE = {
46
+ "type": "snowflake",
47
+ "username": os.environ.get("SNOWFLAKE_USERNAME"),
48
+ "password": os.environ.get("SNOWFLAKE_PASSWORD"),
49
+ "account": os.environ.get("SNOWFLAKE_ACCOUNT"),
50
+ "warehouse": "COMPUTE_WH",
51
+ }
52
+ SQLITE_TEST_DB_PATH: Final[str] = os.path.join(
53
+ os.path.dirname(os.path.dirname(os.path.dirname(__file__))),
54
+ "tests",
55
+ "data",
56
+ "stock_data.sqlite3",
57
+ )
58
+ SQLITE_CONNECTION_DETAILS = {
59
+ "type": "sqlite",
60
+ "database": SQLITE_TEST_DB_PATH,
61
+ }
@@ -0,0 +1,89 @@
1
+ # Copyright (c) Saga Inc.
2
+ # Distributed under the terms of the GNU Affero General Public License v3.0 License.
3
+
4
+ import zipfile
5
+ import logging
6
+ from mito_ai.app_deploy.app_deploy_utils import add_files_to_zip
7
+ from mito_ai.path_utils import AbsoluteNotebookDirPath
8
+
9
+ class TestAddFilesToZip:
10
+ """Test cases for add_files_to_zip helper function"""
11
+
12
+ def test_files_added_correctly(self, tmp_path):
13
+ """Ensure individual files are added correctly to the zip"""
14
+ # Create files
15
+ f1 = tmp_path / "file1.txt"
16
+ f1.write_text("file1 content")
17
+ f2 = tmp_path / "file2.txt"
18
+ f2.write_text("file2 content")
19
+
20
+ zip_path = tmp_path / "test.zip"
21
+ add_files_to_zip(str(zip_path), AbsoluteNotebookDirPath(str(tmp_path)), ["file1.txt", "file2.txt"], 'test-app-file-name.py')
22
+
23
+ with zipfile.ZipFile(zip_path, "r") as zf:
24
+ names = zf.namelist()
25
+ assert "file1.txt" in names
26
+ assert "file2.txt" in names
27
+ assert len(names) == 2
28
+
29
+ def test_renames_app_file(self, tmp_path):
30
+ """Ensure individual files are added correctly to the zip"""
31
+ # Create files
32
+ f1 = tmp_path / "original-file-name.py"
33
+ f1.write_text("file1 content")
34
+ f2 = tmp_path / "file2.txt"
35
+ f2.write_text("file2 content")
36
+
37
+ zip_path = tmp_path / "test.zip"
38
+ add_files_to_zip(str(zip_path), AbsoluteNotebookDirPath(str(tmp_path)), ["original-file-name.py", "file2.txt"], 'original-file-name.py')
39
+
40
+ with zipfile.ZipFile(zip_path, "r") as zf:
41
+ names = zf.namelist()
42
+ assert "app.py" in names
43
+ assert "file2.txt" in names
44
+ assert len(names) == 2
45
+
46
+ def test_directories_added_recursively(self, tmp_path):
47
+ """Ensure directories are added recursively with correct relative paths"""
48
+ nested = tmp_path / "folder"
49
+ nested.mkdir()
50
+ (nested / "nested1.txt").write_text("nested1 content")
51
+ subfolder = nested / "sub"
52
+ subfolder.mkdir()
53
+ (subfolder / "nested2.txt").write_text("nested2 content")
54
+
55
+ zip_path = tmp_path / "test.zip"
56
+ add_files_to_zip(str(zip_path), AbsoluteNotebookDirPath(str(tmp_path)), ["folder"], 'test-app.py')
57
+
58
+ with zipfile.ZipFile(zip_path, "r") as zf:
59
+ names = zf.namelist()
60
+ assert "folder/nested1.txt" in names
61
+ assert "folder/sub/nested2.txt" in names
62
+
63
+ def test_missing_files_skipped(self, tmp_path, caplog):
64
+ """Ensure missing files do not break the function and warning is logged"""
65
+ caplog.set_level(logging.WARNING)
66
+ zip_path = tmp_path / "test.zip"
67
+ add_files_to_zip(str(zip_path), AbsoluteNotebookDirPath(str(tmp_path)), ["does_not_exist.txt"], 'test-app.py', logger=logging.getLogger())
68
+
69
+ # Zip should exist but be empty
70
+ with zipfile.ZipFile(zip_path, "r") as zf:
71
+ assert zf.namelist() == []
72
+
73
+ # Check warning was logged
74
+ assert any("Skipping missing file" in record.message for record in caplog.records)
75
+
76
+ def test_arcname_paths_correct(self, tmp_path):
77
+ """Ensure arcname paths inside zip preserve relative paths to base_path"""
78
+ (tmp_path / "file.txt").write_text("content")
79
+ folder = tmp_path / "folder"
80
+ folder.mkdir()
81
+ (folder / "nested.txt").write_text("nested content")
82
+
83
+ zip_path = tmp_path / "test.zip"
84
+ add_files_to_zip(str(zip_path), AbsoluteNotebookDirPath(str(tmp_path)), ["file.txt", "folder"], 'test-app.py')
85
+
86
+ with zipfile.ZipFile(zip_path, "r") as zf:
87
+ names = zf.namelist()
88
+ assert "file.txt" in names
89
+ assert "folder/nested.txt" in names
@@ -0,0 +1,2 @@
1
+ # Copyright (c) Saga Inc.
2
+ # Distributed under the terms of the GNU Affero General Public License v3.0 License.