rollout 2.6.2 → 3.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.
data/spec/rollout_spec.rb CHANGED
@@ -1,677 +1,246 @@
1
1
  require "spec_helper"
2
2
 
3
- RSpec.describe "Rollout" do
4
- let(:rollout) { Rollout.new($redis) }
3
+ RSpec.describe Rollout do
4
+ let(:rollout) { described_class.new(adapter: RolloutMemoryBackend.new) }
5
5
 
6
- describe "when a group is activated" do
7
- before do
8
- rollout.define_group(:fivesonly) { |user| user.id == 5 }
9
- rollout.activate_group(:chat, :fivesonly)
10
- end
11
-
12
- it "the feature is active for users for which the block evaluates to true" do
13
- expect(rollout).to be_active(:chat, double(id: 5))
14
- end
6
+ describe "#initialize" do
7
+ it "exposes the adapter" do
8
+ adapter = RolloutMemoryBackend.new
9
+ rollout = described_class.new(adapter: adapter)
15
10
 
16
- it "is not active for users for which the block evaluates to false" do
17
- expect(rollout).not_to be_active(:chat, double(id: 1))
18
- end
19
-
20
- it "is not active if a group is found in Redis but not defined in Rollout" do
21
- rollout.activate_group(:chat, :fake)
22
- expect(rollout).not_to be_active(:chat, double(id: 1))
11
+ expect(rollout.adapter).to eq adapter
23
12
  end
24
13
  end
25
14
 
26
- describe "the default all group" do
27
- before do
28
- rollout.activate_group(:chat, :all)
29
- end
30
-
31
- it "evaluates to true no matter what" do
32
- expect(rollout).to be_active(:chat, double(id: 0))
33
- end
34
- end
35
-
36
- describe "deactivating a group" do
37
- before do
38
- rollout.define_group(:fivesonly) { |user| user.id == 5 }
39
- rollout.activate_group(:chat, :all)
40
- rollout.activate_group(:chat, :some)
41
- rollout.activate_group(:chat, :fivesonly)
42
- rollout.deactivate_group(:chat, :all)
43
- rollout.deactivate_group(:chat, "some")
44
- end
45
-
46
- it "deactivates the rules for that group" do
47
- expect(rollout).not_to be_active(:chat, double(id: 10))
48
- end
49
-
50
- it "leaves the other groups active" do
51
- expect(rollout.get(:chat).groups).to eq [:fivesonly]
52
- end
53
-
54
- it "leaves the other groups active using sets" do
55
- @options = rollout.instance_variable_get("@options")
56
- @options[:use_sets] = true
57
- expect(rollout.get(:chat).groups).to eq [:fivesonly].to_set
58
- end
59
- end
60
-
61
- describe "deactivating a feature completely" do
62
- before do
63
- rollout.define_group(:fivesonly) { |user| user.id == 5 }
64
- rollout.activate_group(:chat, :all)
65
- rollout.activate_group(:chat, :fivesonly)
66
- rollout.activate_user(:chat, double(id: 51))
67
- rollout.activate_percentage(:chat, 100)
68
- rollout.activate(:chat)
69
- rollout.deactivate(:chat)
70
- end
71
-
72
- it "removes all of the groups" do
73
- expect(rollout).not_to be_active(:chat, double(id: 0))
74
- end
75
-
76
- it "removes all of the users" do
77
- expect(rollout).not_to be_active(:chat, double(id: 51))
78
- end
79
-
80
- it "removes the percentage" do
81
- expect(rollout).not_to be_active(:chat, double(id: 24))
82
- end
83
-
84
- it "removes globally" do
85
- expect(rollout).not_to be_active(:chat)
86
- end
87
- end
88
-
89
- describe "activating a specific user" do
90
- before do
91
- rollout.activate_user(:chat, double(id: 42))
92
- end
93
-
94
- it "is active for that user" do
95
- expect(rollout).to be_active(:chat, double(id: 42))
96
- end
97
-
98
- it "remains inactive for other users" do
99
- expect(rollout).not_to be_active(:chat, double(id: 24))
100
- end
101
- end
102
-
103
- describe "activating a specific user by ID" do
104
- before do
105
- rollout.activate_user(:chat, 42)
106
- end
107
-
108
- it "is active for that user" do
109
- expect(rollout).to be_active(:chat, double(id: 42))
110
- end
111
-
112
- it "remains inactive for other users" do
113
- expect(rollout).not_to be_active(:chat, double(id: 24))
15
+ describe "#get" do
16
+ it "preserves the requested name type" do
17
+ expect(rollout.get("chat").name).to eq "chat"
18
+ expect(rollout.get(:chat).name).to eq :chat
114
19
  end
