iatoolkit 0.8.1__py3-none-any.whl → 0.63.4__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.

Potentially problematic release.


This version of iatoolkit might be problematic. Click here for more details.

Files changed (159) hide show
  1. iatoolkit/__init__.py +8 -34
  2. iatoolkit/base_company.py +14 -3
  3. iatoolkit/common/routes.py +83 -52
  4. iatoolkit/common/session_manager.py +0 -1
  5. iatoolkit/common/util.py +0 -27
  6. iatoolkit/iatoolkit.py +61 -46
  7. iatoolkit/infra/llm_client.py +7 -8
  8. iatoolkit/infra/openai_adapter.py +1 -1
  9. iatoolkit/infra/redis_session_manager.py +48 -2
  10. iatoolkit/repositories/database_manager.py +17 -2
  11. iatoolkit/repositories/models.py +31 -6
  12. iatoolkit/repositories/profile_repo.py +7 -2
  13. iatoolkit/services/auth_service.py +188 -0
  14. iatoolkit/services/branding_service.py +147 -0
  15. iatoolkit/services/dispatcher_service.py +10 -40
  16. iatoolkit/services/excel_service.py +15 -15
  17. iatoolkit/services/history_service.py +3 -12
  18. iatoolkit/services/jwt_service.py +15 -24
  19. iatoolkit/services/onboarding_service.py +43 -0
  20. iatoolkit/services/profile_service.py +97 -44
  21. iatoolkit/services/query_service.py +124 -81
  22. iatoolkit/services/tasks_service.py +1 -1
  23. iatoolkit/services/user_feedback_service.py +67 -31
  24. iatoolkit/services/user_session_context_service.py +112 -54
  25. iatoolkit/static/images/fernando.jpeg +0 -0
  26. iatoolkit/static/js/{chat_feedback.js → chat_feedback_button.js} +6 -11
  27. iatoolkit/static/js/chat_history_button.js +126 -0
  28. iatoolkit/static/js/chat_logout_button.js +36 -0
  29. iatoolkit/static/js/chat_main.js +130 -220
  30. iatoolkit/static/js/chat_onboarding_button.js +97 -0
  31. iatoolkit/static/js/chat_prompt_manager.js +94 -0
  32. iatoolkit/static/js/chat_reload_button.js +52 -0
  33. iatoolkit/static/styles/chat_iatoolkit.css +329 -507
  34. iatoolkit/static/styles/chat_modal.css +95 -56
  35. iatoolkit/static/styles/landing_page.css +182 -0
  36. iatoolkit/static/styles/onboarding.css +169 -0
  37. iatoolkit/system_prompts/query_main.prompt +3 -12
  38. iatoolkit/templates/_company_header.html +20 -0
  39. iatoolkit/templates/_login_widget.html +40 -0
  40. iatoolkit/templates/base.html +8 -3
  41. iatoolkit/templates/change_password.html +54 -37
  42. iatoolkit/templates/chat.html +149 -66
  43. iatoolkit/templates/chat_modals.html +47 -18
  44. iatoolkit/templates/error.html +41 -8
  45. iatoolkit/templates/forgot_password.html +37 -24
  46. iatoolkit/templates/index.html +140 -0
  47. iatoolkit/templates/login_simulation.html +34 -0
  48. iatoolkit/templates/onboarding_shell.html +105 -0
  49. iatoolkit/templates/signup.html +64 -66
  50. iatoolkit/views/base_login_view.py +81 -0
  51. iatoolkit/views/change_password_view.py +23 -12
  52. iatoolkit/views/external_login_view.py +61 -28
  53. iatoolkit/views/{file_store_view.py → file_store_api_view.py} +9 -2
  54. iatoolkit/views/forgot_password_view.py +23 -13
  55. iatoolkit/views/history_api_view.py +52 -0
  56. iatoolkit/views/home_view.py +58 -25
  57. iatoolkit/views/index_view.py +14 -0
  58. iatoolkit/views/init_context_api_view.py +68 -0
  59. iatoolkit/views/llmquery_api_view.py +45 -0
  60. iatoolkit/views/login_simulation_view.py +81 -0
  61. iatoolkit/views/login_view.py +118 -34
  62. iatoolkit/views/logout_api_view.py +45 -0
  63. iatoolkit/views/{prompt_view.py → prompt_api_view.py} +7 -7
  64. iatoolkit/views/signup_view.py +38 -29
  65. iatoolkit/views/{tasks_view.py → tasks_api_view.py} +10 -36
  66. iatoolkit/views/tasks_review_api_view.py +55 -0
  67. iatoolkit/views/{user_feedback_view.py → user_feedback_api_view.py} +16 -31
  68. iatoolkit/views/verify_user_view.py +13 -8
  69. {iatoolkit-0.8.1.dist-info → iatoolkit-0.63.4.dist-info}/METADATA +2 -2
  70. iatoolkit-0.63.4.dist-info/RECORD +113 -0
  71. {iatoolkit-0.8.1.dist-info → iatoolkit-0.63.4.dist-info}/top_level.txt +0 -1
  72. iatoolkit/common/auth.py +0 -200
  73. iatoolkit/static/images/arrow_up.png +0 -0
  74. iatoolkit/static/images/diagrama_iatoolkit.jpg +0 -0
  75. iatoolkit/static/images/logo_clinica.png +0 -0
  76. iatoolkit/static/images/logo_iatoolkit.png +0 -0
  77. iatoolkit/static/images/logo_maxxa.png +0 -0
  78. iatoolkit/static/images/logo_notaria.png +0 -0
  79. iatoolkit/static/images/logo_tarjeta.png +0 -0
  80. iatoolkit/static/images/logo_umayor.png +0 -0
  81. iatoolkit/static/images/upload.png +0 -0
  82. iatoolkit/static/js/chat_history.js +0 -117
  83. iatoolkit/templates/home.html +0 -201
  84. iatoolkit/templates/login.html +0 -43
  85. iatoolkit/views/chat_token_request_view.py +0 -98
  86. iatoolkit/views/chat_view.py +0 -51
  87. iatoolkit/views/download_file_view.py +0 -58
  88. iatoolkit/views/external_chat_login_view.py +0 -88
  89. iatoolkit/views/history_view.py +0 -57
  90. iatoolkit/views/llmquery_view.py +0 -65
  91. iatoolkit/views/tasks_review_view.py +0 -83
  92. iatoolkit-0.8.1.dist-info/RECORD +0 -175
  93. tests/__init__.py +0 -5
  94. tests/common/__init__.py +0 -0
  95. tests/common/test_auth.py +0 -279
  96. tests/common/test_routes.py +0 -42
  97. tests/common/test_session_manager.py +0 -59
  98. tests/common/test_util.py +0 -444
  99. tests/companies/__init__.py +0 -5
  100. tests/conftest.py +0 -36
  101. tests/infra/__init__.py +0 -5
  102. tests/infra/connectors/__init__.py +0 -5
  103. tests/infra/connectors/test_google_drive_connector.py +0 -107
  104. tests/infra/connectors/test_local_file_connector.py +0 -85
  105. tests/infra/connectors/test_s3_connector.py +0 -95
  106. tests/infra/test_call_service.py +0 -92
  107. tests/infra/test_database_manager.py +0 -59
  108. tests/infra/test_gemini_adapter.py +0 -137
  109. tests/infra/test_google_chat_app.py +0 -68
  110. tests/infra/test_llm_client.py +0 -165
  111. tests/infra/test_llm_proxy.py +0 -122
  112. tests/infra/test_mail_app.py +0 -94
  113. tests/infra/test_openai_adapter.py +0 -105
  114. tests/infra/test_redis_session_manager_service.py +0 -117
  115. tests/repositories/__init__.py +0 -5
  116. tests/repositories/test_database_manager.py +0 -87
  117. tests/repositories/test_document_repo.py +0 -76
  118. tests/repositories/test_llm_query_repo.py +0 -340
  119. tests/repositories/test_models.py +0 -38
  120. tests/repositories/test_profile_repo.py +0 -142
  121. tests/repositories/test_tasks_repo.py +0 -76
  122. tests/repositories/test_vs_repo.py +0 -107
  123. tests/services/__init__.py +0 -5
  124. tests/services/test_dispatcher_service.py +0 -274
  125. tests/services/test_document_service.py +0 -181
  126. tests/services/test_excel_service.py +0 -208
  127. tests/services/test_file_processor_service.py +0 -121
  128. tests/services/test_history_service.py +0 -164
  129. tests/services/test_jwt_service.py +0 -255
  130. tests/services/test_load_documents_service.py +0 -112
  131. tests/services/test_mail_service.py +0 -70
  132. tests/services/test_profile_service.py +0 -379
  133. tests/services/test_prompt_manager_service.py +0 -190
  134. tests/services/test_query_service.py +0 -243
  135. tests/services/test_search_service.py +0 -39
  136. tests/services/test_sql_service.py +0 -160
  137. tests/services/test_tasks_service.py +0 -252
  138. tests/services/test_user_feedback_service.py +0 -389
  139. tests/services/test_user_session_context_service.py +0 -132
  140. tests/views/__init__.py +0 -5
  141. tests/views/test_change_password_view.py +0 -191
  142. tests/views/test_chat_token_request_view.py +0 -188
  143. tests/views/test_chat_view.py +0 -98
  144. tests/views/test_download_file_view.py +0 -149
  145. tests/views/test_external_chat_login_view.py +0 -120
  146. tests/views/test_external_login_view.py +0 -102
  147. tests/views/test_file_store_view.py +0 -128
  148. tests/views/test_forgot_password_view.py +0 -142
  149. tests/views/test_history_view.py +0 -336
  150. tests/views/test_home_view.py +0 -61
  151. tests/views/test_llm_query_view.py +0 -154
  152. tests/views/test_login_view.py +0 -114
  153. tests/views/test_prompt_view.py +0 -111
  154. tests/views/test_signup_view.py +0 -140
  155. tests/views/test_tasks_review_view.py +0 -104
  156. tests/views/test_tasks_view.py +0 -130
  157. tests/views/test_user_feedback_view.py +0 -214
  158. tests/views/test_verify_user_view.py +0 -110
  159. {iatoolkit-0.8.1.dist-info → iatoolkit-0.63.4.dist-info}/WHEEL +0 -0
