zvec-ruby 0.1.0 → 0.2.0
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.
- checksums.yaml +4 -4
- data/README.md +3 -3
- data/Rakefile +3 -0
- data/lib/zvec/active_record.rb +51 -4
- data/lib/zvec/collection.rb +344 -33
- data/lib/zvec/data_types.rb +250 -0
- data/lib/zvec/doc.rb +119 -10
- data/lib/zvec/query.rb +110 -1
- data/lib/zvec/ruby_llm.rb +79 -7
- data/lib/zvec/schema.rb +145 -1
- data/lib/zvec/version.rb +1 -1
- data/lib/zvec.rb +13 -0
- data/test/test_edge_cases.rb +380 -0
- data/test/test_helper.rb +102 -3
- data/test/test_type_detection.rb +258 -0
- data/test/test_validation.rb +305 -0
- data/test/test_version.rb +1 -1
- metadata +4 -1
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
require_relative "test_helper"
|
|
2
|
+
|
|
3
|
+
class TestTypeDetection < Minitest::Test
|
|
4
|
+
# --- DataTypes.detect_type ---
|
|
5
|
+
|
|
6
|
+
def test_detect_type_nil
|
|
7
|
+
assert_nil Zvec::DataTypes.detect_type(nil)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def test_detect_type_string
|
|
11
|
+
assert_equal Zvec::Ext::DataType::STRING, Zvec::DataTypes.detect_type("hello")
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def test_detect_type_integer
|
|
15
|
+
assert_equal Zvec::Ext::DataType::INT64, Zvec::DataTypes.detect_type(42)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def test_detect_type_float
|
|
19
|
+
assert_equal Zvec::Ext::DataType::DOUBLE, Zvec::DataTypes.detect_type(3.14)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def test_detect_type_true
|
|
23
|
+
assert_equal Zvec::Ext::DataType::BOOL, Zvec::DataTypes.detect_type(true)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def test_detect_type_false
|
|
27
|
+
assert_equal Zvec::Ext::DataType::BOOL, Zvec::DataTypes.detect_type(false)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Integer vs Float distinction: 1 should be INT64, 1.0 should be DOUBLE
|
|
31
|
+
def test_detect_type_integer_one
|
|
32
|
+
assert_equal Zvec::Ext::DataType::INT64, Zvec::DataTypes.detect_type(1)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def test_detect_type_float_one
|
|
36
|
+
assert_equal Zvec::Ext::DataType::DOUBLE, Zvec::DataTypes.detect_type(1.0)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# --- Array type detection ---
|
|
40
|
+
|
|
41
|
+
def test_detect_type_empty_array
|
|
42
|
+
# Empty arrays default to VECTOR_FP32
|
|
43
|
+
assert_equal Zvec::Ext::DataType::VECTOR_FP32, Zvec::DataTypes.detect_type([])
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def test_detect_type_float_array
|
|
47
|
+
assert_equal Zvec::Ext::DataType::VECTOR_FP32, Zvec::DataTypes.detect_type([1.0, 2.0])
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def test_detect_type_integer_array
|
|
51
|
+
# Integers in arrays are treated as vectors (float)
|
|
52
|
+
assert_equal Zvec::Ext::DataType::VECTOR_FP32, Zvec::DataTypes.detect_type([1, 2, 3])
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def test_detect_type_string_array
|
|
56
|
+
assert_equal Zvec::Ext::DataType::ARRAY_STRING, Zvec::DataTypes.detect_type(["a", "b"])
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def test_detect_type_bool_array
|
|
60
|
+
assert_equal Zvec::Ext::DataType::ARRAY_BOOL, Zvec::DataTypes.detect_type([true, false])
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def test_detect_type_nil_filled_array
|
|
64
|
+
# Array of all nils defaults to VECTOR_FP32
|
|
65
|
+
assert_equal Zvec::Ext::DataType::VECTOR_FP32, Zvec::DataTypes.detect_type([nil, nil, nil])
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def test_detect_type_array_with_leading_nil
|
|
69
|
+
# Skips nils to find first real element
|
|
70
|
+
assert_equal Zvec::Ext::DataType::ARRAY_STRING, Zvec::DataTypes.detect_type([nil, "hello", "world"])
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# --- DataTypes.coerce_value ---
|
|
74
|
+
|
|
75
|
+
def test_coerce_nil_returns_nil
|
|
76
|
+
assert_nil Zvec::DataTypes.coerce_value(nil, Zvec::Ext::DataType::STRING)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def test_coerce_to_string
|
|
80
|
+
assert_equal "42", Zvec::DataTypes.coerce_value(42, Zvec::Ext::DataType::STRING)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def test_coerce_string_true_to_bool
|
|
84
|
+
assert_equal true, Zvec::DataTypes.coerce_value("true", Zvec::Ext::DataType::BOOL)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def test_coerce_string_false_to_bool
|
|
88
|
+
assert_equal false, Zvec::DataTypes.coerce_value("false", Zvec::Ext::DataType::BOOL)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def test_coerce_actual_bool_true
|
|
92
|
+
assert_equal true, Zvec::DataTypes.coerce_value(true, Zvec::Ext::DataType::BOOL)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def test_coerce_actual_bool_false
|
|
96
|
+
assert_equal false, Zvec::DataTypes.coerce_value(false, Zvec::Ext::DataType::BOOL)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def test_coerce_integer_to_bool_nonzero
|
|
100
|
+
assert_equal true, Zvec::DataTypes.coerce_value(1, Zvec::Ext::DataType::BOOL)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def test_coerce_integer_to_bool_zero
|
|
104
|
+
assert_equal false, Zvec::DataTypes.coerce_value(0, Zvec::Ext::DataType::BOOL)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def test_coerce_invalid_string_to_bool_raises
|
|
108
|
+
assert_raises(ArgumentError) do
|
|
109
|
+
Zvec::DataTypes.coerce_value("maybe", Zvec::Ext::DataType::BOOL)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def test_coerce_float_to_integer
|
|
114
|
+
assert_equal 3, Zvec::DataTypes.coerce_value(3.7, Zvec::Ext::DataType::INT64)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def test_coerce_string_to_integer
|
|
118
|
+
assert_equal 42, Zvec::DataTypes.coerce_value("42", Zvec::Ext::DataType::INT64)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def test_coerce_bad_string_to_integer_raises
|
|
122
|
+
assert_raises(ArgumentError) do
|
|
123
|
+
Zvec::DataTypes.coerce_value("abc", Zvec::Ext::DataType::INT64)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def test_coerce_integer_to_float
|
|
128
|
+
assert_in_delta 42.0, Zvec::DataTypes.coerce_value(42, Zvec::Ext::DataType::DOUBLE), 0.001
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def test_coerce_string_to_float
|
|
132
|
+
assert_in_delta 3.14, Zvec::DataTypes.coerce_value("3.14", Zvec::Ext::DataType::DOUBLE), 0.001
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def test_coerce_bad_string_to_float_raises
|
|
136
|
+
assert_raises(ArgumentError) do
|
|
137
|
+
Zvec::DataTypes.coerce_value("xyz", Zvec::Ext::DataType::DOUBLE)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# --- Vector coercion ---
|
|
142
|
+
|
|
143
|
+
def test_coerce_integer_array_to_vector
|
|
144
|
+
result = Zvec::DataTypes.coerce_value([1, 2, 3], Zvec::Ext::DataType::VECTOR_FP32)
|
|
145
|
+
assert_equal [1.0, 2.0, 3.0], result
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def test_coerce_nil_in_vector_becomes_zero
|
|
149
|
+
result = Zvec::DataTypes.coerce_value([1.0, nil, 3.0], Zvec::Ext::DataType::VECTOR_FP32)
|
|
150
|
+
assert_equal [1.0, 0.0, 3.0], result
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def test_coerce_non_numeric_in_vector_raises
|
|
154
|
+
assert_raises(ArgumentError) do
|
|
155
|
+
Zvec::DataTypes.coerce_value([1.0, "bad", 3.0], Zvec::Ext::DataType::VECTOR_FP32)
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def test_coerce_non_array_to_vector_raises
|
|
160
|
+
assert_raises(ArgumentError) do
|
|
161
|
+
Zvec::DataTypes.coerce_value("not array", Zvec::Ext::DataType::VECTOR_FP32)
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# --- String array coercion ---
|
|
166
|
+
|
|
167
|
+
def test_coerce_string_array
|
|
168
|
+
result = Zvec::DataTypes.coerce_value(["a", "b"], Zvec::Ext::DataType::ARRAY_STRING)
|
|
169
|
+
assert_equal ["a", "b"], result
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def test_coerce_nil_in_string_array_becomes_empty
|
|
173
|
+
result = Zvec::DataTypes.coerce_value(["a", nil, "c"], Zvec::Ext::DataType::ARRAY_STRING)
|
|
174
|
+
assert_equal ["a", "", "c"], result
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def test_coerce_non_array_to_string_array_raises
|
|
178
|
+
assert_raises(ArgumentError) do
|
|
179
|
+
Zvec::DataTypes.coerce_value("not array", Zvec::Ext::DataType::ARRAY_STRING)
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# --- Coerce error messages include field name ---
|
|
184
|
+
|
|
185
|
+
def test_coerce_error_message_includes_field_name
|
|
186
|
+
err = assert_raises(ArgumentError) do
|
|
187
|
+
Zvec::DataTypes.coerce_value("abc", Zvec::Ext::DataType::INT64, field_name: "count")
|
|
188
|
+
end
|
|
189
|
+
assert_includes err.message, "count"
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# --- Doc auto-detection edge cases ---
|
|
193
|
+
|
|
194
|
+
def test_doc_auto_detect_integer_vs_float
|
|
195
|
+
doc = Zvec::Doc.new
|
|
196
|
+
doc["int_val"] = 42
|
|
197
|
+
doc["float_val"] = 42.0
|
|
198
|
+
# int_val stored via set_int64, float_val via set_double
|
|
199
|
+
assert_equal 42, doc["int_val"]
|
|
200
|
+
assert_in_delta 42.0, doc["float_val"], 0.001
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def test_doc_auto_detect_true_boolean
|
|
204
|
+
doc = Zvec::Doc.new
|
|
205
|
+
doc["flag"] = true
|
|
206
|
+
assert_equal true, doc["flag"]
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def test_doc_auto_detect_false_boolean
|
|
210
|
+
doc = Zvec::Doc.new
|
|
211
|
+
doc["flag"] = false
|
|
212
|
+
assert_equal false, doc["flag"]
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def test_doc_string_true_stays_string
|
|
216
|
+
# Without schema, "true" is a String, not a Boolean
|
|
217
|
+
doc = Zvec::Doc.new
|
|
218
|
+
doc["val"] = "true"
|
|
219
|
+
assert_equal "true", doc["val"]
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def test_doc_string_false_stays_string
|
|
223
|
+
doc = Zvec::Doc.new
|
|
224
|
+
doc["val"] = "false"
|
|
225
|
+
assert_equal "false", doc["val"]
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def test_doc_nil_value_in_array_auto_detect
|
|
229
|
+
doc = Zvec::Doc.new
|
|
230
|
+
doc["vec"] = [1.0, nil, 3.0]
|
|
231
|
+
result = doc["vec"]
|
|
232
|
+
assert_kind_of Array, result
|
|
233
|
+
assert_equal 3, result.size
|
|
234
|
+
assert_in_delta 0.0, result[1], 0.001
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def test_doc_empty_array_auto_detect
|
|
238
|
+
doc = Zvec::Doc.new
|
|
239
|
+
doc["vec"] = []
|
|
240
|
+
# Should not crash
|
|
241
|
+
result = doc["vec"]
|
|
242
|
+
assert(result.nil? || result == [])
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def test_doc_schema_coerces_string_bool
|
|
246
|
+
schema = Zvec::Schema.new("test") { bool "active" }
|
|
247
|
+
doc = Zvec::Doc.new(schema: schema)
|
|
248
|
+
doc["active"] = "true"
|
|
249
|
+
assert_equal true, doc["active"]
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def test_doc_schema_coerces_int_to_float
|
|
253
|
+
schema = Zvec::Schema.new("test") { double "score" }
|
|
254
|
+
doc = Zvec::Doc.new(schema: schema)
|
|
255
|
+
doc["score"] = 42
|
|
256
|
+
assert_in_delta 42.0, doc["score"], 0.001
|
|
257
|
+
end
|
|
258
|
+
end
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
require_relative "test_helper"
|
|
2
|
+
|
|
3
|
+
class TestValidation < Minitest::Test
|
|
4
|
+
include TempDirHelper
|
|
5
|
+
|
|
6
|
+
def setup
|
|
7
|
+
@schema = Zvec::Schema.new("test_validation") do
|
|
8
|
+
string "title"
|
|
9
|
+
int32 "count"
|
|
10
|
+
float "rating"
|
|
11
|
+
bool "active"
|
|
12
|
+
vector "embedding", dimension: 4
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# --- Schema validation ---
|
|
17
|
+
|
|
18
|
+
def test_schema_rejects_nil_name
|
|
19
|
+
assert_raises(Zvec::SchemaError) { Zvec::Schema.new(nil) }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def test_schema_rejects_empty_name
|
|
23
|
+
assert_raises(Zvec::SchemaError) { Zvec::Schema.new("") }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def test_schema_rejects_blank_name
|
|
27
|
+
assert_raises(Zvec::SchemaError) { Zvec::Schema.new(" ") }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def test_schema_field_rejects_empty_name
|
|
31
|
+
schema = Zvec::Schema.new("test")
|
|
32
|
+
assert_raises(Zvec::SchemaError) { schema.field("", Zvec::DataTypes::STRING) }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def test_schema_vector_rejects_zero_dimension
|
|
36
|
+
assert_raises(ArgumentError) do
|
|
37
|
+
Zvec::Schema.new("test") { vector "v", dimension: 0 }
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def test_schema_vector_rejects_negative_dimension
|
|
42
|
+
assert_raises(ArgumentError) do
|
|
43
|
+
Zvec::Schema.new("test") { vector "v", dimension: -1 }
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def test_schema_vector_rejects_non_integer_dimension
|
|
48
|
+
assert_raises(ArgumentError) do
|
|
49
|
+
Zvec::Schema.new("test") { vector "v", dimension: 3.5 }
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def test_schema_field_dimension_tracking
|
|
54
|
+
schema = Zvec::Schema.new("test") do
|
|
55
|
+
vector "embedding", dimension: 128
|
|
56
|
+
end
|
|
57
|
+
assert_equal 128, schema.field_dimension("embedding")
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def test_schema_field_dimension_nil_for_non_vector
|
|
61
|
+
schema = Zvec::Schema.new("test") do
|
|
62
|
+
string "title"
|
|
63
|
+
end
|
|
64
|
+
assert_nil schema.field_dimension("title")
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def test_schema_vector_fields_with_dimensions
|
|
68
|
+
schema = Zvec::Schema.new("test") do
|
|
69
|
+
string "title"
|
|
70
|
+
vector "embedding", dimension: 128
|
|
71
|
+
vector "small_embedding", dimension: 32
|
|
72
|
+
end
|
|
73
|
+
dims = schema.vector_fields_with_dimensions
|
|
74
|
+
assert_equal 128, dims["embedding"]
|
|
75
|
+
assert_equal 32, dims["small_embedding"]
|
|
76
|
+
refute dims.key?("title")
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# --- Doc field name validation ---
|
|
80
|
+
|
|
81
|
+
def test_doc_set_rejects_empty_field_name
|
|
82
|
+
doc = Zvec::Doc.new
|
|
83
|
+
assert_raises(ArgumentError) { doc.set("", "value") }
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def test_doc_set_rejects_blank_field_name
|
|
87
|
+
doc = Zvec::Doc.new
|
|
88
|
+
assert_raises(ArgumentError) { doc.set(" ", "value") }
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# --- Dimension validation in Doc ---
|
|
92
|
+
|
|
93
|
+
def test_doc_dimension_mismatch_raises_error
|
|
94
|
+
doc = Zvec::Doc.new(schema: @schema)
|
|
95
|
+
assert_raises(Zvec::DimensionError) do
|
|
96
|
+
doc["embedding"] = [1.0, 2.0, 3.0] # expects 4
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def test_doc_dimension_mismatch_error_message
|
|
101
|
+
doc = Zvec::Doc.new(schema: @schema)
|
|
102
|
+
err = assert_raises(Zvec::DimensionError) do
|
|
103
|
+
doc["embedding"] = [1.0, 2.0]
|
|
104
|
+
end
|
|
105
|
+
assert_includes err.message, "embedding"
|
|
106
|
+
assert_includes err.message, "4"
|
|
107
|
+
assert_includes err.message, "2"
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def test_doc_correct_dimension_accepted
|
|
111
|
+
doc = Zvec::Doc.new(schema: @schema)
|
|
112
|
+
doc["embedding"] = [1.0, 2.0, 3.0, 4.0]
|
|
113
|
+
result = doc["embedding"]
|
|
114
|
+
assert_equal 4, result.size
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def test_doc_empty_vector_accepted
|
|
118
|
+
doc = Zvec::Doc.new(schema: @schema)
|
|
119
|
+
doc["embedding"] = []
|
|
120
|
+
# Empty vectors should be accepted (no dimension to check)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# --- VectorQuery validation ---
|
|
124
|
+
|
|
125
|
+
def test_query_rejects_nil_field_name
|
|
126
|
+
assert_raises(Zvec::QueryError) do
|
|
127
|
+
Zvec::VectorQuery.new(field_name: nil, vector: [1.0])
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def test_query_rejects_empty_field_name
|
|
132
|
+
assert_raises(Zvec::QueryError) do
|
|
133
|
+
Zvec::VectorQuery.new(field_name: "", vector: [1.0])
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def test_query_rejects_empty_vector
|
|
138
|
+
assert_raises(Zvec::QueryError) do
|
|
139
|
+
Zvec::VectorQuery.new(field_name: "vec", vector: [])
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def test_query_rejects_non_array_vector
|
|
144
|
+
assert_raises(Zvec::QueryError) do
|
|
145
|
+
Zvec::VectorQuery.new(field_name: "vec", vector: "not an array")
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def test_query_rejects_non_numeric_vector_elements
|
|
150
|
+
assert_raises(Zvec::QueryError) do
|
|
151
|
+
Zvec::VectorQuery.new(field_name: "vec", vector: [1.0, "bad", 3.0])
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def test_query_rejects_zero_topk
|
|
156
|
+
assert_raises(Zvec::QueryError) do
|
|
157
|
+
Zvec::VectorQuery.new(field_name: "vec", vector: [1.0], topk: 0)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def test_query_rejects_negative_topk
|
|
162
|
+
assert_raises(Zvec::QueryError) do
|
|
163
|
+
Zvec::VectorQuery.new(field_name: "vec", vector: [1.0], topk: -1)
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# --- Collection validation (using stubs) ---
|
|
168
|
+
|
|
169
|
+
def test_collection_create_rejects_nil_path
|
|
170
|
+
assert_raises(ArgumentError) do
|
|
171
|
+
Zvec::Collection.create_and_open(nil, @schema)
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def test_collection_create_rejects_empty_path
|
|
176
|
+
assert_raises(ArgumentError) do
|
|
177
|
+
Zvec::Collection.create_and_open("", @schema)
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def test_collection_create_rejects_non_schema
|
|
182
|
+
assert_raises(ArgumentError) do
|
|
183
|
+
Zvec::Collection.create_and_open("/tmp/test", "not a schema")
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def test_collection_open_rejects_nil_path
|
|
188
|
+
assert_raises(ArgumentError) do
|
|
189
|
+
Zvec::Collection.open(nil)
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def test_collection_add_rejects_nil_pk
|
|
194
|
+
with_temp_dir("zvec_val") do |dir|
|
|
195
|
+
col = Zvec::Collection.create_and_open("#{dir}/col", @schema)
|
|
196
|
+
assert_raises(ArgumentError) do
|
|
197
|
+
col.add(pk: nil, title: "test")
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def test_collection_search_rejects_empty_vector
|
|
203
|
+
with_temp_dir("zvec_val") do |dir|
|
|
204
|
+
col = Zvec::Collection.create_and_open("#{dir}/col", @schema)
|
|
205
|
+
assert_raises(ArgumentError) do
|
|
206
|
+
col.search([])
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def test_collection_search_rejects_non_array_vector
|
|
212
|
+
with_temp_dir("zvec_val") do |dir|
|
|
213
|
+
col = Zvec::Collection.create_and_open("#{dir}/col", @schema)
|
|
214
|
+
assert_raises(ArgumentError) do
|
|
215
|
+
col.search("not an array")
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def test_collection_delete_rejects_empty_pks
|
|
221
|
+
with_temp_dir("zvec_val") do |dir|
|
|
222
|
+
col = Zvec::Collection.create_and_open("#{dir}/col", @schema)
|
|
223
|
+
assert_raises(ArgumentError) do
|
|
224
|
+
col.delete
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def test_collection_fetch_rejects_empty_pks
|
|
230
|
+
with_temp_dir("zvec_val") do |dir|
|
|
231
|
+
col = Zvec::Collection.create_and_open("#{dir}/col", @schema)
|
|
232
|
+
assert_raises(ArgumentError) do
|
|
233
|
+
col.fetch
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def test_collection_query_dimension_mismatch
|
|
239
|
+
with_temp_dir("zvec_val") do |dir|
|
|
240
|
+
col = Zvec::Collection.create_and_open("#{dir}/col", @schema)
|
|
241
|
+
assert_raises(Zvec::DimensionError) do
|
|
242
|
+
col.query(field_name: "embedding", vector: [1.0, 2.0], topk: 5)
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def test_collection_query_dimension_mismatch_message
|
|
248
|
+
with_temp_dir("zvec_val") do |dir|
|
|
249
|
+
col = Zvec::Collection.create_and_open("#{dir}/col", @schema)
|
|
250
|
+
err = assert_raises(Zvec::DimensionError) do
|
|
251
|
+
col.query(field_name: "embedding", vector: [1.0, 2.0], topk: 5)
|
|
252
|
+
end
|
|
253
|
+
assert_includes err.message, "embedding"
|
|
254
|
+
assert_includes err.message, "4"
|
|
255
|
+
assert_includes err.message, "2"
|
|
256
|
+
assert_includes err.message, "test_validation"
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def test_collection_query_correct_dimension_accepted
|
|
261
|
+
with_temp_dir("zvec_val") do |dir|
|
|
262
|
+
col = Zvec::Collection.create_and_open("#{dir}/col", @schema)
|
|
263
|
+
col.add(pk: "d1", title: "Test", count: 1, rating: 1.0, active: true,
|
|
264
|
+
embedding: [1.0, 2.0, 3.0, 4.0])
|
|
265
|
+
results = col.query(field_name: "embedding", vector: [1.0, 2.0, 3.0, 4.0], topk: 5)
|
|
266
|
+
assert_kind_of Array, results
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# --- Error context includes collection name ---
|
|
271
|
+
|
|
272
|
+
def test_error_prefix_includes_collection_name
|
|
273
|
+
with_temp_dir("zvec_val") do |dir|
|
|
274
|
+
col = Zvec::Collection.create_and_open("#{dir}/col", @schema)
|
|
275
|
+
err = assert_raises(ArgumentError) do
|
|
276
|
+
col.delete # no pks
|
|
277
|
+
end
|
|
278
|
+
assert_includes err.message, "test_validation"
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
# --- DimensionError is subclass of Error ---
|
|
283
|
+
|
|
284
|
+
def test_dimension_error_is_zvec_error
|
|
285
|
+
assert Zvec::DimensionError < Zvec::Error
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
# --- Collection name is accessible ---
|
|
289
|
+
|
|
290
|
+
def test_collection_name_from_schema
|
|
291
|
+
with_temp_dir("zvec_val") do |dir|
|
|
292
|
+
col = Zvec::Collection.create_and_open("#{dir}/col", @schema)
|
|
293
|
+
assert_equal "test_validation", col.collection_name
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
# --- Thread safety: collection has a monitor ---
|
|
298
|
+
|
|
299
|
+
def test_collection_has_monitor
|
|
300
|
+
with_temp_dir("zvec_val") do |dir|
|
|
301
|
+
col = Zvec::Collection.create_and_open("#{dir}/col", @schema)
|
|
302
|
+
assert_respond_to col.instance_variable_get(:@monitor), :synchronize
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
end
|
data/test/test_version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: zvec-ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Johannes Dwi Cahyo
|
|
@@ -94,11 +94,14 @@ files:
|
|
|
94
94
|
- test/test_collection.rb
|
|
95
95
|
- test/test_data_types.rb
|
|
96
96
|
- test/test_doc.rb
|
|
97
|
+
- test/test_edge_cases.rb
|
|
97
98
|
- test/test_ext_bindings.rb
|
|
98
99
|
- test/test_helper.rb
|
|
99
100
|
- test/test_query.rb
|
|
100
101
|
- test/test_ruby_llm_store.rb
|
|
101
102
|
- test/test_schema.rb
|
|
103
|
+
- test/test_type_detection.rb
|
|
104
|
+
- test/test_validation.rb
|
|
102
105
|
- test/test_version.rb
|
|
103
106
|
- zvec.gemspec
|
|
104
107
|
homepage: https://github.com/johannesdwicahyo/zvec-ruby
|