Skip to content

Commit 9873576

Browse files
author
Nitin Kanukolanu
committed
Updates: svs-vamana version capability validation tests
1 parent 73954f8 commit 9873576

File tree

2 files changed

+368
-0
lines changed

2 files changed

+368
-0
lines changed

tests/unit/test_fields.py

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1+
from unittest.mock import AsyncMock, Mock, patch
2+
13
import pytest
24
from redis.commands.search.field import GeoField as RedisGeoField
35
from redis.commands.search.field import NumericField as RedisNumericField
46
from redis.commands.search.field import TagField as RedisTagField
57
from redis.commands.search.field import TextField as RedisTextField
68
from redis.commands.search.field import VectorField as RedisVectorField
79

10+
from redisvl.exceptions import RedisModuleVersionError
11+
from redisvl.index import AsyncSearchIndex, SearchIndex
12+
from redisvl.schema import IndexSchema
813
from redisvl.schema.fields import (
914
FieldFactory,
1015
FlatVectorField,
@@ -636,3 +641,188 @@ def test_svs_vector_field_leanvec8x8_with_reduce():
636641
assert redis_field.args[redis_field.args.index("COMPRESSION") + 1] == "LeanVec8x8"
637642
assert "REDUCE" in redis_field.args
638643
assert redis_field.args[redis_field.args.index("REDUCE") + 1] == 512
644+
645+
646+
# ==================== SVS-VAMANA INDEX VALIDATION TESTS ====================
647+
648+
649+
def test_uses_svs_vamana_true():
650+
"""Test _uses_svs_vamana returns True for SVS schema."""
651+
schema_dict = {
652+
"index": {"name": "test_index", "prefix": "doc"},
653+
"fields": [
654+
{"name": "id", "type": "tag"},
655+
{
656+
"name": "embedding",
657+
"type": "vector",
658+
"attrs": {
659+
"dims": 128,
660+
"algorithm": "svs-vamana",
661+
"datatype": "float32",
662+
"distance_metric": "cosine",
663+
},
664+
},
665+
],
666+
}
667+
schema = IndexSchema.from_dict(schema_dict)
668+
669+
mock_client = Mock()
670+
671+
with patch.object(SearchIndex, "_redis_client", mock_client):
672+
index = SearchIndex(schema=schema)
673+
assert index._uses_svs_vamana() is True
674+
675+
676+
def test_check_svs_support_raises_error():
677+
"""Test _check_svs_support raises error when not supported."""
678+
schema_dict = {
679+
"index": {"name": "test_index", "prefix": "doc"},
680+
"fields": [
681+
{
682+
"name": "embedding",
683+
"type": "vector",
684+
"attrs": {
685+
"dims": 128,
686+
"algorithm": "svs-vamana",
687+
"datatype": "float32",
688+
"distance_metric": "cosine",
689+
},
690+
},
691+
],
692+
}
693+
schema = IndexSchema.from_dict(schema_dict)
694+
695+
mock_client = Mock()
696+
mock_client.info.return_value = {"redis_version": "7.2.4"}
697+
698+
with patch(
699+
"redisvl.redis.connection.RedisConnectionFactory.get_modules"
700+
) as mock_get_modules:
701+
mock_get_modules.return_value = {"search": 20612, "searchlight": 20612}
702+
703+
index = SearchIndex(schema=schema)
704+
705+
# Mock the _redis_client property
706+
with patch.object(
707+
type(index),
708+
"_redis_client",
709+
new_callable=lambda: property(lambda self: mock_client),
710+
):
711+
with pytest.raises(RedisModuleVersionError) as exc_info:
712+
index._check_svs_support()
713+
714+
error_msg = str(exc_info.value)
715+
assert "SVS-VAMANA requires Redis >= 8.2.0" in error_msg
716+
assert "Redis 7.2.4" in error_msg
717+
718+
719+
def test_check_svs_support_passes():
720+
"""Test _check_svs_support passes when supported."""
721+
schema_dict = {
722+
"index": {"name": "test_index", "prefix": "doc"},
723+
"fields": [
724+
{
725+
"name": "embedding",
726+
"type": "vector",
727+
"attrs": {
728+
"dims": 128,
729+
"algorithm": "svs-vamana",
730+
"datatype": "float32",
731+
"distance_metric": "cosine",
732+
},
733+
},
734+
],
735+
}
736+
schema = IndexSchema.from_dict(schema_dict)
737+
738+
mock_client = Mock()
739+
mock_client.info.return_value = {"redis_version": "8.2.0"}
740+
741+
with patch(
742+
"redisvl.redis.connection.RedisConnectionFactory.get_modules"
743+
) as mock_get_modules:
744+
mock_get_modules.return_value = {"search": 20810, "searchlight": 20810}
745+
746+
index = SearchIndex(schema=schema)
747+
748+
# Mock the _redis_client property
749+
with patch.object(
750+
type(index),
751+
"_redis_client",
752+
new_callable=lambda: property(lambda self: mock_client),
753+
):
754+
# Should not raise
755+
index._check_svs_support()
756+
757+
758+
@pytest.mark.asyncio
759+
async def test_check_svs_support_async_raises_error():
760+
"""Test _check_svs_support_async raises error when not supported."""
761+
schema_dict = {
762+
"index": {"name": "test_index", "prefix": "doc"},
763+
"fields": [
764+
{
765+
"name": "embedding",
766+
"type": "vector",
767+
"attrs": {
768+
"dims": 128,
769+
"algorithm": "svs-vamana",
770+
"datatype": "float32",
771+
"distance_metric": "cosine",
772+
},
773+
},
774+
],
775+
}
776+
schema = IndexSchema.from_dict(schema_dict)
777+
778+
mock_client = AsyncMock()
779+
mock_client.info.return_value = {"redis_version": "7.2.4"}
780+
781+
with patch(
782+
"redisvl.redis.connection.RedisConnectionFactory.get_modules_async"
783+
) as mock_get_modules:
784+
mock_get_modules.return_value = {"search": 20612, "searchlight": 20612}
785+
786+
index = AsyncSearchIndex(schema=schema)
787+
788+
with patch.object(index, "_get_client", return_value=mock_client):
789+
with pytest.raises(RedisModuleVersionError) as exc_info:
790+
await index._check_svs_support_async()
791+
792+
error_msg = str(exc_info.value)
793+
assert "SVS-VAMANA requires Redis >= 8.2.0" in error_msg
794+
795+
796+
@pytest.mark.asyncio
797+
async def test_check_svs_support_async_passes():
798+
"""Test _check_svs_support_async passes when supported."""
799+
schema_dict = {
800+
"index": {"name": "test_index", "prefix": "doc"},
801+
"fields": [
802+
{
803+
"name": "embedding",
804+
"type": "vector",
805+
"attrs": {
806+
"dims": 128,
807+
"algorithm": "svs-vamana",
808+
"datatype": "float32",
809+
"distance_metric": "cosine",
810+
},
811+
},
812+
],
813+
}
814+
schema = IndexSchema.from_dict(schema_dict)
815+
816+
mock_client = AsyncMock()
817+
mock_client.info.return_value = {"redis_version": "8.2.0"}
818+
819+
with patch(
820+
"redisvl.redis.connection.RedisConnectionFactory.get_modules_async"
821+
) as mock_get_modules:
822+
mock_get_modules.return_value = {"search": 20810, "searchlight": 20810}
823+
824+
index = AsyncSearchIndex(schema=schema)
825+
826+
with patch.object(index, "_get_client", return_value=mock_client):
827+
# Should not raise
828+
await index._check_svs_support_async()
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
"""
2+
Unit tests for SVS-VAMANA capability detection.
3+
4+
Tests the core functionality that determines if SVS-VAMANA vector indexing
5+
is supported on the connected Redis instance.
6+
"""
7+
8+
from unittest.mock import AsyncMock, Mock, patch
9+
10+
import pytest
11+
12+
from redisvl.exceptions import RedisModuleVersionError
13+
from redisvl.redis.connection import (
14+
VectorSupport,
15+
async_supports_svs_vamana,
16+
check_vector_capabilities,
17+
check_vector_capabilities_async,
18+
compare_versions,
19+
format_module_version,
20+
supports_svs_vamana,
21+
)
22+
23+
# ============================================================================
24+
# Helper Function Tests
25+
# ============================================================================
26+
27+
28+
def test_format_version_20810():
29+
"""Test formatting version 20810 -> 2.8.10"""
30+
assert format_module_version(20810) == "2.8.10"
31+
32+
33+
def test_compare_greater_version():
34+
"""Test version comparison: greater version returns True."""
35+
assert compare_versions("8.2.0", "8.1.0") is True
36+
assert compare_versions("8.2.1", "8.2.0") is True
37+
assert compare_versions("9.0.0", "8.2.0") is True
38+
39+
40+
def test_compare_lesser_version():
41+
"""Test version comparison: lesser version returns False."""
42+
assert compare_versions("7.2.4", "8.2.0") is False
43+
assert compare_versions("8.1.9", "8.2.0") is False
44+
assert compare_versions("8.2.0", "8.2.1") is False
45+
46+
47+
# ============================================================================
48+
# check_vector_capabilities Tests
49+
# ============================================================================
50+
51+
52+
def test_check_vector_capabilities_supported():
53+
"""Test check_vector_capabilities when SVS is supported."""
54+
mock_client = Mock()
55+
mock_client.info.return_value = {"redis_version": "8.2.0"}
56+
57+
with patch(
58+
"redisvl.redis.connection.RedisConnectionFactory.get_modules"
59+
) as mock_get_modules:
60+
mock_get_modules.return_value = {"search": 20810, "searchlight": 20810}
61+
62+
caps = check_vector_capabilities(mock_client)
63+
64+
assert caps.redis_version == "8.2.0"
65+
assert caps.search_version == 20810
66+
assert caps.searchlight_version == 20810
67+
assert caps.svs_vamana_supported is True
68+
69+
70+
def test_check_vector_capabilities_old_redis():
71+
"""Test check_vector_capabilities with old Redis version."""
72+
mock_client = Mock()
73+
mock_client.info.return_value = {"redis_version": "7.2.4"}
74+
75+
with patch(
76+
"redisvl.redis.connection.RedisConnectionFactory.get_modules"
77+
) as mock_get_modules:
78+
mock_get_modules.return_value = {"search": 20810, "searchlight": 20810}
79+
80+
caps = check_vector_capabilities(mock_client)
81+
82+
assert caps.redis_version == "7.2.4"
83+
assert caps.svs_vamana_supported is False
84+
85+
86+
def test_check_vector_capabilities_old_modules():
87+
"""Test check_vector_capabilities with old module versions."""
88+
mock_client = Mock()
89+
mock_client.info.return_value = {"redis_version": "8.2.0"}
90+
91+
with patch(
92+
"redisvl.redis.connection.RedisConnectionFactory.get_modules"
93+
) as mock_get_modules:
94+
mock_get_modules.return_value = {"search": 20612, "searchlight": 20612}
95+
96+
caps = check_vector_capabilities(mock_client)
97+
98+
assert caps.search_version == 20612
99+
assert caps.searchlight_version == 20612
100+
assert caps.svs_vamana_supported is False
101+
102+
103+
@pytest.mark.asyncio
104+
async def test_check_vector_capabilities_async_supported():
105+
"""Test check_vector_capabilities_async when SVS is supported."""
106+
mock_client = AsyncMock()
107+
mock_client.info.return_value = {"redis_version": "8.2.0"}
108+
109+
with patch(
110+
"redisvl.redis.connection.RedisConnectionFactory.get_modules_async"
111+
) as mock_get_modules:
112+
mock_get_modules.return_value = {"search": 20810, "searchlight": 20810}
113+
114+
caps = await check_vector_capabilities_async(mock_client)
115+
116+
assert caps.redis_version == "8.2.0"
117+
assert caps.search_version == 20810
118+
assert caps.svs_vamana_supported is True
119+
120+
121+
# ============================================================================
122+
# supports_svs_vamana Tests
123+
# ============================================================================
124+
125+
126+
def test_supports_svs_vamana_true():
127+
"""Test supports_svs_vamana returns True when supported."""
128+
mock_client = Mock()
129+
mock_client.info.return_value = {"redis_version": "8.2.0"}
130+
mock_client.module_list.return_value = [
131+
{"name": b"search", "ver": 20810},
132+
{"name": b"searchlight", "ver": 20810},
133+
]
134+
135+
assert supports_svs_vamana(mock_client) is True
136+
137+
138+
def test_supports_svs_vamana_false_old_redis():
139+
"""Test supports_svs_vamana returns False with old Redis."""
140+
mock_client = Mock()
141+
mock_client.info.return_value = {"redis_version": "7.2.4"}
142+
mock_client.module_list.return_value = [
143+
{"name": b"search", "ver": 20810},
144+
]
145+
146+
assert supports_svs_vamana(mock_client) is False
147+
148+
149+
def test_supports_svs_vamana_exception_handling():
150+
"""Test supports_svs_vamana handles exceptions gracefully."""
151+
mock_client = Mock()
152+
mock_client.info.side_effect = Exception("Connection error")
153+
154+
assert supports_svs_vamana(mock_client) is False
155+
156+
157+
# ============================================================================
158+
# Exception Tests
159+
# ============================================================================
160+
161+
162+
def test_for_svs_vamana_error_message():
163+
"""Test RedisModuleVersionError.for_svs_vamana creates proper exception."""
164+
caps = VectorSupport(
165+
redis_version="7.2.4",
166+
search_version=20612,
167+
searchlight_version=20612,
168+
svs_vamana_supported=False,
169+
)
170+
171+
error = RedisModuleVersionError.for_svs_vamana(caps, "8.2.0")
172+
173+
error_msg = str(error)
174+
assert "SVS-VAMANA requires Redis >= 8.2.0" in error_msg
175+
assert "RediSearch >= 2.8.10" in error_msg
176+
assert "Redis 7.2.4" in error_msg
177+
assert "RediSearch 2.6.12" in error_msg
178+
assert "SearchLight 2.6.12" in error_msg

0 commit comments

Comments
 (0)