From e8d2c2579383897a1dd7f9debd359abe8ae8373d Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Tue, 20 Jul 2021 09:55:51 +0000 Subject: Add latest changes from gitlab-org/gitlab@14-1-stable-ee --- spec/experiments/application_experiment_spec.rb | 164 ++++++++++++--------- .../new_project_readme_content_experiment_spec.rb | 36 +++++ 2 files changed, 127 insertions(+), 73 deletions(-) create mode 100644 spec/experiments/new_project_readme_content_experiment_spec.rb (limited to 'spec/experiments') diff --git a/spec/experiments/application_experiment_spec.rb b/spec/experiments/application_experiment_spec.rb index 22c436e4159..9c03910cf66 100644 --- a/spec/experiments/application_experiment_spec.rb +++ b/spec/experiments/application_experiment_spec.rb @@ -3,11 +3,10 @@ require 'spec_helper' RSpec.describe ApplicationExperiment, :experiment do - subject { described_class.new('namespaced/stub') } + subject { described_class.new('namespaced/stub', **context) } - let(:feature_definition) do - { name: 'namespaced_stub', type: 'experiment', group: 'group::adoption', default_enabled: false } - end + let(:context) { {} } + let(:feature_definition) { { name: 'namespaced_stub', type: 'experiment', default_enabled: false } } around do |example| Feature::Definition.definitions[:namespaced_stub] = Feature::Definition.new('namespaced_stub.yml', feature_definition) @@ -19,19 +18,13 @@ RSpec.describe ApplicationExperiment, :experiment do allow(subject).to receive(:enabled?).and_return(true) end - it "naively assumes a 1x1 relationship to feature flags for tests" do - expect(Feature).to receive(:persist_used!).with('namespaced_stub') - - described_class.new('namespaced/stub') - end - it "doesn't raise an exception without a defined control" do # because we have a default behavior defined expect { experiment('namespaced/stub') { } }.not_to raise_error end - describe "enabled" do + describe "#enabled?" do before do allow(subject).to receive(:enabled?).and_call_original @@ -63,103 +56,104 @@ RSpec.describe ApplicationExperiment, :experiment do end end - describe "publishing results" do - it "doesn't record, track or push data to the client if we shouldn't track", :snowplow do + describe "#publish" do + it "doesn't track or publish to the client or database if we can't track", :snowplow do allow(subject).to receive(:should_track?).and_return(false) - subject.record! - expect(subject).not_to receive(:record_experiment) - expect(subject).not_to receive(:track) - expect(Gon).not_to receive(:push) + expect(subject).not_to receive(:publish_to_client) + expect(subject).not_to receive(:publish_to_database) - subject.publish(:action) + subject.publish expect_no_snowplow_event end - describe 'recording the experiment' do - it 'does not record the experiment if we do not tell it to' do - expect(subject).not_to receive(:record_experiment) - - subject.publish - end - - it 'records the experiment if we tell it to' do - subject.record! - - expect(subject).to receive(:record_experiment) - - subject.publish - end - end - it "tracks the assignment" do expect(subject).to receive(:track).with(:assignment) subject.publish end - it "pushes the experiment knowledge into the client using Gon" do - expect(Gon).to receive(:push).with({ experiment: { 'namespaced/stub' => subject.signature } }, true) + it "publishes the to the client" do + expect(subject).to receive(:publish_to_client) subject.publish end - it "handles when Gon raises exceptions (like when it can't be pushed into)" do - expect(Gon).to receive(:push).and_raise(NoMethodError) + it "publishes to the database if we've opted for that" do + subject.record! + + expect(subject).to receive(:publish_to_database) - expect { subject.publish }.not_to raise_error + subject.publish end - end - it "can exclude from within the block" do - expect(described_class.new('namespaced/stub') { |e| e.exclude! }).to be_excluded - end + describe "#publish_to_client" do + it "adds the data into Gon" do + signature = { key: '86208ac54ca798e11f127e8b23ec396a', variant: 'control' } + expect(Gon).to receive(:push).with({ experiment: { 'namespaced/stub' => hash_including(signature) } }, true) - describe 'recording the experiment subject' do - using RSpec::Parameterized::TableSyntax + subject.publish_to_client + end - subject { described_class.new('namespaced/stub', nil, **context) } + it "handles when Gon raises exceptions (like when it can't be pushed into)" do + expect(Gon).to receive(:push).and_raise(NoMethodError) - before do - subject.record! + expect { subject.publish_to_client }.not_to raise_error + end end - context 'when providing a compatible context' do - where(:context_key, :object_type) do - :namespace | :namespace - :group | :namespace - :project | :project - :user | :user - :actor | :user + describe "#publish_to_database" do + using RSpec::Parameterized::TableSyntax + let(:context) { { context_key => context_value }} + + before do + subject.record! end - with_them do - let(:context) { { context_key => build(object_type) }} + context "when there's a usable subject" do + where(:context_key, :context_value, :object_type) do + :namespace | build(:namespace) | :namespace + :group | build(:namespace) | :namespace + :project | build(:project) | :project + :user | build(:user) | :user + :actor | build(:user) | :user + end - it 'records the experiment and the experiment subject from the context' do - expect { subject.publish }.to change(Experiment, :count).by(1) + with_them do + it "creates an experiment and experiment subject record" do + expect { subject.publish_to_database }.to change(Experiment, :count).by(1) - expect(Experiment.last.name).to eq('namespaced/stub') - expect(ExperimentSubject.last.send(object_type)).to eq(context[context_key]) + expect(Experiment.last.name).to eq('namespaced/stub') + expect(ExperimentSubject.last.send(object_type)).to eq(context[context_key]) + end end end - end - context 'when providing an incompatible or no context' do - where(context_hash: [{ foo: :bar }, {}]) + context "when there's not a usable subject" do + where(:context_key, :context_value) do + :namespace | nil + :foo | :bar + end - with_them do - let(:context) { context_hash } + with_them do + it "doesn't create an experiment record" do + expect { subject.publish_to_database }.not_to change(Experiment, :count) + end - it 'does not record the experiment' do - expect { subject.publish }.not_to change(Experiment, :count) + it "doesn't create an experiment subject record" do + expect { subject.publish_to_database }.not_to change(ExperimentSubject, :count) + end end end end end - describe "tracking events", :snowplow do + describe "#track", :snowplow do + let(:fake_context) do + SnowplowTracker::SelfDescribingJson.new('iglu:com.gitlab/fake/jsonschema/0-0-0', { data: '_data_' }) + end + it "doesn't track if we shouldn't track" do allow(subject).to receive(:should_track?).and_return(false) @@ -169,9 +163,7 @@ RSpec.describe ApplicationExperiment, :experiment do end it "tracks the event with the expected arguments and merged contexts" do - subject.track(:action, property: '_property_', context: [ - SnowplowTracker::SelfDescribingJson.new('iglu:com.gitlab/fake/jsonschema/0-0-0', { data: '_data_' }) - ]) + subject.track(:action, property: '_property_', context: [fake_context]) expect_snowplow_event( category: 'namespaced/stub', @@ -189,9 +181,35 @@ RSpec.describe ApplicationExperiment, :experiment do ] ) end + + it "tracks the event correctly even when using the base class" do + subject = Gitlab::Experiment.new(:unnamed) + subject.track(:action, context: [fake_context]) + + expect_snowplow_event( + category: 'unnamed', + action: 'action', + context: [ + { + schema: 'iglu:com.gitlab/fake/jsonschema/0-0-0', + data: { data: '_data_' } + }, + { + schema: 'iglu:com.gitlab/gitlab_experiment/jsonschema/1-0-0', + data: { experiment: 'unnamed', key: subject.context.key, variant: 'control' } + } + ] + ) + end + end + + describe "#key_for" do + it "generates MD5 hashes" do + expect(subject.key_for(foo: :bar)).to eq('6f9ac12afdb9b58c2f19a136d09f9153') + end end - describe "variant resolution" do + context "when resolving variants" do it "uses the default value as specified in the yaml" do expect(Feature).to receive(:enabled?).with('namespaced_stub', subject, type: :experiment, default_enabled: :yaml) diff --git a/spec/experiments/new_project_readme_content_experiment_spec.rb b/spec/experiments/new_project_readme_content_experiment_spec.rb new file mode 100644 index 00000000000..92a883078df --- /dev/null +++ b/spec/experiments/new_project_readme_content_experiment_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe NewProjectReadmeContentExperiment, :experiment do + subject { described_class.new(namespace: project.namespace) } + + let(:project) { create(:project, name: 'Experimental', description: 'An experiment project') } + + it "renders the basic README" do + expect(subject.run_with(project)).to eq(<<~MARKDOWN.strip) + # Experimental + + An experiment project + MARKDOWN + end + + describe "the advanced variant" do + let(:markdown) { subject.run_with(project, variant: :advanced) } + let(:initial_url) { 'https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file' } + + it "renders the project details" do + expect(markdown).to include(<<~MARKDOWN.strip) + # Experimental + + An experiment project + + ## Getting started + MARKDOWN + end + + it "renders redirect URLs" do + expect(markdown).to include(Rails.application.routes.url_helpers.experiment_redirect_url(subject, initial_url)) + end + end +end -- cgit v1.2.3