gitlab-experiment 0.4.7 → 0.4.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a26425622afb7aa3f341ff403192963e276876b65902237018f852117168a19a
4
- data.tar.gz: 0a5238c73a4b0217810b4f8161df6452b683b21a6573f1b657517302cef9cef0
3
+ metadata.gz: cf6802aee233e6124ae21b95a164a92be868f5c5eda19e9560a813ac9f0ade0a
4
+ data.tar.gz: e8691d32ae73422e172176477c54966edc187ec72a43030070b3412e9f15d346
5
5
  SHA512:
6
- metadata.gz: 567ca93e3f931c167bc1766490b07e1535db72e3b5da5986715412746354945ed799dab4c075e91c5015aa922bf5d7e5a82bdc940808aa8100c8897d5ee623b7
7
- data.tar.gz: 3f053ed2ad8bdfc2201aacaef9c723332fba260f4f4cb54c65a92539d74ec100297540c34c97579c99723f0d98e98dc57cbe383a0731128d623b0a58b4786d7c
6
+ metadata.gz: 0a9eded639610b17e6b0cccbd17a232d187fd9bddc68047cfa14a01500e17f0f6ba78e93e7cd08d87a0cdce6e15e31198d00b0976bb72ff7de5115be15c50859
7
+ data.tar.gz: a6cdae13a053ab7c2e0ae8cdb6934db927a856263a8759499f072c9364be6f6ca6ac8bc5e279f4512e04fc930d2491fe09a72909a949ce57ff64c4971ab43a65
@@ -66,6 +66,10 @@ module Gitlab
66
66
  yield self if block_given?
67
67
  end
68
68
 
69
+ def inspect
70
+ "#<#{self.class.name || 'AnonymousClass'}:#{format('0x%016X', __id__)} @name=#{name} @signature=#{signature}>"
71
+ end
72
+
69
73
  def context(value = nil)
70
74
  return @context if value.blank?
71
75
 
@@ -9,14 +9,32 @@ module Gitlab
9
9
  raise ArgumentError, 'variant must be a symbol or false' unless variant.is_a?(Symbol)
10
10
 
11
11
  klass = Gitlab::Experiment.send(:constantize, name) # rubocop:disable GitlabSecurity/PublicSend
12
- allow(klass).to receive(:new).and_wrap_original do |new, *args, &block|
13
- new.call(*args).tap do |instance|
14
- allow(instance).to receive(:enabled?).and_return(true)
15
- allow(instance).to receive(:resolve_variant_name).and_return(variant.to_s)
16
12
 
17
- block.call(instance) if block.present?
18
- end
19
- end
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)
16
+ allow_any_instance_of(klass).to receive(:resolve_variant_name).and_return(variant.to_s)
17
+ end
18
+ end
19
+
20
+ def wrapped_experiment(experiment, shallow: false, failure: nil, &block)
21
+ if shallow
22
+ yield experiment if block.present?
23
+ return experiment
24
+ end
25
+
26
+ receive_wrapped_new = receive(:new).and_wrap_original do |new, *new_args, &new_block|
27
+ instance = new.call(*new_args)
28
+ instance.tap(&block) if block.present?
29
+ instance.tap(&new_block) if new_block.present?
30
+ instance
31
+ end
32
+
33
+ klass = experiment.class == Class ? experiment : experiment.class
34
+ if failure
35
+ expect(klass).to receive_wrapped_new, failure
36
+ else
37
+ allow(klass).to receive_wrapped_new
20
38
  end
21
39
  end
22
40
  end
@@ -24,10 +42,27 @@ module Gitlab
24
42
  module RSpecMatchers
25
43
  extend RSpec::Matchers::DSL
26
44
 
45
+ def require_experiment(experiment, matcher_name, classes: false)
46
+ klass = experiment.class == Class ? experiment : experiment.class
47
+ unless klass <= Gitlab::Experiment
48
+ raise(
49
+ ArgumentError,
50
+ "#{matcher_name} matcher is limited to experiment instances#{classes ? ' and classes' : ''}"
51
+ )
52
+ end
53
+
54
+ if experiment == klass && !classes
55
+ raise ArgumentError, "#{matcher_name} matcher requires an instance of an experiment"
56
+ end
57
+
58
+ experiment != klass
59
+ end
60
+
27
61
  matcher :exclude do |context|
28
62
  ivar = :'@excluded'
29
63
 
30
64
  match do |experiment|
65
+ require_experiment(experiment, 'exclude')
31
66
  experiment.context(context)
32
67
 
33
68
  experiment.instance_variable_set(ivar, nil)
@@ -47,6 +82,7 @@ module Gitlab
47
82
  ivar = :'@variant_name'
48
83
 
49
84
  match do |experiment|