115
20
  end
116
21
 
117
- describe "activating a specific user with a string id" do
118
- before do
119
- rollout.activate_user(:chat, double(id: "user-72"))
120
- end
121
-
122
- it "is active for that user" do
123
- expect(rollout).to be_active(:chat, double(id: "user-72"))
124
- end
125
-
126
- it "remains inactive for other users" do
127
- expect(rollout).not_to be_active(:chat, double(id: "user-12"))
22
+ describe "#multi_get" do
23
+ it "preserves the requested name types" do
24
+ expect(rollout.multi_get("chat", :beta).map(&:name)).to eq ["chat", :beta]
128
25
  end
129
26
  end
130
27
 
131
- describe "activating a group of users" do
132
- context "specified by user objects" do
133
- let(:users) { [double(id: 1), double(id: 2), double(id: 3)] }
134
-
135
- before { rollout.activate_users(:chat, users) }
136
-
137
- it "is active for the given users" do
138
- users.each { |user| expect(rollout).to be_active(:chat, user) }
28
+ describe "#with_feature" do
29
+ it "preserves the requested name type" do
30
+ feature = rollout.with_feature("chat") do |current|
31
+ current.percentage = 25
139
32
  end
140
33
 
141
- it "remains inactive for other users" do
142
- expect(rollout).not_to be_active(:chat, double(id: 4))
143
- end
34
+ expect(feature.name).to eq "chat"
35
+ expect(rollout.get("chat").name).to eq "chat"
144
36
  end
145
37
 
146
- context "specified by user ids" do
147
- let(:users) { [1, 2, 3] }
148
-
149
- before { rollout.activate_users(:chat, users) }
38
+ it "persists a mutation when nested without resets the thread flag" do
39
+ rollout = described_class.new(adapter: RolloutMemoryBackend.new, logging: true)
150
40
 
151
- it "is active for the given users" do
152
- users.each { |user| expect(rollout).to be_active(:chat, user) }
41
+ rollout.logging.without do
42
+ rollout.with_feature(:chat) do |feature|
43
+ feature.percentage = 25
44
+ rollout.logging.without {}
45
+ end
153
46
  end
154
47
 
155
- it "remains inactive for other users" do
156
- expect(rollout).not_to be_active(:chat, 4)
157
- end
48
+ expect(rollout.get(:chat).percentage).to eq 25.0
158
49
  end
159
- end
160
50
 
161
- describe "deactivating a specific user" do
162
- before do
163
- rollout.activate_user(:chat, double(id: 42))
164
- rollout.activate_user(:chat, double(id: 4242))
165
- rollout.activate_user(:chat, double(id: 24))
166
- rollout.deactivate_user(:chat, double(id: 42))
167
- rollout.deactivate_user(:chat, double(id: "4242"))
168
- end
169
-
170
- it "that user should no longer be active" do
171
- expect(rollout).not_to be_active(:chat, double(id: 42))
172
- end
173
-
174
- it "remains active for other active users" do
175
- @options = rollout.instance_variable_get("@options")
176
- @options[:use_sets] = false
177
- expect(rollout.get(:chat).users).to eq %w(24)
178
- end
179
-
180
- it "remains active for other active users using sets" do
181
- @options = rollout.instance_variable_get("@options")
182
- @options[:use_sets] = true
183
-
184
- expect(rollout.get(:chat).users).to eq %w(24).to_set
185
- end
186
- end
51
+ it "does not notify an observer registered during a mutation" do
52
+ observer = double("observer")
53
+ expect(observer).not_to receive(:update)
187
54
 
