aboutsummaryrefslogtreecommitdiff
path: root/examples/redis-unstable/modules/vector-sets/tests/vrange.py
diff options
context:
space:
mode:
Diffstat (limited to 'examples/redis-unstable/modules/vector-sets/tests/vrange.py')
-rw-r--r--examples/redis-unstable/modules/vector-sets/tests/vrange.py113
1 files changed, 113 insertions, 0 deletions
diff --git a/examples/redis-unstable/modules/vector-sets/tests/vrange.py b/examples/redis-unstable/modules/vector-sets/tests/vrange.py
new file mode 100644
index 0000000..7e57588
--- /dev/null
+++ b/examples/redis-unstable/modules/vector-sets/tests/vrange.py
@@ -0,0 +1,113 @@
1from test import TestCase, generate_random_vector
2import struct
3
4class BasicVRANGE(TestCase):
5 def getname(self):
6 return "VRANGE basic functionality and iteration"
7
8 def test(self):
9 # Add multiple elements with different names for lexicographical ordering
10 elements = [
11 "apple", "apricot", "banana", "cherry", "date",
12 "elderberry", "fig", "grape", "honeydew", "kiwi",
13 "lemon", "mango", "nectarine", "orange", "papaya",
14 "quince", "raspberry", "strawberry", "tangerine", "watermelon"
15 ]
16
17 # Add all elements to the vector set
18 for elem in elements:
19 vec = generate_random_vector(4)
20 vec_bytes = struct.pack('4f', *vec)
21 self.redis.execute_command('VADD', self.test_key, 'FP32', vec_bytes, elem)
22
23 # Test 1: Basic range with inclusive boundaries
24 result = self.redis.execute_command('VRANGE', self.test_key, '[apple', '[grape', '5')
25 result = [r.decode() for r in result]
26 assert result == ['apple', 'apricot', 'banana', 'cherry', 'date'], f"Expected first 5 elements from apple, got {result}"
27
28 # Test 2: Exclusive start boundary
29 result = self.redis.execute_command('VRANGE', self.test_key, '(apple', '[cherry', '10')
30 result = [r.decode() for r in result]
31 assert result == ['apricot', 'banana', 'cherry'], f"Expected elements after apple up to cherry inclusive, got {result}"
32
33 # Test 3: Exclusive end boundary
34 result = self.redis.execute_command('VRANGE', self.test_key, '[banana', '(cherry', '10')
35 result = [r.decode() for r in result]
36 assert result == ['banana'], f"Expected only banana (cherry excluded), got {result}"
37
38 # Test 4: Using '-' for minimum element
39 result = self.redis.execute_command('VRANGE', self.test_key, '-', '[banana', '10')
40 result = [r.decode() for r in result]
41 assert result[0] == 'apple', "Should start from the first element"
42 assert result[-1] == 'banana', "Should end at banana"
43
44 # Test 5: Using '+' for maximum element
45 result = self.redis.execute_command('VRANGE', self.test_key, '[raspberry', '+', '10')
46 result = [r.decode() for r in result]
47 assert 'raspberry' in result and 'strawberry' in result and 'tangerine' in result and 'watermelon' in result, "Should include all elements from raspberry onwards"
48
49 # Test 6: Full range with '-' and '+'
50 result = self.redis.execute_command('VRANGE', self.test_key, '-', '+', '100')
51 result = [r.decode() for r in result]
52 assert len(result) == len(elements), f"Should return all {len(elements)} elements"
53 assert result == sorted(elements), "Elements should be in lexicographical order"
54
55 # Test 7: Iterator pattern - verify each element appears exactly once
56 seen = set()
57 batch_size = 3
58 current = '-'
59
60 while True:
61 if current == '-':
62 # First iteration
63 result = self.redis.execute_command('VRANGE', self.test_key, '-', '+', str(batch_size))
64 else:
65 # Subsequent iterations - exclusive start from last element
66 result = self.redis.execute_command('VRANGE', self.test_key, f'({current}', '+', str(batch_size))
67
68 result = [r.decode() for r in result]
69
70 if not result:
71 break
72
73 # Check no duplicates in this batch
74 for elem in result:
75 assert elem not in seen, f"Element {elem} appeared more than once"
76 seen.add(elem)
77
78 # Update current to last element
79 current = result[-1]
80
81 # Break if we got less than requested (end of set)
82 if len(result) < batch_size:
83 break
84
85 # Verify we saw all elements exactly once
86 assert seen == set(elements), f"Iterator should visit all elements exactly once. Missing: {set(elements) - seen}, Extra: {seen - set(elements)}"
87
88 # Test 8: Count of 0 returns empty array
89 result = self.redis.execute_command('VRANGE', self.test_key, '-', '+', '0')
90 assert result == [], f"Count of 0 should return empty array, got {result}"
91
92 # Test 9: Range with no matching elements
93 result = self.redis.execute_command('VRANGE', self.test_key, '[zebra', '+', '10')
94 assert result == [], f"Range beyond all elements should return empty array, got {result}"
95
96 # Test 10: Non-existent key
97 result = self.redis.execute_command('VRANGE', 'nonexistent_key', '-', '+', '10')
98 assert result == [], f"Non-existent key should return empty array, got {result}"
99
100 # Test 11: Partial word boundaries
101 result = self.redis.execute_command('VRANGE', self.test_key, '[app', '[apr', '10')
102 result = [r.decode() for r in result]
103 assert 'apple' in result, "Should include 'apple' which starts with 'app'"
104 assert 'apricot' not in result, "Should not include 'apricot' as it's >= 'apr'"
105
106 # Test 12: Single element range
107 result = self.redis.execute_command('VRANGE', self.test_key, '[cherry', '[cherry', '10')
108 result = [r.decode() for r in result]
109 assert result == ['cherry'], f"Inclusive single element range should return that element, got {result}"
110
111 # Test 13: Empty range (start > end)
112 result = self.redis.execute_command('VRANGE', self.test_key, '[grape', '[apple', '10')
113 assert result == [], f"Range where start > end should return empty array, got {result}"