@@ -1,149 +0,0 @@
1
- # Copyright (c) 2024 Fernando Libedinsky
2
- # Product: IAToolkit
3
- #
4
- # IAToolkit is open source software.
5
-
6
- import os
7
- import shutil
8
- import tempfile
9
- from unittest.mock import MagicMock, patch
10
-
11
- import pytest
12
- from flask import Flask, jsonify
13
-
14
- from iatoolkit.common.auth import IAuthentication
15
- from iatoolkit.repositories.models import Company
16
- from iatoolkit.services.excel_service import ExcelService
17
- from iatoolkit.services.profile_service import ProfileService
18
- from iatoolkit.views.download_file_view import DownloadFileView
19
-
20
-
21
- class TestDownloadFileView:
22
- @pytest.fixture(autouse=True)
23
- def setup(self):
24
- self.app = Flask(__name__)
25
- self.app.testing = True
26
-
27
- # Create a real temporary directory for static/temp
28
- self.temp_dir_base = tempfile.mkdtemp()
29
- self.app.root_path = self.temp_dir_base
30
- temp_dir_path = os.path.join(self.app.root_path, 'static', 'temp')
31
- os.makedirs(temp_dir_path)
32
-
33
- # Create a real test file
34
- self.test_filename = 'test_file.xlsx'
35
- self.test_file_path = os.path.join(temp_dir_path, self.test_filename)
36
- self.test_content = b'test file content'
37
- with open(self.test_file_path, 'wb') as f:
38
- f.write(self.test_content)
39
-
40
- # Mocks of services
41
- self.iauthentication = MagicMock(spec=IAuthentication)
42
- self.profile_service = MagicMock(spec=ProfileService)
43
- self.excel_service = MagicMock(spec=ExcelService)
44
-
45
- # Default mock configuration for successful case
46
- self.iauthentication.verify.return_value = {'success': True}
47
- self.test_company = Company(id=1, name="Test Company", short_name="test_company")
48
- self.profile_service.get_company_by_short_name.return_value = self.test_company
49
- self.excel_service.validate_file_access.return_value = None
50
-
51
- # Inject mocks and register the route
52
- view = DownloadFileView.as_view(
53
- 'download_file',
54
- iauthentication=self.iauthentication,
55
- profile_service=self.profile_service,
56
- excel_service=self.excel_service,
57
- )
58
- self.app.add_url_rule(
59
- '/<string:company_short_name>/<string:external_user_id>/download-file/<path:filename>',
60
- view_func=view
61
- )
62
-
63
- self.client = self.app.test_client()
64
-
65
- yield
66
-
67
- # Cleanup after test
68
- shutil.rmtree(self.temp_dir_base)
69
-
70
- def test_successful_download(self):
71
- with self.app.app_context():
72
- response = self.client.get(f'/test_company/user_123/download-file/{self.test_filename}')
73
-
74
- assert response.status_code == 200
75
- assert response.data == self.test_content
76
- assert response.headers['Content-Disposition'] == f'attachment; filename={self.test_filename}'
77
-
78
- self.iauthentication.verify.assert_called_once_with('test_company', body_external_user_id='user_123')
79
- self.profile_service.get_company_by_short_name.assert_called_once_with('test_company')
80
- self.excel_service.validate_file_access.assert_called_once_with(self.test_filename)
81
-
82
- def test_missing_external_user_id(self):
83
- # Test with empty external_user_id
84
- with self.app.app_context():
85
- response = self.client.get('/test_company//download-file/file.xlsx')
86
-
87
- view = DownloadFileView(
88
- iauthentication=self.iauthentication,
89
- profile_service=self.profile_service,
90
- excel_service=self.excel_service
91
- )
92
-
93
- with self.app.test_request_context():
94
- response = view.get('test_company', '', 'file.xlsx')
95
-
96
- assert response[1] == 400
97
- data = response[0].get_json()
98
- assert data['error'] == 'Falta external_user_id'
99
-
100
- def test_authentication_failure(self):
101
- self.iauthentication.verify.return_value = {'success': False, 'error': 'Token inválido'}
102
-
103
- with self.app.app_context():
104
- response = self.client.get(f'/test_company/user_123/download-file/{self.test_filename}')
105
-
106
- assert response.status_code == 401
107
- data = response.get_json()
108
- assert data['success'] is False
109
- assert data['error'] == 'Token inválido'
110
-
111
- def test_company_not_found(self):
112
- self.profile_service.get_company_by_short_name.return_value = None
113
-
114
- with self.app.app_context():
115
- response = self.client.get(f'/test_company/user_123/download-file/{self.test_filename}')
116
-
117
- assert response.status_code == 404
118
- data = response.get_json()
119
- assert data['error'] == 'Empresa no encontrada'
120
-
121
- def test_file_validation_error(self):
122
- with self.app.app_context():
123
- self.excel_service.validate_file_access.return_value = (jsonify({"error": "Acceso denegado"}), 403)
124
- response = self.client.get('/test_company/user_123/download-file/unauthorized_file.xlsx')
125
-
126
- assert response.status_code == 403
127
- data = response.get_json()
128
- assert data['error'] == 'Acceso denegado'
129
-
130
- def test_file_not_found_on_disk(self):
131
- with self.app.app_context():
132
- response = self.client.get('/test_company/user_123/download-file/non_existent_file.xlsx')
133
-
134
- assert response.status_code == 500
135
- data = response.get_json()
136
- assert data['error'] == 'Error descargando archivo'
137
-
138
- @patch('iatoolkit.views.download_file_view.logging')
139
- def test_logging_on_success_and_error(self, mock_logging):
140
- with self.app.app_context():
141
- # Success case
142
- self.client.get(f'/test_company/user_123/download-file/{self.test_filename}')
143
- mock_logging.info.assert_called_with(f'Archivo descargado via API: {self.test_filename}')
144
-
145
- # Error case
146
- self.client.get('/test_company/user_123/download-file/non_existent_file.xlsx')
147
- assert mock_logging.error.call_count == 1
148
- args, _ = mock_logging.error.call_args
149
- assert 'Error descargando archivo non_existent_file.xlsx' in args[0]
@@ -1,120 +0,0 @@
1
- import pytest
2
- from flask import Flask
3
- from unittest.mock import MagicMock, patch
4
- from iatoolkit.views.external_chat_login_view import ExternalChatLoginView
5
- from iatoolkit.services.profile_service import ProfileService
6
- from iatoolkit.services.query_service import QueryService
7
- from iatoolkit.services.prompt_manager_service import PromptService
8
- from iatoolkit.services.jwt_service import JWTService # <-- Importar JWTService
9
- from iatoolkit.common.auth import IAuthentication
10
- from iatoolkit.repositories.models import Company
11
-
12
-
13
- # --- Constantes para los Tests ---
14
- MOCK_COMPANY_SHORT_NAME = "test-comp"
15
- MOCK_EXTERNAL_USER_ID = "ext-user-123"
16
- MOCK_API_KEY = "super-secret-api-key"
17
- MOCK_JWT_TOKEN = "a-fake-but-valid-jwt-token"
18
-
19
-
20
- class TestExternalChatLoginView:
21
-
22
- @pytest.fixture(autouse=True)
23
- def setup_method(self):
24
- """Configura la aplicación Flask y los mocks para cada test."""
25
- self.app = Flask(__name__)
26
- self.app.testing = True
27
-
28
- # Mocks de todos los servicios inyectados
29
- self.mock_profile_service = MagicMock(spec=ProfileService)
30
- self.mock_query_service = MagicMock(spec=QueryService)
31
- self.mock_prompt_service = MagicMock(spec=PromptService)
32
- self.mock_iauthentication = MagicMock(spec=IAuthentication)
33
- self.mock_jwt_service = MagicMock(spec=JWTService)
34
-
35
- # Configurar el mock de la compañía que se devolverá
36
- self.mock_company = Company(id=1, name="Test Company", short_name=MOCK_COMPANY_SHORT_NAME)
37
- self.mock_profile_service.get_company_by_short_name.return_value = self.mock_company
38
-
39
- # Registrar la vista en la app de Flask con TODAS las dependencias
40
- view_func = ExternalChatLoginView.as_view(
41
- 'external_chat_login',
42
- profile_service=self.mock_profile_service,
43
- query_service=self.mock_query_service,
44
- prompt_service=self.mock_prompt_service,
45
- iauthentication=self.mock_iauthentication,
46
- jwt_service=self.mock_jwt_service # <-- 2. Inyectar el mock de JWTService
47
- )
48
-
49
- self.app.add_url_rule('/<company_short_name>/chat_login', view_func=view_func, methods=['POST'])
50
- self.client = self.app.test_client()
51
-
52
- def test_login_success(self):
53
- """
54
- Prueba el flujo exitoso, incluyendo la generación del JWT.
55
- """
56
- # Configurar mocks para el caso de éxito
57
- self.mock_iauthentication.verify.return_value = {"success": True}
58
- self.mock_prompt_service.get_user_prompts.return_value = []
59
- # 3. Configurar el mock para que devuelva un token
60
- self.mock_jwt_service.generate_chat_jwt.return_value = MOCK_JWT_TOKEN
61
-
62
- with patch('iatoolkit.views.external_chat_login_view.render_template') as mock_render:
63
- mock_render.return_value = "<html>Chat Page</html>"
64
-
65
- response = self.client.post(
66
- f'/{MOCK_COMPANY_SHORT_NAME}/chat_login',
67
- headers={'Authorization': f'Bearer {MOCK_API_KEY}'},
68
- json={'external_user_id': MOCK_EXTERNAL_USER_ID}
69
- )
70
-
71
- # Verificar que la respuesta es exitosa
72
- assert response.status_code == 200
73
- assert response.data == b"<html>Chat Page</html>"
74
-
75
- # Verificar que se llamó a la autenticación
76
- self.mock_iauthentication.verify.assert_called_once_with(
77
- MOCK_COMPANY_SHORT_NAME,
78
- body_external_user_id=MOCK_EXTERNAL_USER_ID
79
- )
80
-
81
- # 4. Verificar que se intentó generar el JWT con los datos correctos
82
- self.mock_jwt_service.generate_chat_jwt.assert_called_once_with(
83
- company_id=self.mock_company.id,
84
- company_short_name=self.mock_company.short_name,
85
- external_user_id=MOCK_EXTERNAL_USER_ID,
86
- expires_delta_seconds=3600 * 8
87
- )
88
-
89
- # Verificar que se inicializó el contexto y se obtuvieron los prompts
90
- self.mock_query_service.llm_init_context.assert_called_once()
91
- self.mock_prompt_service.get_user_prompts.assert_called_once()
92
-
93
- # 5. Verificar que se renderizó la plantilla con TODOS los datos correctos
94
- mock_render.assert_called_once()
95
- call_kwargs = mock_render.call_args[1]
96
- assert call_kwargs['company_short_name'] == MOCK_COMPANY_SHORT_NAME
97
- assert call_kwargs['external_user_id'] == MOCK_EXTERNAL_USER_ID
98
- assert call_kwargs['auth_method'] == 'jwt'
99
- assert call_kwargs['session_jwt'] == MOCK_JWT_TOKEN # <-- Verificar que el token se pasa
100
-
101
- def test_login_fails_if_jwt_generation_fails(self):
102
- """
103
- Prueba que la vista maneja un error si el JWTService no puede generar un token.
104
- """
105
- self.mock_iauthentication.verify.return_value = {"success": True}
106
- # Simular fallo en la generación del token
107
- self.mock_jwt_service.generate_chat_jwt.return_value = None
108
-
109
- response = self.client.post(
110
- f'/{MOCK_COMPANY_SHORT_NAME}/chat_login',
111
- headers={'Authorization': f'Bearer {MOCK_API_KEY}'},
112
- json={'external_user_id': MOCK_EXTERNAL_USER_ID}
113
- )
114
-
115
- # La vista debería devolver un error 500
116
- assert response.status_code == 500
117
- assert response.is_json
118
- assert 'Error interno' in response.json['error']
119
-
120
- # ... (el resto de los tests, como el de fallo de autenticación, permanecen igual y deberían seguir funcionando) ...
@@ -1,102 +0,0 @@
1
- # Copyright (c) 2024 Fernando Libedinsky
2
- # Product: IAToolkit
3
- #
4
- # IAToolkit is open source software.
5
-
6
- import pytest
7
- from flask import Flask
8
- from unittest.mock import MagicMock, patch
9
- from iatoolkit.common.auth import IAuthentication
10
- from iatoolkit.views.external_login_view import ExternalLoginView
11
- from iatoolkit.services.query_service import QueryService
12
-
13
-
14
- class TestExternalLoginView:
15
- @staticmethod
16
- def create_app():
17
- """Configura la aplicación Flask para pruebas."""
18
- app = Flask(__name__)
19
- app.testing = True
20
- return app
21
-
22
- @pytest.fixture(autouse=True)
23
- def setup(self):
24
- """Configura el cliente y los mocks antes de cada test."""
25
- self.app = self.create_app()
26
- self.client = self.app.test_client()
27
- self.iauthentication = MagicMock(spec=IAuthentication)
28
- self.query_service = MagicMock(spec=QueryService)
29
-
30
- self.iauthentication.verify.return_value = {"success": True}
31
-
32
- # Registrar la vista
33
- view = ExternalLoginView.as_view("external_login",
34
- query_service=self.query_service,
35
- iauthentication=self.iauthentication)
36
- self.app.add_url_rule("/<company_short_name>/external_login/<external_user_id>",
37
- view_func=view, methods=["GET"])
38
-
39
- def test_get_with_valid_parameters(self):
40
- """Test que verifica que la vista funciona con parámetros válidos"""
41
- response = self.client.get("/test_company/external_login/user123")
42
-
43
- assert response.status_code == 200
44
- self.iauthentication.verify.assert_called_once_with("test_company", "user123")
45
- self.query_service.llm_init_context.assert_called_once_with(
46
- company_short_name="test_company",
47
- external_user_id="user123"
48
- )
49
-
50
- def test_get_with_authentication_failure(self):
51
- """Test que verifica el manejo de fallos de autenticación"""
52
- self.iauthentication.verify.return_value = {"success": False, "error": "Invalid credentials"}
53
-
54
- response = self.client.get("/test_company/external_login/user123")
55
-
56
- assert response.status_code == 401
57
- assert b"Invalid credentials" in response.data
58
-
59
- def test_get_with_service_exception(self):
60
- """Test que verifica el manejo de excepciones del servicio"""
61
- self.query_service.llm_init_context.side_effect = Exception("Service error")
62
-
63
- response = self.client.get("/test_company/external_login/user123")
64
-
65
- assert response.status_code == 500
66
- assert b"Service error" in response.data
67
-
68
- def test_get_with_different_company_and_user(self):
69
- """Test que verifica que funciona con diferentes combinaciones de company y user"""
70
- response = self.client.get("/my_company/external_login/employee456")
71
-
72
- assert response.status_code == 200
73
- self.iauthentication.verify.assert_called_once_with("my_company", "employee456")
74
- self.query_service.llm_init_context.assert_called_once_with(
75
- company_short_name="my_company",
76
- external_user_id="employee456"
77
- )
78
-
79
- def test_get_with_special_characters_in_parameters(self):
80
- """Test que verifica el manejo de caracteres especiales en los parámetros"""
81
- response = self.client.get("/test-company/external_login/user-123")
82
-
83
- assert response.status_code == 200
84
- self.iauthentication.verify.assert_called_once_with("test-company", "user-123")
85
- self.query_service.llm_init_context.assert_called_once_with(
86
- company_short_name="test-company",
87
- external_user_id="user-123"
88
- )
89
-
90
- @patch('iatoolkit.views.external_login_view.logging')
91
- def test_get_logs_exception_when_service_fails(self, mock_logging):
92
- """Test que verifica que se registra la excepción cuando falla el servicio"""
93
- self.query_service.llm_init_context.side_effect = Exception("Database connection failed")
94
-
95
- response = self.client.get("/test_company/external_login/user123")
96
-
97
- assert response.status_code == 500
98
- mock_logging.exception.assert_called_once()
99
- # Verificar que el mensaje de log contiene la información correcta
100
- call_args = mock_logging.exception.call_args[0][0]
101
- assert "test_company" in call_args
102
- assert "Database connection failed" in call_args
@@ -1,128 +0,0 @@
1
- # Copyright (c) 2024 Fernando Libedinsky
2
- # Product: IAToolkit
3
- #
4
- # IAToolkit is open source software.
5
-
6
- import pytest
7
- from unittest.mock import MagicMock
8
- from flask import Flask
9
- from iatoolkit.views.file_store_view import FileStoreView
10
- from iatoolkit.services.load_documents_service import LoadDocumentsService
11
- from iatoolkit.repositories.profile_repo import ProfileRepo
12
- import base64
13
-
14
-
15
- class TestFileStoreView:
16
-
17
- def setup_method(self):
18
- self.app = Flask(__name__)
19
- self.client = self.app.test_client()
20
-
21
- # Mock the services
22
- self.mock_doc_service = MagicMock(spec=LoadDocumentsService)
23
- self.mock_profile_repo = MagicMock(spec=ProfileRepo)
24
-
25
- # Instantiate the view with mocked services
26
- self.file_store_view = FileStoreView.as_view("load",
27
- doc_service=self.mock_doc_service,
28
- profile_repo=self.mock_profile_repo)
29
- self.app.add_url_rule('/load', view_func=self.file_store_view, methods=["POST"])
30
-
31
- @pytest.mark.parametrize("missing_field", ["company", "filename", "content"])
32
- def test_post_when_missing_required_fields(self, missing_field):
33
- payload = {
34
- "company": "test_company",
35
- "filename": "test_file.txt",
36
- "content": base64.b64encode(b"test content").decode('utf-8'),
37
- "metadata": {"key": "value"}
38
- }
39
- payload.pop(missing_field)
40
-
41
- response = self.client.post('/load', json=payload)
42
-
43
- assert response.status_code == 400
44
- assert response.get_json() == {
45
- "error": f"El campo {missing_field} es requerido"
46
- }
47
-
48
- self.mock_doc_service.load_file_callback.assert_not_called()
49
-
50
- def test_post_when_company_not_found(self):
51
- # Mock the profile repo to return None for the company
52
- self.mock_profile_repo.get_company_by_short_name.return_value = None
53
-
54
- payload = {
55
- "company": "nonexistent_company",
56
- "filename": "test_file.txt",
57
- "content": base64.b64encode(b"test content").decode('utf-8'),
58
- "metadata": {"key": "value"}
59
- }
60
-
61
- response = self.client.post('/load', json=payload)
62
-
63
- assert response.status_code == 400
64
- assert response.get_json() == {
65
- "error": "La empresa nonexistent_company no existe"
66
- }
67
-
68
- self.mock_profile_repo.get_company_by_short_name.assert_called_once_with("nonexistent_company")
69
- self.mock_doc_service.load_file_callback.assert_not_called()
70
-
71
- def test_post_when_internal_exception_error(self):
72
- # Mock the profile repo to return a company
73
- mock_company = MagicMock()
74
- self.mock_profile_repo.get_company_by_short_name.return_value = mock_company
75
-
76
- # Mock the doc service to raise an exception
77
- self.mock_doc_service.load_file_callback.side_effect = Exception("Internal Error")
78
-
79
- payload = {
80
- "company": "test_company",
81
- "filename": "test_file.txt",
82
- "content": base64.b64encode(b"test content").decode('utf-8'),
83
- "metadata": {"key": "value"}
84
- }
85
-
86
- response = self.client.post('/load', json=payload)
87
-
88
- assert response.status_code == 500
89
- response_json = response.get_json()
90
- assert response_json is not None, "Expected JSON response, got None"
91
-
92
- assert "error" in response_json
93
- assert response_json["error"] == "Internal Error"
94
-
95
- self.mock_profile_repo.get_company_by_short_name.assert_called_once_with("test_company")
96
- self.mock_doc_service.load_file_callback.assert_called_once()
97
-
98
- def test_post_when_successful_file_storage(self):
99
- # Mock the profile repo to return a company
100
- mock_company = MagicMock()
101
- self.mock_profile_repo.get_company_by_short_name.return_value = mock_company
102
-
103
- # Mock the document returned by the service
104
- mock_document = MagicMock()
105
- mock_document.id = 123
106
- self.mock_doc_service.load_file_callback.return_value = mock_document
107
-
108
- payload = {
109
- "company": "test_company",
110
- "filename": "test_file.txt",
111
- "content": base64.b64encode(b"test content").decode('utf-8'),
112
- "metadata": {"key": "value"}
113
- }
114
-
115
- response = self.client.post('/load', json=payload)
116
-
117
- assert response.status_code == 200
118
- assert response.get_json() == {
119
- "document_id": 123
120
- }
121
-
122
- self.mock_profile_repo.get_company_by_short_name.assert_called_once_with("test_company")
123
- self.mock_doc_service.load_file_callback.assert_called_once_with(
124
- filename="test_file.txt",
125
- content=b"test content",
126
- company=mock_company,
127
- context={'metadata':{"key": "value"}}
128
- )
@@ -1,142 +0,0 @@
1
- # Copyright (c) 2024 Fernando Libedinsky
2
- # Product: IAToolkit
3
- #
4
- # IAToolkit is open source software.
5
-
6
- import pytest
7
- from flask import Flask
8
- from unittest.mock import MagicMock, patch
9
- from iatoolkit.services.profile_service import ProfileService
10
- from iatoolkit.repositories.models import Company
11
- from iatoolkit.views.forgot_password_view import ForgotPasswordView
12
- import os
13
-
14
-
15
- class TestForgotPasswordView:
16
- @classmethod
17
- def setup_class(cls):
18
- cls.patcher = patch.dict(os.environ, {"PASS_RESET_KEY": "mocked_reset_key"})
19
- cls.patcher.start()
20
-
21
- @classmethod
22
- def teardown_class(cls):
23
- cls.patcher.stop()
24
-
25
- @staticmethod
26
- def create_app():
27
- """Configura la aplicación Flask para pruebas."""
28
- app = Flask(__name__)
29
- app.testing = True
30
- return app
31
-
32
- @pytest.fixture(autouse=True)
33
- def setup(self):
34
- """Configura el cliente y los mocks antes de cada test."""
35
- self.app = self.create_app()
36
- self.client = self.app.test_client()
37
- self.profile_service = MagicMock(spec=ProfileService)
38
- self.test_company = Company(
39
- id=1,
40
- name="Empresa de Prueba",
41
- short_name="test_company",
42
- logo_file="test_logo.png"
43
- )
44
- self.profile_service.get_company_by_short_name.return_value = self.test_company
45
-
46
- # Registrar la vista
47
- view = ForgotPasswordView.as_view("forgot_password", profile_service=self.profile_service)
48
- self.app.add_url_rule("/<company_short_name>/forgot_password", view_func=view, methods=["GET", "POST"])
49
-
50
- @patch("iatoolkit.views.forgot_password_view.render_template")
51
- def test_get_when_invalid_company(self, mock_render):
52
- self.profile_service.get_company_by_short_name.return_value = None
53
- response = self.client.get("/test_company/forgot_password")
54
- assert response.status_code == 404
55
-
56
-
57
- @patch("iatoolkit.views.forgot_password_view.render_template")
58
- def test_post_when_invalid_company(self, mock_render):
59
- self.profile_service.get_company_by_short_name.return_value = None
60
- response = self.client.post("/test_company/forgot_password",
61
- data={"email": "nonexistent@email.com"},
62
- content_type="application/x-www-form-urlencoded")
63
-
64
- assert response.status_code == 404
65
-
66
- @patch("iatoolkit.views.forgot_password_view.render_template")
67
- def test_get_forgot_password_page(self, mock_render_template):
68
- mock_render_template.return_value = "<html><body><h1>Forgot Password</h1></body></html>"
69
- response = self.client.get("/test_company/forgot_password")
70
-
71
- mock_render_template.assert_called_once_with(
72
- "forgot_password.html",
73
- company=self.test_company,
74
- company_short_name='test_company',
75
- )
76
- assert response.status_code == 200
77
-
78
- @patch("iatoolkit.views.forgot_password_view.url_for")
79
- @patch("iatoolkit.views.forgot_password_view.render_template")
80
- @patch("iatoolkit.views.forgot_password_view.URLSafeTimedSerializer")
81
- def test_post_with_error(self,
82
- mock_serializer,
83
- mock_render_template,
84
- mock_url_for):
85
- mock_serializer.return_value.loads.return_value = "nonexistent@email.com"
86
- mock_render_template.return_value = "<html><body><h1>Signup Page</h1></body></html>"
87
- mock_url_for.return_value = 'http://verification'
88
- self.profile_service.forgot_password.return_value = {'error': 'invalid email'}
89
-
90
- response = self.client.post("/test_company/forgot_password",
91
- data={"email": "nonexistent@email.com"},
92
- content_type="application/x-www-form-urlencoded")
93
-
94
- assert response.status_code == 400
95
- mock_render_template.assert_called_once_with(
96
- "forgot_password.html",
97
- company=self.test_company,
98
- company_short_name='test_company',
99
- form_data={"email": "nonexistent@email.com"},
100
- alert_message='invalid email'
101
- )
102
-
103
- @patch("iatoolkit.views.forgot_password_view.url_for")
104
- @patch("iatoolkit.views.forgot_password_view.render_template")
105
- @patch("iatoolkit.views.forgot_password_view.URLSafeTimedSerializer")
106
- def test_post_ok(self,
107
- mock_serializer,
108
- mock_render_template,
109
- mock_url_for):
110
- mock_serializer.return_value.loads.return_value = "nonexistent@email.com"
111
- mock_render_template.return_value = "<html><body><h1>Signup Page</h1></body></html>"
112
- mock_url_for.return_value = 'http://verification'
113
- self.profile_service.forgot_password.return_value = {"message": "link sent"}
114
-
115
- response = self.client.post("/test_company/forgot_password",
116
- data={"email": "nonexistent@email.com"},
117
- content_type="application/x-www-form-urlencoded")
118
-
119
- assert response.status_code == 200
120
- mock_render_template.assert_called_once_with(
121
- "login.html",
122
- company=self.test_company,
123
- company_short_name='test_company',
124
- alert_icon='success',
125
- alert_message="Hemos enviado un enlace a tu correo para restablecer la contraseña."
126
- )
127
-
128
- @patch("iatoolkit.views.forgot_password_view.render_template")
129
- @patch("iatoolkit.views.forgot_password_view.URLSafeTimedSerializer")
130
- def test_post_unexpected_error(self, mock_serializer, mock_render_template):
131
- mock_serializer.return_value.loads.side_effect = Exception('an error')
132
- response = self.client.post("/test_company/forgot_password",
133
- data={"email": "nonexistent@email.com"},
134
- content_type="application/x-www-form-urlencoded")
135
-
136
- mock_render_template.assert_called_once_with(
137
- "error.html",
138
- company=self.test_company,
139
- company_short_name='test_company',
140
- message="Ha ocurrido un error inesperado."
141
- )
142
- assert response.status_code == 500