85
+ require_experiment(experiment, 'segment')
50
86
  experiment.context(context)
51
87
 
52
88
  experiment.instance_variable_set(ivar, nil)
@@ -72,29 +108,74 @@ module Gitlab
72
108
 
73
109
  def message_details
74
110
  message = ''
75
- message += %(\n into: #{@expected}) if @expected
76
- message += %(\n actual: #{@actual}) if @actual
111
+ message += %( into variant\n expected variant: #{@expected}) if @expected
112
+ message += %(\n actual variant: #{@actual}) if @actual
77
113
  message
78
114
  end
79
115
  end
80
116
 
81
117
  matcher :track do |event, *event_args|
82
118
  match do |experiment|
83
- wrapped(experiment) { |instance| expect(instance).to receive_with(event, *event_args) }
119
+ expect_tracking_on(experiment, event, *event_args)
84
120
  end
85
121
 
86
122
  match_when_negated do |experiment|
87
- wrapped(experiment) { |instance| expect(instance).not_to receive_with(event, *event_args) }
123
+ expect_tracking_on(experiment, event, *event_args, negated: true)
88
124
  end
89
125
 
90
- def wrapped(experiment, &block)
91
- allow(experiment.class).to receive(:new).and_wrap_original do |new, *new_args|
92
- new.call(*new_args).tap(&block)
126
+ chain :for do |expected_variant|
127
+ raise ArgumentError, 'variant name must be provided' if expected.blank?
128
+
129
+ @expected_variant = expected_variant.to_s
130
+ end
131
+
132
+ chain(:with_context) { |expected_context| @expected_context = expected_context }
133
+ chain(:on_any_instance) { @on_self = false }
134
+
135
+ def expect_tracking_on(experiment, event, *event_args, negated: false)
136
+ @experiment = experiment
137
+ @on_self = true if require_experiment(experiment, 'track', classes: !@on_self) && @on_self.nil?
138
+ wrapped_experiment(experiment, shallow: @on_self, failure: failure_message(:no_new, event)) do |instance|
139
+ @experiment = instance
140
+ allow(@experiment).to receive(:track)
141
+
142
+ if negated
143
+ expect(@experiment).not_to receive_tracking_call_for(event, *event_args)
144
+ else
145
+ expect(@experiment).to receive_tracking_call_for(event, *event_args)
146
+ end
93
147
  end
94
148
  end
95
149
 
96
- def receive_with(*args)
97
- receive(:track).with(*args) # rubocop:disable CodeReuse/ActiveRecord
150
+ def receive_tracking_call_for(event, *event_args)
151
+ receive(:track).with(*[event, *event_args]) do # rubocop:disable CodeReuse/ActiveRecord
152
+ if @expected_variant
153
+ expect(@experiment.variant.name).to eq(@expected_variant), failure_message(:variant, event)
154
+ end
155
+
156
+ if @expected_context
157
+ expect(@experiment.context.value).to include(@expected_context), failure_message(:context, event)
158
+ end
159
+ end
160
+ end
161
+
162
+ def failure_message(failure_type, event)
163
+ case failure_type
164
+ when :variant
165
+ <<~MESSAGE.strip
166
+ expected #{@experiment.inspect} to have tracked #{event.inspect} for variant
167
+ expected variant: #{@expected_variant}
168
+ actual variant: #{@experiment.variant.name}
169
+ MESSAGE
170
+ when :context
171
+ <<~MESSAGE.strip
172
+ expected #{@experiment.inspect} to have tracked #{event.inspect} with context
173
+ expected context: #{@expected_context}
174
+ actual context: #{@experiment.context.value}
175
+ MESSAGE
176
+ when :no_new
177
+ %(expected #{@experiment.inspect} to have tracked #{event.inspect}, but no new instances were created)
178
+ end
98
179
  end
99
180
  end
100
181
  end
@@ -103,9 +184,10 @@ end
103
184
 
104
185
  RSpec.configure do |config|
105
186
  config.include Gitlab::Experiment::RSpecHelpers
187
+ config.include Gitlab::Experiment::Dsl
106
188
 
107
189
  config.include Gitlab::Experiment::RSpecMatchers, :experiment
108
- config.define_derived_metadata(file_path: %r{/spec/experiments/}) do |metadata|
190
+ config.define_derived_metadata(file_path: Regexp.new('/spec/experiments/')) do |metadata|
109
191
  metadata[:type] = :experiment
110
192
  end
111
193
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Gitlab
4
4
  class Experiment
5
- VERSION = '0.4.7'
5
+ VERSION = '0.4.8'
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.4.7
4
+ version: 0.4.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitLab
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-01-22 00:00:00.000000000 Z
11
+ date: 2021-01-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport