|
2 | 2 | """ |
3 | 3 | Copyright (c) 2024, 2025, Oracle and/or its affiliates. |
4 | 4 | Licensed under the Universal Permissive License v1.0 as shown at http://oss.oracle.com/licenses/upl. |
| 5 | +
|
| 6 | +Note: Vector store selection tests have been moved to test_vs_options.py |
| 7 | +following the refactor that moved vector store functionality from st_common.py |
| 8 | +to vs_options.py. |
5 | 9 | """ |
6 | 10 | # spell-checker: disable |
7 | 11 |
|
8 | | -from unittest.mock import patch |
9 | | - |
10 | | -import pandas as pd |
11 | | -import pytest |
12 | | -import streamlit as st |
13 | | -from streamlit import session_state as state |
14 | | - |
15 | | -from client.utils import st_common |
16 | | - |
17 | | - |
18 | | -############################################################################# |
19 | | -# Fixtures |
20 | | -############################################################################# |
21 | | -@pytest.fixture |
22 | | -def vector_store_state(sample_vector_store_data): |
23 | | - """Setup common vector store state for tests using shared test data""" |
24 | | - # Setup initial state with vector search settings |
25 | | - state.client_settings = { |
26 | | - "vector_search": { |
27 | | - "enabled": True, |
28 | | - **sample_vector_store_data, |
29 | | - "top_k": 10, |
30 | | - "search_type": "Similarity", |
31 | | - "score_threshold": 0.5, |
32 | | - "fetch_k": 20, |
33 | | - "lambda_mult": 0.5, |
34 | | - }, |
35 | | - "database": {"alias": "DEFAULT"}, |
36 | | - "ll_model": {"model": "gpt-4", "temperature": 0.8}, |
37 | | - } |
38 | | - |
39 | | - # Set widget states to simulate user selections |
40 | | - state.selected_vector_search_model = sample_vector_store_data["model"] |
41 | | - state.selected_vector_search_chunk_size = sample_vector_store_data["chunk_size"] |
42 | | - state.selected_vector_search_chunk_overlap = sample_vector_store_data["chunk_overlap"] |
43 | | - state.selected_vector_search_distance_metric = sample_vector_store_data["distance_metric"] |
44 | | - state.selected_vector_search_alias = sample_vector_store_data["alias"] |
45 | | - state.selected_vector_search_index_type = sample_vector_store_data["index_type"] |
46 | | - |
47 | | - yield state |
48 | | - |
49 | | - # Cleanup after test |
50 | | - for key in list(state.keys()): |
51 | | - if key.startswith("selected_vector_search_"): |
52 | | - del state[key] |
53 | | - |
54 | | - |
55 | | -############################################################################# |
56 | | -# Test Vector Store Reset Button Functionality - Integration Tests |
57 | | -############################################################################# |
58 | | -class TestVectorStoreResetButtonIntegration: |
59 | | - """Integration tests for vector store selection Reset button""" |
60 | | - |
61 | | - def test_reset_button_callback_execution(self, app_server, vector_store_state, sample_vector_store_data): |
62 | | - """Test that the Reset button callback is properly executed when clicked""" |
63 | | - assert app_server is not None |
64 | | - assert vector_store_state is not None |
65 | | - |
66 | | - reset_callback_executed = False |
67 | | - |
68 | | - def mock_button(label, **kwargs): |
69 | | - nonlocal reset_callback_executed |
70 | | - if "Reset" in label and "on_click" in kwargs: |
71 | | - # Execute the callback to simulate button click |
72 | | - kwargs["on_click"]() |
73 | | - reset_callback_executed = True |
74 | | - return True |
75 | | - |
76 | | - with ( |
77 | | - patch.object(st.sidebar, "subheader"), |
78 | | - patch.object(st.sidebar, "button", side_effect=mock_button), |
79 | | - patch.object(st.sidebar, "selectbox"), |
80 | | - patch.object(st, "info"), |
81 | | - ): |
82 | | - # Create test dataframe using shared test data |
83 | | - vs_df = pd.DataFrame([sample_vector_store_data]) |
84 | | - |
85 | | - # Mock enabled models |
86 | | - with patch.object(st_common, "enabled_models_lookup") as mock_models: |
87 | | - mock_models.return_value = {"openai/text-embed-3": {"id": "text-embed-3"}} |
88 | | - |
89 | | - # Call the function |
90 | | - st_common.render_vector_store_selection(vs_df) |
91 | | - |
92 | | - # Verify reset callback was executed |
93 | | - assert reset_callback_executed |
94 | | - |
95 | | - # Verify all widget states are cleared |
96 | | - assert state.selected_vector_search_model == "" |
97 | | - assert state.selected_vector_search_chunk_size == "" |
98 | | - assert state.selected_vector_search_chunk_overlap == "" |
99 | | - assert state.selected_vector_search_distance_metric == "" |
100 | | - assert state.selected_vector_search_alias == "" |
101 | | - assert state.selected_vector_search_index_type == "" |
102 | | - |
103 | | - # Verify client_settings are also cleared |
104 | | - assert state.client_settings["vector_search"]["model"] == "" |
105 | | - assert state.client_settings["vector_search"]["chunk_size"] == "" |
106 | | - assert state.client_settings["vector_search"]["chunk_overlap"] == "" |
107 | | - assert state.client_settings["vector_search"]["distance_metric"] == "" |
108 | | - assert state.client_settings["vector_search"]["vector_store"] == "" |
109 | | - assert state.client_settings["vector_search"]["alias"] == "" |
110 | | - assert state.client_settings["vector_search"]["index_type"] == "" |
111 | | - |
112 | | - def test_reset_preserves_non_vector_store_settings(self, app_server, vector_store_state, sample_vector_store_data): |
113 | | - """Test that Reset only affects vector store fields, not other settings""" |
114 | | - assert app_server is not None |
115 | | - assert vector_store_state is not None |
116 | | - |
117 | | - def mock_button(label, **kwargs): |
118 | | - if "Reset" in label and "on_click" in kwargs: |
119 | | - kwargs["on_click"]() |
120 | | - return True |
121 | | - |
122 | | - with ( |
123 | | - patch.object(st.sidebar, "subheader"), |
124 | | - patch.object(st.sidebar, "button", side_effect=mock_button), |
125 | | - patch.object(st.sidebar, "selectbox"), |
126 | | - patch.object(st, "info"), |
127 | | - ): |
128 | | - vs_df = pd.DataFrame([sample_vector_store_data]) |
129 | | - |
130 | | - with patch.object(st_common, "enabled_models_lookup") as mock_models: |
131 | | - mock_models.return_value = {"openai/text-embed-3": {"id": "text-embed-3"}} |
132 | | - st_common.render_vector_store_selection(vs_df) |
133 | | - |
134 | | - # Vector store fields should be cleared |
135 | | - assert state.client_settings["vector_search"]["model"] == "" |
136 | | - assert state.client_settings["vector_search"]["alias"] == "" |
137 | | - |
138 | | - # Other settings should be preserved |
139 | | - assert state.client_settings["vector_search"]["top_k"] == 10 |
140 | | - assert state.client_settings["vector_search"]["search_type"] == "Similarity" |
141 | | - assert state.client_settings["vector_search"]["score_threshold"] == 0.5 |
142 | | - assert state.client_settings["database"]["alias"] == "DEFAULT" |
143 | | - assert state.client_settings["ll_model"]["model"] == "gpt-4" |
144 | | - assert state.client_settings["ll_model"]["temperature"] == 0.8 |
145 | | - |
146 | | - def test_auto_population_after_reset_single_option(self, app_server, sample_vector_store_data): |
147 | | - """Test that fields with single options are auto-populated after reset""" |
148 | | - assert app_server is not None |
149 | | - |
150 | | - # Setup clean state |
151 | | - state.client_settings = { |
152 | | - "vector_search": { |
153 | | - "enabled": True, |
154 | | - "model": "", # Empty after reset |
155 | | - "chunk_size": "", |
156 | | - "chunk_overlap": "", |
157 | | - "distance_metric": "", |
158 | | - "vector_store": "", |
159 | | - "alias": "", |
160 | | - "index_type": "", |
161 | | - "top_k": 10, |
162 | | - "search_type": "Similarity", |
163 | | - "score_threshold": 0.5, |
164 | | - "fetch_k": 20, |
165 | | - "lambda_mult": 0.5, |
166 | | - }, |
167 | | - "database": {"alias": "DEFAULT"}, |
168 | | - } |
169 | | - |
170 | | - # Clear widget states (simulating post-reset state) |
171 | | - state.selected_vector_search_model = "" |
172 | | - state.selected_vector_search_chunk_size = "" |
173 | | - state.selected_vector_search_chunk_overlap = "" |
174 | | - state.selected_vector_search_distance_metric = "" |
175 | | - state.selected_vector_search_alias = "" |
176 | | - state.selected_vector_search_index_type = "" |
177 | | - |
178 | | - selectbox_calls = [] |
179 | | - |
180 | | - def mock_selectbox(label, options, key, index, disabled=False): |
181 | | - selectbox_calls.append( |
182 | | - {"label": label, "options": options, "key": key, "index": index, "disabled": disabled} |
183 | | - ) |
184 | | - # Return the value at index |
185 | | - return options[index] if 0 <= index < len(options) else "" |
186 | | - |
187 | | - with ( |
188 | | - patch.object(st.sidebar, "subheader"), |
189 | | - patch.object(st.sidebar, "button"), |
190 | | - patch.object(st.sidebar, "selectbox", side_effect=mock_selectbox), |
191 | | - patch.object(st, "info"), |
192 | | - ): |
193 | | - # Create dataframe with single option per field using shared fixture |
194 | | - single_vs = sample_vector_store_data.copy() |
195 | | - single_vs["alias"] = "single_alias" |
196 | | - single_vs["vector_store"] = "single_vs" |
197 | | - vs_df = pd.DataFrame([single_vs]) |
198 | | - |
199 | | - with patch.object(st_common, "enabled_models_lookup") as mock_models: |
200 | | - mock_models.return_value = {"openai/text-embed-3": {"id": "text-embed-3"}} |
201 | | - st_common.render_vector_store_selection(vs_df) |
202 | | - |
203 | | - # Verify auto-population happened for single options |
204 | | - assert state.client_settings["vector_search"]["alias"] == "single_alias" |
205 | | - assert state.client_settings["vector_search"]["model"] == sample_vector_store_data["model"] |
206 | | - assert state.client_settings["vector_search"]["chunk_size"] == sample_vector_store_data["chunk_size"] |
207 | | - assert state.client_settings["vector_search"]["chunk_overlap"] == sample_vector_store_data["chunk_overlap"] |
208 | | - assert ( |
209 | | - state.client_settings["vector_search"]["distance_metric"] |
210 | | - == sample_vector_store_data["distance_metric"] |
211 | | - ) |
212 | | - assert state.client_settings["vector_search"]["index_type"] == sample_vector_store_data["index_type"] |
213 | | - |
214 | | - # Verify widget states were also set |
215 | | - assert state.selected_vector_search_alias == "single_alias" |
216 | | - assert state.selected_vector_search_model == sample_vector_store_data["model"] |
217 | | - |
218 | | - def test_no_auto_population_with_multiple_options( |
219 | | - self, app_server, sample_vector_store_data, sample_vector_store_data_alt |
220 | | - ): |
221 | | - """Test that fields with multiple options are NOT auto-populated after reset""" |
222 | | - assert app_server is not None |
223 | | - |
224 | | - # Setup clean state after reset |
225 | | - state.client_settings = { |
226 | | - "vector_search": { |
227 | | - "enabled": True, |
228 | | - "model": "", |
229 | | - "chunk_size": "", |
230 | | - "chunk_overlap": "", |
231 | | - "distance_metric": "", |
232 | | - "vector_store": "", |
233 | | - "alias": "", |
234 | | - "index_type": "", |
235 | | - "top_k": 10, |
236 | | - "search_type": "Similarity", |
237 | | - "score_threshold": 0.5, |
238 | | - "fetch_k": 20, |
239 | | - "lambda_mult": 0.5, |
240 | | - }, |
241 | | - "database": {"alias": "DEFAULT"}, |
242 | | - } |
243 | | - |
244 | | - # Clear widget states |
245 | | - for key in ["model", "chunk_size", "chunk_overlap", "distance_metric", "alias", "index_type"]: |
246 | | - state[f"selected_vector_search_{key}"] = "" |
247 | | - |
248 | | - with ( |
249 | | - patch.object(st.sidebar, "subheader"), |
250 | | - patch.object(st.sidebar, "button"), |
251 | | - patch.object(st.sidebar, "selectbox", return_value=""), |
252 | | - patch.object(st, "info"), |
253 | | - ): |
254 | | - # Create dataframe with multiple options using shared fixtures |
255 | | - vs1 = sample_vector_store_data.copy() |
256 | | - vs1["alias"] = "alias1" |
257 | | - vs2 = sample_vector_store_data_alt.copy() |
258 | | - vs2["alias"] = "alias2" |
259 | | - vs_df = pd.DataFrame([vs1, vs2]) |
260 | | - |
261 | | - with patch.object(st_common, "enabled_models_lookup") as mock_models: |
262 | | - mock_models.return_value = {"openai/text-embed-3": {"id": "text-embed-3"}} |
263 | | - st_common.render_vector_store_selection(vs_df) |
264 | | - |
265 | | - # With multiple options, fields should remain empty (no auto-population) |
266 | | - assert state.client_settings["vector_search"]["alias"] == "" |
267 | | - assert state.client_settings["vector_search"]["chunk_size"] == "" |
268 | | - assert state.client_settings["vector_search"]["chunk_overlap"] == "" |
269 | | - assert state.client_settings["vector_search"]["distance_metric"] == "" |
270 | | - assert state.client_settings["vector_search"]["index_type"] == "" |
271 | | - |
272 | | - def test_reset_button_with_filtered_dataframe( |
273 | | - self, app_server, sample_vector_store_data, sample_vector_store_data_alt |
274 | | - ): |
275 | | - """Test reset button behavior with dynamically filtered dataframes""" |
276 | | - assert app_server is not None |
277 | | - |
278 | | - # Setup state with a filter already applied |
279 | | - state.client_settings = { |
280 | | - "vector_search": { |
281 | | - "enabled": True, |
282 | | - "model": sample_vector_store_data["model"], |
283 | | - "chunk_size": sample_vector_store_data["chunk_size"], |
284 | | - "chunk_overlap": "", |
285 | | - "distance_metric": "", |
286 | | - "vector_store": "", |
287 | | - "alias": "alias1", # Filter applied |
288 | | - "index_type": "", |
289 | | - "top_k": 10, |
290 | | - "search_type": "Similarity", |
291 | | - "score_threshold": 0.5, |
292 | | - "fetch_k": 20, |
293 | | - "lambda_mult": 0.5, |
294 | | - }, |
295 | | - "database": {"alias": "DEFAULT"}, |
296 | | - } |
297 | | - |
298 | | - state.selected_vector_search_alias = "alias1" |
299 | | - state.selected_vector_search_model = sample_vector_store_data["model"] |
300 | | - state.selected_vector_search_chunk_size = sample_vector_store_data["chunk_size"] |
301 | | - |
302 | | - def mock_button(label, **kwargs): |
303 | | - if "Reset" in label and "on_click" in kwargs: |
304 | | - kwargs["on_click"]() |
305 | | - return True |
306 | | - |
307 | | - with ( |
308 | | - patch.object(st.sidebar, "subheader"), |
309 | | - patch.object(st.sidebar, "button", side_effect=mock_button), |
310 | | - patch.object(st.sidebar, "selectbox", return_value=""), |
311 | | - patch.object(st, "info"), |
312 | | - ): |
313 | | - # Create dataframe with same alias using shared fixtures |
314 | | - vs1 = sample_vector_store_data.copy() |
315 | | - vs1["alias"] = "alias1" |
316 | | - vs2 = sample_vector_store_data_alt.copy() |
317 | | - vs2["alias"] = "alias1" |
318 | | - vs_df = pd.DataFrame([vs1, vs2]) |
319 | | - |
320 | | - with patch.object(st_common, "enabled_models_lookup") as mock_models: |
321 | | - mock_models.return_value = {"openai/text-embed-3": {"id": "text-embed-3"}} |
322 | | - st_common.render_vector_store_selection(vs_df) |
323 | | - |
324 | | - # After reset, all filters should be cleared |
325 | | - assert state.selected_vector_search_alias == "" |
326 | | - assert state.selected_vector_search_model == "" |
327 | | - assert state.selected_vector_search_chunk_size == "" |
328 | | - assert state.client_settings["vector_search"]["alias"] == "" |
329 | | - assert state.client_settings["vector_search"]["model"] == "" |
330 | | - assert state.client_settings["vector_search"]["chunk_size"] == "" |
| 12 | +# This file previously contained integration tests for vector store selection |
| 13 | +# functionality that was part of st_common.py. Those tests have been moved to: |
| 14 | +# tests/client/integration/utils/test_vs_options.py |
| 15 | +# |
| 16 | +# The st_common.py module no longer contains vector store selection functions. |
| 17 | +# See vs_options.py for: |
| 18 | +# - vector_search_sidebar() |
| 19 | +# - vector_store_selection() |
| 20 | +# - Related helper functions (_get_vs_fields, _reset_selections, etc.) |
0 commit comments