188
- describe "deactivating a group of users" do
189
- context "specified by user objects" do
190
- let(:active_users) { [double(id: 1), double(id: 2)] }
191
- let(:inactive_users) { [double(id: 3), double(id: 4)] }
192
-
193
- before do
194
- rollout.activate_users(:chat, active_users + inactive_users)
195
- rollout.deactivate_users(:chat, inactive_users)
196
- end
197
-
198
- it "is active for the active users" do
199
- active_users.each { |user| expect(rollout).to be_active(:chat, user) }
200
- end
201
-
202
- it "is not active for inactive users" do
203
- inactive_users.each { |user| expect(rollout).not_to be_active(:chat, user) }
55
+ rollout.with_feature(:chat) do |feature|
56
+ feature.percentage = 25
57
+ rollout.add_observer(observer)
204
58
  end
205
59
  end
206
60
 
207
- context "specified by user ids" do
208
- let(:active_users) { [1, 2] }
209
- let(:inactive_users) { [3, 4] }
210
-
211
- before do
212
- rollout.activate_users(:chat, active_users + inactive_users)
213
- rollout.deactivate_users(:chat, inactive_users)
61
+ it "notifies observers registered before a mutation" do
62
+ observer = double("observer")
63
+ expect(observer).to receive(:update) do |_event, before, after|
64
+ expect(before.name).to eq :chat
65
+ expect(before.percentage).to eq 0
66
+ expect(after.percentage).to eq 25
214
67
  end
215
68
 
216
- it "is active for the active users" do
217
- active_users.each { |user| expect(rollout).to be_active(:chat, user) }
218
- end
219
-
220
- it "is not active for inactive users" do
221
- inactive_users.each { |user| expect(rollout).not_to be_active(:chat, user) }
222
- end
69
+ rollout.add_observer(observer)
70
+ rollout.activate_percentage(:chat, 25)
223
71
  end
224
- end
225
72
 
73
+ it "does not persist when the mutation block raises" do
74
+ expect {
75
+ rollout.with_feature(:chat) { raise "nope" }
76
+ }.to raise_error("nope")
226
77
 
227
- describe 'set a group of users' do
228
- it 'should replace the users with the given array' do
229
- users = %w(1 2 3 4)
230
- rollout.activate_users(:chat, %w(10 20 30))
231
- rollout.set_users(:chat, users)
232
- expect(rollout.get(:chat).users).to eq(users)
78
+ expect(rollout.exists?(:chat)).to eq false
233
79
  end
234
- end
235
80
 
236
- describe "activating a feature globally" do
237
- before do
238
- rollout.activate(:chat)
239
- end
81
+ it "records a history event only when logging is enabled and state changes" do
82
+ backend = RolloutMemoryBackend.new
83
+ rollout = described_class.new(adapter: backend, logging: true)
240
84
 
241
- it "activates the feature" do
242
- expect(rollout).to be_active(:chat)
243
- end
244
-
245
- it "sets @data to empty hash" do
246
- expect(rollout.get(:chat).data).to eq({})
247
- end
248
- end
85
+ rollout.activate_percentage(:chat, 25)
86
+ expect(backend.feature_events(:chat).count).to eq 1
249
87
 
250
- describe "activating a feature for a percentage of users" do
251
- before do
252
- rollout.activate_percentage(:chat, 20)
88
+ rollout.activate_percentage(:chat, 25)
89
+ expect(backend.feature_events(:chat).count).to eq 1
253
90
  end
254
91
 
255
- it "activates the feature for that percentage of the users" do
256
- expect((1..100).select { |id| rollout.active?(:chat, double(id: id)) }.length).to be_within(2).of(20)
257
- end
258
- end
92
+ it "canonicalizes metadata assigned during a mutation" do
93
+ released_at = Time.utc(2026, 1, 1)
259
94
 
