diff options
author | Sean McGivern <sean@gitlab.com> | 2019-07-18 09:23:54 +0000 |
---|---|---|
committer | Sean McGivern <sean@gitlab.com> | 2019-07-18 09:23:54 +0000 |
commit | 0d1adc9f7f990254fd88ba03011908cc4f83461d (patch) | |
tree | 9ae9ca3abd0e8db7dcf3fe21b424070d0b3c7f39 /spec | |
parent | f4ce99c54225b846a1d176d819d9db94de68ac52 (diff) | |
parent | 196dfd1243d5b5cf9786ff5019049104f7c22c7a (diff) | |
download | gitlab-ce-0d1adc9f7f990254fd88ba03011908cc4f83461d.tar.gz |
Merge branch 'adjust-cycle-analytics-to-group-level' into 'master'
Adjust cycle analytics to group level
See merge request gitlab-org/gitlab-ce!30391
Diffstat (limited to 'spec')
25 files changed, 520 insertions, 87 deletions
diff --git a/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb b/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb index 8b07da11c5d..b7a64adc2ff 100644 --- a/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb @@ -9,12 +9,12 @@ describe Gitlab::CycleAnalytics::BaseEventFetcher do let(:options) do { start_time_attrs: start_time_attrs, end_time_attrs: end_time_attrs, - from: 30.days.ago } + from: 30.days.ago, + project: project } end subject do - described_class.new(project: project, - stage: :issue, + described_class.new(stage: :issue, options: options).fetch end diff --git a/spec/lib/gitlab/cycle_analytics/code_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/code_stage_spec.rb index c738cc49c1f..933f3c7896e 100644 --- a/spec/lib/gitlab/cycle_analytics/code_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/code_stage_spec.rb @@ -5,40 +5,114 @@ describe Gitlab::CycleAnalytics::CodeStage do let(:stage_name) { :code } let(:project) { create(:project) } - let!(:issue_1) { create(:issue, project: project, created_at: 90.minutes.ago) } - let!(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) } - let!(:issue_3) { create(:issue, project: project, created_at: 60.minutes.ago) } - let!(:mr_1) { create(:merge_request, source_project: project, created_at: 15.minutes.ago) } - let!(:mr_2) { create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'A') } - let!(:mr_3) { create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'B') } - let(:stage) { described_class.new(project: project, options: { from: 2.days.ago, current_user: project.creator }) } + let(:issue_1) { create(:issue, project: project, created_at: 90.minutes.ago) } + let(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) } + let(:issue_3) { create(:issue, project: project, created_at: 60.minutes.ago) } + let(:mr_1) { create(:merge_request, source_project: project, created_at: 15.minutes.ago) } + let(:mr_2) { create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'A') } + let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: project.creator, project: project }) } before do issue_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago, first_mentioned_in_commit_at: 45.minutes.ago) issue_2.metrics.update!(first_added_to_board_at: 60.minutes.ago, first_mentioned_in_commit_at: 40.minutes.ago) issue_3.metrics.update!(first_added_to_board_at: 60.minutes.ago, first_mentioned_in_commit_at: 40.minutes.ago) + create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'B') create(:merge_requests_closing_issues, merge_request: mr_1, issue: issue_1) create(:merge_requests_closing_issues, merge_request: mr_2, issue: issue_2) end it_behaves_like 'base stage' - describe '#median' do + describe '#project_median' do around do |example| Timecop.freeze { example.run } end it 'counts median from issues with metrics' do - expect(stage.median).to eq(ISSUES_MEDIAN) + expect(stage.project_median).to eq(ISSUES_MEDIAN) end end describe '#events' do + subject { stage.events } + it 'exposes merge requests that closes issues' do - result = stage.events + expect(subject.count).to eq(2) + expect(subject.map { |event| event[:title] }).to contain_exactly(mr_1.title, mr_2.title) + end + end + + context 'when group is given' do + let(:user) { create(:user) } + let(:group) { create(:group) } + let(:project_2) { create(:project, group: group) } + let(:project_3) { create(:project, group: group) } + let(:issue_2_1) { create(:issue, project: project_2, created_at: 90.minutes.ago) } + let(:issue_2_2) { create(:issue, project: project_3, created_at: 60.minutes.ago) } + let(:issue_2_3) { create(:issue, project: project_2, created_at: 60.minutes.ago) } + let(:mr_2_1) { create(:merge_request, source_project: project_2, created_at: 15.minutes.ago) } + let(:mr_2_2) { create(:merge_request, source_project: project_3, created_at: 10.minutes.ago, source_branch: 'A') } + let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: user, group: group }) } + + before do + group.add_owner(user) + issue_2_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago, first_mentioned_in_commit_at: 45.minutes.ago) + issue_2_2.metrics.update!(first_added_to_board_at: 60.minutes.ago, first_mentioned_in_commit_at: 40.minutes.ago) + issue_2_3.metrics.update!(first_added_to_board_at: 60.minutes.ago, first_mentioned_in_commit_at: 40.minutes.ago) + create(:merge_requests_closing_issues, merge_request: mr_2_1, issue: issue_2_1) + create(:merge_requests_closing_issues, merge_request: mr_2_2, issue: issue_2_2) + end + + describe '#group_median' do + around do |example| + Timecop.freeze { example.run } + end + + it 'counts median from issues with metrics' do + expect(stage.group_median).to eq(ISSUES_MEDIAN) + end + end + + describe '#events' do + subject { stage.events } + + it 'exposes merge requests that close issues' do + expect(subject.count).to eq(2) + expect(subject.map { |event| event[:title] }).to contain_exactly(mr_2_1.title, mr_2_2.title) + end + end + + context 'when subgroup is given' do + let(:subgroup) { create(:group, parent: group) } + let(:project_4) { create(:project, group: subgroup) } + let(:project_5) { create(:project, group: subgroup) } + let(:issue_3_1) { create(:issue, project: project_4, created_at: 90.minutes.ago) } + let(:issue_3_2) { create(:issue, project: project_5, created_at: 60.minutes.ago) } + let(:issue_3_3) { create(:issue, project: project_5, created_at: 60.minutes.ago) } + let(:mr_3_1) { create(:merge_request, source_project: project_4, created_at: 15.minutes.ago) } + let(:mr_3_2) { create(:merge_request, source_project: project_5, created_at: 10.minutes.ago, source_branch: 'A') } + + before do + issue_3_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago, first_mentioned_in_commit_at: 45.minutes.ago) + issue_3_2.metrics.update!(first_added_to_board_at: 60.minutes.ago, first_mentioned_in_commit_at: 40.minutes.ago) + issue_3_3.metrics.update!(first_added_to_board_at: 60.minutes.ago, first_mentioned_in_commit_at: 40.minutes.ago) + create(:merge_requests_closing_issues, merge_request: mr_3_1, issue: issue_3_1) + create(:merge_requests_closing_issues, merge_request: mr_3_2, issue: issue_3_2) + end + + describe '#events' do + subject { stage.events } + + it 'exposes merge requests that close issues' do + expect(subject.count).to eq(4) + expect(subject.map { |event| event[:title] }).to contain_exactly(mr_2_1.title, mr_2_2.title, mr_3_1.title, mr_3_2.title) + end - expect(result.count).to eq(2) - expect(result.map { |event| event[:title] }).to contain_exactly(mr_1.title, mr_2.title) + it 'exposes merge requests that close issues with full path for subgroup' do + expect(subject.count).to eq(4) + expect(subject.find { |event| event[:title] == mr_3_1.title }[:url]).to include("#{subgroup.full_path}") + end + end end end end diff --git a/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb b/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb new file mode 100644 index 00000000000..eea4f33ccb8 --- /dev/null +++ b/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true +require 'spec_helper' + +describe Gitlab::CycleAnalytics::GroupStageSummary do + let(:group) { create(:group) } + let(:project) { create(:project, :repository, namespace: group) } + let(:project_2) { create(:project, :repository, namespace: group) } + let(:from) { 1.day.ago } + let(:user) { create(:user, :admin) } + + subject { described_class.new(group, from: Time.now, current_user: user).data } + + describe "#new_issues" do + context 'with from date' do + before do + Timecop.freeze(5.days.ago) { create(:issue, project: project) } + Timecop.freeze(5.days.ago) { create(:issue, project: project_2) } + Timecop.freeze(5.days.from_now) { create(:issue, project: project) } + Timecop.freeze(5.days.from_now) { create(:issue, project: project_2) } + end + + it "finds the number of issues created after it" do + expect(subject.first[:value]).to eq(2) + end + + context 'with subgroups' do + before do + Timecop.freeze(5.days.from_now) { create(:issue, project: create(:project, namespace: create(:group, parent: group))) } + end + + it "finds issues from them" do + expect(subject.first[:value]).to eq(3) + end + end + end + + context 'with other projects' do + before do + Timecop.freeze(5.days.from_now) { create(:issue, project: create(:project, namespace: create(:group))) } + Timecop.freeze(5.days.from_now) { create(:issue, project: project) } + Timecop.freeze(5.days.from_now) { create(:issue, project: project_2) } + end + + it "doesn't find issues from them" do + expect(subject.first[:value]).to eq(2) + end + end + end + + describe "#deploys" do + context 'with from date' do + before do + Timecop.freeze(5.days.ago) { create(:deployment, :success, project: project) } + Timecop.freeze(5.days.from_now) { create(:deployment, :success, project: project) } + Timecop.freeze(5.days.ago) { create(:deployment, :success, project: project_2) } + Timecop.freeze(5.days.from_now) { create(:deployment, :success, project: project_2) } + end + + it "finds the number of deploys made created after it" do + expect(subject.second[:value]).to eq(2) + end + + context 'with subgroups' do + before do + Timecop.freeze(5.days.from_now) do + create(:deployment, :success, project: create(:project, :repository, namespace: create(:group, parent: group))) + end + end + + it "finds deploys from them" do + expect(subject.second[:value]).to eq(3) + end + end + end + + context 'with other projects' do + before do + Timecop.freeze(5.days.from_now) do + create(:deployment, :success, project: create(:project, :repository, namespace: create(:group))) + end + end + + it "doesn't find deploys from them" do + expect(subject.second[:value]).to eq(0) + end + end + end +end diff --git a/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb index 3b6af9cbaed..ffd0b84cb57 100644 --- a/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb @@ -4,11 +4,11 @@ require 'lib/gitlab/cycle_analytics/shared_stage_spec' describe Gitlab::CycleAnalytics::IssueStage do let(:stage_name) { :issue } let(:project) { create(:project) } - let!(:issue_1) { create(:issue, project: project, created_at: 90.minutes.ago) } - let!(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) } - let!(:issue_3) { create(:issue, project: project, created_at: 30.minutes.ago) } + let(:issue_1) { create(:issue, project: project, created_at: 90.minutes.ago) } + let(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) } + let(:issue_3) { create(:issue, project: project, created_at: 30.minutes.ago) } let!(:issue_without_milestone) { create(:issue, project: project, created_at: 1.minute.ago) } - let(:stage) { described_class.new(project: project, options: { from: 2.days.ago, current_user: project.creator }) } + let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: project.creator, project: project }) } before do issue_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago ) @@ -24,7 +24,7 @@ describe Gitlab::CycleAnalytics::IssueStage do end it 'counts median from issues with metrics' do - expect(stage.median).to eq(ISSUES_MEDIAN) + expect(stage.project_median).to eq(ISSUES_MEDIAN) end end @@ -36,4 +36,67 @@ describe Gitlab::CycleAnalytics::IssueStage do expect(result.map { |event| event[:title] }).to contain_exactly(issue_1.title, issue_2.title, issue_3.title) end end + context 'when group is given' do + let(:user) { create(:user) } + let(:group) { create(:group) } + let(:project_2) { create(:project, group: group) } + let(:project_3) { create(:project, group: group) } + let(:issue_2_1) { create(:issue, project: project_2, created_at: 90.minutes.ago) } + let(:issue_2_2) { create(:issue, project: project_3, created_at: 60.minutes.ago) } + let(:issue_2_3) { create(:issue, project: project_2, created_at: 60.minutes.ago) } + let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: user, group: group }) } + + before do + group.add_owner(user) + issue_2_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago) + issue_2_2.metrics.update!(first_added_to_board_at: 30.minutes.ago) + end + + describe '#group_median' do + around do |example| + Timecop.freeze { example.run } + end + + it 'counts median from issues with metrics' do + expect(stage.group_median).to eq(ISSUES_MEDIAN) + end + end + + describe '#events' do + subject { stage.events } + + it 'exposes merge requests that close issues' do + expect(subject.count).to eq(2) + expect(subject.map { |event| event[:title] }).to contain_exactly(issue_2_1.title, issue_2_2.title) + end + end + + context 'when subgroup is given' do + let(:subgroup) { create(:group, parent: group) } + let(:project_4) { create(:project, group: subgroup) } + let(:project_5) { create(:project, group: subgroup) } + let(:issue_3_1) { create(:issue, project: project_4, created_at: 90.minutes.ago) } + let(:issue_3_2) { create(:issue, project: project_5, created_at: 60.minutes.ago) } + let(:issue_3_3) { create(:issue, project: project_5, created_at: 60.minutes.ago) } + + before do + issue_3_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago) + issue_3_2.metrics.update!(first_added_to_board_at: 30.minutes.ago) + end + + describe '#events' do + subject { stage.events } + + it 'exposes merge requests that close issues' do + expect(subject.count).to eq(4) + expect(subject.map { |event| event[:title] }).to contain_exactly(issue_2_1.title, issue_2_2.title, issue_3_1.title, issue_3_2.title) + end + + it 'exposes merge requests that close issues with full path for subgroup' do + expect(subject.count).to eq(4) + expect(subject.find { |event| event[:title] == issue_3_1.title }[:url]).to include("#{subgroup.full_path}") + end + end + end + end end diff --git a/spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb index 506a8160412..3cd1320ca9c 100644 --- a/spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb @@ -8,7 +8,7 @@ describe Gitlab::CycleAnalytics::PlanStage do let!(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) } let!(:issue_3) { create(:issue, project: project, created_at: 30.minutes.ago) } let!(:issue_without_milestone) { create(:issue, project: project, created_at: 1.minute.ago) } - let(:stage) { described_class.new(project: project, options: { from: 2.days.ago, current_user: project.creator }) } + let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: project.creator, project: project }) } before do issue_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago, first_mentioned_in_commit_at: 10.minutes.ago) @@ -18,22 +18,88 @@ describe Gitlab::CycleAnalytics::PlanStage do it_behaves_like 'base stage' - describe '#median' do + describe '#project_median' do around do |example| Timecop.freeze { example.run } end it 'counts median from issues with metrics' do - expect(stage.median).to eq(ISSUES_MEDIAN) + expect(stage.project_median).to eq(ISSUES_MEDIAN) end end describe '#events' do + subject { stage.events } + it 'exposes issues with metrics' do - result = stage.events + expect(subject.count).to eq(2) + expect(subject.map { |event| event[:title] }).to contain_exactly(issue_1.title, issue_2.title) + end + end + + context 'when group is given' do + let(:user) { create(:user) } + let(:group) { create(:group) } + let(:project_2) { create(:project, group: group) } + let(:project_3) { create(:project, group: group) } + let(:issue_2_1) { create(:issue, project: project_2, created_at: 90.minutes.ago) } + let(:issue_2_2) { create(:issue, project: project_3, created_at: 60.minutes.ago) } + let(:issue_2_3) { create(:issue, project: project_2, created_at: 60.minutes.ago) } + let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: user, group: group }) } + + before do + group.add_owner(user) + issue_2_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago, first_mentioned_in_commit_at: 10.minutes.ago) + issue_2_2.metrics.update!(first_added_to_board_at: 30.minutes.ago, first_mentioned_in_commit_at: 20.minutes.ago) + issue_2_3.metrics.update!(first_added_to_board_at: 15.minutes.ago) + end + + describe '#group_median' do + around do |example| + Timecop.freeze { example.run } + end + + it 'counts median from issues with metrics' do + expect(stage.group_median).to eq(ISSUES_MEDIAN) + end + end + + describe '#events' do + subject { stage.events } + + it 'exposes merge requests that close issues' do + expect(subject.count).to eq(2) + expect(subject.map { |event| event[:title] }).to contain_exactly(issue_2_1.title, issue_2_2.title) + end + end + + context 'when subgroup is given' do + let(:subgroup) { create(:group, parent: group) } + let(:project_4) { create(:project, group: subgroup) } + let(:project_5) { create(:project, group: subgroup) } + let(:issue_3_1) { create(:issue, project: project_4, created_at: 90.minutes.ago) } + let(:issue_3_2) { create(:issue, project: project_5, created_at: 60.minutes.ago) } + let(:issue_3_3) { create(:issue, project: project_5, created_at: 60.minutes.ago) } + + before do + issue_3_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago, first_mentioned_in_commit_at: 10.minutes.ago) + issue_3_2.metrics.update!(first_added_to_board_at: 30.minutes.ago, first_mentioned_in_commit_at: 20.minutes.ago) + issue_3_3.metrics.update!(first_added_to_board_at: 15.minutes.ago) + end + + describe '#events' do + subject { stage.events } + + it 'exposes merge requests that close issues' do + expect(subject.count).to eq(4) + expect(subject.map { |event| event[:title] }).to contain_exactly(issue_2_1.title, issue_2_2.title, issue_3_1.title, issue_3_2.title) + end - expect(result.count).to eq(2) - expect(result.map { |event| event[:title] }).to contain_exactly(issue_1.title, issue_2.title) + it 'exposes merge requests that close issues with full path for subgroup' do + expect(subject.count).to eq(4) + expect(subject.find { |event| event[:title] == issue_3_1.title }[:url]).to include("#{subgroup.full_path}") + end + end end end end diff --git a/spec/lib/gitlab/cycle_analytics/review_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/review_stage_spec.rb index f072a9644e8..6d14973c711 100644 --- a/spec/lib/gitlab/cycle_analytics/review_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/review_stage_spec.rb @@ -4,14 +4,14 @@ require 'lib/gitlab/cycle_analytics/shared_stage_spec' describe Gitlab::CycleAnalytics::ReviewStage do let(:stage_name) { :review } let(:project) { create(:project) } - let!(:issue_1) { create(:issue, project: project, created_at: 90.minutes.ago) } - let!(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) } - let!(:issue_3) { create(:issue, project: project, created_at: 60.minutes.ago) } - let!(:mr_1) { create(:merge_request, :closed, source_project: project, created_at: 60.minutes.ago) } - let!(:mr_2) { create(:merge_request, :closed, source_project: project, created_at: 40.minutes.ago, source_branch: 'A') } - let!(:mr_3) { create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'B') } + let(:issue_1) { create(:issue, project: project, created_at: 90.minutes.ago) } + let(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) } + let(:issue_3) { create(:issue, project: project, created_at: 60.minutes.ago) } + let(:mr_1) { create(:merge_request, :closed, source_project: project, created_at: 60.minutes.ago) } + let(:mr_2) { create(:merge_request, :closed, source_project: project, created_at: 40.minutes.ago, source_branch: 'A') } + let(:mr_3) { create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'B') } let!(:mr_4) { create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'C') } - let(:stage) { described_class.new(project: project, options: { from: 2.days.ago, current_user: project.creator }) } + let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: project.creator, project: project }) } before do mr_1.metrics.update!(merged_at: 30.minutes.ago) @@ -24,22 +24,66 @@ describe Gitlab::CycleAnalytics::ReviewStage do it_behaves_like 'base stage' - describe '#median' do + describe '#project_median' do around do |example| Timecop.freeze { example.run } end it 'counts median from issues with metrics' do - expect(stage.median).to eq(ISSUES_MEDIAN) + expect(stage.project_median).to eq(ISSUES_MEDIAN) end end describe '#events' do + subject { stage.events } + it 'exposes merge requests that close issues' do - result = stage.events + expect(subject.count).to eq(2) + expect(subject.map { |event| event[:title] }).to contain_exactly(mr_1.title, mr_2.title) + end + end + + context 'when group is given' do + let(:user) { create(:user) } + let(:group) { create(:group) } + let(:project_2) { create(:project, group: group) } + let(:project_3) { create(:project, group: group) } + let(:issue_2_1) { create(:issue, project: project_2, created_at: 90.minutes.ago) } + let(:issue_2_2) { create(:issue, project: project_3, created_at: 60.minutes.ago) } + let(:issue_2_3) { create(:issue, project: project_2, created_at: 60.minutes.ago) } + let(:mr_2_1) { create(:merge_request, :closed, source_project: project_2, created_at: 60.minutes.ago) } + let(:mr_2_2) { create(:merge_request, :closed, source_project: project_3, created_at: 40.minutes.ago, source_branch: 'A') } + let(:mr_2_3) { create(:merge_request, source_project: project_2, created_at: 10.minutes.ago, source_branch: 'B') } + let!(:mr_2_4) { create(:merge_request, source_project: project_3, created_at: 10.minutes.ago, source_branch: 'C') } + let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: user, group: group }) } + + before do + group.add_owner(user) + mr_2_1.metrics.update!(merged_at: 30.minutes.ago) + mr_2_2.metrics.update!(merged_at: 10.minutes.ago) + + create(:merge_requests_closing_issues, merge_request: mr_2_1, issue: issue_2_1) + create(:merge_requests_closing_issues, merge_request: mr_2_2, issue: issue_2_2) + create(:merge_requests_closing_issues, merge_request: mr_2_3, issue: issue_2_3) + end + + describe '#group_median' do + around do |example| + Timecop.freeze { example.run } + end + + it 'counts median from issues with metrics' do + expect(stage.group_median).to eq(ISSUES_MEDIAN) + end + end + + describe '#events' do + subject { stage.events } - expect(result.count).to eq(2) - expect(result.map { |event| event[:title] }).to contain_exactly(mr_1.title, mr_2.title) + it 'exposes merge requests that close issues' do + expect(subject.count).to eq(2) + expect(subject.map { |event| event[:title] }).to contain_exactly(mr_2_1.title, mr_2_2.title) + end end end end diff --git a/spec/lib/gitlab/cycle_analytics/shared_event_spec.rb b/spec/lib/gitlab/cycle_analytics/shared_event_spec.rb index c22d27f60d6..b001a46001e 100644 --- a/spec/lib/gitlab/cycle_analytics/shared_event_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/shared_event_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' shared_examples 'default query config' do let(:project) { create(:project) } - let(:event) { described_class.new(project: project, stage: stage_name, options: { from: 1.day.ago }) } + let(:event) { described_class.new(stage: stage_name, options: { from: 1.day.ago, project: project }) } it 'has the stage attribute' do expect(event.stage).not_to be_nil diff --git a/spec/lib/gitlab/cycle_analytics/shared_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/shared_stage_spec.rb index 1a4b572cc11..c146146723f 100644 --- a/spec/lib/gitlab/cycle_analytics/shared_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/shared_stage_spec.rb @@ -3,10 +3,10 @@ require 'spec_helper' shared_examples 'base stage' do ISSUES_MEDIAN = 30.minutes.to_i - let(:stage) { described_class.new(project: double, options: {}) } + let(:stage) { described_class.new(options: { project: double }) } before do - allow(stage).to receive(:median).and_return(1.12) + allow(stage).to receive(:project_median).and_return(1.12) allow_any_instance_of(Gitlab::CycleAnalytics::BaseEventFetcher).to receive(:event_result).and_return({}) end diff --git a/spec/lib/gitlab/cycle_analytics/staging_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/staging_stage_spec.rb index 17d5fbb9733..9ca12cc448c 100644 --- a/spec/lib/gitlab/cycle_analytics/staging_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/staging_stage_spec.rb @@ -5,16 +5,16 @@ describe Gitlab::CycleAnalytics::StagingStage do let(:stage_name) { :staging } let(:project) { create(:project) } - let!(:issue_1) { create(:issue, project: project, created_at: 90.minutes.ago) } - let!(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) } - let!(:issue_3) { create(:issue, project: project, created_at: 60.minutes.ago) } - let!(:mr_1) { create(:merge_request, :closed, source_project: project, created_at: 60.minutes.ago) } - let!(:mr_2) { create(:merge_request, :closed, source_project: project, created_at: 40.minutes.ago, source_branch: 'A') } - let!(:mr_3) { create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'B') } + let(:issue_1) { create(:issue, project: project, created_at: 90.minutes.ago) } + let(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) } + let(:issue_3) { create(:issue, project: project, created_at: 60.minutes.ago) } + let(:mr_1) { create(:merge_request, :closed, source_project: project, created_at: 60.minutes.ago) } + let(:mr_2) { create(:merge_request, :closed, source_project: project, created_at: 40.minutes.ago, source_branch: 'A') } + let(:mr_3) { create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'B') } let(:build_1) { create(:ci_build, project: project) } let(:build_2) { create(:ci_build, project: project) } - let(:stage) { described_class.new(project: project, options: { from: 2.days.ago, current_user: project.creator }) } + let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: project.creator, project: project }) } before do mr_1.metrics.update!(merged_at: 80.minutes.ago, first_deployed_to_production_at: 50.minutes.ago, pipeline_id: build_1.commit_id) @@ -28,22 +28,68 @@ describe Gitlab::CycleAnalytics::StagingStage do it_behaves_like 'base stage' - describe '#median' do + describe '#project_median' do around do |example| Timecop.freeze { example.run } end it 'counts median from issues with metrics' do - expect(stage.median).to eq(ISSUES_MEDIAN) + expect(stage.project_median).to eq(ISSUES_MEDIAN) end end describe '#events' do + subject { stage.events } + it 'exposes builds connected to merge request' do - result = stage.events + expect(subject.count).to eq(2) + expect(subject.map { |event| event[:name] }).to contain_exactly(build_1.name, build_2.name) + end + end + + context 'when group is given' do + let(:user) { create(:user) } + let(:group) { create(:group) } + let(:project_2) { create(:project, group: group) } + let(:project_3) { create(:project, group: group) } + let(:issue_2_1) { create(:issue, project: project_2, created_at: 90.minutes.ago) } + let(:issue_2_2) { create(:issue, project: project_3, created_at: 60.minutes.ago) } + let(:issue_2_3) { create(:issue, project: project_2, created_at: 60.minutes.ago) } + let(:mr_1) { create(:merge_request, :closed, source_project: project_2, created_at: 60.minutes.ago) } + let(:mr_2) { create(:merge_request, :closed, source_project: project_3, created_at: 40.minutes.ago, source_branch: 'A') } + let(:mr_3) { create(:merge_request, source_project: project_2, created_at: 10.minutes.ago, source_branch: 'B') } + let(:build_1) { create(:ci_build, project: project_2) } + let(:build_2) { create(:ci_build, project: project_3) } + let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: user, group: group }) } + + before do + group.add_owner(user) + mr_1.metrics.update!(merged_at: 80.minutes.ago, first_deployed_to_production_at: 50.minutes.ago, pipeline_id: build_1.commit_id) + mr_2.metrics.update!(merged_at: 60.minutes.ago, first_deployed_to_production_at: 30.minutes.ago, pipeline_id: build_2.commit_id) + mr_3.metrics.update!(merged_at: 10.minutes.ago, first_deployed_to_production_at: 3.days.ago, pipeline_id: create(:ci_build, project: project_2).commit_id) + + create(:merge_requests_closing_issues, merge_request: mr_1, issue: issue_2_1) + create(:merge_requests_closing_issues, merge_request: mr_2, issue: issue_2_2) + create(:merge_requests_closing_issues, merge_request: mr_3, issue: issue_2_3) + end + + describe '#group_median' do + around do |example| + Timecop.freeze { example.run } + end + + it 'counts median from issues with metrics' do + expect(stage.group_median).to eq(ISSUES_MEDIAN) + end + end + + describe '#events' do + subject { stage.events } - expect(result.count).to eq(2) - expect(result.map { |event| event[:name] }).to contain_exactly(build_1.name, build_2.name) + it 'exposes merge requests that close issues' do + expect(subject.count).to eq(2) + expect(subject.map { |event| event[:name] }).to contain_exactly(build_1.name, build_2.name) + end end end end diff --git a/spec/lib/gitlab/cycle_analytics/test_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/test_stage_spec.rb index 8633a63849f..41028c44a00 100644 --- a/spec/lib/gitlab/cycle_analytics/test_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/test_stage_spec.rb @@ -4,7 +4,7 @@ require 'lib/gitlab/cycle_analytics/shared_stage_spec' describe Gitlab::CycleAnalytics::TestStage do let(:stage_name) { :test } let(:project) { create(:project) } - let(:stage) { described_class.new(project: project, options: { from: 2.days.ago, current_user: project.creator }) } + let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: project.creator, project: project }) } it_behaves_like 'base stage' @@ -36,7 +36,7 @@ describe Gitlab::CycleAnalytics::TestStage do end it 'counts median from issues with metrics' do - expect(stage.median).to eq(ISSUES_MEDIAN) + expect(stage.project_median).to eq(ISSUES_MEDIAN) end end end diff --git a/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb b/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb index 8122e85a981..ad61bdeace7 100644 --- a/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb @@ -34,7 +34,7 @@ describe Gitlab::CycleAnalytics::UsageData do expect(result).to have_key(:avg_cycle_analytics) - CycleAnalytics::Base::STAGES.each do |stage| + CycleAnalytics::LevelBase::STAGES.each do |stage| expect(result[:avg_cycle_analytics]).to have_key(stage) stage_values = result[:avg_cycle_analytics][stage] diff --git a/spec/models/cycle_analytics/code_spec.rb b/spec/models/cycle_analytics/code_spec.rb index db6e70973ae..808659552ff 100644 --- a/spec/models/cycle_analytics/code_spec.rb +++ b/spec/models/cycle_analytics/code_spec.rb @@ -38,7 +38,7 @@ describe 'CycleAnalytics#code' do merge_merge_requests_closing_issue(user, project, issue) deploy_master(user, project) - expect(subject[:code].median).to be_nil + expect(subject[:code].project_median).to be_nil end end end @@ -68,7 +68,7 @@ describe 'CycleAnalytics#code' do merge_merge_requests_closing_issue(user, project, issue) - expect(subject[:code].median).to be_nil + expect(subject[:code].project_median).to be_nil end end end diff --git a/spec/models/cycle_analytics/group_level_spec.rb b/spec/models/cycle_analytics/group_level_spec.rb new file mode 100644 index 00000000000..154c1b9c0f8 --- /dev/null +++ b/spec/models/cycle_analytics/group_level_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe CycleAnalytics::GroupLevel do + let(:group) { create(:group)} + let(:project) { create(:project, :repository, namespace: group) } + let(:from_date) { 10.days.ago } + let(:user) { create(:user, :admin) } + let(:issue) { create(:issue, project: project, created_at: 2.days.ago) } + let(:milestone) { create(:milestone, project: project) } + let(:mr) { create_merge_request_closing_issue(user, project, issue, commit_message: "References #{issue.to_reference}") } + let(:pipeline) { create(:ci_empty_pipeline, status: 'created', project: project, ref: mr.source_branch, sha: mr.source_branch_sha, head_pipeline_of: mr) } + + subject { described_class.new(group: group, options: { from: from_date, current_user: user }) } + + describe '#permissions' do + it 'returns true for all stages' do + expect(subject.permissions.values.uniq).to eq([true]) + end + end + + describe '#stats' do + before do + allow_any_instance_of(Gitlab::ReferenceExtractor).to receive(:issues).and_return([issue]) + + create_cycle(user, project, issue, mr, milestone, pipeline) + deploy_master(user, project) + end + + it 'returns medians for each stage for a specific group' do + expect(subject.no_stats?).to eq(false) + end + end + + describe '#summary' do + before do + create_cycle(user, project, issue, mr, milestone, pipeline) + deploy_master(user, project) + end + + it 'returns medians for each stage for a specific group' do + expect(subject.summary.map { |summary| summary[:value] }).to contain_exactly(1, 1) + end + end +end diff --git a/spec/models/cycle_analytics/issue_spec.rb b/spec/models/cycle_analytics/issue_spec.rb index 4ccbdf29df6..8cdf83b1292 100644 --- a/spec/models/cycle_analytics/issue_spec.rb +++ b/spec/models/cycle_analytics/issue_spec.rb @@ -43,7 +43,7 @@ describe 'CycleAnalytics#issue' do create_merge_request_closing_issue(user, project, issue) merge_merge_requests_closing_issue(user, project, issue) - expect(subject[:issue].median).to be_nil + expect(subject[:issue].project_median).to be_nil end end end diff --git a/spec/models/cycle_analytics/plan_spec.rb b/spec/models/cycle_analytics/plan_spec.rb index c99c38e9cf3..28ad9bd194d 100644 --- a/spec/models/cycle_analytics/plan_spec.rb +++ b/spec/models/cycle_analytics/plan_spec.rb @@ -47,7 +47,7 @@ describe 'CycleAnalytics#plan' do create_merge_request_closing_issue(user, project, issue, source_branch: branch_name) merge_merge_requests_closing_issue(user, project, issue) - expect(subject[:issue].median).to be_nil + expect(subject[:issue].project_median).to be_nil end end end diff --git a/spec/models/cycle_analytics/production_spec.rb b/spec/models/cycle_analytics/production_spec.rb index ddd199362d1..613c1786540 100644 --- a/spec/models/cycle_analytics/production_spec.rb +++ b/spec/models/cycle_analytics/production_spec.rb @@ -41,7 +41,7 @@ describe 'CycleAnalytics#production' do MergeRequests::MergeService.new(project, user).execute(merge_request) deploy_master(user, project) - expect(subject[:production].median).to be_nil + expect(subject[:production].project_median).to be_nil end end @@ -52,7 +52,7 @@ describe 'CycleAnalytics#production' do MergeRequests::MergeService.new(project, user).execute(merge_request) deploy_master(user, project, environment: 'staging') - expect(subject[:production].median).to be_nil + expect(subject[:production].project_median).to be_nil end end end diff --git a/spec/models/cycle_analytics/project_level_spec.rb b/spec/models/cycle_analytics/project_level_spec.rb index 77bd0bfeb9c..4de01b1c679 100644 --- a/spec/models/cycle_analytics/project_level_spec.rb +++ b/spec/models/cycle_analytics/project_level_spec.rb @@ -23,7 +23,7 @@ describe CycleAnalytics::ProjectLevel do it 'returns every median for each stage for a specific project' do values = described_class::STAGES.each_with_object({}) do |stage_name, hsh| - hsh[stage_name] = subject[stage_name].median.presence + hsh[stage_name] = subject[stage_name].project_median.presence end expect(subject.all_medians_by_stage).to eq(values) diff --git a/spec/models/cycle_analytics/review_spec.rb b/spec/models/cycle_analytics/review_spec.rb index 63c481ed465..ef88fd86340 100644 --- a/spec/models/cycle_analytics/review_spec.rb +++ b/spec/models/cycle_analytics/review_spec.rb @@ -28,7 +28,7 @@ describe 'CycleAnalytics#review' do it "returns nil" do MergeRequests::MergeService.new(project, user).execute(create(:merge_request)) - expect(subject[:review].median).to be_nil + expect(subject[:review].project_median).to be_nil end end end diff --git a/spec/models/cycle_analytics/staging_spec.rb b/spec/models/cycle_analytics/staging_spec.rb index c134b97553f..571792559d8 100644 --- a/spec/models/cycle_analytics/staging_spec.rb +++ b/spec/models/cycle_analytics/staging_spec.rb @@ -45,7 +45,7 @@ describe 'CycleAnalytics#staging' do MergeRequests::MergeService.new(project, user).execute(merge_request) deploy_master(user, project) - expect(subject[:staging].median).to be_nil + expect(subject[:staging].project_median).to be_nil end end @@ -56,7 +56,7 @@ describe 'CycleAnalytics#staging' do MergeRequests::MergeService.new(project, user).execute(merge_request) deploy_master(user, project, environment: 'staging') - expect(subject[:staging].median).to be_nil + expect(subject[:staging].project_median).to be_nil end end end diff --git a/spec/models/cycle_analytics/test_spec.rb b/spec/models/cycle_analytics/test_spec.rb index a6ea73b2699..7b3001d2bd8 100644 --- a/spec/models/cycle_analytics/test_spec.rb +++ b/spec/models/cycle_analytics/test_spec.rb @@ -36,7 +36,7 @@ describe 'CycleAnalytics#test' do merge_merge_requests_closing_issue(user, project, issue) - expect(subject[:test].median).to be_nil + expect(subject[:test].project_median).to be_nil end end @@ -47,7 +47,7 @@ describe 'CycleAnalytics#test' do pipeline.run! pipeline.succeed! - expect(subject[:test].median).to be_nil + expect(subject[:test].project_median).to be_nil end end @@ -62,7 +62,7 @@ describe 'CycleAnalytics#test' do merge_merge_requests_closing_issue(user, project, issue) - expect(subject[:test].median).to be_nil + expect(subject[:test].project_median).to be_nil end end @@ -77,7 +77,7 @@ describe 'CycleAnalytics#test' do merge_merge_requests_closing_issue(user, project, issue) - expect(subject[:test].median).to be_nil + expect(subject[:test].project_median).to be_nil end end end diff --git a/spec/serializers/analytics_issue_entity_spec.rb b/spec/serializers/analytics_issue_entity_spec.rb index 89588b4df2b..dd5e43a4b62 100644 --- a/spec/serializers/analytics_issue_entity_spec.rb +++ b/spec/serializers/analytics_issue_entity_spec.rb @@ -9,12 +9,14 @@ describe AnalyticsIssueEntity do iid: "1", id: "1", created_at: "2016-11-12 15:04:02.948604", - author: user + author: user, + name: project.name, + path: project.namespace } end let(:project) { create(:project) } - let(:request) { EntityRequest.new(project: project, entity: :merge_request) } + let(:request) { EntityRequest.new(entity: :merge_request) } let(:entity) do described_class.new(entity_hash, request: request, project: project) diff --git a/spec/serializers/analytics_issue_serializer_spec.rb b/spec/serializers/analytics_issue_serializer_spec.rb index 5befc28f4fa..c9ffe1c5dad 100644 --- a/spec/serializers/analytics_issue_serializer_spec.rb +++ b/spec/serializers/analytics_issue_serializer_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe AnalyticsIssueSerializer do subject do described_class - .new(project: project, entity: :merge_request) + .new(entity: :merge_request) .represent(resource) end @@ -16,7 +16,9 @@ describe AnalyticsIssueSerializer do iid: "1", id: "1", created_at: "2016-11-12 15:04:02.948604", - author: user + author: user, + name: project.name, + path: project.namespace } end diff --git a/spec/serializers/analytics_merge_request_serializer_spec.rb b/spec/serializers/analytics_merge_request_serializer_spec.rb index 62067cc0ef2..123d7d795ce 100644 --- a/spec/serializers/analytics_merge_request_serializer_spec.rb +++ b/spec/serializers/analytics_merge_request_serializer_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe AnalyticsMergeRequestSerializer do subject do described_class - .new(project: project, entity: :merge_request) + .new(entity: :merge_request) .represent(resource) end @@ -17,7 +17,9 @@ describe AnalyticsMergeRequestSerializer do id: "1", state: 'open', created_at: "2016-11-12 15:04:02.948604", - author: user + author: user, + name: project.name, + path: project.namespace } end diff --git a/spec/serializers/analytics_stage_serializer_spec.rb b/spec/serializers/analytics_stage_serializer_spec.rb index 5b05c2f2ef3..86a796a2d94 100644 --- a/spec/serializers/analytics_stage_serializer_spec.rb +++ b/spec/serializers/analytics_stage_serializer_spec.rb @@ -6,11 +6,11 @@ describe AnalyticsStageSerializer do end let(:resource) do - Gitlab::CycleAnalytics::CodeStage.new(project: double, options: {}) + Gitlab::CycleAnalytics::CodeStage.new(options: { project: double }) end before do - allow_any_instance_of(Gitlab::CycleAnalytics::BaseStage).to receive(:median).and_return(1.12) + allow_any_instance_of(Gitlab::CycleAnalytics::BaseStage).to receive(:project_median).and_return(1.12) allow_any_instance_of(Gitlab::CycleAnalytics::BaseEventFetcher).to receive(:event_result).and_return({}) end @@ -24,7 +24,7 @@ describe AnalyticsStageSerializer do context 'when median is equal 0' do before do - allow_any_instance_of(Gitlab::CycleAnalytics::BaseStage).to receive(:median).and_return(0) + allow_any_instance_of(Gitlab::CycleAnalytics::BaseStage).to receive(:project_median).and_return(0) end it 'sets the value to nil' do @@ -34,7 +34,7 @@ describe AnalyticsStageSerializer do context 'when median is below 1' do before do - allow_any_instance_of(Gitlab::CycleAnalytics::BaseStage).to receive(:median).and_return(0.12) + allow_any_instance_of(Gitlab::CycleAnalytics::BaseStage).to receive(:project_median).and_return(0.12) end it 'sets the value to equal to median' do @@ -44,7 +44,7 @@ describe AnalyticsStageSerializer do context 'when median is above 1' do before do - allow_any_instance_of(Gitlab::CycleAnalytics::BaseStage).to receive(:median).and_return(60.12) + allow_any_instance_of(Gitlab::CycleAnalytics::BaseStage).to receive(:project_median).and_return(60.12) end it 'sets the value to equal to median' do diff --git a/spec/support/cycle_analytics_helpers/test_generation.rb b/spec/support/cycle_analytics_helpers/test_generation.rb index 19b32c84d81..be1c2bc3046 100644 --- a/spec/support/cycle_analytics_helpers/test_generation.rb +++ b/spec/support/cycle_analytics_helpers/test_generation.rb @@ -50,7 +50,7 @@ module CycleAnalyticsHelpers end median_time_difference = time_differences.sort[2] - expect(subject[phase].median).to be_within(5).of(median_time_difference) + expect(subject[phase].project_median).to be_within(5).of(median_time_difference) end context "when the data belongs to another project" do @@ -80,7 +80,7 @@ module CycleAnalyticsHelpers # Turn off the stub before checking assertions allow(self).to receive(:project).and_call_original - expect(subject[phase].median).to be_nil + expect(subject[phase].project_median).to be_nil end end @@ -103,7 +103,7 @@ module CycleAnalyticsHelpers Timecop.freeze(end_time + 1.day) { post_fn[self, data] } if post_fn - expect(subject[phase].median).to be_nil + expect(subject[phase].project_median).to be_nil end end end @@ -121,7 +121,7 @@ module CycleAnalyticsHelpers Timecop.freeze(end_time + 1.day) { post_fn[self, data] } if post_fn - expect(subject[phase].median).to be_nil + expect(subject[phase].project_median).to be_nil end end end @@ -138,7 +138,7 @@ module CycleAnalyticsHelpers post_fn[self, data] if post_fn - expect(subject[phase].median).to be_nil + expect(subject[phase].project_median).to be_nil end end end @@ -146,7 +146,7 @@ module CycleAnalyticsHelpers context "when none of the start / end conditions are matched" do it "returns nil" do - expect(subject[phase].median).to be_nil + expect(subject[phase].project_median).to be_nil end end end |