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,379 +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, patch
|
|
7
|
-
from iatoolkit.repositories.profile_repo import ProfileRepo
|
|
8
|
-
from iatoolkit.services.profile_service import ProfileService
|
|
9
|
-
from iatoolkit.services.query_service import QueryService
|
|
10
|
-
from iatoolkit.services.user_session_context_service import UserSessionContextService
|
|
11
|
-
from flask_bcrypt import generate_password_hash
|
|
12
|
-
from iatoolkit.repositories.models import User, Company
|
|
13
|
-
from iatoolkit.infra.mail_app import MailApp
|
|
14
|
-
import os
|
|
15
|
-
|
|
16
|
-
class TestProfileService:
|
|
17
|
-
@classmethod
|
|
18
|
-
def setup_class(cls):
|
|
19
|
-
cls.patcher = patch.dict(os.environ, {"USER_VERIF_KEY": "mocked_secret_key"})
|
|
20
|
-
cls.patcher.start()
|
|
21
|
-
|
|
22
|
-
def setup_method(self):
|
|
23
|
-
self.repo = MagicMock(ProfileRepo)
|
|
24
|
-
self.mail_app = MagicMock(MailApp)
|
|
25
|
-
self.session_context = MagicMock(UserSessionContextService)
|
|
26
|
-
self.query_service = MagicMock(QueryService)
|
|
27
|
-
|
|
28
|
-
# init the service with the mock
|
|
29
|
-
self.service = ProfileService(
|
|
30
|
-
profile_repo=self.repo,
|
|
31
|
-
session_context_service=self.session_context,
|
|
32
|
-
query_service=self.query_service,
|
|
33
|
-
mail_app=self.mail_app)
|
|
34
|
-
|
|
35
|
-
self.mock_user = User(email='test@opensoft.cl',
|
|
36
|
-
first_name='fernando', last_name='libe',
|
|
37
|
-
password=generate_password_hash("password").decode("utf-8"), verified=True)
|
|
38
|
-
|
|
39
|
-
self.mock_company = Company(name='my company',
|
|
40
|
-
short_name='test_company',
|
|
41
|
-
logo_file='company_logo.jpg')
|
|
42
|
-
self.repo.get_company_by_short_name.return_value = self.mock_company
|
|
43
|
-
|
|
44
|
-
self.mock_user.companies = [self.mock_company, Company(id=2, name="Test Company 2")]
|
|
45
|
-
|
|
46
|
-
def teardown_method(self):
|
|
47
|
-
self.patcher.stop()
|
|
48
|
-
|
|
49
|
-
def test_login_when_exception(self):
|
|
50
|
-
self.repo.get_user_by_email.side_effect = Exception('an error')
|
|
51
|
-
response = self.service.login(self.mock_company.short_name,
|
|
52
|
-
'fernando',
|
|
53
|
-
'a password'
|
|
54
|
-
)
|
|
55
|
-
|
|
56
|
-
assert "an error" == response['error']
|
|
57
|
-
|
|
58
|
-
def test_login_when_user_not_exist(self):
|
|
59
|
-
self.repo.get_user_by_email.return_value = None
|
|
60
|
-
response = self.service.login(self.mock_company.short_name,
|
|
61
|
-
'fernando',
|
|
62
|
-
'a password')
|
|
63
|
-
|
|
64
|
-
assert "Usuario no encontrado" == response['error']
|
|
65
|
-
|
|
66
|
-
def test_login_when_invalid_password(self):
|
|
67
|
-
# Simula un usuario válido pero con una contraseña incorrecta
|
|
68
|
-
mock_user = MagicMock()
|
|
69
|
-
mock_user.password = generate_password_hash("correct_password").decode("utf-8")
|
|
70
|
-
mock_user.verified = True
|
|
71
|
-
self.repo.get_user_by_email.return_value = mock_user
|
|
72
|
-
|
|
73
|
-
response = self.service.login(self.mock_company.short_name,
|
|
74
|
-
'fernando',
|
|
75
|
-
'wrong_password'
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
assert "Contraseña inválida" == response['error']
|
|
79
|
-
|
|
80
|
-
def test_login_when_company_not_in_user_companies(self):
|
|
81
|
-
self.repo.get_user_by_email.return_value = self.mock_user
|
|
82
|
-
self.mock_user.password = generate_password_hash("password").decode("utf-8")
|
|
83
|
-
self.mock_user.companies = [Company(id=2, name="Test Company 2")]
|
|
84
|
-
|
|
85
|
-
response = self.service.login(self.mock_company.short_name,
|
|
86
|
-
'fernando',
|
|
87
|
-
'password'
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
assert "Usuario no esta autorizado para esta empresa" == response['error']
|
|
91
|
-
|
|
92
|
-
def test_login_when_unverified_account(self):
|
|
93
|
-
self.mock_user.password = generate_password_hash("password").decode("utf-8")
|
|
94
|
-
self.mock_user.verified = False
|
|
95
|
-
self.repo.get_user_by_email.return_value = self.mock_user
|
|
96
|
-
|
|
97
|
-
response = self.service.login(self.mock_company.short_name,
|
|
98
|
-
'fernando',
|
|
99
|
-
'password'
|
|
100
|
-
)
|
|
101
|
-
|
|
102
|
-
assert "Tu cuenta no ha sido verificada" in response['error']
|
|
103
|
-
|
|
104
|
-
@patch("iatoolkit.services.profile_service.SessionManager")
|
|
105
|
-
def test_login_when_ok(self, mock_session):
|
|
106
|
-
mock_session.set.return_value = True
|
|
107
|
-
self.mock_user.password = generate_password_hash("password").decode("utf-8")
|
|
108
|
-
self.repo.get_user_by_email.return_value = self.mock_user
|
|
109
|
-
|
|
110
|
-
response = self.service.login(self.mock_company.short_name,
|
|
111
|
-
'test@email.com',
|
|
112
|
-
'password'
|
|
113
|
-
)
|
|
114
|
-
|
|
115
|
-
assert "Login exitoso" == response['message']
|
|
116
|
-
|
|
117
|
-
def test_signup_when_invalid_company(self):
|
|
118
|
-
self.repo.get_company_by_short_name.return_value = None
|
|
119
|
-
|
|
120
|
-
response = self.service.signup(
|
|
121
|
-
self.mock_company.short_name,
|
|
122
|
-
email='test@email.com',
|
|
123
|
-
first_name='Test', last_name='User',
|
|
124
|
-
password="password", confirm_password="password",
|
|
125
|
-
verification_url='http://verification'
|
|
126
|
-
)
|
|
127
|
-
|
|
128
|
-
assert "empresa test_company no existe" in response['error']
|
|
129
|
-
|
|
130
|
-
def test_signup_when_user_exist_and_invalid_password(self):
|
|
131
|
-
self.mock_user.password = generate_password_hash("password").decode("utf-8")
|
|
132
|
-
self.repo.get_user_by_email.return_value = self.mock_user
|
|
133
|
-
|
|
134
|
-
response = self.service.signup(
|
|
135
|
-
self.mock_company.short_name,
|
|
136
|
-
email='test@email.com',
|
|
137
|
-
first_name='Test', last_name='User',
|
|
138
|
-
password="invalid_password", confirm_password="password",
|
|
139
|
-
verification_url='http://verification'
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
assert "contraseña es incorrecta" in response['error']
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
def test_signup_when_user_exist_and_already_register(self):
|
|
146
|
-
self.repo.get_user_by_email.return_value = self.mock_user
|
|
147
|
-
|
|
148
|
-
response = self.service.signup(
|
|
149
|
-
self.mock_company.short_name,
|
|
150
|
-
email='test@email.com',
|
|
151
|
-
first_name='Test', last_name='User',
|
|
152
|
-
password="password", confirm_password="password",
|
|
153
|
-
verification_url='http://verification'
|
|
154
|
-
)
|
|
155
|
-
|
|
156
|
-
assert "Usuario ya registrado" in response['error']
|
|
157
|
-
|
|
158
|
-
def test_signup_when_user_exist_and_not_in_company(self):
|
|
159
|
-
self.repo.get_user_by_email.return_value = self.mock_user
|
|
160
|
-
self.mock_user.companies = []
|
|
161
|
-
|
|
162
|
-
response = self.service.signup(
|
|
163
|
-
self.mock_company.short_name,
|
|
164
|
-
email='test@email.com',
|
|
165
|
-
first_name='Test', last_name='User',
|
|
166
|
-
password="password", confirm_password="password",
|
|
167
|
-
verification_url='http://verification'
|
|
168
|
-
)
|
|
169
|
-
|
|
170
|
-
assert "Usuario asociado" in response['message']
|
|
171
|
-
self.repo.save_user.assert_called_once()
|
|
172
|
-
|
|
173
|
-
def test_signup_when_passwords_different(self):
|
|
174
|
-
self.repo.get_user_by_email.return_value = None
|
|
175
|
-
|
|
176
|
-
response = self.service.signup(
|
|
177
|
-
self.mock_company.short_name,
|
|
178
|
-
email='test@email.com',
|
|
179
|
-
first_name='Test', last_name='User',
|
|
180
|
-
password="Password1", confirm_password="Password2$1",
|
|
181
|
-
verification_url='http://verification'
|
|
182
|
-
)
|
|
183
|
-
|
|
184
|
-
assert "contraseñas no coinciden" in response['error']
|
|
185
|
-
|
|
186
|
-
def test_signup_when_passwords_incorrect2(self):
|
|
187
|
-
self.repo.get_user_by_email.return_value = None
|
|
188
|
-
|
|
189
|
-
response = self.service.signup(
|
|
190
|
-
self.mock_company.short_name,
|
|
191
|
-
email='test@email.com',
|
|
192
|
-
first_name='Test', last_name='User',
|
|
193
|
-
password="Password", confirm_password="Password",
|
|
194
|
-
verification_url='http://verification'
|
|
195
|
-
)
|
|
196
|
-
|
|
197
|
-
assert "número" in response['error']
|
|
198
|
-
|
|
199
|
-
def test_signup_when_passwords_incorrect3(self):
|
|
200
|
-
self.repo.get_user_by_email.return_value = None
|
|
201
|
-
|
|
202
|
-
response = self.service.signup(
|
|
203
|
-
self.mock_company.short_name,
|
|
204
|
-
email='test@email.com',
|
|
205
|
-
first_name='Test', last_name='User',
|
|
206
|
-
password="Passw1", confirm_password="Passw1",
|
|
207
|
-
verification_url='http://verification'
|
|
208
|
-
)
|
|
209
|
-
|
|
210
|
-
assert "8 caracteres" in response['error']
|
|
211
|
-
|
|
212
|
-
def test_signup_when_passwords_incorrect4(self):
|
|
213
|
-
self.repo.get_user_by_email.return_value = None
|
|
214
|
-
|
|
215
|
-
response = self.service.signup(
|
|
216
|
-
self.mock_company.short_name,
|
|
217
|
-
email='test@email.com',
|
|
218
|
-
first_name='Test', last_name='User',
|
|
219
|
-
password="password123", confirm_password="password123",
|
|
220
|
-
verification_url='http://verification'
|
|
221
|
-
)
|
|
222
|
-
|
|
223
|
-
assert "mayúscula" in response['error']
|
|
224
|
-
|
|
225
|
-
def test_signup_when_passwords_incorrect5(self):
|
|
226
|
-
self.repo.get_user_by_email.return_value = None
|
|
227
|
-
|
|
228
|
-
response = self.service.signup(
|
|
229
|
-
self.mock_company.short_name,
|
|
230
|
-
email='test@email.com',
|
|
231
|
-
first_name='Test', last_name='User',
|
|
232
|
-
password="Password123", confirm_password="Password123",
|
|
233
|
-
verification_url='http://verification'
|
|
234
|
-
)
|
|
235
|
-
|
|
236
|
-
assert "especial" in response['error']
|
|
237
|
-
|
|
238
|
-
def test_signup_when_ok(self):
|
|
239
|
-
self.repo.get_user_by_email.return_value = None
|
|
240
|
-
self.mail_app.send_email.return_value = True
|
|
241
|
-
|
|
242
|
-
response = self.service.signup(
|
|
243
|
-
self.mock_company.short_name,
|
|
244
|
-
email='test@email.com',
|
|
245
|
-
first_name='Test', last_name='User',
|
|
246
|
-
password="Password$1", confirm_password="Password$1",
|
|
247
|
-
verification_url='http://verification'
|
|
248
|
-
)
|
|
249
|
-
|
|
250
|
-
assert "Registro exitoso" in response['message']
|
|
251
|
-
self.mail_app.send_email.assert_called()
|
|
252
|
-
|
|
253
|
-
def test_signup_when_exception(self):
|
|
254
|
-
self.repo.get_user_by_email.side_effect = Exception('an error')
|
|
255
|
-
response = self.service.signup(
|
|
256
|
-
self.mock_company.short_name,
|
|
257
|
-
email='test@email.com',
|
|
258
|
-
first_name='Test', last_name='User',
|
|
259
|
-
password="password", confirm_password="password",
|
|
260
|
-
verification_url='http://verification'
|
|
261
|
-
)
|
|
262
|
-
|
|
263
|
-
assert "an error" == response['error']
|
|
264
|
-
|
|
265
|
-
def test_get_companies_when_ok(self):
|
|
266
|
-
self.repo.get_companies.return_value = [self.mock_company]
|
|
267
|
-
companies = self.service.get_companies()
|
|
268
|
-
assert companies == [self.mock_company]
|
|
269
|
-
|
|
270
|
-
def test_get_company_by_short_name_when_ok(self):
|
|
271
|
-
company = self.service.get_company_by_short_name('test_company')
|
|
272
|
-
assert company == self.mock_company
|
|
273
|
-
|
|
274
|
-
def test_update_user(self):
|
|
275
|
-
self.repo.update_user.return_value = self.mock_user
|
|
276
|
-
user = self.service.update_user('fl@opensoft.cl', first_name='fernando')
|
|
277
|
-
|
|
278
|
-
assert user == self.mock_user
|
|
279
|
-
|
|
280
|
-
def test_verify_account_when_user_not_exist(self):
|
|
281
|
-
self.repo.get_user_by_email.return_value = None
|
|
282
|
-
|
|
283
|
-
response = self.service.verify_account(email='test@email.com')
|
|
284
|
-
|
|
285
|
-
assert "El usuario no existe." in response['error']
|
|
286
|
-
|
|
287
|
-
def test_verify_account_when_exception(self):
|
|
288
|
-
self.repo.get_user_by_email.side_effect = Exception('an error')
|
|
289
|
-
response = self.service.verify_account(email='test@email.com')
|
|
290
|
-
|
|
291
|
-
assert "an error" == response['error']
|
|
292
|
-
|
|
293
|
-
def test_verify_account_when_ok(self):
|
|
294
|
-
self.repo.get_user_by_email.return_value = self.mock_user
|
|
295
|
-
response = self.service.verify_account(email='test@email.com')
|
|
296
|
-
|
|
297
|
-
assert "cuenta ha sido verificada" in response['message']
|
|
298
|
-
|
|
299
|
-
def test_change_password_when_password_mismatch(self):
|
|
300
|
-
response = self.service.change_password(
|
|
301
|
-
email='test@email.com',
|
|
302
|
-
temp_code='ABC',
|
|
303
|
-
new_password='pass1',
|
|
304
|
-
confirm_password='pass2'
|
|
305
|
-
)
|
|
306
|
-
assert "contraseñas no coinciden" in response['error']
|
|
307
|
-
|
|
308
|
-
def test_change_passworwd_when_invalid_code(self):
|
|
309
|
-
self.repo.get_user_by_email.return_value = self.mock_user
|
|
310
|
-
self.mock_user.temp_code = 'xYhvt'
|
|
311
|
-
response = self.service.change_password(
|
|
312
|
-
email='test@email.com',
|
|
313
|
-
temp_code='ABC',
|
|
314
|
-
new_password='pass1',
|
|
315
|
-
confirm_password='pass1'
|
|
316
|
-
)
|
|
317
|
-
assert "código temporal no es válido" in response['error']
|
|
318
|
-
|
|
319
|
-
def test_change_password_when_ok(self):
|
|
320
|
-
self.repo.get_user_by_email.return_value = self.mock_user
|
|
321
|
-
self.mock_user.temp_code = 'ABC'
|
|
322
|
-
response = self.service.change_password(
|
|
323
|
-
email='test@email.com',
|
|
324
|
-
temp_code=self.mock_user.temp_code,
|
|
325
|
-
new_password='pass1',
|
|
326
|
-
confirm_password='pass1'
|
|
327
|
-
)
|
|
328
|
-
assert "clave se cambio correctamente" in response['message']
|
|
329
|
-
|
|
330
|
-
def test_change_password_when_exception(self):
|
|
331
|
-
self.repo.get_user_by_email.return_value = self.mock_user
|
|
332
|
-
self.repo.update_password.side_effect = Exception('db error')
|
|
333
|
-
response = self.service.change_password(
|
|
334
|
-
email='test@email.com',
|
|
335
|
-
temp_code=self.mock_user.temp_code,
|
|
336
|
-
new_password='pass1',
|
|
337
|
-
confirm_password='pass1'
|
|
338
|
-
)
|
|
339
|
-
assert "db error" == response['error']
|
|
340
|
-
|
|
341
|
-
def test_forgot_password_when_user_not_exist(self):
|
|
342
|
-
self.repo.get_user_by_email.return_value = None
|
|
343
|
-
response = self.service.forgot_password(
|
|
344
|
-
email='test@email.com',
|
|
345
|
-
reset_url='http://a_reset_utl'
|
|
346
|
-
)
|
|
347
|
-
assert "El usuario no existe" in response['error']
|
|
348
|
-
|
|
349
|
-
def test_forgot_password_when_ok(self):
|
|
350
|
-
self.repo.get_user_by_email.return_value = self.mock_user
|
|
351
|
-
response = self.service.forgot_password(
|
|
352
|
-
email='test@email.com',
|
|
353
|
-
reset_url='http://a_reset_utl'
|
|
354
|
-
)
|
|
355
|
-
assert "se envio mail para cambio de clave" in response['message']
|
|
356
|
-
self.mail_app.send_email.assert_called()
|
|
357
|
-
|
|
358
|
-
def test_forgot_password_when_exception(self):
|
|
359
|
-
self.repo.get_user_by_email.return_value = self.mock_user
|
|
360
|
-
self.mail_app.send_email.side_effect = Exception('mail error')
|
|
361
|
-
response = self.service.forgot_password(
|
|
362
|
-
email='test@email.com',
|
|
363
|
-
reset_url='http://a_reset_utl'
|
|
364
|
-
)
|
|
365
|
-
|
|
366
|
-
assert "mail error" == response['error']
|
|
367
|
-
|
|
368
|
-
def test_new_api_key_when_not_company(self):
|
|
369
|
-
self.repo.get_company_by_short_name.return_value = None
|
|
370
|
-
response = self.service.new_api_key(company_short_name='test_company')
|
|
371
|
-
assert "test_company no existe" in response['error']
|
|
372
|
-
|
|
373
|
-
def test_new_api_key_when_ok(self):
|
|
374
|
-
self.repo.get_company_by_short_name.return_value = self.mock_company
|
|
375
|
-
response = self.service.new_api_key(company_short_name='test_company')
|
|
376
|
-
|
|
377
|
-
self.repo.create_api_key.assert_called()
|
|
378
|
-
assert response['api-key'] != ''
|
|
379
|
-
|
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
from unittest.mock import MagicMock, patch, mock_open
|
|
3
|
-
|
|
4
|
-
# Asegúrate de que todas las importaciones necesarias estén presentes y correctas
|
|
5
|
-
from iatoolkit.services.prompt_manager_service import PromptService
|
|
6
|
-
from iatoolkit.repositories.llm_query_repo import LLMQueryRepo
|
|
7
|
-
from iatoolkit.repositories.profile_repo import ProfileRepo
|
|
8
|
-
from iatoolkit.repositories.models import Prompt, PromptCategory, Company
|
|
9
|
-
from iatoolkit.common.exceptions import IAToolkitException
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class TestPromptService:
|
|
13
|
-
@pytest.fixture(autouse=True)
|
|
14
|
-
def setup(self):
|
|
15
|
-
"""Configura mocks y la instancia del servicio para cada test."""
|
|
16
|
-
self.llm_query_repo = MagicMock(spec=LLMQueryRepo)
|
|
17
|
-
self.profile_repo = MagicMock(spec=ProfileRepo)
|
|
18
|
-
self.prompt_service = PromptService(
|
|
19
|
-
llm_query_repo=self.llm_query_repo,
|
|
20
|
-
profile_repo=self.profile_repo
|
|
21
|
-
)
|
|
22
|
-
self.mock_company = MagicMock(spec=Company)
|
|
23
|
-
self.mock_company.id = 1
|
|
24
|
-
self.mock_company.name = 'Test Company'
|
|
25
|
-
self.mock_company.short_name = 'test_co'
|
|
26
|
-
|
|
27
|
-
# --- Tests para get_user_prompts ---
|
|
28
|
-
|
|
29
|
-
def test_get_user_prompts_company_not_found(self):
|
|
30
|
-
"""Prueba que se devuelve un error cuando la empresa no existe."""
|
|
31
|
-
self.profile_repo.get_company_by_short_name.return_value = None
|
|
32
|
-
result = self.prompt_service.get_user_prompts(company_short_name='nonexistent_company')
|
|
33
|
-
assert result == {'error': 'No existe la empresa: nonexistent_company'}
|
|
34
|
-
|
|
35
|
-
def test_get_user_prompts_no_prompts_exist(self):
|
|
36
|
-
"""Prueba que se devuelve una lista vacía cuando la empresa no tiene prompts."""
|
|
37
|
-
self.profile_repo.get_company_by_short_name.return_value = self.mock_company
|
|
38
|
-
self.llm_query_repo.get_prompts.return_value = []
|
|
39
|
-
result = self.prompt_service.get_user_prompts(company_short_name='test_company')
|
|
40
|
-
assert result == {'message': []}
|
|
41
|
-
|
|
42
|
-
def test_get_user_prompts_filters_inactive_and_groups_correctly(self):
|
|
43
|
-
"""Prueba que los prompts inactivos se filtran y que los activos se agrupan correctamente."""
|
|
44
|
-
# Usamos instancias reales de los modelos en lugar de Mocks para los datos.
|
|
45
|
-
category = PromptCategory(name='General', order=1)
|
|
46
|
-
active_prompt = Prompt(name='active_prompt', description='Active', active=True, order=1, category=category)
|
|
47
|
-
inactive_prompt = Prompt(name='inactive_prompt', description='Inactive', active=False, order=2,
|
|
48
|
-
category=category)
|
|
49
|
-
|
|
50
|
-
self.profile_repo.get_company_by_short_name.return_value = self.mock_company
|
|
51
|
-
self.llm_query_repo.get_prompts.return_value = [active_prompt, inactive_prompt]
|
|
52
|
-
|
|
53
|
-
result = self.prompt_service.get_user_prompts(company_short_name='test_company')
|
|
54
|
-
|
|
55
|
-
# Verificar que solo hay una categoría en el resultado
|
|
56
|
-
assert len(result['message']) == 1
|
|
57
|
-
# Verificar que dentro de esa categoría, solo hay un prompt (el activo)
|
|
58
|
-
assert len(result['message'][0]['prompts']) == 1
|
|
59
|
-
# Verificar que el prompt es el correcto
|
|
60
|
-
assert result['message'][0]['prompts'][0]['prompt'] == 'active_prompt'
|
|
61
|
-
|
|
62
|
-
# --- Tests para get_system_prompt ---
|
|
63
|
-
|
|
64
|
-
@patch('iatoolkit.services.prompt_manager_service.importlib.resources.read_text')
|
|
65
|
-
def test_get_system_prompt_success(self, mock_read_text):
|
|
66
|
-
"""Prueba la obtención exitosa de los prompts de sistema concatenados."""
|
|
67
|
-
prompt1 = Prompt(filename='system1.prompt')
|
|
68
|
-
prompt2 = Prompt(filename='system2.prompt')
|
|
69
|
-
self.llm_query_repo.get_system_prompts.return_value = [prompt1, prompt2]
|
|
70
|
-
|
|
71
|
-
# Configurar el mock para devolver diferentes contenidos en cada llamada
|
|
72
|
-
mock_read_text.side_effect = [
|
|
73
|
-
'Contenido 1',
|
|
74
|
-
'Contenido 2'
|
|
75
|
-
]
|
|
76
|
-
|
|
77
|
-
result = self.prompt_service.get_system_prompt()
|
|
78
|
-
|
|
79
|
-
assert result == "Contenido 1\nContenido 2"
|
|
80
|
-
assert mock_read_text.call_count == 2
|
|
81
|
-
|
|
82
|
-
@patch('iatoolkit.services.prompt_manager_service.os.path.exists', return_value=False)
|
|
83
|
-
@patch('iatoolkit.services.prompt_manager_service.logging')
|
|
84
|
-
def test_get_system_prompt_file_not_found(self, mock_logging, mock_exists):
|
|
85
|
-
"""Prueba que se loguea una advertencia si un archivo de prompt no existe."""
|
|
86
|
-
prompt1 = Prompt(filename='missing.prompt')
|
|
87
|
-
self.llm_query_repo.get_system_prompts.return_value = [prompt1]
|
|
88
|
-
|
|
89
|
-
result = self.prompt_service.get_system_prompt()
|
|
90
|
-
|
|
91
|
-
assert result == ""
|
|
92
|
-
mock_logging.warning.assert_called_once()
|
|
93
|
-
assert "no existe" in mock_logging.warning.call_args[0][0]
|
|
94
|
-
|
|
95
|
-
def test_get_system_prompt_handles_repo_exception(self):
|
|
96
|
-
"""Prueba que se maneja una excepción del repositorio."""
|
|
97
|
-
self.llm_query_repo.get_system_prompts.side_effect = Exception("DB Connection Error")
|
|
98
|
-
|
|
99
|
-
with pytest.raises(IAToolkitException) as exc_info:
|
|
100
|
-
self.prompt_service.get_system_prompt()
|
|
101
|
-
|
|
102
|
-
assert exc_info.value.error_type == IAToolkitException.ErrorType.PROMPT_ERROR
|
|
103
|
-
assert "DB Connection Error" in str(exc_info.value)
|
|
104
|
-
|
|
105
|
-
# --- Tests para get_prompt_content ---
|
|
106
|
-
|
|
107
|
-
@patch('iatoolkit.services.prompt_manager_service.os.path.exists', return_value=True)
|
|
108
|
-
@patch('builtins.open', new_callable=mock_open, read_data='Contenido específico del prompt.')
|
|
109
|
-
def test_get_prompt_content_success(self, mock_file, mock_exists):
|
|
110
|
-
"""Prueba la obtención exitosa del contenido de un prompt específico."""
|
|
111
|
-
mock_prompt = Prompt(filename='my_prompt.prompt')
|
|
112
|
-
self.llm_query_repo.get_prompt_by_name.return_value = mock_prompt
|
|
113
|
-
|
|
114
|
-
result = self.prompt_service.get_prompt_content(self.mock_company, 'my_prompt')
|
|
115
|
-
|
|
116
|
-
assert result == 'Contenido específico del prompt.'
|
|
117
|
-
self.llm_query_repo.get_prompt_by_name.assert_called_once_with(self.mock_company, 'my_prompt')
|
|
118
|
-
|
|
119
|
-
def test_get_prompt_content_prompt_not_in_db(self):
|
|
120
|
-
"""Prueba que se lanza una excepción si el prompt no se encuentra en la BD."""
|
|
121
|
-
self.llm_query_repo.get_prompt_by_name.return_value = None
|
|
122
|
-
|
|
123
|
-
with pytest.raises(IAToolkitException) as exc_info:
|
|
124
|
-
self.prompt_service.get_prompt_content(self.mock_company, 'non_existent_prompt')
|
|
125
|
-
|
|
126
|
-
assert exc_info.value.error_type == IAToolkitException.ErrorType.DOCUMENT_NOT_FOUND
|
|
127
|
-
|
|
128
|
-
# --- Tests para create_prompt ---
|
|
129
|
-
|
|
130
|
-
@patch('iatoolkit.services.prompt_manager_service.os.path.exists', return_value=True)
|
|
131
|
-
def test_create_prompt_success_for_company(self, mock_exists):
|
|
132
|
-
"""Prueba la creación exitosa de un prompt para una compañía."""
|
|
133
|
-
self.prompt_service.create_prompt(
|
|
134
|
-
prompt_name='new_prompt',
|
|
135
|
-
description='A new prompt',
|
|
136
|
-
order=1,
|
|
137
|
-
company=self.mock_company,
|
|
138
|
-
custom_fields = [{'data_key': 'key', 'label': ' a label'}]
|
|
139
|
-
)
|
|
140
|
-
|
|
141
|
-
self.llm_query_repo.create_or_update_prompt.assert_called_once()
|
|
142
|
-
call_args = self.llm_query_repo.create_or_update_prompt.call_args[0]
|
|
143
|
-
prompt_object = call_args[0]
|
|
144
|
-
|
|
145
|
-
assert isinstance(prompt_object, Prompt)
|
|
146
|
-
assert prompt_object.name == 'new_prompt'
|
|
147
|
-
assert prompt_object.company_id == self.mock_company.id
|
|
148
|
-
assert not prompt_object.is_system_prompt
|
|
149
|
-
assert 'new_prompt.prompt' in prompt_object.filename
|
|
150
|
-
assert prompt_object.custom_fields == [{'data_key': 'key', 'label': ' a label', 'type': 'text'}]
|
|
151
|
-
|
|
152
|
-
@patch('iatoolkit.services.prompt_manager_service.os.path.exists', return_value=True)
|
|
153
|
-
def test_create_prompt_when_invalid_custom_fields(self, mock_exists):
|
|
154
|
-
with pytest.raises(IAToolkitException) as exc_info:
|
|
155
|
-
self.prompt_service.create_prompt(
|
|
156
|
-
prompt_name='new_prompt',
|
|
157
|
-
description='A new prompt',
|
|
158
|
-
order=1,
|
|
159
|
-
company=self.mock_company,
|
|
160
|
-
custom_fields=[{'label': ' a label'}]
|
|
161
|
-
)
|
|
162
|
-
|
|
163
|
-
assert exc_info.value.error_type == IAToolkitException.ErrorType.INVALID_PARAMETER
|
|
164
|
-
|
|
165
|
-
@patch('iatoolkit.services.prompt_manager_service.os.path.exists', return_value=False)
|
|
166
|
-
def test_create_prompt_fails_if_file_does_not_exist(self, mock_exists):
|
|
167
|
-
"""Prueba que la creación falla si el archivo de plantilla no existe."""
|
|
168
|
-
with pytest.raises(IAToolkitException) as exc_info:
|
|
169
|
-
self.prompt_service.create_prompt(
|
|
170
|
-
prompt_name='prompt_with_missing_file',
|
|
171
|
-
description='Desc',
|
|
172
|
-
order=1,
|
|
173
|
-
company=self.mock_company
|
|
174
|
-
)
|
|
175
|
-
assert exc_info.value.error_type == IAToolkitException.ErrorType.INVALID_NAME
|
|
176
|
-
assert "No existe el archivo de prompt" in str(exc_info.value)
|
|
177
|
-
|
|
178
|
-
@patch('iatoolkit.services.prompt_manager_service.os.path.exists', return_value=True)
|
|
179
|
-
def test_create_prompt_handles_db_exception(self, mock_exists):
|
|
180
|
-
"""Prueba que se maneja una excepción de la base de datos al guardar."""
|
|
181
|
-
self.llm_query_repo.create_or_update_prompt.side_effect = Exception("DB Unique Constraint Failed")
|
|
182
|
-
|
|
183
|
-
with pytest.raises(IAToolkitException) as exc_info:
|
|
184
|
-
self.prompt_service.create_prompt(
|
|
185
|
-
prompt_name='any_prompt',
|
|
186
|
-
description='Desc',
|
|
187
|
-
order=1,
|
|
188
|
-
company=self.mock_company
|
|
189
|
-
)
|
|
190
|
-
assert exc_info.value.error_type == IAToolkitException.ErrorType.DATABASE_ERROR
|