260
- describe "activating a feature for a percentage of users" do
261
- before do
262
- rollout.activate_percentage(:chat, 20)
263
- end
95
+ rollout.with_feature(:chat) do |feature|
96
+ feature.data["released_at"] = released_at
97
+ feature.data["label"] = :beta
98
+ end
264
99
 
265
- it "activates the feature for that percentage of the users" do
266
- expect((1..200).select { |id| rollout.active?(:chat, double(id: id)) }.length).to be_within(4).of(40)
100
+ expect(rollout.get(:chat).data).to eq(
101
+ "released_at" => JSON.parse({ "value" => released_at }.to_json)["value"],
102
+ "label" => "beta",
103
+ )
267
104
  end
268
- end
269
105
 
270
- describe "activating a feature for a percentage of users" do
271
- before do
272
- rollout.activate_percentage(:chat, 5)
273
- end
106
+ it "does not record a history event when metadata JSON is unchanged" do
107
+ backend = RolloutMemoryBackend.new
108
+ rollout = described_class.new(adapter: backend, logging: true)
109
+ released_at = Time.utc(2026, 1, 1)
274
110
 
275
- it "activates the feature for that percentage of the users" do
276
- expect((1..100).select { |id| rollout.active?(:chat, double(id: id)) }.length).to be_within(2).of(5)
277
- end
278
- end
111
+ rollout.set_feature_data(:chat, released_at: released_at, label: "beta")
112
+ expect(backend.feature_events(:chat).count).to eq 1
279
113
 
280
- describe "activating a feature for a percentage of users" do
281
- before do
282
- rollout.activate_percentage(:chat, 0.1)
114
+ rollout.set_feature_data(:chat, released_at: released_at, label: :beta)
115
+ expect(backend.feature_events(:chat).count).to eq 1
283
116
  end
284
117
 
285
- it "activates the feature for that percentage of the users" do
286
- expect((1..10_000).to_set.select { |id| rollout.active?(:chat, double(id: id)) }.length).to be_within(2).of(10)
287
- end
288
- end
118
+ it "records canonical metadata in history events" do
119
+ backend = RolloutMemoryBackend.new
120
+ rollout = described_class.new(adapter: backend, logging: true)
121
+ released_at = Time.utc(2026, 1, 1)
289
122
 
290
- describe "activating a feature for a percentage of users" do
291
- before do
292
- rollout.activate_percentage(:chat, 20)
293
- rollout.activate_percentage(:beta, 20)
294
- @options = rollout.instance_variable_get("@options")
295
- end
123
+ rollout.set_feature_data(:chat, released_at: released_at, label: :beta)
124
+ event = backend.feature_events(:chat).last
296
125
 
297
- it "activates the feature for a random set of users when opt is set" do
298
- @options[:randomize_percentage] = true
299
- chat_users = (1..100).select { |id| rollout.active?(:chat, double(id: id)) }
300
- beta_users = (1..100).select { |id| rollout.active?(:beta, double(id: id)) }
301
- expect(chat_users).not_to eq beta_users
302
- end
303
- it "activates the feature for the same set of users when opt is not set" do
304
- @options[:randomize_percentage] = false
305
- chat_users = (1..100).select { |id| rollout.active?(:chat, double(id: id)) }
306
- beta_users = (1..100).select { |id| rollout.active?(:beta, double(id: id)) }
307
- expect(chat_users).to eq beta_users
126
+ expect(event.data).to eq(
127
+ before: { "data.released_at" => nil, "data.label" => nil },
128
+ after: {
129
+ "data.released_at" => JSON.parse({ "value" => released_at }.to_json)["value"],
130
+ "data.label" => "beta",
131
+ },
132
+ )
308
133
  end
309
134
  end
310
135
 
311
- describe "activating a feature for a group as a string" do
312
- before do
313
- rollout.define_group(:admins) { |user| user.id == 5 }
314
- rollout.activate_group(:chat, "admins")
315
- end
316
-
317
- it "the feature is active for users for which the block evaluates to true" do
318
- expect(rollout).to be_active(:chat, double(id: 5))
136
+ describe "#set_feature_data" do
137
+ def json_value(value)
138
+ JSON.parse({ "value" => value }.to_json)["value"]
319
139
  end
