Total Complexity | 41 |
Total Lines | 472 |
Duplicated Lines | 23.94 % |
Changes | 0 |
Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like test_delete often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
1 | import time |
||
2 | import random |
||
3 | import pdb |
||
4 | import copy |
||
5 | import threading |
||
6 | import logging |
||
7 | from multiprocessing import Pool, Process |
||
8 | import pytest |
||
9 | from utils import * |
||
10 | |||
11 | |||
12 | dim = 128 |
||
13 | segment_row_count = 5000 |
||
14 | collection_id = "test_delete" |
||
15 | DELETE_TIMEOUT = 60 |
||
16 | tag = "1970-01-01" |
||
17 | nb = 6000 |
||
18 | field_name = "float_vector" |
||
19 | entity = gen_entities(1) |
||
20 | raw_vector, binary_entity = gen_binary_entities(1) |
||
21 | entities = gen_entities(nb) |
||
22 | raw_vectors, binary_entities = gen_binary_entities(nb) |
||
23 | default_single_query = { |
||
24 | "bool": { |
||
25 | "must": [ |
||
26 | {"vector": {field_name: {"topk": 10, "query": gen_vectors(1, dim), "params": {"nprobe": 10}}}} |
||
27 | ] |
||
28 | } |
||
29 | } |
||
30 | |||
31 | class TestDeleteBase: |
||
32 | """ |
||
33 | ****************************************************************** |
||
34 | The following cases are used to test `delete_entity_by_id` function |
||
35 | ****************************************************************** |
||
36 | """ |
||
37 | View Code Duplication | @pytest.fixture( |
|
|
|||
38 | scope="function", |
||
39 | params=gen_simple_index() |
||
40 | ) |
||
41 | def get_simple_index(self, request, connect): |
||
42 | if str(connect._cmd("mode")) == "GPU": |
||
43 | if not request.param["index_type"] not in ivf(): |
||
44 | pytest.skip("Only support index_type: idmap/ivf") |
||
45 | if str(connect._cmd("mode")) == "CPU": |
||
46 | if request.param["index_type"] in index_cpu_not_support(): |
||
47 | pytest.skip("CPU not support index_type: ivf_sq8h") |
||
48 | return request.param |
||
49 | |||
50 | @pytest.fixture( |
||
51 | scope="function", |
||
52 | params=[ |
||
53 | 1, |
||
54 | 6000 |
||
55 | ], |
||
56 | ) |
||
57 | def insert_count(self, request): |
||
58 | yield request.param |
||
59 | |||
60 | def test_delete_entity_id_not_exised(self, connect, collection): |
||
61 | ''' |
||
62 | target: test delete entity, params entity_id not existed |
||
63 | method: add entity and delete |
||
64 | expected: status DELETED |
||
65 | ''' |
||
66 | ids = connect.insert(collection, entity) |
||
67 | connect.flush([collection]) |
||
68 | status = connect.delete_entity_by_id(collection, [0]) |
||
69 | assert status |
||
70 | |||
71 | # TODO |
||
72 | def test_delete_empty_collection(self, connect, collection): |
||
73 | ''' |
||
74 | target: test delete entity, params collection_name not existed |
||
75 | method: add entity and delete |
||
76 | expected: status DELETED |
||
77 | ''' |
||
78 | status = connect.delete_entity_by_id(collection, [0]) |
||
79 | assert status |
||
80 | |||
81 | def test_delete_entity_collection_not_existed(self, connect, collection): |
||
82 | ''' |
||
83 | target: test delete entity, params collection_name not existed |
||
84 | method: add entity and delete |
||
85 | expected: error raised |
||
86 | ''' |
||
87 | collection_new = gen_unique_str() |
||
88 | with pytest.raises(Exception) as e: |
||
89 | status = connect.delete_entity_by_id(collection_new, [0]) |
||
90 | |||
91 | def test_delete_entity_collection_not_existed(self, connect, collection): |
||
92 | ''' |
||
93 | target: test delete entity, params collection_name not existed |
||
94 | method: add entity and delete |
||
95 | expected: error raised |
||
96 | ''' |
||
97 | ids = connect.insert(collection, entity) |
||
98 | connect.flush([collection]) |
||
99 | collection_new = gen_unique_str() |
||
100 | with pytest.raises(Exception) as e: |
||
101 | status = connect.delete_entity_by_id(collection_new, [0]) |
||
102 | |||
103 | # TODO: |
||
104 | def test_insert_delete(self, connect, collection, insert_count): |
||
105 | ''' |
||
106 | target: test delete entity |
||
107 | method: add entities and delete |
||
108 | expected: no error raised |
||
109 | ''' |
||
110 | entities = gen_entities(insert_count) |
||
111 | ids = connect.insert(collection, entities) |
||
112 | connect.flush([collection]) |
||
113 | delete_ids = [ids[0]] |
||
114 | status = connect.delete_entity_by_id(collection, delete_ids) |
||
115 | assert status |
||
116 | |||
117 | View Code Duplication | @pytest.mark.level(2) |
|
118 | def test_insert_delete_A(self, connect, collection): |
||
119 | ''' |
||
120 | target: test delete entity |
||
121 | method: add entities and delete one in collection, and one not in collection |
||
122 | expected: no error raised |
||
123 | ''' |
||
124 | ids = connect.insert(collection, entities) |
||
125 | connect.flush([collection]) |
||
126 | delete_ids = [ids[0], 1] |
||
127 | status = connect.delete_entity_by_id(collection, delete_ids) |
||
128 | assert status |
||
129 | connect.flush([collection]) |
||
130 | res_count = connect.count_entities(collection) |
||
131 | assert res_count == nb - 1 |
||
132 | |||
133 | @pytest.mark.level(2) |
||
134 | def test_insert_delete_B(self, connect, collection): |
||
135 | ''' |
||
136 | target: test delete entity |
||
137 | method: add entities with the same ids, and delete the id in collection |
||
138 | expected: no error raised, all entities deleted |
||
139 | ''' |
||
140 | ids = [1 for i in range(nb)] |
||
141 | res_ids = connect.insert(collection, entities, ids) |
||
142 | connect.flush([collection]) |
||
143 | delete_ids = [1] |
||
144 | status = connect.delete_entity_by_id(collection, delete_ids) |
||
145 | assert status |
||
146 | connect.flush([collection]) |
||
147 | res_count = connect.count_entities(collection) |
||
148 | assert res_count == 0 |
||
149 | |||
150 | @pytest.mark.level(2) |
||
151 | def test_delete_exceed_limit(self, connect, collection): |
||
152 | ''' |
||
153 | target: test delete entity |
||
154 | method: add one entity and delete two ids |
||
155 | expected: error raised |
||
156 | ''' |
||
157 | ids = connect.insert(collection, entity) |
||
158 | connect.flush([collection]) |
||
159 | delete_ids = [ids[0], 1] |
||
160 | status = connect.delete_entity_by_id(collection, delete_ids) |
||
161 | connect.flush([collection]) |
||
162 | res_count = connect.count_entities(collection) |
||
163 | assert res_count == 0 |
||
164 | |||
165 | # TODO |
||
166 | View Code Duplication | @pytest.mark.level(2) |
|
167 | def test_flush_after_delete(self, connect, collection): |
||
168 | ''' |
||
169 | target: test delete entity |
||
170 | method: add entities and delete, then flush |
||
171 | expected: entity deleted and no error raised |
||
172 | ''' |
||
173 | ids = connect.insert(collection, entities) |
||
174 | connect.flush([collection]) |
||
175 | delete_ids = [ids[0], ids[-1]] |
||
176 | status = connect.delete_entity_by_id(collection, delete_ids) |
||
177 | assert status |
||
178 | connect.flush([collection]) |
||
179 | res_count = connect.count_entities(collection) |
||
180 | assert res_count == nb - len(delete_ids) |
||
181 | |||
182 | # TODO |
||
183 | @pytest.mark.level(2) |
||
184 | def test_flush_after_delete_ip(self, connect, ip_collection): |
||
185 | ''' |
||
186 | target: test delete entity |
||
187 | method: add entities and delete, then flush |
||
188 | expected: entity deleted and no error raised |
||
189 | ''' |
||
190 | ids = connect.insert(ip_collection, entities) |
||
191 | connect.flush([ip_collection]) |
||
192 | delete_ids = [ids[0], ids[-1]] |
||
193 | status = connect.delete_entity_by_id(ip_collection, delete_ids) |
||
194 | assert status |
||
195 | connect.flush([ip_collection]) |
||
196 | res_count = connect.count_entities(ip_collection) |
||
197 | assert res_count == nb - len(delete_ids) |
||
198 | |||
199 | # TODO |
||
200 | @pytest.mark.level(2) |
||
201 | def test_flush_after_delete_jac(self, connect, jac_collection): |
||
202 | ''' |
||
203 | target: test delete entity |
||
204 | method: add entities and delete, then flush |
||
205 | expected: entity deleted and no error raised |
||
206 | ''' |
||
207 | ids = connect.insert(jac_collection, binary_entities) |
||
208 | connect.flush([jac_collection]) |
||
209 | delete_ids = [ids[0], ids[-1]] |
||
210 | status = connect.delete_entity_by_id(jac_collection, delete_ids) |
||
211 | assert status |
||
212 | connect.flush([jac_collection]) |
||
213 | res_count = connect.count_entities(jac_collection) |
||
214 | assert res_count == nb - len(delete_ids) |
||
215 | |||
216 | # TODO |
||
217 | View Code Duplication | @pytest.mark.level(2) |
|
218 | def test_insert_same_ids_after_delete(self, connect, collection): |
||
219 | ''' |
||
220 | method: add entities and delete |
||
221 | expected: status DELETED |
||
222 | ''' |
||
223 | insert_ids = [i for i in range(nb)] |
||
224 | ids = connect.insert(collection, entities, insert_ids) |
||
225 | connect.flush([collection]) |
||
226 | delete_ids = [ids[0], ids[-1]] |
||
227 | status = connect.delete_entity_by_id(collection, delete_ids) |
||
228 | assert status |
||
229 | new_ids = connect.insert(collection, entity, [ids[0]]) |
||
230 | assert new_ids == [ids[0]] |
||
231 | connect.flush([collection]) |
||
232 | res_count = connect.count_entities(collection) |
||
233 | assert res_count == nb - 1 |
||
234 | |||
235 | # TODO |
||
236 | View Code Duplication | @pytest.mark.level(2) |
|
237 | def test_insert_same_ids_after_delete_ip(self, connect, ip_collection): |
||
238 | ''' |
||
239 | method: add entities and delete |
||
240 | expected: status DELETED |
||
241 | ''' |
||
242 | insert_ids = [i for i in range(nb)] |
||
243 | ids = connect.insert(ip_collection, entities, insert_ids) |
||
244 | connect.flush([ip_collection]) |
||
245 | delete_ids = [ids[0], ids[-1]] |
||
246 | status = connect.delete_entity_by_id(ip_collection, delete_ids) |
||
247 | assert status |
||
248 | new_ids = connect.insert(ip_collection, entity, [ids[0]]) |
||
249 | assert new_ids == [ids[0]] |
||
250 | connect.flush([ip_collection]) |
||
251 | res_count = connect.count_entities(ip_collection) |
||
252 | assert res_count == nb - 1 |
||
253 | |||
254 | # TODO |
||
255 | @pytest.mark.level(2) |
||
256 | def test_insert_same_ids_after_delete_jac(self, connect, jac_collection): |
||
257 | ''' |
||
258 | method: add entities, with the same id and delete the ids |
||
259 | expected: status DELETED, all id deleted |
||
260 | ''' |
||
261 | insert_ids = [i for i in range(nb)] |
||
262 | ids = connect.insert(jac_collection, binary_entities, insert_ids) |
||
263 | connect.flush([jac_collection]) |
||
264 | delete_ids = [ids[0], ids[-1]] |
||
265 | status = connect.delete_entity_by_id(jac_collection, delete_ids) |
||
266 | assert status |
||
267 | new_ids = connect.insert(jac_collection, binary_entity, [ids[0]]) |
||
268 | assert new_ids == [ids[0]] |
||
269 | connect.flush([jac_collection]) |
||
270 | res_count = connect.count_entities(jac_collection) |
||
271 | assert res_count == nb - 1 |
||
272 | |||
273 | # TODO: |
||
274 | @pytest.mark.level(2) |
||
275 | def test_search_after_delete(self, connect, collection): |
||
276 | ''' |
||
277 | target: test delete entity |
||
278 | method: add entities and delete, then search |
||
279 | expected: entity deleted and no error raised |
||
280 | ''' |
||
281 | ids = connect.insert(collection, entities) |
||
282 | connect.flush([collection]) |
||
283 | delete_ids = [ids[0], ids[-1]] |
||
284 | status = connect.delete_entity_by_id(collection, delete_ids) |
||
285 | assert status |
||
286 | query = copy.deepcopy(default_single_query) |
||
287 | query["bool"]["must"][0]["vector"][field_name]["query"] = [entity[-1]["values"][0], entities[-1]["values"][0], entities[-1]["values"][-1]] |
||
288 | res = connect.search(collection, query) |
||
289 | logging.getLogger().debug(res) |
||
290 | assert len(res) == len(query["bool"]["must"][0]["vector"][field_name]["query"]) |
||
291 | assert res[0]._distances[0] > epsilon |
||
292 | assert res[1]._distances[0] < epsilon |
||
293 | assert res[2]._distances[0] < epsilon |
||
294 | |||
295 | # TODO |
||
296 | @pytest.mark.level(2) |
||
297 | def test_create_index_after_delete(self, connect, collection, get_simple_index): |
||
298 | ''' |
||
299 | method: add entitys and delete, then create index |
||
300 | expected: vectors deleted, index created |
||
301 | ''' |
||
302 | ids = connect.insert(collection, entities) |
||
303 | connect.flush([collection]) |
||
304 | delete_ids = [ids[0], ids[-1]] |
||
305 | status = connect.delete_entity_by_id(collection, delete_ids) |
||
306 | connect.create_index(collection, field_name, get_simple_index) |
||
307 | # assert index info |
||
308 | |||
309 | # TODO |
||
310 | def test_delete_multiable_times(self, connect, collection): |
||
311 | ''' |
||
312 | method: add entities and delete id serveral times |
||
313 | expected: entities deleted |
||
314 | ''' |
||
315 | ids = connect.insert(collection, entities) |
||
316 | connect.flush([collection]) |
||
317 | delete_ids = [ids[0], ids[-1]] |
||
318 | status = connect.delete_entity_by_id(collection, delete_ids) |
||
319 | assert status |
||
320 | connect.flush([collection]) |
||
321 | for i in range(10): |
||
322 | status = connect.delete_entity_by_id(collection, delete_ids) |
||
323 | assert status |
||
324 | |||
325 | # TODO |
||
326 | @pytest.mark.level(2) |
||
327 | def test_index_insert_batch_delete_get(self, connect, collection, get_simple_index): |
||
328 | ''' |
||
329 | method: create index, insert entities, and delete |
||
330 | expected: entities deleted |
||
331 | ''' |
||
332 | connect.create_index(collection, field_name, get_simple_index) |
||
333 | ids = connect.insert(collection, entities) |
||
334 | connect.flush([collection]) |
||
335 | delete_ids = [ids[0], ids[-1]] |
||
336 | status = connect.delete_entity_by_id(collection, delete_ids) |
||
337 | assert status |
||
338 | connect.flush([collection]) |
||
339 | res_count = connect.count_entities(collection) |
||
340 | assert res_count == nb - len(delete_ids) |
||
341 | res_get = connect.get_entity_by_id(collection, delete_ids) |
||
342 | assert res_get[0] is None |
||
343 | |||
344 | # TODO |
||
345 | @pytest.mark.level(2) |
||
346 | def test_index_insert_single_delete_get(self, connect, collection, get_simple_index): |
||
347 | ''' |
||
348 | method: create index, insert entities, and delete |
||
349 | expected: entities deleted |
||
350 | ''' |
||
351 | ids = [i for i in range(nb)] |
||
352 | connect.create_index(collection, field_name, get_simple_index) |
||
353 | for i in range(nb): |
||
354 | connect.insert(collection, entity, [ids[i]]) |
||
355 | connect.flush([collection]) |
||
356 | delete_ids = [ids[0], ids[-1]] |
||
357 | status = connect.delete_entity_by_id(collection, delete_ids) |
||
358 | assert status |
||
359 | connect.flush([collection]) |
||
360 | res_count = connect.count_entities(collection) |
||
361 | assert res_count == nb - len(delete_ids) |
||
362 | |||
363 | """ |
||
364 | ****************************************************************** |
||
365 | The following cases are used to test `delete_entity_by_id` function, with tags |
||
366 | ****************************************************************** |
||
367 | """ |
||
368 | # TODO: |
||
369 | def test_insert_tag_delete(self, connect, collection): |
||
370 | ''' |
||
371 | method: add entitys with given tag, delete entities with the return ids |
||
372 | expected: entities deleted |
||
373 | ''' |
||
374 | connect.create_partition(collection, tag) |
||
375 | ids = connect.insert(collection, entities, partition_tag=tag) |
||
376 | connect.flush([collection]) |
||
377 | delete_ids = [ids[0], ids[-1]] |
||
378 | status = connect.delete_entity_by_id(collection, delete_ids) |
||
379 | assert status |
||
380 | |||
381 | # TODO: |
||
382 | def test_insert_default_tag_delete(self, connect, collection): |
||
383 | ''' |
||
384 | method: add entitys, delete entities with the return ids |
||
385 | expected: entities deleted |
||
386 | ''' |
||
387 | connect.create_partition(collection, tag) |
||
388 | ids = connect.insert(collection, entities) |
||
389 | connect.flush([collection]) |
||
390 | delete_ids = [ids[0], ids[-1]] |
||
391 | status = connect.delete_entity_by_id(collection, delete_ids) |
||
392 | assert status |
||
393 | |||
394 | # TODO: |
||
395 | View Code Duplication | @pytest.mark.level(2) |
|
396 | def test_insert_tags_delete(self, connect, collection): |
||
397 | ''' |
||
398 | method: add entitys with given two tags, delete entities with the return ids |
||
399 | expected: entities deleted |
||
400 | ''' |
||
401 | tag_new = "tag_new" |
||
402 | connect.create_partition(collection, tag) |
||
403 | connect.create_partition(collection, tag_new) |
||
404 | ids = connect.insert(collection, entities, partition_tag=tag) |
||
405 | ids_new = connect.insert(collection, entities, partition_tag=tag_new) |
||
406 | connect.flush([collection]) |
||
407 | delete_ids = [ids[0], ids_new[0]] |
||
408 | status = connect.delete_entity_by_id(collection, delete_ids) |
||
409 | assert status |
||
410 | connect.flush([collection]) |
||
411 | res_count = connect.count_entities(collection) |
||
412 | assert res_count == 2 * (nb - 1) |
||
413 | |||
414 | # TODO: |
||
415 | View Code Duplication | @pytest.mark.level(2) |
|
416 | def test_insert_tags_index_delete(self, connect, collection, get_simple_index): |
||
417 | ''' |
||
418 | method: add entitys with given tag, create index, delete entities with the return ids |
||
419 | expected: entities deleted |
||
420 | ''' |
||
421 | tag_new = "tag_new" |
||
422 | connect.create_partition(collection, tag) |
||
423 | connect.create_partition(collection, tag_new) |
||
424 | ids = connect.insert(collection, entities, partition_tag=tag) |
||
425 | ids_new = connect.insert(collection, entities, partition_tag=tag_new) |
||
426 | connect.flush([collection]) |
||
427 | connect.create_index(collection, field_name, get_simple_index) |
||
428 | delete_ids = [ids[0], ids_new[0]] |
||
429 | status = connect.delete_entity_by_id(collection, delete_ids) |
||
430 | assert status |
||
431 | connect.flush([collection]) |
||
432 | res_count = connect.count_entities(collection) |
||
433 | assert res_count == 2 * (nb - 1) |
||
434 | |||
435 | |||
436 | class TestDeleteInvalid(object): |
||
437 | |||
438 | """ |
||
439 | Test adding vectors with invalid vectors |
||
440 | """ |
||
441 | @pytest.fixture( |
||
442 | scope="function", |
||
443 | params=gen_invalid_ints() |
||
444 | ) |
||
445 | def gen_entity_id(self, request): |
||
446 | yield request.param |
||
447 | |||
448 | @pytest.fixture( |
||
449 | scope="function", |
||
450 | params=gen_invalid_strs() |
||
451 | ) |
||
452 | def get_collection_name(self, request): |
||
453 | yield request.param |
||
454 | |||
455 | @pytest.mark.level(1) |
||
456 | def test_delete_entity_id_invalid(self, connect, collection, gen_entity_id): |
||
457 | invalid_id = gen_entity_id |
||
458 | with pytest.raises(Exception) as e: |
||
459 | status = connect.delete_entity_by_id(collection, [invalid_id]) |
||
460 | |||
461 | @pytest.mark.level(2) |
||
462 | def test_delete_entity_ids_invalid(self, connect, collection, gen_entity_id): |
||
463 | invalid_id = gen_entity_id |
||
464 | with pytest.raises(Exception) as e: |
||
465 | status = connect.delete_entity_by_id(collection, [1, invalid_id]) |
||
466 | |||
467 | @pytest.mark.level(2) |
||
468 | def test_delete_entity_with_invalid_collection_name(self, connect, get_collection_name): |
||
469 | collection_name = get_collection_name |
||
470 | with pytest.raises(Exception) as e: |
||
471 | status = connect.delete_entity_by_id(collection_name, [1]) |
||
472 | |||
484 |