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.
- iatoolkit/__init__.py +8 -34
- iatoolkit/base_company.py +14 -3
- iatoolkit/common/routes.py +83 -52
- iatoolkit/common/session_manager.py +0 -1
- iatoolkit/common/util.py +0 -27
- iatoolkit/iatoolkit.py +61 -46
- iatoolkit/infra/llm_client.py +7 -8
- iatoolkit/infra/openai_adapter.py +1 -1
- iatoolkit/infra/redis_session_manager.py +48 -2
- iatoolkit/repositories/database_manager.py +17 -2
- iatoolkit/repositories/models.py +31 -6
- iatoolkit/repositories/profile_repo.py +7 -2
- iatoolkit/services/auth_service.py +188 -0
- iatoolkit/services/branding_service.py +147 -0
- iatoolkit/services/dispatcher_service.py +10 -40
- iatoolkit/services/excel_service.py +15 -15
- iatoolkit/services/history_service.py +3 -12
- iatoolkit/services/jwt_service.py +15 -24
- iatoolkit/services/onboarding_service.py +43 -0
- iatoolkit/services/profile_service.py +97 -44
- iatoolkit/services/query_service.py +124 -81
- iatoolkit/services/tasks_service.py +1 -1
- iatoolkit/services/user_feedback_service.py +67 -31
- iatoolkit/services/user_session_context_service.py +112 -54
- iatoolkit/static/images/fernando.jpeg +0 -0
- iatoolkit/static/js/{chat_feedback.js → chat_feedback_button.js} +6 -11
- iatoolkit/static/js/chat_history_button.js +126 -0
- iatoolkit/static/js/chat_logout_button.js +36 -0
- iatoolkit/static/js/chat_main.js +130 -220
- iatoolkit/static/js/chat_onboarding_button.js +97 -0
- iatoolkit/static/js/chat_prompt_manager.js +94 -0
- iatoolkit/static/js/chat_reload_button.js +52 -0
- iatoolkit/static/styles/chat_iatoolkit.css +329 -507
- iatoolkit/static/styles/chat_modal.css +95 -56
- iatoolkit/static/styles/landing_page.css +182 -0
- iatoolkit/static/styles/onboarding.css +169 -0
- iatoolkit/system_prompts/query_main.prompt +3 -12
- iatoolkit/templates/_company_header.html +20 -0
- iatoolkit/templates/_login_widget.html +40 -0
- iatoolkit/templates/base.html +8 -3
- iatoolkit/templates/change_password.html +54 -37
- iatoolkit/templates/chat.html +149 -66
- iatoolkit/templates/chat_modals.html +47 -18
- iatoolkit/templates/error.html +41 -8
- iatoolkit/templates/forgot_password.html +37 -24
- iatoolkit/templates/index.html +140 -0
- iatoolkit/templates/login_simulation.html +34 -0
- iatoolkit/templates/onboarding_shell.html +105 -0
- iatoolkit/templates/signup.html +64 -66
- iatoolkit/views/base_login_view.py +81 -0
- iatoolkit/views/change_password_view.py +23 -12
- iatoolkit/views/external_login_view.py +61 -28
- iatoolkit/views/{file_store_view.py → file_store_api_view.py} +9 -2
- iatoolkit/views/forgot_password_view.py +23 -13
- iatoolkit/views/history_api_view.py +52 -0
- iatoolkit/views/home_view.py +58 -25
- iatoolkit/views/index_view.py +14 -0
- iatoolkit/views/init_context_api_view.py +68 -0
- iatoolkit/views/llmquery_api_view.py +45 -0
- iatoolkit/views/login_simulation_view.py +81 -0
- iatoolkit/views/login_view.py +118 -34
- iatoolkit/views/logout_api_view.py +45 -0
- iatoolkit/views/{prompt_view.py → prompt_api_view.py} +7 -7
- iatoolkit/views/signup_view.py +38 -29
- iatoolkit/views/{tasks_view.py → tasks_api_view.py} +10 -36
- iatoolkit/views/tasks_review_api_view.py +55 -0
- iatoolkit/views/{user_feedback_view.py → user_feedback_api_view.py} +16 -31
- iatoolkit/views/verify_user_view.py +13 -8
- {iatoolkit-0.8.1.dist-info → iatoolkit-0.63.4.dist-info}/METADATA +2 -2
- iatoolkit-0.63.4.dist-info/RECORD +113 -0
- {iatoolkit-0.8.1.dist-info → iatoolkit-0.63.4.dist-info}/top_level.txt +0 -1
- iatoolkit/common/auth.py +0 -200
- iatoolkit/static/images/arrow_up.png +0 -0
- iatoolkit/static/images/diagrama_iatoolkit.jpg +0 -0
- iatoolkit/static/images/logo_clinica.png +0 -0
- iatoolkit/static/images/logo_iatoolkit.png +0 -0
- iatoolkit/static/images/logo_maxxa.png +0 -0
- iatoolkit/static/images/logo_notaria.png +0 -0
- iatoolkit/static/images/logo_tarjeta.png +0 -0
- iatoolkit/static/images/logo_umayor.png +0 -0
- iatoolkit/static/images/upload.png +0 -0
- iatoolkit/static/js/chat_history.js +0 -117
- iatoolkit/templates/home.html +0 -201
- iatoolkit/templates/login.html +0 -43
- iatoolkit/views/chat_token_request_view.py +0 -98
- iatoolkit/views/chat_view.py +0 -51
- iatoolkit/views/download_file_view.py +0 -58
- iatoolkit/views/external_chat_login_view.py +0 -88
- iatoolkit/views/history_view.py +0 -57
- iatoolkit/views/llmquery_view.py +0 -65
- iatoolkit/views/tasks_review_view.py +0 -83
- iatoolkit-0.8.1.dist-info/RECORD +0 -175
- tests/__init__.py +0 -5
- tests/common/__init__.py +0 -0
- tests/common/test_auth.py +0 -279
- tests/common/test_routes.py +0 -42
- tests/common/test_session_manager.py +0 -59
- tests/common/test_util.py +0 -444
- tests/companies/__init__.py +0 -5
- tests/conftest.py +0 -36
- tests/infra/__init__.py +0 -5
- tests/infra/connectors/__init__.py +0 -5
- tests/infra/connectors/test_google_drive_connector.py +0 -107
- tests/infra/connectors/test_local_file_connector.py +0 -85
- tests/infra/connectors/test_s3_connector.py +0 -95
- tests/infra/test_call_service.py +0 -92
- tests/infra/test_database_manager.py +0 -59
- tests/infra/test_gemini_adapter.py +0 -137
- tests/infra/test_google_chat_app.py +0 -68
- tests/infra/test_llm_client.py +0 -165
- tests/infra/test_llm_proxy.py +0 -122
- tests/infra/test_mail_app.py +0 -94
- tests/infra/test_openai_adapter.py +0 -105
- tests/infra/test_redis_session_manager_service.py +0 -117
- tests/repositories/__init__.py +0 -5
- tests/repositories/test_database_manager.py +0 -87
- tests/repositories/test_document_repo.py +0 -76
- tests/repositories/test_llm_query_repo.py +0 -340
- tests/repositories/test_models.py +0 -38
- tests/repositories/test_profile_repo.py +0 -142
- tests/repositories/test_tasks_repo.py +0 -76
- tests/repositories/test_vs_repo.py +0 -107
- tests/services/__init__.py +0 -5
- tests/services/test_dispatcher_service.py +0 -274
- tests/services/test_document_service.py +0 -181
- tests/services/test_excel_service.py +0 -208
- tests/services/test_file_processor_service.py +0 -121
- tests/services/test_history_service.py +0 -164
- tests/services/test_jwt_service.py +0 -255
- tests/services/test_load_documents_service.py +0 -112
- tests/services/test_mail_service.py +0 -70
- tests/services/test_profile_service.py +0 -379
- tests/services/test_prompt_manager_service.py +0 -190
- tests/services/test_query_service.py +0 -243
- tests/services/test_search_service.py +0 -39
- tests/services/test_sql_service.py +0 -160
- tests/services/test_tasks_service.py +0 -252
- tests/services/test_user_feedback_service.py +0 -389
- tests/services/test_user_session_context_service.py +0 -132
- tests/views/__init__.py +0 -5
- tests/views/test_change_password_view.py +0 -191
- tests/views/test_chat_token_request_view.py +0 -188
- tests/views/test_chat_view.py +0 -98
- tests/views/test_download_file_view.py +0 -149
- tests/views/test_external_chat_login_view.py +0 -120
- tests/views/test_external_login_view.py +0 -102
- tests/views/test_file_store_view.py +0 -128
- tests/views/test_forgot_password_view.py +0 -142
- tests/views/test_history_view.py +0 -336
- tests/views/test_home_view.py +0 -61
- tests/views/test_llm_query_view.py +0 -154
- tests/views/test_login_view.py +0 -114
- tests/views/test_prompt_view.py +0 -111
- tests/views/test_signup_view.py +0 -140
- tests/views/test_tasks_review_view.py +0 -104
- tests/views/test_tasks_view.py +0 -130
- tests/views/test_user_feedback_view.py +0 -214
- tests/views/test_verify_user_view.py +0 -110
- {iatoolkit-0.8.1.dist-info → iatoolkit-0.63.4.dist-info}/WHEEL +0 -0
|
@@ -1,255 +0,0 @@
|
|
|
1
|
-
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
-
# Product: IAToolkit
|
|
3
|
-
#
|
|
4
|
-
# IAToolkit is open source software.
|
|
5
|
-
|
|
6
|
-
import pytest
|
|
7
|
-
import time
|
|
8
|
-
import jwt # Necesario para crear tokens con payloads específicos para casos de error
|
|
9
|
-
from flask import Flask
|
|
10
|
-
from iatoolkit.services.jwt_service import JWTService # Asegúrate que la ruta de importación sea correcta
|
|
11
|
-
from unittest.mock import patch
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
# Datos comunes para los tests, pueden ser definidos a nivel de módulo o clase
|
|
15
|
-
COMPANY_ID = 1
|
|
16
|
-
COMPANY_SHORT_NAME = "testcomp"
|
|
17
|
-
EXTERNAL_USER_ID = "user123"
|
|
18
|
-
EXPIRES_DELTA_SECONDS = 300 # 5 minutos
|
|
19
|
-
TEST_SECRET_KEY = 'test-super-secret-key'
|
|
20
|
-
TEST_ALGORITHM = 'HS256'
|
|
21
|
-
|
|
22
|
-
@pytest.fixture
|
|
23
|
-
def app():
|
|
24
|
-
"""Fixture para crear una instancia de la aplicación Flask para tests."""
|
|
25
|
-
flask_app = Flask(__name__)
|
|
26
|
-
flask_app.config['JWT_SECRET_KEY'] = TEST_SECRET_KEY
|
|
27
|
-
flask_app.config['JWT_ALGORITHM'] = TEST_ALGORITHM
|
|
28
|
-
return flask_app
|
|
29
|
-
|
|
30
|
-
@pytest.fixture
|
|
31
|
-
def jwt_service(app):
|
|
32
|
-
"""Fixture para crear una instancia de JWTService."""
|
|
33
|
-
return JWTService(app)
|
|
34
|
-
|
|
35
|
-
class TestJWTService:
|
|
36
|
-
|
|
37
|
-
def test_initialization_missing_config(self):
|
|
38
|
-
"""Prueba que JWTService levante RuntimeError si falta configuración."""
|
|
39
|
-
app_sin_config_key = Flask(__name__)
|
|
40
|
-
app_sin_config_key.config['JWT_ALGORITHM'] = TEST_ALGORITHM # Falta JWT_SECRET_KEY
|
|
41
|
-
with pytest.raises(RuntimeError, match="Configuración JWT esencial faltante: 'JWT_SECRET_KEY'"):
|
|
42
|
-
JWTService(app_sin_config_key)
|
|
43
|
-
|
|
44
|
-
app_sin_config_algo = Flask(__name__)
|
|
45
|
-
app_sin_config_algo.config['JWT_SECRET_KEY'] = TEST_SECRET_KEY # Falta JWT_ALGORITHM
|
|
46
|
-
with pytest.raises(RuntimeError, match="Configuración JWT esencial faltante: 'JWT_ALGORITHM'"):
|
|
47
|
-
JWTService(app_sin_config_algo)
|
|
48
|
-
|
|
49
|
-
def test_generate_chat_jwt_success(self, jwt_service, app):
|
|
50
|
-
"""Prueba la generación exitosa de un JWT."""
|
|
51
|
-
token = jwt_service.generate_chat_jwt(
|
|
52
|
-
COMPANY_ID,
|
|
53
|
-
COMPANY_SHORT_NAME,
|
|
54
|
-
EXTERNAL_USER_ID,
|
|
55
|
-
EXPIRES_DELTA_SECONDS
|
|
56
|
-
)
|
|
57
|
-
assert token is not None
|
|
58
|
-
assert isinstance(token, str)
|
|
59
|
-
|
|
60
|
-
# Decodificar para verificar el payload
|
|
61
|
-
payload = jwt.decode(
|
|
62
|
-
token,
|
|
63
|
-
app.config['JWT_SECRET_KEY'],
|
|
64
|
-
algorithms=[app.config['JWT_ALGORITHM']]
|
|
65
|
-
)
|
|
66
|
-
assert payload['company_id'] == COMPANY_ID
|
|
67
|
-
assert payload['company_short_name'] == COMPANY_SHORT_NAME
|
|
68
|
-
assert payload['external_user_id'] == EXTERNAL_USER_ID
|
|
69
|
-
assert payload['type'] == 'chat_session'
|
|
70
|
-
assert 'exp' in payload
|
|
71
|
-
assert 'iat' in payload
|
|
72
|
-
# Usar pytest.approx para comparar timestamps con una tolerancia
|
|
73
|
-
assert payload['exp'] == pytest.approx(time.time() + EXPIRES_DELTA_SECONDS, abs=5)
|
|
74
|
-
|
|
75
|
-
@patch('jwt.encode')
|
|
76
|
-
def test_generate_chat_jwt_encode_exception(self, mock_jwt_encode, jwt_service):
|
|
77
|
-
mock_jwt_encode.side_effect = Exception("JWT Encode Error")
|
|
78
|
-
token = jwt_service.generate_chat_jwt(
|
|
79
|
-
COMPANY_ID,
|
|
80
|
-
COMPANY_SHORT_NAME,
|
|
81
|
-
EXTERNAL_USER_ID,
|
|
82
|
-
EXPIRES_DELTA_SECONDS
|
|
83
|
-
)
|
|
84
|
-
assert token is None
|
|
85
|
-
|
|
86
|
-
def test_validate_chat_jwt_success(self, jwt_service):
|
|
87
|
-
"""Prueba la validación exitosa de un JWT."""
|
|
88
|
-
token = jwt_service.generate_chat_jwt(
|
|
89
|
-
COMPANY_ID,
|
|
90
|
-
COMPANY_SHORT_NAME,
|
|
91
|
-
EXTERNAL_USER_ID,
|
|
92
|
-
EXPIRES_DELTA_SECONDS
|
|
93
|
-
)
|
|
94
|
-
payload = jwt_service.validate_chat_jwt(token, COMPANY_SHORT_NAME)
|
|
95
|
-
assert payload is not None
|
|
96
|
-
assert payload['company_id'] == COMPANY_ID
|
|
97
|
-
assert payload['company_short_name'] == COMPANY_SHORT_NAME
|
|
98
|
-
assert payload['external_user_id'] == EXTERNAL_USER_ID
|
|
99
|
-
assert payload['type'] == 'chat_session'
|
|
100
|
-
|
|
101
|
-
def test_validate_chat_jwt_expired(self, jwt_service):
|
|
102
|
-
"""Prueba la validación de un JWT expirado."""
|
|
103
|
-
token = jwt_service.generate_chat_jwt(
|
|
104
|
-
COMPANY_ID,
|
|
105
|
-
COMPANY_SHORT_NAME,
|
|
106
|
-
EXTERNAL_USER_ID,
|
|
107
|
-
expires_delta_seconds=1 # Expira en 1 segundo
|
|
108
|
-
)
|
|
109
|
-
time.sleep(2) # Esperar a que expire
|
|
110
|
-
payload = jwt_service.validate_chat_jwt(token, COMPANY_SHORT_NAME)
|
|
111
|
-
assert payload is None
|
|
112
|
-
|
|
113
|
-
def test_validate_chat_jwt_invalid_signature(self, jwt_service, app):
|
|
114
|
-
"""Prueba la validación de un JWT con firma inválida."""
|
|
115
|
-
payload_data = {
|
|
116
|
-
'company_id': COMPANY_ID,
|
|
117
|
-
'company_short_name': COMPANY_SHORT_NAME,
|
|
118
|
-
'external_user_id': EXTERNAL_USER_ID,
|
|
119
|
-
'exp': time.time() + EXPIRES_DELTA_SECONDS,
|
|
120
|
-
'iat': time.time(),
|
|
121
|
-
'type': 'chat_session'
|
|
122
|
-
}
|
|
123
|
-
# Generar token con una clave secreta diferente
|
|
124
|
-
invalid_token = jwt.encode(
|
|
125
|
-
payload_data,
|
|
126
|
-
'another-wrong-secret-key',
|
|
127
|
-
algorithm=app.config['JWT_ALGORITHM']
|
|
128
|
-
)
|
|
129
|
-
payload = jwt_service.validate_chat_jwt(invalid_token, COMPANY_SHORT_NAME)
|
|
130
|
-
assert payload is None
|
|
131
|
-
|
|
132
|
-
def test_validate_chat_jwt_wrong_company_short_name(self, jwt_service):
|
|
133
|
-
"""Prueba la validación con un company_short_name incorrecto."""
|
|
134
|
-
token = jwt_service.generate_chat_jwt(
|
|
135
|
-
COMPANY_ID,
|
|
136
|
-
COMPANY_SHORT_NAME, # "testcomp"
|
|
137
|
-
EXTERNAL_USER_ID,
|
|
138
|
-
EXPIRES_DELTA_SECONDS
|
|
139
|
-
)
|
|
140
|
-
payload = jwt_service.validate_chat_jwt(token, "anothercomp")
|
|
141
|
-
assert payload is None
|
|
142
|
-
|
|
143
|
-
def test_validate_chat_jwt_incorrect_type(self, jwt_service, app):
|
|
144
|
-
"""Prueba la validación de un JWT con un tipo incorrecto."""
|
|
145
|
-
payload_data = {
|
|
146
|
-
'company_id': COMPANY_ID,
|
|
147
|
-
'company_short_name': COMPANY_SHORT_NAME,
|
|
148
|
-
'external_user_id': EXTERNAL_USER_ID,
|
|
149
|
-
'exp': time.time() + EXPIRES_DELTA_SECONDS,
|
|
150
|
-
'iat': time.time(),
|
|
151
|
-
'type': 'not_chat_session' # Tipo incorrecto
|
|
152
|
-
}
|
|
153
|
-
token_wrong_type = jwt.encode(
|
|
154
|
-
payload_data,
|
|
155
|
-
app.config['JWT_SECRET_KEY'],
|
|
156
|
-
algorithm=app.config['JWT_ALGORITHM']
|
|
157
|
-
)
|
|
158
|
-
payload = jwt_service.validate_chat_jwt(token_wrong_type, COMPANY_SHORT_NAME)
|
|
159
|
-
assert payload is None
|
|
160
|
-
|
|
161
|
-
def test_validate_chat_jwt_missing_external_user_id(self, jwt_service, app):
|
|
162
|
-
"""Prueba la validación de un JWT sin external_user_id."""
|
|
163
|
-
payload_data_missing = {
|
|
164
|
-
'company_id': COMPANY_ID,
|
|
165
|
-
'company_short_name': COMPANY_SHORT_NAME,
|
|
166
|
-
# 'external_user_id': EXTERNAL_USER_ID, # Ausente
|
|
167
|
-
'exp': time.time() + EXPIRES_DELTA_SECONDS,
|
|
168
|
-
'iat': time.time(),
|
|
169
|
-
'type': 'chat_session'
|
|
170
|
-
}
|
|
171
|
-
token_missing_field = jwt.encode(
|
|
172
|
-
payload_data_missing,
|
|
173
|
-
app.config['JWT_SECRET_KEY'],
|
|
174
|
-
algorithm=app.config['JWT_ALGORITHM']
|
|
175
|
-
)
|
|
176
|
-
payload = jwt_service.validate_chat_jwt(token_missing_field, COMPANY_SHORT_NAME)
|
|
177
|
-
assert payload is None
|
|
178
|
-
|
|
179
|
-
payload_data_empty_user_id = {
|
|
180
|
-
'company_id': COMPANY_ID,
|
|
181
|
-
'company_short_name': COMPANY_SHORT_NAME,
|
|
182
|
-
'external_user_id': "", # Vacío
|
|
183
|
-
'exp': time.time() + EXPIRES_DELTA_SECONDS,
|
|
184
|
-
'iat': time.time(),
|
|
185
|
-
'type': 'chat_session'
|
|
186
|
-
}
|
|
187
|
-
token_empty_user_id = jwt.encode(
|
|
188
|
-
payload_data_empty_user_id,
|
|
189
|
-
app.config['JWT_SECRET_KEY'],
|
|
190
|
-
algorithm=app.config['JWT_ALGORITHM']
|
|
191
|
-
)
|
|
192
|
-
payload = jwt_service.validate_chat_jwt(token_empty_user_id, COMPANY_SHORT_NAME)
|
|
193
|
-
assert payload is None
|
|
194
|
-
|
|
195
|
-
def test_validate_chat_jwt_missing_company_id(self, jwt_service, app):
|
|
196
|
-
"""Prueba la validación de un JWT sin company_id."""
|
|
197
|
-
payload_data = {
|
|
198
|
-
# 'company_id': COMPANY_ID, # Ausente
|
|
199
|
-
'company_short_name': COMPANY_SHORT_NAME,
|
|
200
|
-
'external_user_id': EXTERNAL_USER_ID,
|
|
201
|
-
'exp': time.time() + EXPIRES_DELTA_SECONDS,
|
|
202
|
-
'iat': time.time(),
|
|
203
|
-
'type': 'chat_session'
|
|
204
|
-
}
|
|
205
|
-
token_missing_field = jwt.encode(
|
|
206
|
-
payload_data,
|
|
207
|
-
app.config['JWT_SECRET_KEY'],
|
|
208
|
-
algorithm=app.config['JWT_ALGORITHM']
|
|
209
|
-
)
|
|
210
|
-
payload = jwt_service.validate_chat_jwt(token_missing_field, COMPANY_SHORT_NAME)
|
|
211
|
-
assert payload is None
|
|
212
|
-
|
|
213
|
-
def test_validate_chat_jwt_invalid_company_id_type(self, jwt_service, app):
|
|
214
|
-
"""Prueba la validación de un JWT con company_id de tipo incorrecto."""
|
|
215
|
-
payload_data = {
|
|
216
|
-
'company_id': "not_an_int", # Tipo incorrecto
|
|
217
|
-
'company_short_name': COMPANY_SHORT_NAME,
|
|
218
|
-
'external_user_id': EXTERNAL_USER_ID,
|
|
219
|
-
'exp': time.time() + EXPIRES_DELTA_SECONDS,
|
|
220
|
-
'iat': time.time(),
|
|
221
|
-
'type': 'chat_session'
|
|
222
|
-
}
|
|
223
|
-
token_invalid_type = jwt.encode(
|
|
224
|
-
payload_data,
|
|
225
|
-
app.config['JWT_SECRET_KEY'],
|
|
226
|
-
algorithm=app.config['JWT_ALGORITHM']
|
|
227
|
-
)
|
|
228
|
-
payload = jwt_service.validate_chat_jwt(token_invalid_type, COMPANY_SHORT_NAME)
|
|
229
|
-
assert payload is None
|
|
230
|
-
|
|
231
|
-
def test_validate_chat_jwt_empty_token(self, jwt_service):
|
|
232
|
-
"""Prueba la validación con un token vacío."""
|
|
233
|
-
payload = jwt_service.validate_chat_jwt("", COMPANY_SHORT_NAME)
|
|
234
|
-
assert payload is None
|
|
235
|
-
|
|
236
|
-
def test_validate_chat_jwt_none_token(self, jwt_service):
|
|
237
|
-
"""Prueba la validación con un token None."""
|
|
238
|
-
payload = jwt_service.validate_chat_jwt(None, COMPANY_SHORT_NAME)
|
|
239
|
-
assert payload is None
|
|
240
|
-
|
|
241
|
-
@patch('jwt.decode')
|
|
242
|
-
def test_validate_chat_jwt_decode_exception(self, mock_jwt_decode, jwt_service):
|
|
243
|
-
"""Prueba que validate_chat_jwt maneje excepciones generales de jwt.decode (no InvalidTokenError)."""
|
|
244
|
-
mock_jwt_decode.side_effect = Exception("JWT Decode Error")
|
|
245
|
-
# Necesitamos un token, aunque será el mock el que falle.
|
|
246
|
-
# Podríamos pasar cualquier string, pero generar uno real es más consistente.
|
|
247
|
-
token = jwt_service.generate_chat_jwt(
|
|
248
|
-
COMPANY_ID,
|
|
249
|
-
COMPANY_SHORT_NAME,
|
|
250
|
-
EXTERNAL_USER_ID,
|
|
251
|
-
EXPIRES_DELTA_SECONDS
|
|
252
|
-
)
|
|
253
|
-
payload = jwt_service.validate_chat_jwt(token, COMPANY_SHORT_NAME)
|
|
254
|
-
assert payload is None
|
|
255
|
-
|
|
@@ -1,112 +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 patch, MagicMock
|
|
8
|
-
from iatoolkit.services.load_documents_service import LoadDocumentsService
|
|
9
|
-
from iatoolkit.repositories.models import Company
|
|
10
|
-
from iatoolkit.common.exceptions import IAToolkitException
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class TestLoadDocumentsService:
|
|
14
|
-
def setup_method(self):
|
|
15
|
-
self.mock_vector_store = MagicMock()
|
|
16
|
-
self.mock_file_connector_factory = MagicMock()
|
|
17
|
-
self.mock_dispatcher = MagicMock()
|
|
18
|
-
self.mock_doc_service = MagicMock()
|
|
19
|
-
self.mock_doc_repo = MagicMock()
|
|
20
|
-
self.mock_profile_repo = MagicMock()
|
|
21
|
-
self.mock_llm_query_repo = MagicMock()
|
|
22
|
-
|
|
23
|
-
self.service = LoadDocumentsService(
|
|
24
|
-
doc_service=self.mock_doc_service,
|
|
25
|
-
doc_repo=self.mock_doc_repo,
|
|
26
|
-
profile_repo=self.mock_profile_repo,
|
|
27
|
-
llm_query_repo=self.mock_llm_query_repo,
|
|
28
|
-
vector_store=self.mock_vector_store,
|
|
29
|
-
file_connector_factory=self.mock_file_connector_factory,
|
|
30
|
-
dispatcher=self.mock_dispatcher
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
self.company = Company(
|
|
34
|
-
id=1,
|
|
35
|
-
name='a big company',
|
|
36
|
-
short_name='company'
|
|
37
|
-
)
|
|
38
|
-
self.mock_profile_repo.get_companies.return_value = [self.company]
|
|
39
|
-
|
|
40
|
-
def test_load_company_files_when_missing_connector(self):
|
|
41
|
-
|
|
42
|
-
with pytest.raises(IAToolkitException) as excinfo:
|
|
43
|
-
self.service.load_company_files(company=self.company,
|
|
44
|
-
connector_config={})
|
|
45
|
-
|
|
46
|
-
assert excinfo.value.error_type == IAToolkitException.ErrorType.MISSING_PARAMETER
|
|
47
|
-
assert "configurar conector" in str(excinfo.value)
|
|
48
|
-
|
|
49
|
-
@patch("logging.exception")
|
|
50
|
-
def test_load_company_files_when_exception(self, mock_logging_exception):
|
|
51
|
-
mock_connector_config = {"type": "s3"}
|
|
52
|
-
self.mock_file_connector_factory.create.side_effect = Exception("Test exception")
|
|
53
|
-
|
|
54
|
-
result = self.service.load_company_files(company=self.company,
|
|
55
|
-
connector_config=mock_connector_config)
|
|
56
|
-
|
|
57
|
-
assert result == {"error": "Test exception"}
|
|
58
|
-
mock_logging_exception.assert_called_once_with("Loading files error: %s", "Test exception")
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def test_load_file_when_document_exists(self, ):
|
|
62
|
-
filename = "mock_file.pdf"
|
|
63
|
-
content = b"mock content"
|
|
64
|
-
self.mock_doc_repo.get.return_value = True
|
|
65
|
-
|
|
66
|
-
self.service.load_file_callback(company=self.company,
|
|
67
|
-
filename=filename,
|
|
68
|
-
content=content)
|
|
69
|
-
|
|
70
|
-
self.service.doc_service.file_to_txt.assert_not_called()
|
|
71
|
-
self.mock_doc_repo.insert.assert_not_called()
|
|
72
|
-
self.service.vector_store.add_document.assert_not_called()
|
|
73
|
-
|
|
74
|
-
def test_load_files_when_exception_adding_document(self):
|
|
75
|
-
self.mock_doc_repo.get.return_value = None
|
|
76
|
-
self.mock_vector_store.add_document.side_effect = Exception("Error adding document")
|
|
77
|
-
|
|
78
|
-
filename = "mock_file.pdf"
|
|
79
|
-
content = b"mock content"
|
|
80
|
-
with pytest.raises(IAToolkitException) as excinfo:
|
|
81
|
-
result = self.service.load_file_callback(
|
|
82
|
-
company=self.company,
|
|
83
|
-
filename=filename,
|
|
84
|
-
content=content)
|
|
85
|
-
|
|
86
|
-
assert excinfo.value.error_type == IAToolkitException.ErrorType.LOAD_DOCUMENT_ERROR
|
|
87
|
-
assert "Error al procesar el archivo" in str(excinfo.value)
|
|
88
|
-
|
|
89
|
-
def test_load_when_file_is_created(self):
|
|
90
|
-
# Mock del archivo y contenido
|
|
91
|
-
filename = "mock_file.pdf"
|
|
92
|
-
content = b"mock content"
|
|
93
|
-
self.service.company = self.company
|
|
94
|
-
context = {'metadata': {"document_type": "certificate"}}
|
|
95
|
-
|
|
96
|
-
# Mock simulando que el archivo no existe
|
|
97
|
-
self.mock_doc_repo.get.return_value = None
|
|
98
|
-
|
|
99
|
-
# Mock de extracción de texto
|
|
100
|
-
extracted_text = "mock extracted content"
|
|
101
|
-
self.service.doc_service.file_to_txt.return_value = extracted_text
|
|
102
|
-
self.mock_dispatcher.get_metadata_from_filename.return_value = {}
|
|
103
|
-
|
|
104
|
-
self.service.load_file_callback(company=self.company,
|
|
105
|
-
filename=filename,
|
|
106
|
-
content=content,
|
|
107
|
-
context=context)
|
|
108
|
-
|
|
109
|
-
# Verificaciones
|
|
110
|
-
self.mock_doc_repo.get.assert_called_once_with(company_id=self.company.id, filename=filename)
|
|
111
|
-
self.service.doc_service.file_to_txt.assert_called_once_with(filename, content)
|
|
112
|
-
self.service.vector_store.add_document.assert_called_once()
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
-
# Product: IAToolkit
|
|
3
|
-
#
|
|
4
|
-
# IAToolkit is open source software.
|
|
5
|
-
|
|
6
|
-
from unittest.mock import MagicMock
|
|
7
|
-
from iatoolkit.services.mail_service import MailService
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class TestMailService:
|
|
11
|
-
|
|
12
|
-
def setup_method(self):
|
|
13
|
-
self.mock_mail_app = MagicMock()
|
|
14
|
-
|
|
15
|
-
# Instancia de MailService con la dependencia inyectada como mock
|
|
16
|
-
self.mail_service = MailService(mail_app=self.mock_mail_app)
|
|
17
|
-
|
|
18
|
-
# Datos por defecto para enviar email
|
|
19
|
-
self.recipient = 'destinatario@test.com'
|
|
20
|
-
self.subject = 'Prueba'
|
|
21
|
-
self.body = 'Contenido del mensaje'
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def test_send_mail_when_success(self):
|
|
25
|
-
result = self.mail_service.send_mail(
|
|
26
|
-
recipient=self.recipient,
|
|
27
|
-
subject=self.subject,
|
|
28
|
-
body=self.body,
|
|
29
|
-
attachments=[]
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
# Comprobamos si mail_app.send_email fue invocado correctamente
|
|
33
|
-
self.mock_mail_app.send_email.assert_called_once_with(
|
|
34
|
-
sender={'email': 'iatoolkit@iatoolkit.com', 'name': 'IAToolkit'},
|
|
35
|
-
to=self.recipient,
|
|
36
|
-
subject=self.subject,
|
|
37
|
-
body=self.body,
|
|
38
|
-
attachments=[] )
|
|
39
|
-
assert result == 'mail enviado'
|
|
40
|
-
|
|
41
|
-
def test_send_mail_when_no_recipient(self):
|
|
42
|
-
# Probamos el comportamiento cuando falta el receptor del mensaje
|
|
43
|
-
result = self.mail_service.send_mail(
|
|
44
|
-
subject=self.subject,
|
|
45
|
-
body=self.body
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
'''
|
|
49
|
-
self.mock_mail_app.send_email.assert_called_once_with(
|
|
50
|
-
sender={'email': 'iatoolkit@iatoolkit.com', 'name': 'IAToolkit'},
|
|
51
|
-
to=None,
|
|
52
|
-
subject=self.subject,
|
|
53
|
-
body=self.body,
|
|
54
|
-
attach=None,
|
|
55
|
-
attachments=None
|
|
56
|
-
)
|
|
57
|
-
assert result == 'mail enviado'
|
|
58
|
-
'''
|
|
59
|
-
|
|
60
|
-
def test_send_mail_partial_args(self):
|
|
61
|
-
# Comprobamos cuando faltan parcialmente argumentos como subject y body
|
|
62
|
-
result = self.mail_service.send_mail(recipient=self.recipient)
|
|
63
|
-
|
|
64
|
-
self.mock_mail_app.send_email.assert_called_once_with(
|
|
65
|
-
sender={'email': 'iatoolkit@iatoolkit.com', 'name': 'IAToolkit'},
|
|
66
|
-
to=self.recipient,
|
|
67
|
-
subject=None,
|
|
68
|
-
body=None,
|
|
69
|
-
attachments=[] )
|
|
70
|
-
assert result == 'mail enviado'
|