yggdrasil-engine 1.0.0-x64-mingw-ucrt
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 +7 -0
- data/README.md +36 -0
- data/lib/custom_strategy.rb +60 -0
- data/lib/yggdrasil_engine.rb +145 -0
- data/lib/yggdrasilffi_x86_64.dll +0 -0
- data/spec/custom_strategy_spec.rb +136 -0
- data/spec/yggdrasil_engine_spec.rb +161 -0
- metadata +63 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ea76118d13a05cc0d733785f1493c1e45af63f3e78ddb89074070663a9cc4dee
|
4
|
+
data.tar.gz: dc49b39ee6b39e5a69e84e31ac260abdf672729b4f7bdeda1f024d2258b8f413
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 25e030238b05317d09cc2de7c58ce490f3636dce89420a9fbc94881f6902744c2f526732a560a8000185eb8a71793e842f8e6a093bf2d8783c0563029e30e5f6
|
7
|
+
data.tar.gz: 2c9f57642e5dd55152ba9f369149f729800c709b593edbf9f74abc9370a475be6b25b6d7f8036c7c8ff8337d5e096f94a173233fc5533742c9e3f3d3e64adcee
|
data/README.md
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# Ruby Bindings to Yggdrasil
|
2
|
+
|
3
|
+
## Running the tests
|
4
|
+
|
5
|
+
First make sure that you have built the native FFI code and it's located in the right place. This can be done with the build script in `build.sh`:
|
6
|
+
|
7
|
+
|
8
|
+
```bash
|
9
|
+
./build.sh
|
10
|
+
```
|
11
|
+
|
12
|
+
Then you can run the tests with:
|
13
|
+
|
14
|
+
```bash
|
15
|
+
rspec
|
16
|
+
```
|
17
|
+
|
18
|
+
There's also a `mem_check.rb` in the scripts folder. This is not a bullet proof test, but it can be helpful for detecting large leaks. This requires human interaction - you need to read the output and understand what it's telling you, so it's not run as part of the test suite.
|
19
|
+
|
20
|
+
```bash
|
21
|
+
ruby scripts/mem_check.rb
|
22
|
+
```
|
23
|
+
|
24
|
+
## Build
|
25
|
+
|
26
|
+
You can build the gem with:
|
27
|
+
|
28
|
+
```bash
|
29
|
+
gem build yggdrasil-engine.gemspec
|
30
|
+
```
|
31
|
+
|
32
|
+
Then you can install the gem for local development with:
|
33
|
+
|
34
|
+
```
|
35
|
+
gem install yggdrasil-engine-0.0.1.gem
|
36
|
+
```
|
@@ -0,0 +1,60 @@
|
|
1
|
+
STANDARD_STRATEGIES = [
|
2
|
+
"default",
|
3
|
+
"userWithId",
|
4
|
+
"gradualRolloutUserId",
|
5
|
+
"gradualRolloutSessionId",
|
6
|
+
"gradualRolloutRandom",
|
7
|
+
"flexibleRollout",
|
8
|
+
"remoteAddress",
|
9
|
+
].freeze
|
10
|
+
|
11
|
+
class CustomStrategyHandler
|
12
|
+
def initialize
|
13
|
+
@custom_strategies_definitions = {}
|
14
|
+
@custom_strategy_implementations = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def update_strategies(json_str)
|
18
|
+
custom_strategies = {}
|
19
|
+
parsed_json = JSON.parse(json_str)
|
20
|
+
|
21
|
+
parsed_json["features"].each do |feature|
|
22
|
+
toggle_name = feature["name"]
|
23
|
+
strategies = feature["strategies"]
|
24
|
+
|
25
|
+
custom_strategies_for_toggle = strategies.select do |strategy|
|
26
|
+
!STANDARD_STRATEGIES.include?(strategy["name"])
|
27
|
+
end
|
28
|
+
|
29
|
+
unless custom_strategies_for_toggle.empty?
|
30
|
+
custom_strategies[toggle_name] = custom_strategies_for_toggle
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
@custom_strategies_definitions = custom_strategies
|
35
|
+
end
|
36
|
+
|
37
|
+
def register_custom_strategies(strategies)
|
38
|
+
strategies.each do |strategy|
|
39
|
+
if strategy.respond_to?(:name) && strategy.name.is_a?(String) &&
|
40
|
+
strategy.respond_to?(:enabled?)
|
41
|
+
@custom_strategy_implementations[strategy.name] = strategy
|
42
|
+
else
|
43
|
+
raise "Invalid strategy object. Must have a name method that returns a String and an enabled? method."
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def evaluate_custom_strategies(toggle_name, context)
|
49
|
+
results = {}
|
50
|
+
|
51
|
+
@custom_strategies_definitions[toggle_name]&.each_with_index do |strategy, index|
|
52
|
+
key = "customStrategy#{index + 1}"
|
53
|
+
strategy_impl = @custom_strategy_implementations[strategy["name"]]
|
54
|
+
result = strategy_impl&.enabled?(strategy["parameters"], context) || false
|
55
|
+
results[key] = result
|
56
|
+
end
|
57
|
+
|
58
|
+
results
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
require 'json'
|
3
|
+
require 'custom_strategy'
|
4
|
+
|
5
|
+
TOGGLE_MISSING_RESPONSE = 'NotFound'.freeze
|
6
|
+
ERROR_RESPONSE = 'Error'.freeze
|
7
|
+
OK_RESPONSE = 'Ok'.freeze
|
8
|
+
|
9
|
+
def platform_specific_lib
|
10
|
+
os = RbConfig::CONFIG['host_os']
|
11
|
+
cpu = RbConfig::CONFIG['host_cpu']
|
12
|
+
|
13
|
+
extension, prefix = case os
|
14
|
+
when /darwin|mac os/
|
15
|
+
['dylib', 'lib']
|
16
|
+
when /linux/
|
17
|
+
['so', 'lib']
|
18
|
+
when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
|
19
|
+
['dll', '']
|
20
|
+
else
|
21
|
+
raise "unsupported platform #{os}"
|
22
|
+
end
|
23
|
+
|
24
|
+
arch_suffix = case cpu
|
25
|
+
when /x86_64/
|
26
|
+
'x86_64'
|
27
|
+
when /arm|aarch64/
|
28
|
+
'arm64'
|
29
|
+
else
|
30
|
+
raise "unsupported architecture #{cpu}"
|
31
|
+
end
|
32
|
+
|
33
|
+
lib_type_suffix = if os =~ /linux/
|
34
|
+
musl = system("ldd /bin/sh | grep -q musl")
|
35
|
+
musl ? "-musl" : ""
|
36
|
+
else
|
37
|
+
""
|
38
|
+
end
|
39
|
+
|
40
|
+
"#{prefix}yggdrasilffi_#{arch_suffix}#{lib_type_suffix}.#{extension}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_variant(raw_variant)
|
44
|
+
payload = raw_variant[:payload] && raw_variant[:payload].transform_keys(&:to_s)
|
45
|
+
{
|
46
|
+
name: raw_variant[:name],
|
47
|
+
enabled: raw_variant[:enabled],
|
48
|
+
feature_enabled: raw_variant[:featureEnabled],
|
49
|
+
payload: payload,
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
class YggdrasilEngine
|
54
|
+
extend FFI::Library
|
55
|
+
ffi_lib File.expand_path(platform_specific_lib, __dir__)
|
56
|
+
|
57
|
+
attach_function :new_engine, [], :pointer
|
58
|
+
attach_function :free_engine, [:pointer], :void
|
59
|
+
|
60
|
+
attach_function :take_state, %i[pointer string], :pointer
|
61
|
+
attach_function :check_enabled, %i[pointer string string string], :pointer
|
62
|
+
attach_function :check_variant, %i[pointer string string string], :pointer
|
63
|
+
attach_function :get_metrics, [:pointer], :pointer
|
64
|
+
attach_function :free_response, [:pointer], :void
|
65
|
+
|
66
|
+
attach_function :count_toggle, %i[pointer string bool], :void
|
67
|
+
attach_function :count_variant, %i[pointer string string], :void
|
68
|
+
|
69
|
+
attach_function :list_known_toggles, [:pointer], :pointer
|
70
|
+
|
71
|
+
def initialize
|
72
|
+
@engine = YggdrasilEngine.new_engine
|
73
|
+
@custom_strategy_handler = CustomStrategyHandler.new
|
74
|
+
ObjectSpace.define_finalizer(self, self.class.finalize(@engine))
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.finalize(engine)
|
78
|
+
proc { YggdrasilEngine.free_engine(engine) }
|
79
|
+
end
|
80
|
+
|
81
|
+
def take_state(toggles)
|
82
|
+
@custom_strategy_handler.update_strategies(toggles)
|
83
|
+
response_ptr = YggdrasilEngine.take_state(@engine, toggles)
|
84
|
+
take_toggles_response = JSON.parse(response_ptr.read_string, symbolize_names: true)
|
85
|
+
YggdrasilEngine.free_response(response_ptr)
|
86
|
+
end
|
87
|
+
|
88
|
+
def get_variant(name, context)
|
89
|
+
context_json = (context || {}).to_json
|
90
|
+
custom_strategy_results = @custom_strategy_handler.evaluate_custom_strategies(name, context).to_json
|
91
|
+
|
92
|
+
variant_def_json_ptr = YggdrasilEngine.check_variant(@engine, name, context_json, custom_strategy_results)
|
93
|
+
variant_def_json = variant_def_json_ptr.read_string
|
94
|
+
YggdrasilEngine.free_response(variant_def_json_ptr)
|
95
|
+
variant_response = JSON.parse(variant_def_json, symbolize_names: true)
|
96
|
+
|
97
|
+
return nil if variant_response[:status_code] == TOGGLE_MISSING_RESPONSE
|
98
|
+
variant = variant_response[:value]
|
99
|
+
|
100
|
+
return to_variant(variant) if variant_response[:status_code] == OK_RESPONSE
|
101
|
+
end
|
102
|
+
|
103
|
+
def enabled?(toggle_name, context)
|
104
|
+
context_json = (context || {}).to_json
|
105
|
+
custom_strategy_results = @custom_strategy_handler.evaluate_custom_strategies(toggle_name, context).to_json
|
106
|
+
|
107
|
+
response_ptr = YggdrasilEngine.check_enabled(@engine, toggle_name, context_json, custom_strategy_results)
|
108
|
+
response_json = response_ptr.read_string
|
109
|
+
YggdrasilEngine.free_response(response_ptr)
|
110
|
+
response = JSON.parse(response_json, symbolize_names: true)
|
111
|
+
|
112
|
+
raise "Error: #{response[:error_message]}" if response[:status_code] == ERROR_RESPONSE
|
113
|
+
return nil if response[:status_code] == TOGGLE_MISSING_RESPONSE
|
114
|
+
|
115
|
+
return response[:value] == true
|
116
|
+
end
|
117
|
+
|
118
|
+
def count_toggle(toggle_name, enabled)
|
119
|
+
response_ptr = YggdrasilEngine.count_toggle(@engine, toggle_name, enabled)
|
120
|
+
YggdrasilEngine.free_response(response_ptr)
|
121
|
+
end
|
122
|
+
|
123
|
+
def count_variant(toggle_name, variant_name)
|
124
|
+
response_ptr = YggdrasilEngine.count_variant(@engine, toggle_name, variant_name)
|
125
|
+
YggdrasilEngine.free_response(response_ptr)
|
126
|
+
end
|
127
|
+
|
128
|
+
def get_metrics
|
129
|
+
metrics_ptr = YggdrasilEngine.get_metrics(@engine)
|
130
|
+
metrics = JSON.parse(metrics_ptr.read_string, symbolize_names: true)
|
131
|
+
YggdrasilEngine.free_response(metrics_ptr)
|
132
|
+
metrics[:value]
|
133
|
+
end
|
134
|
+
|
135
|
+
def list_known_toggles
|
136
|
+
response_ptr = YggdrasilEngine.list_known_toggles(@engine)
|
137
|
+
response_json = response_ptr.read_string
|
138
|
+
YggdrasilEngine.free_response(response_ptr)
|
139
|
+
JSON.parse(response_json, symbolize_names: true)
|
140
|
+
end
|
141
|
+
|
142
|
+
def register_custom_strategies(strategies)
|
143
|
+
@custom_strategy_handler.register_custom_strategies(strategies)
|
144
|
+
end
|
145
|
+
end
|
Binary file
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require_relative '../lib/custom_strategy'
|
2
|
+
|
3
|
+
RSpec.describe "custom strategies" do
|
4
|
+
let(:raw_state) do
|
5
|
+
{
|
6
|
+
"version": 1,
|
7
|
+
"features": [
|
8
|
+
{
|
9
|
+
"name": "Feature.A",
|
10
|
+
"enabled": true,
|
11
|
+
"strategies": [
|
12
|
+
{
|
13
|
+
"name": "default",
|
14
|
+
"parameters": {}
|
15
|
+
},
|
16
|
+
{
|
17
|
+
"name": "custom",
|
18
|
+
"parameters": {
|
19
|
+
"gerkhins": "yes"
|
20
|
+
}
|
21
|
+
},
|
22
|
+
{
|
23
|
+
"name": "some-other-custom",
|
24
|
+
"parameters": {
|
25
|
+
"gerkhins": "yes"
|
26
|
+
}
|
27
|
+
},
|
28
|
+
]
|
29
|
+
}
|
30
|
+
]
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
let(:handler) { CustomStrategyHandler.new }
|
35
|
+
|
36
|
+
before do
|
37
|
+
handler.update_strategies(raw_state.to_json)
|
38
|
+
end
|
39
|
+
|
40
|
+
describe 'computing custom strategies' do
|
41
|
+
it 'respects the logic contained in the enabled function' do
|
42
|
+
class TestStrategy
|
43
|
+
attr_reader :name
|
44
|
+
|
45
|
+
def initialize(name)
|
46
|
+
@name = name
|
47
|
+
end
|
48
|
+
|
49
|
+
def enabled?(params, context)
|
50
|
+
params["gerkhins"] == "yes"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
handler.register_custom_strategies([TestStrategy.new("custom")])
|
55
|
+
strategy_results = handler.evaluate_custom_strategies("Feature.A", {})
|
56
|
+
expect(strategy_results.length).to eq(2)
|
57
|
+
expect(strategy_results["customStrategy1"]).to eq(true)
|
58
|
+
expect(strategy_results["customStrategy2"]).to eq(false)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'creates a strategy result for every custom strategy thats implemented and defined' do
|
62
|
+
class TestStrategy
|
63
|
+
attr_reader :name
|
64
|
+
|
65
|
+
def initialize(name)
|
66
|
+
@name = name
|
67
|
+
end
|
68
|
+
|
69
|
+
def enabled?(params, context)
|
70
|
+
params["gerkhins"] == "yes"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
handler.register_custom_strategies([TestStrategy.new("custom"), TestStrategy.new("some-other-custom")])
|
75
|
+
strategy_results = handler.evaluate_custom_strategies("Feature.A", {})
|
76
|
+
expect(strategy_results.length).to eq(2)
|
77
|
+
expect(strategy_results["customStrategy1"]).to eq(true)
|
78
|
+
expect(strategy_results["customStrategy2"]).to eq(true)
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'returns false for missing implementations' do
|
82
|
+
handler.register_custom_strategies([])
|
83
|
+
strategy_results = handler.evaluate_custom_strategies("Feature.A", {})
|
84
|
+
expect(strategy_results.length).to eq(2)
|
85
|
+
expect(strategy_results["customStrategy1"]).to eq(false)
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should calculate custom strategies e2e" do
|
89
|
+
class TestStrategy
|
90
|
+
attr_reader :name
|
91
|
+
|
92
|
+
def initialize(name)
|
93
|
+
@name = name
|
94
|
+
end
|
95
|
+
|
96
|
+
def enabled?(params, context)
|
97
|
+
context[:userId] == "123"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
state = {
|
102
|
+
"version": 1,
|
103
|
+
"features": [
|
104
|
+
{
|
105
|
+
"name": "Feature.A",
|
106
|
+
"enabled": true,
|
107
|
+
"strategies": [
|
108
|
+
{
|
109
|
+
"name": "custom",
|
110
|
+
"parameters": {
|
111
|
+
"gerkhins": "yes"
|
112
|
+
}
|
113
|
+
}
|
114
|
+
]
|
115
|
+
}
|
116
|
+
]
|
117
|
+
}
|
118
|
+
|
119
|
+
engine = YggdrasilEngine.new
|
120
|
+
engine.register_custom_strategies([TestStrategy.new("custom")])
|
121
|
+
|
122
|
+
engine.take_state(state.to_json)
|
123
|
+
|
124
|
+
should_be_enabled = engine.enabled?("Feature.A", {
|
125
|
+
userId: "123"
|
126
|
+
})
|
127
|
+
|
128
|
+
should_not_be_enabled = engine.enabled?("Feature.A", {
|
129
|
+
userId: "456"
|
130
|
+
})
|
131
|
+
|
132
|
+
expect(should_be_enabled).to eq(true)
|
133
|
+
expect(should_not_be_enabled).to eq(false)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'json'
|
3
|
+
require_relative '../lib/yggdrasil_engine'
|
4
|
+
|
5
|
+
index_file_path = '../client-specification/specifications/index.json'
|
6
|
+
test_suites = JSON.parse(File.read(index_file_path))
|
7
|
+
|
8
|
+
def test_suite_variant(base_variant)
|
9
|
+
payload = base_variant[:payload] && base_variant[:payload].transform_keys(&:to_s)
|
10
|
+
{
|
11
|
+
name: base_variant[:name],
|
12
|
+
enabled: base_variant[:enabled],
|
13
|
+
feature_enabled: base_variant[:feature_enabled],
|
14
|
+
payload: payload,
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
RSpec.describe YggdrasilEngine do
|
19
|
+
let(:yggdrasil_engine) { YggdrasilEngine.new }
|
20
|
+
|
21
|
+
describe '#checking a toggle' do
|
22
|
+
it 'that does not exist should yield a not found' do
|
23
|
+
is_enabled = yggdrasil_engine.enabled?('missing-toggle', {})
|
24
|
+
expect(is_enabled).to be_nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#metrics' do
|
29
|
+
it 'should clear metrics when get_metrics is called' do
|
30
|
+
feature_name = 'Feature.A'
|
31
|
+
|
32
|
+
suite_path = File.join('../client-specification/specifications', '01-simple-examples.json')
|
33
|
+
suite_data = JSON.parse(File.read(suite_path))
|
34
|
+
|
35
|
+
yggdrasil_engine.take_state(suite_data['state'].to_json)
|
36
|
+
|
37
|
+
yggdrasil_engine.count_toggle(feature_name, true)
|
38
|
+
yggdrasil_engine.count_toggle(feature_name, false)
|
39
|
+
|
40
|
+
metrics = yggdrasil_engine.get_metrics() # This should clear the metrics buffer
|
41
|
+
|
42
|
+
metric = metrics[:toggles][feature_name.to_sym]
|
43
|
+
expect(metric[:yes]).to eq(1)
|
44
|
+
expect(metric[:no]).to eq(1)
|
45
|
+
|
46
|
+
metrics = yggdrasil_engine.get_metrics()
|
47
|
+
expect(metrics).to be_nil
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should increment toggle count when it exists' do
|
51
|
+
toggle_name = 'Feature.A'
|
52
|
+
|
53
|
+
suite_path = File.join('../client-specification/specifications', '01-simple-examples.json')
|
54
|
+
suite_data = JSON.parse(File.read(suite_path))
|
55
|
+
|
56
|
+
yggdrasil_engine.take_state(suite_data['state'].to_json)
|
57
|
+
|
58
|
+
yggdrasil_engine.count_toggle(toggle_name, true)
|
59
|
+
yggdrasil_engine.count_toggle(toggle_name, false)
|
60
|
+
|
61
|
+
metrics = yggdrasil_engine.get_metrics()
|
62
|
+
metric = metrics[:toggles][toggle_name.to_sym]
|
63
|
+
|
64
|
+
expect(metric[:yes]).to eq(1)
|
65
|
+
expect(metric[:no]).to eq(1)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should increment toggle count when the toggle does not exist' do
|
69
|
+
toggle_name = 'Feature.X'
|
70
|
+
|
71
|
+
yggdrasil_engine.count_toggle(toggle_name, true)
|
72
|
+
yggdrasil_engine.count_toggle(toggle_name, false)
|
73
|
+
|
74
|
+
metrics = yggdrasil_engine.get_metrics()
|
75
|
+
metric = metrics[:toggles][toggle_name.to_sym]
|
76
|
+
|
77
|
+
expect(metric[:yes]).to eq(1)
|
78
|
+
expect(metric[:no]).to eq(1)
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should increment variant' do
|
82
|
+
toggle_name = 'Feature.Q'
|
83
|
+
|
84
|
+
suite_path = File.join('../client-specification/specifications', '01-simple-examples.json')
|
85
|
+
suite_data = JSON.parse(File.read(suite_path))
|
86
|
+
|
87
|
+
yggdrasil_engine.take_state(suite_data['state'].to_json)
|
88
|
+
|
89
|
+
yggdrasil_engine.count_variant(toggle_name, 'disabled')
|
90
|
+
|
91
|
+
metrics = yggdrasil_engine.get_metrics()
|
92
|
+
metric = metrics[:toggles][toggle_name.to_sym]
|
93
|
+
|
94
|
+
expect(metric[:variants][:disabled]).to eq(1)
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'should list all the features that were loaded' do
|
98
|
+
suite_path = File.join('../client-specification/specifications', '01-simple-examples.json')
|
99
|
+
suite_data = JSON.parse(File.read(suite_path))
|
100
|
+
|
101
|
+
yggdrasil_engine.take_state(suite_data['state'].to_json)
|
102
|
+
|
103
|
+
toggles = yggdrasil_engine.list_known_toggles()
|
104
|
+
expect(toggles.length).to eq(3)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
RSpec.describe 'Client Specification' do
|
110
|
+
let(:yggdrasil_engine) { YggdrasilEngine.new }
|
111
|
+
|
112
|
+
test_suites.each do |suite|
|
113
|
+
suite_path = File.join('../client-specification/specifications', suite)
|
114
|
+
suite_data = JSON.parse(File.read(suite_path), symbolize_names: true)
|
115
|
+
|
116
|
+
describe "Suite '#{suite}'" do
|
117
|
+
before(:each) do
|
118
|
+
yggdrasil_engine.take_state(suite_data[:state].to_json)
|
119
|
+
end
|
120
|
+
|
121
|
+
suite_data.fetch(:tests, []).each do |test|
|
122
|
+
describe "Test '#{test[:description]}'" do
|
123
|
+
let(:context) { test[:context] }
|
124
|
+
let(:toggle_name) { test[:toggleName] }
|
125
|
+
let(:expected_result) { test[:expectedResult] }
|
126
|
+
|
127
|
+
it 'returns correct result for `is_enabled?` method' do
|
128
|
+
result = yggdrasil_engine.enabled?(toggle_name, context) || false
|
129
|
+
|
130
|
+
expect(result).to eq(expected_result),
|
131
|
+
"Failed test '#{test['description']}': expected #{expected_result}, got #{result}"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
suite_data.fetch(:variantTests, []).each do |test|
|
137
|
+
next unless test[:expectedResult]
|
138
|
+
|
139
|
+
describe "Variant Test '#{test[:description]}'" do
|
140
|
+
let(:context) { test[:context] }
|
141
|
+
let(:toggle_name) { test[:toggleName] }
|
142
|
+
let(:expected_result) { test_suite_variant(test[:expectedResult]) }
|
143
|
+
|
144
|
+
it 'returns correct result for `get_variant` method' do
|
145
|
+
result = yggdrasil_engine.get_variant(toggle_name, context) || {
|
146
|
+
:name => 'disabled',
|
147
|
+
:payload => nil,
|
148
|
+
:enabled => false,
|
149
|
+
:feature_enabled => false
|
150
|
+
}
|
151
|
+
|
152
|
+
expect(result[:name]).to eq(expected_result[:name])
|
153
|
+
expect(result[:payload]).to eq(expected_result[:payload])
|
154
|
+
expect(result[:enabled]).to eq(expected_result[:enabled])
|
155
|
+
expect(result[:feature_enabled]).to eq(expected_result[:feature_enabled])
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: yggdrasil-engine
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: x64-mingw-ucrt
|
6
|
+
authors:
|
7
|
+
- Unleash
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-06-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: ffi
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.16.3
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.16.3
|
27
|
+
description: "..."
|
28
|
+
email: liquidwicked64@gmail.com
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- README.md
|
34
|
+
- lib/custom_strategy.rb
|
35
|
+
- lib/yggdrasil_engine.rb
|
36
|
+
- lib/yggdrasilffi_x86_64.dll
|
37
|
+
- spec/custom_strategy_spec.rb
|
38
|
+
- spec/yggdrasil_engine_spec.rb
|
39
|
+
homepage: http://github.com/username/my_gem
|
40
|
+
licenses:
|
41
|
+
- MIT
|
42
|
+
metadata:
|
43
|
+
yggdrasil_core_version: 0.14.2
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options: []
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
requirements: []
|
59
|
+
rubygems_version: 3.3.5
|
60
|
+
signing_key:
|
61
|
+
specification_version: 4
|
62
|
+
summary: Unleash engine for evaluating feature toggles
|
63
|
+
test_files: []
|