320
140
 
321
- it "is not active for users for which the block evaluates to false" do
322
- expect(rollout).not_to be_active(:chat, double(id: 1))
323
- end
324
- end
141
+ it "canonicalizes JSON-serializable metadata" do
142
+ released_at = Time.utc(2026, 1, 1)
325
143
 
326
- describe "deactivating the percentage of users" do
327
- before do
328
- rollout.activate_percentage(:chat, 100)
329
- rollout.deactivate_percentage(:chat)
330
- end
144
+ rollout.set_feature_data(:chat, released_at: released_at, label: :beta)
331
145
 
332
- it "becomes inactivate for all users" do
333
- expect(rollout).not_to be_active(:chat, double(id: 24))
146
+ expect(rollout.get(:chat).data).to eq(
147
+ "released_at" => json_value(released_at),
148
+ "label" => "beta",
149
+ )
334
150
  end
335
- end
336
151
 
337
- describe "deactivating the feature globally" do
338
- before do
339
- rollout.activate(:chat)
340
- rollout.deactivate(:chat)
341
- end
152
+ it "does not persist metadata that cannot be serialized as JSON" do
153
+ rollout.set_feature_data(:chat, description: "foo")
154
+ cyclic = {}
155
+ cyclic["self"] = cyclic
342
156
 
343
- it "becomes inactivate" do
344
- expect(rollout).not_to be_active(:chat)
345
- end
346
- end
347
-
348
- describe "setting a feature on" do
349
- before do
350
- rollout.set(:chat, true)
351
- end
157
+ expect {
158
+ rollout.set_feature_data(:chat, cyclic)
159
+ }.to raise_error(JSON::JSONError)
352
160
 
353
- it "becomes activated" do
354
- expect(rollout).to be_active(:chat)
161
+ expect(rollout.get(:chat).data).to eq("description" => "foo")
355
162
  end
356
163
  end
357
164
 
358
- describe "setting a feature off" do
359
- before do
360
- rollout.set(:chat, false)
361
- end
165
+ describe "#clear!" do
166
+ it "asks the backend to clear remaining registry state" do
167
+ backend = RolloutMemoryBackend.new
168
+ expect(backend).to receive(:clear_features).and_call_original
362
169
 
363
- it "becomes inactivated" do
364
- expect(rollout).not_to be_active(:chat)
170
+ described_class.new(adapter: backend).clear!
365
171
  end
366
172
  end
367
173
 
368
- describe "deleting a feature" do
369
- before do
370
- rollout.set(:chat, true)
371
- end
174
+ describe "#delete" do
175
+ it "does not delete feature history when logging is disabled" do
176
+ backend = RolloutMemoryBackend.new
177
+ logged = described_class.new(adapter: backend, logging: true)
178
+ logged.activate_percentage(:chat, 25)
372
179
 
373
- context "when feature was passed as string" do
374
- it "should be removed from features list" do
375
- expect(rollout.features.size).to eq 1
376
- rollout.delete('chat')
377
- expect(rollout.features.size).to eq 0
378
- end
379
- end
180
+ described_class.new(adapter: backend).delete(:chat)
380
181
 
381
- it "should be removed from features list" do
382
- expect(rollout.features.size).to eq 1
383
- rollout.delete(:chat)
384
- expect(rollout.features.size).to eq 0
182
+ expect(logged.exists?(:chat)).to eq false
183
+ expect(logged.logging.events(:chat)).not_to eq []
385
184
  end
386
185
 
387
- it "should have metadata cleared" do
388
- expect(rollout.get(:chat).percentage).to eq 100
186
+ it "deletes feature history when logging is enabled" do
187
+ backend = RolloutMemoryBackend.new
188
+ rollout = described_class.new(adapter: backend, logging: true)
189
+ rollout.activate_percentage(:chat, 25)
389
190
  rollout.delete(:chat)
