162 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Ruby
		
	
	
	
			
		
		
	
	
			162 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Ruby
		
	
	
	
| # frozen_string_literal: true
 | |
| 
 | |
| require 'spec_helper'
 | |
| 
 | |
| RSpec.describe Gitlab::Experimentation do
 | |
|   using RSpec::Parameterized::TableSyntax
 | |
| 
 | |
|   before do
 | |
|     stub_const('Gitlab::Experimentation::EXPERIMENTS', {
 | |
|       test_experiment: {
 | |
|         tracking_category: 'Team'
 | |
|       },
 | |
|       tabular_experiment: {
 | |
|         tracking_category: 'Team',
 | |
|         rollout_strategy: rollout_strategy
 | |
|       }
 | |
|     })
 | |
| 
 | |
|     skip_feature_flags_yaml_validation
 | |
|     skip_default_enabled_yaml_check
 | |
|     Feature.enable_percentage_of_time(:test_experiment_experiment_percentage, enabled_percentage)
 | |
|     allow(Gitlab).to receive(:com?).and_return(true)
 | |
|   end
 | |
| 
 | |
|   let(:enabled_percentage) { 10 }
 | |
|   let(:rollout_strategy) { nil }
 | |
| 
 | |
|   describe '.get_experiment' do
 | |
|     subject { described_class.get_experiment(:test_experiment) }
 | |
| 
 | |
|     context 'returns experiment' do
 | |
|       it { is_expected.to be_instance_of(Gitlab::Experimentation::Experiment) }
 | |
|     end
 | |
| 
 | |
|     context 'experiment is not defined' do
 | |
|       subject { described_class.get_experiment(:missing_experiment) }
 | |
| 
 | |
|       it { is_expected.to be_nil }
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   describe '.active?' do
 | |
|     subject { described_class.active?(:test_experiment) }
 | |
| 
 | |
|     context 'feature toggle is enabled' do
 | |
|       it { is_expected.to eq(true) }
 | |
|     end
 | |
| 
 | |
|     describe 'experiment is not defined' do
 | |
|       it 'returns false' do
 | |
|         expect(described_class.active?(:missing_experiment)).to eq(false)
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     describe 'experiment is disabled' do
 | |
|       let(:enabled_percentage) { 0 }
 | |
| 
 | |
|       it { is_expected.to eq(false) }
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   describe '.in_experiment_group?' do
 | |
|     let(:enabled_percentage) { 50 }
 | |
|     let(:experiment_subject) { 'z' } # Zlib.crc32('test_experimentz') % 100 = 33
 | |
| 
 | |
|     subject { described_class.in_experiment_group?(:test_experiment, subject: experiment_subject) }
 | |
| 
 | |
|     context 'when experiment is active' do
 | |
|       context 'when subject is part of the experiment' do
 | |
|         it { is_expected.to eq(true) }
 | |
|       end
 | |
| 
 | |
|       context 'when subject is not part of the experiment' do
 | |
|         let(:experiment_subject) { 'a' } # Zlib.crc32('test_experimenta') % 100 = 61
 | |
| 
 | |
|         it { is_expected.to eq(false) }
 | |
|       end
 | |
| 
 | |
|       context 'when subject has a global_id' do
 | |
|         let(:experiment_subject) { double(:subject, to_global_id: 'z') }
 | |
| 
 | |
|         it { is_expected.to eq(true) }
 | |
|       end
 | |
| 
 | |
|       context 'when subject is nil' do
 | |
|         let(:experiment_subject) { nil }
 | |
| 
 | |
|         it { is_expected.to eq(false) }
 | |
|       end
 | |
| 
 | |
|       context 'when subject is an empty string' do
 | |
|         let(:experiment_subject) { '' }
 | |
| 
 | |
|         it { is_expected.to eq(false) }
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     context 'when experiment is not active' do
 | |
|       before do
 | |
|         allow(described_class).to receive(:active?).and_return(false)
 | |
|       end
 | |
| 
 | |
|       it { is_expected.to eq(false) }
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   describe '.log_invalid_rollout' do
 | |
|     subject { described_class.log_invalid_rollout(:test_experiment, 1) }
 | |
| 
 | |
|     before do
 | |
|       allow(described_class).to receive(:valid_subject_for_rollout_strategy?).and_return(valid)
 | |
|     end
 | |
| 
 | |
|     context 'subject is not valid for experiment' do
 | |
|       let(:valid) { false }
 | |
| 
 | |
|       it 'logs a warning message' do
 | |
|         expect_next_instance_of(Gitlab::ExperimentationLogger) do |logger|
 | |
|           expect(logger)
 | |
|             .to receive(:warn)
 | |
|                   .with(
 | |
|                     message: 'Subject must conform to the rollout strategy',
 | |
|                     experiment_key: :test_experiment,
 | |
|                     subject: 'Integer',
 | |
|                     rollout_strategy: :cookie
 | |
|                   )
 | |
|         end
 | |
| 
 | |
|         subject
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     context 'subject is valid for experiment' do
 | |
|       let(:valid) { true }
 | |
| 
 | |
|       it 'does not log a warning message' do
 | |
|         expect(Gitlab::ExperimentationLogger).not_to receive(:build)
 | |
| 
 | |
|         subject
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   describe '.valid_subject_for_rollout_strategy?' do
 | |
|     subject { described_class.valid_subject_for_rollout_strategy?(:tabular_experiment, experiment_subject) }
 | |
| 
 | |
|     where(:rollout_strategy, :experiment_subject, :result) do
 | |
|       :cookie | nil       | true
 | |
|       nil     | nil       | true
 | |
|       :cookie | 'string'  | true
 | |
|       nil     | User.new  | false
 | |
|       :user   | User.new  | true
 | |
|       :group  | User.new  | false
 | |
|       :group  | Group.new | true
 | |
|     end
 | |
| 
 | |
|     with_them do
 | |
|       it { is_expected.to be(result) }
 | |
|     end
 | |
|   end
 | |
| end
 |