Fix plan stage query
Fix plan stage query and the way it is displayed
This commit is contained in:
parent
ea1aa78573
commit
976b8b5391
32 changed files with 382 additions and 167 deletions
|
@ -1,59 +0,0 @@
|
|||
<script>
|
||||
import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
|
||||
import iconCommit from '../svg/icon_commit.svg';
|
||||
import limitWarning from './limit_warning_component.vue';
|
||||
import totalTime from './total_time_component.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
userAvatarImage,
|
||||
totalTime,
|
||||
limitWarning,
|
||||
},
|
||||
props: {
|
||||
items: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
stage: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
iconCommit() {
|
||||
return iconCommit;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<div class="events-description">
|
||||
{{ stage.description }}
|
||||
<limit-warning :count="items.length" />
|
||||
</div>
|
||||
<ul class="stage-event-list">
|
||||
<li v-for="(commit, i) in items" :key="i" class="stage-event-item">
|
||||
<div class="item-details item-conmmit-component">
|
||||
<!-- FIXME: Pass an alt attribute here for accessibility -->
|
||||
<user-avatar-image :img-src="commit.author.avatarUrl" />
|
||||
<h5 class="item-title commit-title">
|
||||
<a :href="commit.commitUrl"> {{ commit.title }} </a>
|
||||
</h5>
|
||||
<span>
|
||||
{{ s__('FirstPushedBy|First') }} <span class="commit-icon" v-html="iconCommit"> </span>
|
||||
<a :href="commit.commitUrl" class="commit-hash-link commit-sha">{{
|
||||
commit.shortSha
|
||||
}}</a>
|
||||
{{ s__('FirstPushedBy|pushed by') }}
|
||||
<a :href="commit.author.webUrl" class="commit-author-link">
|
||||
{{ commit.author.name }}
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="item-time"><total-time :time="commit.totalTime" /></div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
|
@ -5,7 +5,6 @@ import Flash from '../flash';
|
|||
import Translate from '../vue_shared/translate';
|
||||
import banner from './components/banner.vue';
|
||||
import stageCodeComponent from './components/stage_code_component.vue';
|
||||
import stagePlanComponent from './components/stage_plan_component.vue';
|
||||
import stageComponent from './components/stage_component.vue';
|
||||
import stageReviewComponent from './components/stage_review_component.vue';
|
||||
import stageStagingComponent from './components/stage_staging_component.vue';
|
||||
|
@ -26,7 +25,7 @@ export default () => {
|
|||
components: {
|
||||
banner,
|
||||
'stage-issue-component': stageComponent,
|
||||
'stage-plan-component': stagePlanComponent,
|
||||
'stage-plan-component': stageComponent,
|
||||
'stage-code-component': stageCodeComponent,
|
||||
'stage-test-component': stageTestComponent,
|
||||
'stage-review-component': stageReviewComponent,
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Change logic behind cycle analytics
|
||||
merge_request: 29018
|
||||
author:
|
||||
type: changed
|
36
lib/gitlab/cycle_analytics/builds_event_helper.rb
Normal file
36
lib/gitlab/cycle_analytics/builds_event_helper.rb
Normal file
|
@ -0,0 +1,36 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module CycleAnalytics
|
||||
module BuildsEventHelper
|
||||
def initialize(*args)
|
||||
@projections = [build_table[:id]]
|
||||
@order = build_table[:created_at]
|
||||
|
||||
super(*args)
|
||||
end
|
||||
|
||||
def fetch
|
||||
Updater.update!(event_result, from: 'id', to: 'build', klass: ::Ci::Build)
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
def events_query
|
||||
base_query.join(build_table).on(mr_metrics_table[:pipeline_id].eq(build_table[:commit_id]))
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def allowed_ids
|
||||
nil
|
||||
end
|
||||
|
||||
def serialize(event)
|
||||
AnalyticsBuildSerializer.new.represent(event['build'])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,6 +3,8 @@
|
|||
module Gitlab
|
||||
module CycleAnalytics
|
||||
class CodeEventFetcher < BaseEventFetcher
|
||||
include CodeHelper
|
||||
|
||||
def initialize(*args)
|
||||
@projections = [mr_table[:title],
|
||||
mr_table[:iid],
|
||||
|
|
11
lib/gitlab/cycle_analytics/code_helper.rb
Normal file
11
lib/gitlab/cycle_analytics/code_helper.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module CycleAnalytics
|
||||
module CodeHelper
|
||||
def stage_query(project_ids)
|
||||
super(project_ids).where(mr_table[:created_at].gteq(issue_metrics_table[:first_mentioned_in_commit_at]))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,6 +3,8 @@
|
|||
module Gitlab
|
||||
module CycleAnalytics
|
||||
class CodeStage < BaseStage
|
||||
include CodeHelper
|
||||
|
||||
def start_time_attrs
|
||||
@start_time_attrs ||= issue_metrics_table[:first_mentioned_in_commit_at]
|
||||
end
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
module Gitlab
|
||||
module CycleAnalytics
|
||||
class IssueEventFetcher < BaseEventFetcher
|
||||
include IssueHelper
|
||||
|
||||
def initialize(*args)
|
||||
@projections = [issue_table[:title],
|
||||
issue_table[:iid],
|
||||
|
|
17
lib/gitlab/cycle_analytics/issue_helper.rb
Normal file
17
lib/gitlab/cycle_analytics/issue_helper.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module CycleAnalytics
|
||||
module IssueHelper
|
||||
def stage_query(project_ids)
|
||||
query = issue_table.join(issue_metrics_table).on(issue_table[:id].eq(issue_metrics_table[:issue_id]))
|
||||
.project(issue_table[:project_id].as("project_id"))
|
||||
.where(issue_table[:project_id].in(project_ids))
|
||||
.where(issue_table[:created_at].gteq(@options[:from])) # rubocop:disable Gitlab/ModuleWithInstanceVariables
|
||||
.where(issue_metrics_table[:first_added_to_board_at].not_eq(nil).or(issue_metrics_table[:first_associated_with_milestone_at].not_eq(nil)))
|
||||
|
||||
query
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,6 +3,8 @@
|
|||
module Gitlab
|
||||
module CycleAnalytics
|
||||
class IssueStage < BaseStage
|
||||
include IssueHelper
|
||||
|
||||
def start_time_attrs
|
||||
@start_time_attrs ||= issue_table[:created_at]
|
||||
end
|
||||
|
|
|
@ -3,60 +3,26 @@
|
|||
module Gitlab
|
||||
module CycleAnalytics
|
||||
class PlanEventFetcher < BaseEventFetcher
|
||||
include PlanHelper
|
||||
|
||||
def initialize(*args)
|
||||
@projections = [mr_diff_table[:id],
|
||||
issue_metrics_table[:first_mentioned_in_commit_at]]
|
||||
@projections = [issue_table[:title],
|
||||
issue_table[:iid],
|
||||
issue_table[:id],
|
||||
issue_table[:created_at],
|
||||
issue_table[:author_id]]
|
||||
|
||||
super(*args)
|
||||
end
|
||||
|
||||
def events_query
|
||||
base_query
|
||||
.join(mr_diff_table)
|
||||
.on(mr_diff_table[:merge_request_id].eq(mr_table[:id]))
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def allowed_ids
|
||||
nil
|
||||
end
|
||||
|
||||
def merge_request_diff_commits
|
||||
@merge_request_diff_commits ||=
|
||||
MergeRequestDiffCommit
|
||||
.where(merge_request_diff_id: event_result.map { |event| event['id'] })
|
||||
.group_by(&:merge_request_diff_id)
|
||||
end
|
||||
|
||||
def serialize(event)
|
||||
commit = first_time_reference_commit(event)
|
||||
|
||||
return unless commit
|
||||
|
||||
serialize_commit(event, commit, query)
|
||||
AnalyticsIssueSerializer.new(project: @project).represent(event)
|
||||
end
|
||||
|
||||
def first_time_reference_commit(event)
|
||||
return unless event && merge_request_diff_commits
|
||||
|
||||
commits = merge_request_diff_commits[event['id'].to_i]
|
||||
|
||||
return if commits.blank?
|
||||
|
||||
commits.find do |commit|
|
||||
next unless commit[:committed_date] && event['first_mentioned_in_commit_at']
|
||||
|
||||
commit[:committed_date].to_i == DateTime.parse(event['first_mentioned_in_commit_at'].to_s).to_i
|
||||
end
|
||||
end
|
||||
|
||||
def serialize_commit(event, commit, query)
|
||||
commit = Commit.from_hash(commit.to_hash, @project)
|
||||
|
||||
AnalyticsCommitSerializer.new(project: @project, total_time: event['total_time']).represent(commit)
|
||||
def allowed_ids_finder_class
|
||||
IssuesFinder
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
18
lib/gitlab/cycle_analytics/plan_helper.rb
Normal file
18
lib/gitlab/cycle_analytics/plan_helper.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module CycleAnalytics
|
||||
module PlanHelper
|
||||
def stage_query(project_ids)
|
||||
query = issue_table.join(issue_metrics_table).on(issue_table[:id].eq(issue_metrics_table[:issue_id]))
|
||||
.project(issue_table[:project_id].as("project_id"))
|
||||
.where(issue_table[:project_id].in(project_ids))
|
||||
.where(issue_table[:created_at].gteq(@options[:from])) # rubocop:disable Gitlab/ModuleWithInstanceVariables
|
||||
.where(issue_metrics_table[:first_added_to_board_at].not_eq(nil).or(issue_metrics_table[:first_associated_with_milestone_at].not_eq(nil)))
|
||||
.where(issue_metrics_table[:first_mentioned_in_commit_at].not_eq(nil))
|
||||
|
||||
query
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,6 +3,8 @@
|
|||
module Gitlab
|
||||
module CycleAnalytics
|
||||
class PlanStage < BaseStage
|
||||
include PlanHelper
|
||||
|
||||
def start_time_attrs
|
||||
@start_time_attrs ||= [issue_metrics_table[:first_associated_with_milestone_at],
|
||||
issue_metrics_table[:first_added_to_board_at]]
|
||||
|
@ -21,7 +23,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def legend
|
||||
_("Related Commits")
|
||||
_("Related Issues")
|
||||
end
|
||||
|
||||
def description
|
||||
|
|
|
@ -2,7 +2,28 @@
|
|||
|
||||
module Gitlab
|
||||
module CycleAnalytics
|
||||
class ProductionEventFetcher < IssueEventFetcher
|
||||
class ProductionEventFetcher < BaseEventFetcher
|
||||
include ProductionHelper
|
||||
|
||||
def initialize(*args)
|
||||
@projections = [issue_table[:title],
|
||||
issue_table[:iid],
|
||||
issue_table[:id],
|
||||
issue_table[:created_at],
|
||||
issue_table[:author_id]]
|
||||
|
||||
super(*args)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def serialize(event)
|
||||
AnalyticsIssueSerializer.new(project: @project).represent(event)
|
||||
end
|
||||
|
||||
def allowed_ids_finder_class
|
||||
IssuesFinder
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
module Gitlab
|
||||
module CycleAnalytics
|
||||
class ReviewEventFetcher < BaseEventFetcher
|
||||
include ReviewHelper
|
||||
|
||||
def initialize(*args)
|
||||
@projections = [mr_table[:title],
|
||||
mr_table[:iid],
|
||||
|
|
11
lib/gitlab/cycle_analytics/review_helper.rb
Normal file
11
lib/gitlab/cycle_analytics/review_helper.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module CycleAnalytics
|
||||
module ReviewHelper
|
||||
def stage_query(project_ids)
|
||||
super(project_ids).where(mr_metrics_table[:merged_at].not_eq(nil))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,6 +3,8 @@
|
|||
module Gitlab
|
||||
module CycleAnalytics
|
||||
class ReviewStage < BaseStage
|
||||
include ReviewHelper
|
||||
|
||||
def start_time_attrs
|
||||
@start_time_attrs ||= mr_table[:created_at]
|
||||
end
|
||||
|
|
|
@ -3,34 +3,8 @@
|
|||
module Gitlab
|
||||
module CycleAnalytics
|
||||
class StagingEventFetcher < BaseEventFetcher
|
||||
def initialize(*args)
|
||||
@projections = [build_table[:id]]
|
||||
@order = build_table[:created_at]
|
||||
|
||||
super(*args)
|
||||
end
|
||||
|
||||
def fetch
|
||||
Updater.update!(event_result, from: 'id', to: 'build', klass: ::Ci::Build)
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
def events_query
|
||||
base_query.join(build_table).on(mr_metrics_table[:pipeline_id].eq(build_table[:commit_id]))
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def allowed_ids
|
||||
nil
|
||||
end
|
||||
|
||||
def serialize(event)
|
||||
AnalyticsBuildSerializer.new.represent(event['build'])
|
||||
end
|
||||
include ProductionHelper
|
||||
include BuildsEventHelper
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,6 +4,7 @@ module Gitlab
|
|||
module CycleAnalytics
|
||||
class StagingStage < BaseStage
|
||||
include ProductionHelper
|
||||
|
||||
def start_time_attrs
|
||||
@start_time_attrs ||= mr_metrics_table[:merged_at]
|
||||
end
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
module Gitlab
|
||||
module CycleAnalytics
|
||||
class TestEventFetcher < StagingEventFetcher
|
||||
class TestEventFetcher < BaseEventFetcher
|
||||
include TestHelper
|
||||
include BuildsEventHelper
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
21
lib/gitlab/cycle_analytics/test_helper.rb
Normal file
21
lib/gitlab/cycle_analytics/test_helper.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module CycleAnalytics
|
||||
module TestHelper
|
||||
def stage_query(project_ids)
|
||||
if branch
|
||||
super(project_ids).where(build_table[:ref].eq(branch))
|
||||
else
|
||||
super(project_ids)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def branch
|
||||
@branch ||= @options[:branch] # rubocop:disable Gitlab/ModuleWithInstanceVariables
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,6 +3,8 @@
|
|||
module Gitlab
|
||||
module CycleAnalytics
|
||||
class TestStage < BaseStage
|
||||
include TestHelper
|
||||
|
||||
def start_time_attrs
|
||||
@start_time_attrs ||= mr_metrics_table[:latest_build_started_at]
|
||||
end
|
||||
|
@ -26,14 +28,6 @@ module Gitlab
|
|||
def description
|
||||
_("Total test time for all commits/merges")
|
||||
end
|
||||
|
||||
def stage_query(project_ids)
|
||||
if @options[:branch]
|
||||
super(project_ids).where(build_table[:ref].eq(@options[:branch]))
|
||||
else
|
||||
super(project_ids)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4500,12 +4500,6 @@ msgstr ""
|
|||
msgid "First day of the week"
|
||||
msgstr ""
|
||||
|
||||
msgid "FirstPushedBy|First"
|
||||
msgstr ""
|
||||
|
||||
msgid "FirstPushedBy|pushed by"
|
||||
msgstr ""
|
||||
|
||||
msgid "FlowdockService|Flowdock Git source token"
|
||||
msgstr ""
|
||||
|
||||
|
@ -8313,9 +8307,6 @@ msgstr ""
|
|||
msgid "Registry"
|
||||
msgstr ""
|
||||
|
||||
msgid "Related Commits"
|
||||
msgstr ""
|
||||
|
||||
msgid "Related Deployed Jobs"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ describe 'Cycle Analytics', :js do
|
|||
expect_issue_to_be_present
|
||||
|
||||
click_stage('Plan')
|
||||
expect(find('.stage-events')).to have_content(mr.commits.last.title)
|
||||
expect_issue_to_be_present
|
||||
|
||||
click_stage('Code')
|
||||
expect_merge_request_to_be_present
|
||||
|
|
|
@ -4,5 +4,41 @@ require 'lib/gitlab/cycle_analytics/shared_stage_spec'
|
|||
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 }) }
|
||||
|
||||
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_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
|
||||
around do |example|
|
||||
Timecop.freeze { example.run }
|
||||
end
|
||||
|
||||
it 'counts median from issues with metrics' do
|
||||
expect(stage.median).to eq(ISSUES_MEDIAN)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#events' do
|
||||
it 'exposes merge requests that closes issues' do
|
||||
result = stage.events
|
||||
|
||||
expect(result.count).to eq(2)
|
||||
expect(result.map { |event| event[:title] }).to contain_exactly(mr_1.title, mr_2.title)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -53,22 +53,30 @@ describe 'cycle analytics events' do
|
|||
describe '#plan_events' do
|
||||
let(:stage) { :plan }
|
||||
|
||||
it 'has a title' do
|
||||
expect(events.first[:title]).not_to be_nil
|
||||
end
|
||||
|
||||
it 'has a sha short ID' do
|
||||
expect(events.first[:short_sha]).not_to be_nil
|
||||
end
|
||||
|
||||
it 'has the URL' do
|
||||
expect(events.first[:commit_url]).not_to be_nil
|
||||
before do
|
||||
create_commit_referencing_issue(context)
|
||||
end
|
||||
|
||||
it 'has the total time' do
|
||||
expect(events.first[:total_time]).not_to be_empty
|
||||
end
|
||||
|
||||
it 'has a title' do
|
||||
expect(events.first[:title]).to eq(context.title)
|
||||
end
|
||||
|
||||
it 'has the URL' do
|
||||
expect(events.first[:url]).not_to be_nil
|
||||
end
|
||||
|
||||
it 'has an iid' do
|
||||
expect(events.first[:iid]).to eq(context.iid.to_s)
|
||||
end
|
||||
|
||||
it 'has a created_at timestamp' do
|
||||
expect(events.first[:created_at]).to end_with('ago')
|
||||
end
|
||||
|
||||
it "has the author's URL" do
|
||||
expect(events.first[:author][:web_url]).not_to be_nil
|
||||
end
|
||||
|
@ -78,12 +86,13 @@ describe 'cycle analytics events' do
|
|||
end
|
||||
|
||||
it "has the author's name" do
|
||||
expect(events.first[:author][:name]).not_to be_nil
|
||||
expect(events.first[:author][:name]).to eq(context.author.name)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#code_events' do
|
||||
let(:stage) { :code }
|
||||
let!(:merge_request) { MergeRequest.first }
|
||||
|
||||
before do
|
||||
create_commit_referencing_issue(context)
|
||||
|
@ -122,6 +131,7 @@ describe 'cycle analytics events' do
|
|||
let(:stage) { :test }
|
||||
|
||||
let(:merge_request) { MergeRequest.first }
|
||||
let!(:context) { create(:issue, project: project, created_at: 2.days.ago) }
|
||||
|
||||
let!(:pipeline) do
|
||||
create(:ci_pipeline,
|
||||
|
@ -137,6 +147,7 @@ describe 'cycle analytics events' do
|
|||
|
||||
pipeline.run!
|
||||
pipeline.succeed!
|
||||
merge_merge_requests_closing_issue(user, project, context)
|
||||
end
|
||||
|
||||
it 'has the name' do
|
||||
|
@ -180,6 +191,10 @@ describe 'cycle analytics events' do
|
|||
let(:stage) { :review }
|
||||
let!(:context) { create(:issue, project: project, created_at: 2.days.ago) }
|
||||
|
||||
before do
|
||||
merge_merge_requests_closing_issue(user, project, context)
|
||||
end
|
||||
|
||||
it 'has the total time' do
|
||||
expect(events.first[:total_time]).not_to be_empty
|
||||
end
|
||||
|
|
|
@ -3,6 +3,37 @@ 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_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 }) }
|
||||
|
||||
before do
|
||||
issue_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago )
|
||||
issue_2.metrics.update!(first_added_to_board_at: 30.minutes.ago)
|
||||
issue_3.metrics.update!(first_added_to_board_at: 15.minutes.ago)
|
||||
end
|
||||
|
||||
it_behaves_like 'base stage'
|
||||
|
||||
describe '#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)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#events' do
|
||||
it 'exposes issues with metrics' do
|
||||
result = stage.events
|
||||
|
||||
expect(result.count).to eq(3)
|
||||
expect(result.map { |event| event[:title] }).to contain_exactly(issue_1.title, issue_2.title, issue_3.title)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,6 +3,37 @@ require 'lib/gitlab/cycle_analytics/shared_stage_spec'
|
|||
|
||||
describe Gitlab::CycleAnalytics::PlanStage do
|
||||
let(:stage_name) { :plan }
|
||||
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_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 }) }
|
||||
|
||||
before do
|
||||
issue_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago, first_mentioned_in_commit_at: 10.minutes.ago)
|
||||
issue_2.metrics.update!(first_added_to_board_at: 30.minutes.ago, first_mentioned_in_commit_at: 20.minutes.ago)
|
||||
issue_3.metrics.update!(first_added_to_board_at: 15.minutes.ago)
|
||||
end
|
||||
|
||||
it_behaves_like 'base stage'
|
||||
|
||||
describe '#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)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#events' do
|
||||
it 'exposes issues with metrics' do
|
||||
result = stage.events
|
||||
|
||||
expect(result.count).to eq(2)
|
||||
expect(result.map { |event| event[:title] }).to contain_exactly(issue_1.title, issue_2.title)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,6 +3,43 @@ 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!(: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 }) }
|
||||
|
||||
before do
|
||||
mr_1.metrics.update!(merged_at: 30.minutes.ago)
|
||||
mr_2.metrics.update!(merged_at: 10.minutes.ago)
|
||||
|
||||
create(:merge_requests_closing_issues, merge_request: mr_1, issue: issue_1)
|
||||
create(:merge_requests_closing_issues, merge_request: mr_2, issue: issue_2)
|
||||
create(:merge_requests_closing_issues, merge_request: mr_3, issue: issue_3)
|
||||
end
|
||||
|
||||
it_behaves_like 'base stage'
|
||||
|
||||
describe '#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)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#events' do
|
||||
it 'exposes merge requests that close issues' do
|
||||
result = stage.events
|
||||
|
||||
expect(result.count).to eq(2)
|
||||
expect(result.map { |event| event[:title] }).to contain_exactly(mr_1.title, mr_2.title)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
require 'spec_helper'
|
||||
|
||||
shared_examples 'base stage' do
|
||||
ISSUES_MEDIAN = 30.minutes.to_i
|
||||
|
||||
let(:stage) { described_class.new(project: double, options: {}) }
|
||||
|
||||
before do
|
||||
|
|
|
@ -4,5 +4,46 @@ require 'lib/gitlab/cycle_analytics/shared_stage_spec'
|
|||
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(: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 }) }
|
||||
|
||||
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)
|
||||
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).commit_id)
|
||||
|
||||
create(:merge_requests_closing_issues, merge_request: mr_1, issue: issue_1)
|
||||
create(:merge_requests_closing_issues, merge_request: mr_2, issue: issue_2)
|
||||
create(:merge_requests_closing_issues, merge_request: mr_3, issue: issue_3)
|
||||
end
|
||||
|
||||
it_behaves_like 'base stage'
|
||||
|
||||
describe '#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)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#events' do
|
||||
it 'exposes builds connected to merge request' do
|
||||
result = stage.events
|
||||
|
||||
expect(result.count).to eq(2)
|
||||
expect(result.map { |event| event[:name] }).to contain_exactly(build_1.name, build_2.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -32,10 +32,10 @@ describe 'cycle analytics events' do
|
|||
it 'lists the plan events' do
|
||||
get project_cycle_analytics_plan_path(project, format: :json)
|
||||
|
||||
first_mr_short_sha = project.merge_requests.sort_by_attribute(:created_asc).first.commits.first.short_id
|
||||
first_issue_iid = project.issues.sort_by_attribute(:created_desc).pluck(:iid).first.to_s
|
||||
|
||||
expect(json_response['events']).not_to be_empty
|
||||
expect(json_response['events'].first['short_sha']).to eq(first_mr_short_sha)
|
||||
expect(json_response['events'].first['iid']).to eq(first_issue_iid)
|
||||
end
|
||||
|
||||
it 'lists the code events' do
|
||||
|
|
Loading…
Reference in a new issue