390
- expect(rollout.get(:chat).percentage).to eq 0
391
- end
392
- end
393
-
394
- describe "keeps a list of features" do
395
- it "saves the feature" do
396
- rollout.activate(:chat)
397
- expect(rollout.features).to be_include(:chat)
398
- end
399
191
 
400
- it "does not contain doubles" do
401
- rollout.activate(:chat)
402
- rollout.activate(:chat)
403
- expect(rollout.features.size).to eq(1)
192
+ expect(rollout.logging.events(:chat)).to eq []
404
193
  end
405
194
 
406
- it "does not contain doubles when using string" do
407
- rollout.activate(:chat)
408
- rollout.activate("chat")
409
- expect(rollout.features.size).to eq(1)
410
- end
411
- end
412
-
413
- describe "#get" do
414
- before do
415
- rollout.activate_percentage(:chat, 10)
416
- rollout.activate_group(:chat, :caretakers)
417
- rollout.activate_group(:chat, :greeters)
418
- rollout.activate(:signup)
419
- rollout.activate_user(:chat, double(id: 42))
420
- end
195
+ it "records a global deletion event with the current context" do
196
+ backend = RolloutMemoryBackend.new
197
+ rollout = described_class.new(adapter: backend, logging: { global: true })
198
+ rollout.activate_percentage(:chat, 25)
421
199
 
422
- it "returns the feature object" do
423
- feature = rollout.get(:chat)
424
- expect(feature.groups).to eq [:caretakers, :greeters]
425
- expect(feature.percentage).to eq 10
426
- expect(feature.users).to eq %w(42)
427
- expect(feature.to_hash).to eq(
428
- groups: [:caretakers, :greeters],
429
- percentage: 10,
430
- users: %w(42),
431
- data: {},
432
- )
200
+ rollout.logging.with_context(actor: "alice") { rollout.delete(:chat) }
433
201
 
434
- feature = rollout.get(:signup)
435
- expect(feature.groups).to be_empty
436
- expect(feature.users).to be_empty
437
- expect(feature.percentage).to eq(100)
202
+ event = rollout.logging.global_events.last
203
+ expect(event.name).to eq :delete
204
+ expect(event.feature).to eq "chat"
205
+ expect(event.data).to eq({})
206
+ expect(event.context).to eq(actor: "alice")
207
+ expect(event.created_at).to be_a(Time)
438
208
  end
439
209
 
440
- it "returns the feature objects using sets" do
441
- @options = rollout.instance_variable_get("@options")
442
- @options[:use_sets] = true
443
-
444
- feature = rollout.get(:chat)
445
- expect(feature.groups).to eq [:caretakers, :greeters].to_set
446
- expect(feature.percentage).to eq 10
447
- expect(feature.users).to eq %w(42).to_set
448
- expect(feature.to_hash).to eq(
449
- groups: [:caretakers, :greeters].to_set,
450
- percentage: 10,
451
- users: %w(42).to_set,
452
- data: {},
453
- )
454
-
455
- feature = rollout.get(:signup)
456
- expect(feature.groups).to be_empty
457
- expect(feature.users).to be_empty
458
- expect(feature.percentage).to eq(100)
459
- end
460
- end
461
-
462
- describe "#clear" do
463
- let(:features) { %w(signup beta alpha gm) }
464
-
465
- before do
466
- features.each { |f| rollout.activate(f) }
467
-
468
- rollout.clear!
469
- end
470
-
471
- it "each feature is cleared" do
472
- features.each do |feature|
473
- expect(rollout.get(feature).to_hash).to eq(
474
- percentage: 0,
475
- users: [],
476
- groups: [],
477
- data: {},
478
- )
479
- end
480
- end
481
-
482
- it "each feature is cleared with sets" do
483
- @options = rollout.instance_variable_get("@options")
484
- @options[:use_sets] = true
485
- features.each do |feature|
486
- expect(rollout.get(feature).to_hash).to eq(
487
- percentage: 0,
488
- users: Set.new,
489
- groups: Set.new,
490
- data: {},
491
- )
492
- end
493
- end
494
-
495
- it "removes all features" do
496
- expect(rollout.features).to be_empty
497
- end
498
- end
499
-
500
- describe "#feature_states" do
501
- let(:user_double) { double(id: 7) }
502
-
503
- before do
504
- rollout.activate(:chat)
505
- rollout.activate_user(:video, user_double)
506
- rollout.deactivate(:vr)
507
- end
508
-
509
- it "returns a hash" do
510
- expect(rollout.feature_states).to be_a(Hash)
511
- end
512
-
513
- context "with user argument" do
514
- it "maps active feature as true" do
515
- state = rollout.feature_states(user_double)[:video]
516
- expect(state).to eq(true)
517
- end
518
-
519
- it "maps inactive feature as false" do
520
- state = rollout.feature_states[:vr]
521
- expect(state).to eq(false)
522
- end
523
- end
210
+ it "does not record a deletion event for a missing feature" do
211
+ backend = RolloutMemoryBackend.new
212
+ rollout = described_class.new(adapter: backend, logging: { global: true })
524
213
 
