# frozen_string_literal: true require 'spec_helper' # As each associated, backwards-compatible experiment gets cleaned up and removed from the EXPERIMENTS list, its key will also get removed from this list. Once the list here is empty, we can remove the backwards compatibility code altogether. # Originally created as part of https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45733 for https://gitlab.com/gitlab-org/gitlab/-/issues/270858. RSpec.describe Gitlab::Experimentation::EXPERIMENTS do it 'temporarily ensures we know what experiments exist for backwards compatibility' do expected_experiment_keys = [ :onboarding_issues, :ci_notification_dot, :upgrade_link_in_user_menu_a, :invite_members_version_a, :invite_members_version_b, :invite_members_empty_group_version_a, :contact_sales_btn_in_app, :customize_homepage, :group_only_trials, :default_to_issues_board ] backwards_compatible_experiment_keys = described_class.filter { |_, v| v[:use_backwards_compatible_subject_index] }.keys expect(backwards_compatible_experiment_keys).not_to be_empty, "Oh, hey! Let's clean up that :use_backwards_compatible_subject_index stuff now :D" expect(backwards_compatible_experiment_keys).to match(expected_experiment_keys) end end RSpec.describe Gitlab::Experimentation do before do stub_const('Gitlab::Experimentation::EXPERIMENTS', { backwards_compatible_test_experiment: { tracking_category: 'Team', use_backwards_compatible_subject_index: true }, test_experiment: { tracking_category: 'Team' } }) skip_feature_flags_yaml_validation skip_default_enabled_yaml_check Feature.enable_percentage_of_time(:backwards_compatible_test_experiment_experiment_percentage, enabled_percentage) Feature.enable_percentage_of_time(:test_experiment_experiment_percentage, enabled_percentage) allow(Gitlab).to receive(:com?).and_return(true) end let(:enabled_percentage) { 10 } 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 context 'with new index calculation' 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 context 'with backwards compatible index calculation' do let(:experiment_subject) { 'abcd' } # Digest::SHA1.hexdigest('abcd').hex % 100 = 7 subject { described_class.in_experiment_group?(:backwards_compatible_test_experiment, subject: experiment_subject) } context 'when experiment is active' do before do allow(described_class).to receive(:active?).and_return(true) end 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) { 'abc' } # Digest::SHA1.hexdigest('abc').hex % 100 = 17 it { is_expected.to eq(false) } end context 'when subject has a global_id' do let(:experiment_subject) { double(:subject, to_global_id: 'abcd') } 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 end end