featurevisor 0.1.1 → 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 +24 -2
- data/bin/commands/assess_distribution.rb +6 -20
- data/bin/commands/benchmark.rb +2 -16
- data/bin/commands/test.rb +83 -96
- data/lib/featurevisor/conditions.rb +2 -2
- data/lib/featurevisor/datafile_reader.rb +4 -0
- data/lib/featurevisor/instance.rb +2 -2
- data/lib/featurevisor/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 984dcbada7b55c304a639cf58935c7b1dd561db2d6159b178a3829709473925a
|
|
4
|
+
data.tar.gz: 2a4068e17a151e671b45befb743be0935b19432e91411a243e2b3f8cbfa2c1e2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b813718a6962bd78a1142e5cd7758c3a0ea176d6fc8943114bfb6c630ece7e2dc160281ec7ca661d9de012366ba7b1e3ceeff9af6f6e7bfcdc4b53d89e3ac04b
|
|
7
|
+
data.tar.gz: 3fcc3e23b3b773c7780e5d018679c74a453e263797342b0912b02c74d634d60b4029c7e042f74225ba6550d7f293ab9819ff657f51a318f90a026b145fc896bb
|
data/README.md
CHANGED
|
@@ -81,7 +81,9 @@ require 'json'
|
|
|
81
81
|
# Fetch datafile from URL
|
|
82
82
|
datafile_url = 'https://cdn.yoursite.com/datafile.json'
|
|
83
83
|
response = Net::HTTP.get_response(URI(datafile_url))
|
|
84
|
-
|
|
84
|
+
|
|
85
|
+
# Parse JSON with symbolized keys (required)
|
|
86
|
+
datafile_content = JSON.parse(response.body, symbolize_names: true)
|
|
85
87
|
|
|
86
88
|
# Create SDK instance
|
|
87
89
|
f = Featurevisor.create_instance(
|
|
@@ -89,6 +91,19 @@ f = Featurevisor.create_instance(
|
|
|
89
91
|
)
|
|
90
92
|
```
|
|
91
93
|
|
|
94
|
+
**Important**: When parsing JSON datafiles, you must use `symbolize_names: true` to ensure proper key handling by the SDK.
|
|
95
|
+
|
|
96
|
+
Alternatively, you can pass a JSON string directly and the SDK will parse it automatically:
|
|
97
|
+
|
|
98
|
+
```ruby
|
|
99
|
+
# Option 1: Parse JSON yourself (recommended)
|
|
100
|
+
datafile_content = JSON.parse(json_string, symbolize_names: true)
|
|
101
|
+
f = Featurevisor.create_instance(datafile: datafile_content)
|
|
102
|
+
|
|
103
|
+
# Option 2: Pass JSON string directly (automatic parsing)
|
|
104
|
+
f = Featurevisor.create_instance(datafile: json_string)
|
|
105
|
+
```
|
|
106
|
+
|
|
92
107
|
## Evaluation types
|
|
93
108
|
|
|
94
109
|
We can evaluate 3 types of values against a particular [feature](https://featurevisor.com/docs/features/):
|
|
@@ -334,9 +349,16 @@ f.set_sticky({
|
|
|
334
349
|
You may also initialize the SDK without passing `datafile`, and set it later on:
|
|
335
350
|
|
|
336
351
|
```ruby
|
|
352
|
+
# Parse with symbolized keys before setting
|
|
353
|
+
datafile_content = JSON.parse(json_string, symbolize_names: true)
|
|
337
354
|
f.set_datafile(datafile_content)
|
|
355
|
+
|
|
356
|
+
# Or pass JSON string directly for automatic parsing
|
|
357
|
+
f.set_datafile(json_string)
|
|
338
358
|
```
|
|
339
359
|
|
|
360
|
+
**Important**: When calling `set_datafile()`, ensure JSON is parsed with `symbolize_names: true` if you're parsing it yourself.
|
|
361
|
+
|
|
340
362
|
### Updating datafile
|
|
341
363
|
|
|
342
364
|
You can set the datafile as many times as you want in your application, which will result in emitting a [`datafile_set`](#datafile_set) event that you can listen and react to accordingly.
|
|
@@ -715,7 +737,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
|
715
737
|
- Push commit to `main` branch
|
|
716
738
|
- Wait for CI to complete
|
|
717
739
|
- Tag the release with the version number
|
|
718
|
-
- This will trigger a new release to RubyGems
|
|
740
|
+
- This will trigger a new release to [RubyGems](https://rubygems.org/gems/featurevisor)
|
|
719
741
|
|
|
720
742
|
## License
|
|
721
743
|
|
|
@@ -58,8 +58,8 @@ module FeaturevisorCLI
|
|
|
58
58
|
|
|
59
59
|
# Initialize evaluation counters
|
|
60
60
|
flag_evaluations = {
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
enabled: 0,
|
|
62
|
+
disabled: 0
|
|
63
63
|
}
|
|
64
64
|
variation_evaluations = {}
|
|
65
65
|
|
|
@@ -78,9 +78,9 @@ module FeaturevisorCLI
|
|
|
78
78
|
# Evaluate flag
|
|
79
79
|
flag_evaluation = instance.is_enabled(@options.feature, context_copy)
|
|
80
80
|
if flag_evaluation
|
|
81
|
-
flag_evaluations[
|
|
81
|
+
flag_evaluations[:enabled] += 1
|
|
82
82
|
else
|
|
83
|
-
flag_evaluations[
|
|
83
|
+
flag_evaluations[:disabled] += 1
|
|
84
84
|
end
|
|
85
85
|
|
|
86
86
|
# Evaluate variation if feature has variations
|
|
@@ -147,7 +147,7 @@ module FeaturevisorCLI
|
|
|
147
147
|
end
|
|
148
148
|
|
|
149
149
|
begin
|
|
150
|
-
JSON.parse(stdout)
|
|
150
|
+
JSON.parse(stdout, symbolize_names: true)
|
|
151
151
|
rescue JSON::ParserError => e
|
|
152
152
|
puts "Error: Failed to parse datafile JSON: #{e.message}"
|
|
153
153
|
exit 1
|
|
@@ -160,27 +160,13 @@ module FeaturevisorCLI
|
|
|
160
160
|
end
|
|
161
161
|
|
|
162
162
|
def create_instance(datafile)
|
|
163
|
-
# Convert datafile to proper format for the SDK
|
|
164
|
-
symbolized_datafile = symbolize_keys(datafile)
|
|
165
|
-
|
|
166
163
|
# Create SDK instance
|
|
167
164
|
Featurevisor.create_instance(
|
|
168
|
-
datafile:
|
|
165
|
+
datafile: datafile,
|
|
169
166
|
log_level: get_logger_level
|
|
170
167
|
)
|
|
171
168
|
end
|
|
172
169
|
|
|
173
|
-
def symbolize_keys(obj)
|
|
174
|
-
case obj
|
|
175
|
-
when Hash
|
|
176
|
-
obj.transform_keys(&:to_sym).transform_values { |v| symbolize_keys(v) }
|
|
177
|
-
when Array
|
|
178
|
-
obj.map { |item| symbolize_keys(item) }
|
|
179
|
-
else
|
|
180
|
-
obj
|
|
181
|
-
end
|
|
182
|
-
end
|
|
183
|
-
|
|
184
170
|
def get_logger_level
|
|
185
171
|
if @options.verbose
|
|
186
172
|
"debug"
|
data/bin/commands/benchmark.rb
CHANGED
|
@@ -120,7 +120,7 @@ module FeaturevisorCLI
|
|
|
120
120
|
|
|
121
121
|
# Parse the JSON output
|
|
122
122
|
begin
|
|
123
|
-
JSON.parse(datafile_output)
|
|
123
|
+
JSON.parse(datafile_output, symbolize_names: true)
|
|
124
124
|
rescue JSON::ParserError => e
|
|
125
125
|
puts "Error: Failed to parse datafile JSON: #{e.message}"
|
|
126
126
|
puts "Command output: #{datafile_output}"
|
|
@@ -143,16 +143,13 @@ module FeaturevisorCLI
|
|
|
143
143
|
end
|
|
144
144
|
|
|
145
145
|
def create_instance(datafile)
|
|
146
|
-
# Convert string keys to symbols for the SDK
|
|
147
|
-
symbolized_datafile = symbolize_keys(datafile)
|
|
148
|
-
|
|
149
146
|
# Create a real Featurevisor instance
|
|
150
147
|
instance = Featurevisor.create_instance(
|
|
151
148
|
log_level: get_logger_level
|
|
152
149
|
)
|
|
153
150
|
|
|
154
151
|
# Explicitly set the datafile
|
|
155
|
-
instance.set_datafile(
|
|
152
|
+
instance.set_datafile(datafile)
|
|
156
153
|
|
|
157
154
|
instance
|
|
158
155
|
end
|
|
@@ -167,17 +164,6 @@ module FeaturevisorCLI
|
|
|
167
164
|
end
|
|
168
165
|
end
|
|
169
166
|
|
|
170
|
-
def symbolize_keys(obj)
|
|
171
|
-
case obj
|
|
172
|
-
when Hash
|
|
173
|
-
obj.transform_keys(&:to_sym).transform_values { |v| symbolize_keys(v) }
|
|
174
|
-
when Array
|
|
175
|
-
obj.map { |item| symbolize_keys(item) }
|
|
176
|
-
else
|
|
177
|
-
obj
|
|
178
|
-
end
|
|
179
|
-
end
|
|
180
|
-
|
|
181
167
|
def benchmark_feature_flag(instance, feature_key, context, n)
|
|
182
168
|
start_time = Time.now
|
|
183
169
|
|
data/bin/commands/test.rb
CHANGED
|
@@ -19,13 +19,13 @@ module FeaturevisorCLI
|
|
|
19
19
|
|
|
20
20
|
# Get project configuration
|
|
21
21
|
config = get_config
|
|
22
|
-
environments = config[
|
|
22
|
+
environments = config[:environments] || []
|
|
23
23
|
segments_by_key = get_segments
|
|
24
24
|
|
|
25
25
|
# Use CLI schemaVersion option or fallback to config
|
|
26
26
|
schema_version = @options.schema_version
|
|
27
27
|
if schema_version.nil? || schema_version.empty?
|
|
28
|
-
schema_version = config[
|
|
28
|
+
schema_version = config[:schemaVersion]
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
# Build datafiles for all environments
|
|
@@ -57,7 +57,7 @@ module FeaturevisorCLI
|
|
|
57
57
|
config_output = execute_command(command)
|
|
58
58
|
|
|
59
59
|
begin
|
|
60
|
-
JSON.parse(config_output)
|
|
60
|
+
JSON.parse(config_output, symbolize_names: true)
|
|
61
61
|
rescue JSON::ParserError => e
|
|
62
62
|
puts "Error: Failed to parse config JSON: #{e.message}"
|
|
63
63
|
puts "Command output: #{config_output}"
|
|
@@ -71,11 +71,11 @@ module FeaturevisorCLI
|
|
|
71
71
|
segments_output = execute_command(command)
|
|
72
72
|
|
|
73
73
|
begin
|
|
74
|
-
segments = JSON.parse(segments_output)
|
|
74
|
+
segments = JSON.parse(segments_output, symbolize_names: true)
|
|
75
75
|
segments_by_key = {}
|
|
76
76
|
segments.each do |segment|
|
|
77
|
-
if segment[
|
|
78
|
-
segments_by_key[segment[
|
|
77
|
+
if segment[:key]
|
|
78
|
+
segments_by_key[segment[:key]] = segment
|
|
79
79
|
end
|
|
80
80
|
end
|
|
81
81
|
segments_by_key
|
|
@@ -110,7 +110,7 @@ module FeaturevisorCLI
|
|
|
110
110
|
datafile_output = execute_command(command)
|
|
111
111
|
|
|
112
112
|
begin
|
|
113
|
-
datafile = JSON.parse(datafile_output)
|
|
113
|
+
datafile = JSON.parse(datafile_output, symbolize_names: true)
|
|
114
114
|
datafiles_by_environment[environment] = datafile
|
|
115
115
|
rescue JSON::ParserError => e
|
|
116
116
|
puts "Error: Failed to parse datafile JSON for #{environment}: #{e.message}"
|
|
@@ -147,7 +147,7 @@ module FeaturevisorCLI
|
|
|
147
147
|
tests_output = execute_command(command)
|
|
148
148
|
|
|
149
149
|
begin
|
|
150
|
-
JSON.parse(tests_output)
|
|
150
|
+
JSON.parse(tests_output, symbolize_names: true)
|
|
151
151
|
rescue JSON::ParserError => e
|
|
152
152
|
puts "Error: Failed to parse tests JSON: #{e.message}"
|
|
153
153
|
puts "Command output: #{tests_output}"
|
|
@@ -161,12 +161,9 @@ module FeaturevisorCLI
|
|
|
161
161
|
environments.each do |environment|
|
|
162
162
|
datafile = datafiles_by_environment[environment]
|
|
163
163
|
|
|
164
|
-
# Convert string keys to symbols for the SDK
|
|
165
|
-
symbolized_datafile = symbolize_keys(datafile)
|
|
166
|
-
|
|
167
164
|
# Create SDK instance
|
|
168
165
|
instance = Featurevisor.create_instance(
|
|
169
|
-
datafile:
|
|
166
|
+
datafile: datafile,
|
|
170
167
|
log_level: level,
|
|
171
168
|
hooks: [
|
|
172
169
|
{
|
|
@@ -189,8 +186,8 @@ module FeaturevisorCLI
|
|
|
189
186
|
failed_assertions_count = 0
|
|
190
187
|
|
|
191
188
|
tests.each do |test|
|
|
192
|
-
test_key = test[
|
|
193
|
-
assertions = test[
|
|
189
|
+
test_key = test[:key]
|
|
190
|
+
assertions = test[:assertions] || []
|
|
194
191
|
results = ""
|
|
195
192
|
test_has_error = false
|
|
196
193
|
test_duration = 0.0
|
|
@@ -199,8 +196,8 @@ module FeaturevisorCLI
|
|
|
199
196
|
if assertion.is_a?(Hash)
|
|
200
197
|
test_result = nil
|
|
201
198
|
|
|
202
|
-
if test[
|
|
203
|
-
environment = assertion[
|
|
199
|
+
if test[:feature]
|
|
200
|
+
environment = assertion[:environment]
|
|
204
201
|
instance = sdk_instances_by_environment[environment]
|
|
205
202
|
|
|
206
203
|
# Show datafile if requested
|
|
@@ -212,12 +209,11 @@ module FeaturevisorCLI
|
|
|
212
209
|
end
|
|
213
210
|
|
|
214
211
|
# If "at" parameter is provided, create a new instance with the specific hook
|
|
215
|
-
if assertion[
|
|
212
|
+
if assertion[:at]
|
|
216
213
|
datafile = datafiles_by_environment[environment]
|
|
217
|
-
symbolized_datafile = symbolize_keys(datafile)
|
|
218
214
|
|
|
219
215
|
instance = Featurevisor.create_instance(
|
|
220
|
-
datafile:
|
|
216
|
+
datafile: datafile,
|
|
221
217
|
log_level: level,
|
|
222
218
|
hooks: [
|
|
223
219
|
{
|
|
@@ -225,7 +221,7 @@ module FeaturevisorCLI
|
|
|
225
221
|
bucket_value: ->(options) do
|
|
226
222
|
# Match JavaScript implementation: assertion.at * (MAX_BUCKETED_NUMBER / 100)
|
|
227
223
|
# MAX_BUCKETED_NUMBER is 100000, so this becomes assertion.at * 1000
|
|
228
|
-
at = assertion[
|
|
224
|
+
at = assertion[:at]
|
|
229
225
|
if at.is_a?(Numeric)
|
|
230
226
|
(at * 1000).to_i
|
|
231
227
|
else
|
|
@@ -237,9 +233,9 @@ module FeaturevisorCLI
|
|
|
237
233
|
)
|
|
238
234
|
end
|
|
239
235
|
|
|
240
|
-
test_result = run_test_feature(assertion, test[
|
|
241
|
-
elsif test[
|
|
242
|
-
segment_key = test[
|
|
236
|
+
test_result = run_test_feature(assertion, test[:feature], instance, level)
|
|
237
|
+
elsif test[:segment]
|
|
238
|
+
segment_key = test[:segment]
|
|
243
239
|
segment = segments_by_key[segment_key]
|
|
244
240
|
if segment.is_a?(Hash)
|
|
245
241
|
test_result = run_test_segment(assertion, segment, level)
|
|
@@ -250,12 +246,12 @@ module FeaturevisorCLI
|
|
|
250
246
|
test_duration += test_result[:duration]
|
|
251
247
|
|
|
252
248
|
if test_result[:has_error]
|
|
253
|
-
results += " ✘ #{assertion[
|
|
249
|
+
results += " ✘ #{assertion[:description]} (#{(test_result[:duration] * 1000).round(2)}ms)\n"
|
|
254
250
|
results += test_result[:errors]
|
|
255
251
|
test_has_error = true
|
|
256
252
|
failed_assertions_count += 1
|
|
257
253
|
else
|
|
258
|
-
results += " ✔ #{assertion[
|
|
254
|
+
results += " ✔ #{assertion[:description]} (#{(test_result[:duration] * 1000).round(2)}ms)\n"
|
|
259
255
|
passed_assertions_count += 1
|
|
260
256
|
end
|
|
261
257
|
end
|
|
@@ -285,8 +281,8 @@ module FeaturevisorCLI
|
|
|
285
281
|
end
|
|
286
282
|
|
|
287
283
|
def run_test_feature(assertion, feature_key, instance, level)
|
|
288
|
-
context = parse_context(assertion[
|
|
289
|
-
sticky = parse_sticky(assertion[
|
|
284
|
+
context = parse_context(assertion[:context])
|
|
285
|
+
sticky = parse_sticky(assertion[:sticky])
|
|
290
286
|
|
|
291
287
|
# Set context and sticky for this assertion
|
|
292
288
|
instance.set_context(context, false)
|
|
@@ -302,8 +298,8 @@ module FeaturevisorCLI
|
|
|
302
298
|
start_time = Time.now
|
|
303
299
|
|
|
304
300
|
# Test expectedToBeEnabled
|
|
305
|
-
if assertion.key?(
|
|
306
|
-
expected_to_be_enabled = assertion[
|
|
301
|
+
if assertion.key?(:expectedToBeEnabled)
|
|
302
|
+
expected_to_be_enabled = assertion[:expectedToBeEnabled]
|
|
307
303
|
is_enabled = instance.is_enabled(feature_key, context, override_options)
|
|
308
304
|
|
|
309
305
|
if is_enabled != expected_to_be_enabled
|
|
@@ -313,8 +309,8 @@ module FeaturevisorCLI
|
|
|
313
309
|
end
|
|
314
310
|
|
|
315
311
|
# Test expectedVariation
|
|
316
|
-
if assertion.key?(
|
|
317
|
-
expected_variation = assertion[
|
|
312
|
+
if assertion.key?(:expectedVariation)
|
|
313
|
+
expected_variation = assertion[:expectedVariation]
|
|
318
314
|
variation = instance.get_variation(feature_key, context, override_options)
|
|
319
315
|
|
|
320
316
|
variation_value = variation.nil? ? nil : variation
|
|
@@ -325,12 +321,12 @@ module FeaturevisorCLI
|
|
|
325
321
|
end
|
|
326
322
|
|
|
327
323
|
# Test expectedVariables
|
|
328
|
-
if assertion[
|
|
329
|
-
expected_variables = assertion[
|
|
324
|
+
if assertion[:expectedVariables]
|
|
325
|
+
expected_variables = assertion[:expectedVariables]
|
|
330
326
|
expected_variables.each do |variable_key, expected_value|
|
|
331
327
|
# Set default variable value for this specific variable
|
|
332
|
-
if assertion[
|
|
333
|
-
override_options[:default_variable_value] = assertion[
|
|
328
|
+
if assertion[:defaultVariableValues] && assertion[:defaultVariableValues][variable_key]
|
|
329
|
+
override_options[:default_variable_value] = assertion[:defaultVariableValues][variable_key]
|
|
334
330
|
end
|
|
335
331
|
|
|
336
332
|
actual_value = instance.get_variable(feature_key, variable_key, context, override_options)
|
|
@@ -369,13 +365,13 @@ module FeaturevisorCLI
|
|
|
369
365
|
end
|
|
370
366
|
|
|
371
367
|
# Test expectedEvaluations
|
|
372
|
-
if assertion[
|
|
373
|
-
expected_evaluations = assertion[
|
|
368
|
+
if assertion[:expectedEvaluations]
|
|
369
|
+
expected_evaluations = assertion[:expectedEvaluations]
|
|
374
370
|
|
|
375
371
|
# Test flag evaluations
|
|
376
|
-
if expected_evaluations[
|
|
372
|
+
if expected_evaluations[:flag]
|
|
377
373
|
evaluation = instance.evaluate_flag(feature_key, context, override_options)
|
|
378
|
-
expected_evaluations[
|
|
374
|
+
expected_evaluations[:flag].each do |key, expected_value|
|
|
379
375
|
actual_value = get_evaluation_value(evaluation, key)
|
|
380
376
|
if !compare_values(actual_value, expected_value)
|
|
381
377
|
has_error = true
|
|
@@ -385,9 +381,9 @@ module FeaturevisorCLI
|
|
|
385
381
|
end
|
|
386
382
|
|
|
387
383
|
# Test variation evaluations
|
|
388
|
-
if expected_evaluations[
|
|
384
|
+
if expected_evaluations[:variation]
|
|
389
385
|
evaluation = instance.evaluate_variation(feature_key, context, override_options)
|
|
390
|
-
expected_evaluations[
|
|
386
|
+
expected_evaluations[:variation].each do |key, expected_value|
|
|
391
387
|
actual_value = get_evaluation_value(evaluation, key)
|
|
392
388
|
if !compare_values(actual_value, expected_value)
|
|
393
389
|
has_error = true
|
|
@@ -397,8 +393,8 @@ module FeaturevisorCLI
|
|
|
397
393
|
end
|
|
398
394
|
|
|
399
395
|
# Test variable evaluations
|
|
400
|
-
if expected_evaluations[
|
|
401
|
-
expected_evaluations[
|
|
396
|
+
if expected_evaluations[:variables]
|
|
397
|
+
expected_evaluations[:variables].each do |variable_key, expected_eval|
|
|
402
398
|
if expected_eval.is_a?(Hash)
|
|
403
399
|
evaluation = instance.evaluate_variable(feature_key, variable_key, context, override_options)
|
|
404
400
|
expected_eval.each do |key, expected_value|
|
|
@@ -414,10 +410,10 @@ module FeaturevisorCLI
|
|
|
414
410
|
end
|
|
415
411
|
|
|
416
412
|
# Test children
|
|
417
|
-
if assertion[
|
|
418
|
-
assertion[
|
|
413
|
+
if assertion[:children]
|
|
414
|
+
assertion[:children].each do |child|
|
|
419
415
|
if child.is_a?(Hash)
|
|
420
|
-
child_context = parse_context(child[
|
|
416
|
+
child_context = parse_context(child[:context])
|
|
421
417
|
|
|
422
418
|
# Create override options for child with sticky values
|
|
423
419
|
child_override_options = create_override_options(child)
|
|
@@ -452,7 +448,7 @@ module FeaturevisorCLI
|
|
|
452
448
|
end
|
|
453
449
|
|
|
454
450
|
def run_test_feature_child(assertion, feature_key, instance, level)
|
|
455
|
-
context = parse_context(assertion[
|
|
451
|
+
context = parse_context(assertion[:context])
|
|
456
452
|
override_options = create_override_options(assertion)
|
|
457
453
|
|
|
458
454
|
has_error = false
|
|
@@ -460,8 +456,8 @@ module FeaturevisorCLI
|
|
|
460
456
|
start_time = Time.now
|
|
461
457
|
|
|
462
458
|
# Test expectedToBeEnabled
|
|
463
|
-
if assertion.key?(
|
|
464
|
-
expected_to_be_enabled = assertion[
|
|
459
|
+
if assertion.key?(:expectedToBeEnabled)
|
|
460
|
+
expected_to_be_enabled = assertion[:expectedToBeEnabled]
|
|
465
461
|
is_enabled = instance.is_enabled(feature_key, context, override_options)
|
|
466
462
|
|
|
467
463
|
if is_enabled != expected_to_be_enabled
|
|
@@ -471,8 +467,8 @@ module FeaturevisorCLI
|
|
|
471
467
|
end
|
|
472
468
|
|
|
473
469
|
# Test expectedVariation
|
|
474
|
-
if assertion.key?(
|
|
475
|
-
expected_variation = assertion[
|
|
470
|
+
if assertion.key?(:expectedVariation)
|
|
471
|
+
expected_variation = assertion[:expectedVariation]
|
|
476
472
|
variation = instance.get_variation(feature_key, context, override_options)
|
|
477
473
|
|
|
478
474
|
variation_value = variation.nil? ? nil : variation
|
|
@@ -483,12 +479,12 @@ module FeaturevisorCLI
|
|
|
483
479
|
end
|
|
484
480
|
|
|
485
481
|
# Test expectedVariables
|
|
486
|
-
if assertion[
|
|
487
|
-
expected_variables = assertion[
|
|
482
|
+
if assertion[:expectedVariables]
|
|
483
|
+
expected_variables = assertion[:expectedVariables]
|
|
488
484
|
expected_variables.each do |variable_key, expected_value|
|
|
489
485
|
# Set default variable value for this specific variable
|
|
490
|
-
if assertion[
|
|
491
|
-
override_options[:default_variable_value] = assertion[
|
|
486
|
+
if assertion[:defaultVariableValues] && assertion[:defaultVariableValues][variable_key]
|
|
487
|
+
override_options[:default_variable_value] = assertion[:defaultVariableValues][variable_key]
|
|
492
488
|
end
|
|
493
489
|
|
|
494
490
|
actual_value = instance.get_variable(feature_key, variable_key, context, override_options)
|
|
@@ -536,8 +532,8 @@ module FeaturevisorCLI
|
|
|
536
532
|
end
|
|
537
533
|
|
|
538
534
|
def run_test_segment(assertion, segment, level)
|
|
539
|
-
context = parse_context(assertion[
|
|
540
|
-
conditions = segment[
|
|
535
|
+
context = parse_context(assertion[:context])
|
|
536
|
+
conditions = segment[:conditions]
|
|
541
537
|
|
|
542
538
|
# Create a minimal datafile for segment testing
|
|
543
539
|
datafile = {
|
|
@@ -549,7 +545,7 @@ module FeaturevisorCLI
|
|
|
549
545
|
|
|
550
546
|
# Create SDK instance for segment testing
|
|
551
547
|
instance = Featurevisor.create_instance(
|
|
552
|
-
datafile:
|
|
548
|
+
datafile: datafile,
|
|
553
549
|
log_level: level
|
|
554
550
|
)
|
|
555
551
|
|
|
@@ -557,8 +553,8 @@ module FeaturevisorCLI
|
|
|
557
553
|
errors = ""
|
|
558
554
|
start_time = Time.now
|
|
559
555
|
|
|
560
|
-
if assertion.key?(
|
|
561
|
-
expected_to_match = assertion[
|
|
556
|
+
if assertion.key?(:expectedToMatch)
|
|
557
|
+
expected_to_match = assertion[:expectedToMatch]
|
|
562
558
|
actual = instance.instance_variable_get(:@datafile_reader).all_conditions_are_matched(conditions, context)
|
|
563
559
|
|
|
564
560
|
if actual != expected_to_match
|
|
@@ -593,16 +589,16 @@ module FeaturevisorCLI
|
|
|
593
589
|
if value.is_a?(Hash)
|
|
594
590
|
evaluated_feature = {}
|
|
595
591
|
|
|
596
|
-
if value.key?(
|
|
597
|
-
evaluated_feature[:enabled] = value[
|
|
592
|
+
if value.key?(:enabled)
|
|
593
|
+
evaluated_feature[:enabled] = value[:enabled]
|
|
598
594
|
end
|
|
599
595
|
|
|
600
|
-
if value.key?(
|
|
601
|
-
evaluated_feature[:variation] = value[
|
|
596
|
+
if value.key?(:variation)
|
|
597
|
+
evaluated_feature[:variation] = value[:variation]
|
|
602
598
|
end
|
|
603
599
|
|
|
604
|
-
if value[
|
|
605
|
-
evaluated_feature[:variables] = value[
|
|
600
|
+
if value[:variables] && value[:variables].is_a?(Hash)
|
|
601
|
+
evaluated_feature[:variables] = value[:variables].transform_keys(&:to_sym)
|
|
606
602
|
end
|
|
607
603
|
|
|
608
604
|
sticky_features[key.to_sym] = evaluated_feature
|
|
@@ -618,8 +614,8 @@ module FeaturevisorCLI
|
|
|
618
614
|
def create_override_options(assertion)
|
|
619
615
|
options = {}
|
|
620
616
|
|
|
621
|
-
if assertion[
|
|
622
|
-
options[:default_variation_value] = assertion[
|
|
617
|
+
if assertion[:defaultVariationValue]
|
|
618
|
+
options[:default_variation_value] = assertion[:defaultVariationValue]
|
|
623
619
|
end
|
|
624
620
|
|
|
625
621
|
options
|
|
@@ -627,41 +623,41 @@ module FeaturevisorCLI
|
|
|
627
623
|
|
|
628
624
|
def get_evaluation_value(evaluation, key)
|
|
629
625
|
case key
|
|
630
|
-
when
|
|
626
|
+
when :type
|
|
631
627
|
evaluation[:type]
|
|
632
|
-
when
|
|
628
|
+
when :featureKey
|
|
633
629
|
evaluation[:feature_key]
|
|
634
|
-
when
|
|
630
|
+
when :reason
|
|
635
631
|
evaluation[:reason]
|
|
636
|
-
when
|
|
632
|
+
when :bucketKey
|
|
637
633
|
evaluation[:bucket_key]
|
|
638
|
-
when
|
|
634
|
+
when :bucketValue
|
|
639
635
|
evaluation[:bucket_value]
|
|
640
|
-
when
|
|
636
|
+
when :ruleKey
|
|
641
637
|
evaluation[:rule_key]
|
|
642
|
-
when
|
|
638
|
+
when :error
|
|
643
639
|
evaluation[:error]
|
|
644
|
-
when
|
|
640
|
+
when :enabled
|
|
645
641
|
evaluation[:enabled]
|
|
646
|
-
when
|
|
642
|
+
when :traffic
|
|
647
643
|
evaluation[:traffic]
|
|
648
|
-
when
|
|
644
|
+
when :forceIndex
|
|
649
645
|
evaluation[:force_index]
|
|
650
|
-
when
|
|
646
|
+
when :force
|
|
651
647
|
evaluation[:force]
|
|
652
|
-
when
|
|
648
|
+
when :required
|
|
653
649
|
evaluation[:required]
|
|
654
|
-
when
|
|
650
|
+
when :sticky
|
|
655
651
|
evaluation[:sticky]
|
|
656
|
-
when
|
|
652
|
+
when :variation
|
|
657
653
|
evaluation[:variation]
|
|
658
|
-
when
|
|
654
|
+
when :variationValue
|
|
659
655
|
evaluation[:variation_value]
|
|
660
|
-
when
|
|
656
|
+
when :variableKey
|
|
661
657
|
evaluation[:variable_key]
|
|
662
|
-
when
|
|
658
|
+
when :variableValue
|
|
663
659
|
evaluation[:variable_value]
|
|
664
|
-
when
|
|
660
|
+
when :variableSchema
|
|
665
661
|
evaluation[:variable_schema]
|
|
666
662
|
else
|
|
667
663
|
nil
|
|
@@ -765,16 +761,7 @@ module FeaturevisorCLI
|
|
|
765
761
|
end
|
|
766
762
|
end
|
|
767
763
|
|
|
768
|
-
|
|
769
|
-
case obj
|
|
770
|
-
when Hash
|
|
771
|
-
obj.transform_keys(&:to_sym).transform_values { |v| symbolize_keys(v) }
|
|
772
|
-
when Array
|
|
773
|
-
obj.map { |v| symbolize_keys(v) }
|
|
774
|
-
else
|
|
775
|
-
obj
|
|
776
|
-
end
|
|
777
|
-
end
|
|
764
|
+
|
|
778
765
|
|
|
779
766
|
def execute_command(command)
|
|
780
767
|
stdout, stderr, status = Open3.capture3(command)
|
|
@@ -53,12 +53,12 @@ module Featurevisor
|
|
|
53
53
|
if value.is_a?(Array) && (context_value_from_path.is_a?(String) || context_value_from_path.is_a?(Numeric) || context_value_from_path.nil?)
|
|
54
54
|
# Check if the attribute key actually exists in the context
|
|
55
55
|
key_exists = context.key?(attribute.to_sym) || context.key?(attribute.to_s)
|
|
56
|
-
|
|
56
|
+
|
|
57
57
|
# If key doesn't exist, notIn should fail (return false), in should also fail
|
|
58
58
|
if !key_exists
|
|
59
59
|
return false
|
|
60
60
|
end
|
|
61
|
-
|
|
61
|
+
|
|
62
62
|
value_in_context = context_value_from_path.to_s
|
|
63
63
|
|
|
64
64
|
if operator == "in"
|
|
@@ -183,6 +183,8 @@ module Featurevisor
|
|
|
183
183
|
end
|
|
184
184
|
end
|
|
185
185
|
|
|
186
|
+
|
|
187
|
+
|
|
186
188
|
if conditions.is_a?(Array)
|
|
187
189
|
return conditions.all? { |c| all_conditions_are_matched(c, context) }
|
|
188
190
|
end
|
|
@@ -245,6 +247,8 @@ module Featurevisor
|
|
|
245
247
|
all_segments_are_matched({ "and" => group_segments["not"] }, context) == false
|
|
246
248
|
end
|
|
247
249
|
end
|
|
250
|
+
|
|
251
|
+
|
|
248
252
|
end
|
|
249
253
|
|
|
250
254
|
if group_segments.is_a?(Array)
|
|
@@ -42,7 +42,7 @@ module Featurevisor
|
|
|
42
42
|
|
|
43
43
|
if options[:datafile]
|
|
44
44
|
@datafile_reader = Featurevisor::DatafileReader.new(
|
|
45
|
-
datafile: options[:datafile].is_a?(String) ? JSON.parse(options[:datafile]) : options[:datafile],
|
|
45
|
+
datafile: options[:datafile].is_a?(String) ? JSON.parse(options[:datafile], symbolize_names: true) : options[:datafile],
|
|
46
46
|
logger: @logger
|
|
47
47
|
)
|
|
48
48
|
end
|
|
@@ -61,7 +61,7 @@ module Featurevisor
|
|
|
61
61
|
def set_datafile(datafile)
|
|
62
62
|
begin
|
|
63
63
|
new_datafile_reader = Featurevisor::DatafileReader.new(
|
|
64
|
-
datafile: datafile.is_a?(String) ? JSON.parse(datafile) : datafile,
|
|
64
|
+
datafile: datafile.is_a?(String) ? JSON.parse(datafile, symbolize_names: true) : datafile,
|
|
65
65
|
logger: @logger
|
|
66
66
|
)
|
|
67
67
|
|
data/lib/featurevisor/version.rb
CHANGED