gitlab-experiment 0.5.1 → 0.5.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 13f170ab47c88f393041b3f4b8b69c168d8a159fcfcdf2fe7458ceca75a9b9db
4
- data.tar.gz: 5bc7fe4a5c8a493ce14413c109ac4424bdd61d8b0c3513530963e52586bfaf5f
3
+ metadata.gz: d4b1d80362166d1e86dd434fffe2a8e31676440f48b578e4bfcca4624fc819a8
4
+ data.tar.gz: 4535076f1c77f3faefc92f59be23689a5412f462bda088b67a4fbbdb58c74254
5
5
  SHA512:
6
- metadata.gz: bc52008aea6015b274fb7200c83b14145b355be637f1d286a8c9590df40c5d5e2c1d6da9e6f41f7f1970c576dfb13af69f43610e162eebff642d3945455e1b24
7
- data.tar.gz: 9f16a5b7bdd2ae9ea9b624946d5055878c67d67e3f755900bccdac153c330fab77ed98d1d88b0f1a869e3bbd1b1f347366252db98207513e789c387c6fd509d0
6
+ metadata.gz: d9d3445f7ee60be9d445760f103162861b1983c35ed6a9347e8703bc44c4bef2d7b154a62b2b05aa02c84471b8cfa95f91aa5521ee5743b9afa8ea5b4a4088eb
7
+ data.tar.gz: cb49261fe14971ff76799bac943e5826b04cad84982b8d061cbd7b2843a0817854772f2b3e8e55600023aff9b5266c0ba562df50c47bb90703cb686c4f973ebd
@@ -3,44 +3,78 @@
3
3
  module Gitlab
4
4
  class Experiment
5
5
  module RSpecHelpers
6
- def stub_experiments(experiments)
7
- experiments.each do |name, variant|
8
- variant = :control if variant == false
6
+ def stub_experiments(experiments, times = nil)
7
+ experiments.each { |experiment| wrapped_experiment(experiment, times) }
8
+ end
9
+
10
+ def wrapped_experiment(experiment, times = nil, expected = false, &block)
11
+ klass, experiment_name, variant_name = *experiment_details(experiment)
12
+ base_klass = Configuration.base_class.constantize
9
13
 
10
- base = Configuration.base_class.constantize
11
- klass = base.constantize(name) || base
14
+ # Set expectations on experiment classes so we can and_wrap_original with more specific args
15
+ experiment_klasses = base_klass.descendants.reject { |k| k == klass }
16
+ experiment_klasses.push(base_klass).each do |k|
17
+ allow(k).to receive(:new).and_call_original
18
+ end
12
19
 
13
- # We have to use this high level any_instance behavior as there's
14
- # not an alternative that allows multiple wrappings of `new`.
15
- allow_any_instance_of(klass).to receive(:enabled?).and_return(true)
20
+ receiver = receive(:new)
16
21
 
17
- if variant == true # passing true allows the rollout to do its job
18
- allow_any_instance_of(klass).to receive(:experiment_group?).and_return(true)
19
- else
20
- allow_any_instance_of(klass).to receive(:resolve_variant_name).and_return(variant.to_s)
22
+ # Be specific for BaseClass calls
23
+ receiver = receiver.with(experiment_name, any_args) if experiment_name && klass == base_klass
24
+
25
+ receiver.exactly(times).times if times
26
+
27
+ # Set expectations on experiment class of interest
28
+ allow_or_expect_klass = expected ? expect(klass) : allow(klass)
29
+ allow_or_expect_klass.to receiver.and_wrap_original do |method, *original_args, &original_block|
30
+ method.call(*original_args).tap do |e|
31
+ # Stub internal methods before calling the original_block
32
+ allow(e).to receive(:enabled?).and_return(true)
33
+
34
+ if variant_name == true # passing true allows the rollout to do its job
35
+ allow(e).to receive(:experiment_group?).and_return(true)
36
+ else
37
+ allow(e).to receive(:resolve_variant_name).and_return(variant_name.to_s)
38
+ end
39
+
40
+ # Stub/set expectations before calling the original_block
41
+ yield e if block
42
+
43
+ original_block.call(e) if original_block.present?
21
44
  end
22
45
  end
23
46
  end
24
47
 
25
- def wrapped_experiment(experiment, shallow: false, failure: nil, &block)
26
- if shallow
27
- yield experiment if block.present?
28
- return experiment
29
- end
48
+ private
30
49
 
31
- receive_wrapped_new = receive(:new).and_wrap_original do |new, *new_args, &new_block|
32
- instance = new.call(*new_args)
33
- instance.tap(&block) if block.present?
34
- instance.tap(&new_block) if new_block.present?
35
- instance
50
+ def experiment_details(experiment)
51
+ if experiment.is_a?(Symbol)
52
+ experiment_name = experiment
53
+ variant_name = nil
36
54
  end