525
- context "with no argument" do
526
- it "maps active feature as true" do
527
- state = rollout.feature_states[:chat]
528
- expect(state).to eq(true)
529
- end
530
-
531
- it "maps inactive feature as false" do
532
- state = rollout.feature_states[:video]
533
- expect(state).to eq(false)
534
- end
535
- end
536
- end
537
-
538
- describe "#active_features" do
539
- let(:user_double) { double(id: 19) }
540
-
541
- before do
542
- rollout.activate(:chat)
543
- rollout.activate_user(:video, user_double)
544
- rollout.deactivate(:vr)
545
- end
546
-
547
- it "returns an array" do
548
- expect(rollout.active_features).to be_a(Array)
549
- end
550
-
551
- context "with user argument" do
552
- it "includes active feature" do
553
- features = rollout.active_features(user_double)
554
- expect(features).to include(:video)
555
- expect(features).to include(:chat)
556
- end
557
-
558
- it "excludes inactive feature" do
559
- features = rollout.active_features(user_double)
560
- expect(features).to_not include(:vr)
561
- end
562
- end
563
-
564
- context "with no argument" do
565
- it "includes active feature" do
566
- features = rollout.active_features
567
- expect(features).to include(:chat)
568
- end
569
-
570
- it "excludes inactive feature" do
571
- features = rollout.active_features
572
- expect(features).to_not include(:video)
573
- end
574
- end
575
- end
214
+ rollout.delete(:chat)
576
215
 
577
- describe "#user_in_active_users?" do
578
- it "returns true if activated for user" do
579
- rollout.activate_user(:chat, double(id: 5))
580
- expect(rollout.user_in_active_users?(:chat, "5")).to eq(true)
216
+ expect(rollout.logging.global_events).to eq []
581
217
  end
582
218
 
583
- it "returns false if activated for group" do
584
- rollout.activate_group(:chat, :all)
585
- expect(rollout.user_in_active_users?(:chat, "5")).to eq(false)
586
- end
587
- end
219
+ it "does not record a deletion event when logging is suppressed" do
220
+ backend = RolloutMemoryBackend.new
221
+ rollout = described_class.new(adapter: backend, logging: { global: true })
222
+ rollout.activate_percentage(:chat, 25)
588
223
 
589
- describe "#multi_get" do
590
- before do
591
- rollout.activate_percentage(:chat, 10)
592
- rollout.activate_group(:chat, :caretakers)
593
- rollout.activate_group(:videos, :greeters)
594
- rollout.activate(:signup)
595
- rollout.activate_user(:photos, double(id: 42))
596
- end
224
+ rollout.logging.without { rollout.delete(:chat) }
597
225
 
598
- it "returns an array of features" do
599
- features = rollout.multi_get(:chat, :videos, :signup)
600
- expect(features[0].name).to eq :chat
601
- expect(features[0].groups).to eq [:caretakers]
602
- expect(features[0].percentage).to eq 10
603
- expect(features[1].name).to eq :videos
604
- expect(features[1].groups).to eq [:greeters]
605
- expect(features[2].name).to eq :signup
606
- expect(features[2].percentage).to eq 100
607
- expect(features.size).to eq 3
226
+ expect(rollout.exists?(:chat)).to eq false
227
+ expect(rollout.logging.events(:chat)).to eq []
228
+ expect(rollout.logging.global_events.map(&:name)).to eq [:update]
608
229
  end
609
230
 
610
- describe 'when given feature keys is empty' do
611
- it 'returns empty array' do
612
- expect(rollout.multi_get(*[])).to match_array([])
231
+ it "preserves deletion behavior for adapters without event-aware deletion" do
232
+ backend_class = Class.new(RolloutMemoryBackend) do
233
+ undef_method :delete_feature_with_history
613
234
  end
614
- end
615
- end
616
-
617
- describe "#set_feature_data" do
618
- before do
619
- rollout.set_feature_data(:chat, description: 'foo', release_date: 'bar')
620
- end
621
-
622
- it 'sets the data attribute on feature' do
623
- expect(rollout.get(:chat).data).to include('description' => 'foo', 'release_date' => 'bar')
624
- end
625
-
626
- it 'updates a data attribute' do
627
- rollout.set_feature_data(:chat, description: 'baz')
628
- expect(rollout.get(:chat).data).to include('description' => 'baz', 'release_date' => 'bar')
629
- end
630
-
631
- it 'only sets data on specified feature' do
632
- rollout.set_feature_data(:talk, image_url: 'kittens.png')
633
- expect(rollout.get(:chat).data).not_to include('image_url' => 'kittens.png')
634
- expect(rollout.get(:chat).data).to include('description' => 'foo', 'release_date' => 'bar')
635
- end
636
-
637
- it 'does not modify @data if param is nil' do
638
- expect(rollout.get(:chat).data).to include('description' => 'foo', 'release_date' => 'bar')
639
- rollout.set_feature_data(:chat, nil)
640
- expect(rollout.get(:chat).data).to include('description' => 'foo', 'release_date' => 'bar')
641
- end
642
-
643
- it 'does not modify @data if param is empty string' do
644
- expect(rollout.get(:chat).data).to include('description' => 'foo', 'release_date' => 'bar')
645
- rollout.set_feature_data(:chat, " ")
646
- expect(rollout.get(:chat).data).to include('description' => 'foo', 'release_date' => 'bar')
647
- end
648
-
649
- it 'properly parses data when it contains a |' do
650
- user = double("User", id: 8)
651
- rollout.activate_user(:chat, user)
652
- rollout.set_feature_data(:chat, "|call||text|" => "a|bunch|of|stuff")
653
- expect(rollout.get(:chat).data).to include("|call||text|" => "a|bunch|of|stuff")
654
- expect(rollout.active?(:chat, user)).to be true
655
- end
656
- end
657
-
658
- describe "#clear_feature_data" do
659
- it 'resets data to empty string' do
660
- rollout.set_feature_data(:chat, description: 'foo')
661
- expect(rollout.get(:chat).data).to include('description' => 'foo')
662
- rollout.clear_feature_data(:chat)
663
- expect(rollout.get(:chat).data).to eq({})
664
- end
665
- end
235
+ backend = backend_class.new
236
+ rollout = described_class.new(adapter: backend, logging: { global: true })
237
+ rollout.activate_percentage(:chat, 25)
666
238
 
667
- describe 'Check if feature exists' do
668
- it 'it should return true if the feature is exist' do
669
- rollout.activate_percentage(:chat, 1)
670
- expect(rollout.exists?(:chat)).to be true
671
- end
239
+ expect { rollout.delete(:chat) }.not_to raise_error
672
240
 
673
- it 'it should return false if the feature is not exist' do
674
- expect(rollout.exists?(:chat)).to be false
241
+ expect(rollout.exists?(:chat)).to eq false
242
+ expect(rollout.logging.events(:chat)).to eq []
243
+ expect(rollout.logging.global_events.map(&:name)).to eq [:update]
675
244
  end
676
245
  end
677
246
  end