37
55
 
38
- klass = experiment.class == Class ? experiment : experiment.class
39
- if failure
40
- expect(klass).to receive_wrapped_new, failure
56
+ experiment_name, variant_name = *experiment if experiment.is_a?(Array)
57
+
58
+ base_klass = Configuration.base_class.constantize
59
+ variant_name = experiment.variant.name if experiment.is_a?(base_klass)
60
+
61
+ if experiment.class.name.nil? # Anonymous class instance
62
+ klass = experiment.class
63
+ elsif experiment.instance_of?(Class) # Class level stubbing, eg. "MyExperiment"
64
+ klass = experiment
41
65
  else
42
- allow(klass).to receive_wrapped_new
66
+ experiment_name ||= experiment.instance_variable_get(:@name)
67
+ klass = base_klass.constantize(experiment_name)
68
+ end
69
+
70
+ if experiment_name && klass == base_klass
71
+ experiment_name = experiment_name.to_sym
72
+
73
+ # For experiment names like: "group/experiment-name"
74
+ experiment_name = experiment_name.to_s if experiment_name.inspect.include?('"')
43
75
  end
76
+
77
+ [klass, experiment_name, variant_name]
44
78
  end
45
79
  end
46
80
 
@@ -135,34 +169,41 @@ module Gitlab
135
169
  end
136
170
 
137
171
  chain(:with_context) { |expected_context| @expected_context = expected_context }
138
- chain(:on_any_instance) { @on_self = false }
172
+
173
+ chain(:on_next_instance) { @on_next_instance = true }
139
174
 
140
175
  def expect_tracking_on(experiment, negated, event, *event_args)
141
- @experiment = experiment
142
- @on_self = true if require_experiment(experiment, 'track', classes: !@on_self) && @on_self.nil?
143
- wrapped_experiment(experiment, shallow: @on_self, failure: failure_message(:no_new, event)) do |instance|
144
- @experiment = instance
145
- allow(@experiment).to receive(:track)
176
+ klass = experiment.instance_of?(Class) ? experiment : experiment.class
177
+ unless klass <= Gitlab::Experiment
178
+ raise(
179
+ ArgumentError,
180
+ "track matcher is limited to experiment instances and classes"
181
+ )
182
+ end
183
+
184
+ expectations = proc do |e|
185
+ @experiment = e
186
+ allow(e).to receive(:track).and_call_original
146
187
 
147
188
  if negated
148
- expect(@experiment).not_to receive_tracking_call_for(event, *event_args)
189
+ expect(e).not_to receive(:track).with(*[event, *event_args])
149
190
  else
150
- expect(@experiment).to receive_tracking_call_for(event, *event_args)
151
- end
152
- end
153
- end
191
+ if @expected_variant
192
+ expect(@experiment.variant.name).to eq(@expected_variant), failure_message(:variant, event)
193
+ end
154
194
 
155
- def receive_tracking_call_for(expected_event, *expected_event_args)
156
- receive(:track).with(*[expected_event, *expected_event_args]).and_wrap_original do |track, event, *event_args|
157
- track.call(event, *event_args) # call the original
195
+ if @expected_context
196
+ expect(@experiment.context.value).to include(@expected_context), failure_message(:context, event)
197
+ end
158
198
 
159
- if @expected_variant
160
- expect(@experiment.variant.name).to eq(@expected_variant), failure_message(:variant, expected_event)
199
+ expect(e).to receive(:track).with(*[event, *event_args]).and_call_original
161
200
  end
201
+ end
162
202
 
163
- if @expected_context
164
- expect(@experiment.context.value).to include(@expected_context), failure_message(:context, expected_event)
165
- end
203
+ if experiment.instance_of?(Class) || @on_next_instance
204
+ wrapped_experiment(experiment, nil, true) { |e| expectations.call(e) }
205
+ else
206
+ expectations.call(experiment)
166
207
  end
167
208
  end
168
209
 
@@ -180,8 +221,6 @@ module Gitlab
180
221
  expected context: #{@expected_context}
181
222
  actual context: #{@experiment.context.value}
182
223
  MESSAGE
183
- when :no_new
184
- %(expected #{@experiment.inspect} to have tracked #{event.inspect}, but no new instances were created)
185
224
  end
186
225
  end
187
226
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Gitlab
4
4
  class Experiment
5
- VERSION = '0.5.1'
5
+ VERSION = '0.5.2'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gitlab-experiment
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitLab
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-17 00:00:00.000000000 Z
11
+ date: 2021-04-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport