diff --git a/spec/models/cycle_analytics/issue_spec.rb b/spec/models/cycle_analytics/issue_spec.rb index 4dc7f62af98..a176a15b2b7 100644 --- a/spec/models/cycle_analytics/issue_spec.rb +++ b/spec/models/cycle_analytics/issue_spec.rb @@ -5,33 +5,28 @@ describe 'CycleAnalytics#issue', models: true do let(:from_date) { 10.days.ago } subject { CycleAnalytics.new(project, from: from_date) } - context "when calculating the median of times between: - start: issue created_at - end: milestone first added to issue - OR - list-label first added to issue - " do - context "when a milestone is added to the issue" do - it "calculates the median of available durations" do - start_and_end_times = Array.new(5) do - start_time = Time.now - end_time = rand(1..10).days.from_now + context "when a milestone is added to the issue" do + it "calculates the median of available durations (between issue creation and milestone addition)" do + time_differences = Array.new(5) do + start_time = Time.now + end_time = rand(1..10).days.from_now - milestone = create(:milestone, project: project) - issue = Timecop.freeze(start_time) { create(:issue, project: project) } - Timecop.freeze(end_time) { issue.update(milestone: milestone) } + milestone = create(:milestone, project: project) + issue = Timecop.freeze(start_time) { create(:issue, project: project) } + Timecop.freeze(end_time) { issue.update(milestone: milestone) } - [start_time, end_time] - end - - median_start_time, median_end_time = start_and_end_times[2] - expect(subject.issue).to eq(median_end_time - median_start_time) + end_time - start_time end - end - context "when a label is added to the issue" do - it "calculates the median of available durations when the label is a list-label" do - start_and_end_times = Array.new(5) do + median_time_difference = time_differences.sort[2] + expect(subject.issue).to eq(median_time_difference) + end + end + + context "when a label is added to the issue" do + context "when the label is a list-label" do + it "calculates the median of available durations (between issue creation and label addition)" do + time_differences = Array.new(5) do start_time = Time.now end_time = rand(1..10).days.from_now @@ -39,61 +34,60 @@ describe 'CycleAnalytics#issue', models: true do issue = Timecop.freeze(start_time) { create(:issue, project: project) } Timecop.freeze(end_time) { issue.update(label_ids: [list_label.id]) } - [start_time, end_time] + end_time - start_time end - median_start_time, median_end_time = start_and_end_times[2] - expect(subject.issue).to eq(median_end_time - median_start_time) - end - - it "does not make a calculation for regular labels" do - 5.times do - regular_label = create(:label) - issue = create(:issue, project: project) - issue.update(label_ids: [regular_label.id]) - end - - expect(subject.issue).to be_nil + median_time_difference = time_differences.sort[2] + expect(subject.issue).to eq(median_time_difference) end end - context "when a milestone and list-label are both added to the issue" do - it "uses the time the milestone was added as the 'end time'" do - start_time = Time.now - milestone_add_time = rand(1..10).days.from_now - list_label_add_time = rand(1..10).days.from_now - - milestone = create(:milestone, project: project) - list_label = create(:label, lists: [create(:list)]) - issue = Timecop.freeze(start_time) { create(:issue, project: project) } - Timecop.freeze(milestone_add_time) { issue.update(milestone: milestone) } - Timecop.freeze(list_label_add_time) { issue.update(label_ids: [list_label.id]) } - - expect(subject.issue).to eq(milestone_add_time - start_time) + it "does not make a calculation for regular labels" do + 5.times do + regular_label = create(:label) + issue = create(:issue, project: project) + issue.update(label_ids: [regular_label.id]) end + expect(subject.issue).to be_nil + end + end - it "does not include issues from other projects" do - milestone = create(:milestone, project: project) - list_label = create(:label, lists: [create(:list)]) - issue = create(:issue) - issue.update(milestone: milestone) - issue.update(label_ids: [list_label.id]) + context "when a milestone and list-label are both added to the issue" do + it "calculates the median of available durations (between issue creation and milestone addition)" do + start_time = Time.now + milestone_add_time = rand(1..10).days.from_now + list_label_add_time = rand(1..10).days.from_now - expect(subject.issue).to be_nil - end + milestone = create(:milestone, project: project) + list_label = create(:label, lists: [create(:list)]) + issue = Timecop.freeze(start_time) { create(:issue, project: project) } + Timecop.freeze(milestone_add_time) { issue.update(milestone: milestone) } + Timecop.freeze(list_label_add_time) { issue.update(label_ids: [list_label.id]) } - it "excludes issues created before the 'from' date" do - before_from_date = from_date - 5.days + expect(subject.issue).to eq(milestone_add_time - start_time) + end - milestone = create(:milestone, project: project) - list_label = create(:label, lists: [create(:list)]) - issue = Timecop.freeze(before_from_date) { create(:issue, project: project)} - issue.update(milestone: milestone) - issue.update(label_ids: [list_label.id]) + it "does not include issues from other projects" do + milestone = create(:milestone, project: project) + list_label = create(:label, lists: [create(:list)]) + issue = create(:issue) + issue.update(milestone: milestone) + issue.update(label_ids: [list_label.id]) - expect(subject.issue).to be_nil - end + expect(subject.issue).to be_nil + end + + it "excludes issues created before the 'from' date" do + before_from_date = from_date - 5.days + + milestone = create(:milestone, project: project) + list_label = create(:label, lists: [create(:list)]) + issue = Timecop.freeze(before_from_date) { create(:issue, project: project)} + issue.update(milestone: milestone) + issue.update(label_ids: [list_label.id]) + + expect(subject.issue).to be_nil end end end diff --git a/spec/models/cycle_analytics/plan_spec.rb b/spec/models/cycle_analytics/plan_spec.rb new file mode 100644 index 00000000000..e3eeb0e505c --- /dev/null +++ b/spec/models/cycle_analytics/plan_spec.rb @@ -0,0 +1,131 @@ +require 'spec_helper' + +describe 'CycleAnalytics#plan', feature: true do + let(:project) { create(:project) } + let(:from_date) { 10.days.ago } + let(:user) { create(:user, :admin) } + subject { CycleAnalytics.new(project, from: from_date) } + + def create_commit_referencing_issue(issue, time: Time.now) + sha = Timecop.freeze(time) { project.repository.commit_file(user, FFaker::Product.brand, "content", "Commit for ##{issue.iid}", "master", false) } + commit = project.repository.commit(sha) + commit.create_cross_references! + end + + context "when a milestone is added to the issue" do + context "when the issue is mentioned in a commit" do + it "calculates the median of available durations between the two" do + time_differences = Array.new(5) do + start_time = Time.now + end_time = rand(1..10).days.from_now + + milestone = create(:milestone, project: project) + issue = create(:issue, project: project) + + Timecop.freeze(start_time) { issue.update(milestone: milestone) } + create_commit_referencing_issue(issue, time: end_time) + + end_time - start_time + end + + median_time_difference = time_differences.sort[2] + + # Use `be_within` to account for time lost between Rails invoking CLI git + # and the commit being created, which Timecop can't freeze. + expect(subject.plan).to be_within(2).of(median_time_difference) + end + end + end + + context "when a label is added to the issue" do + context "when the issue is mentioned in a commit" do + context "when the label is a list-label" do + it "calculates the median of available durations between the two" do + time_differences = Array.new(5) do + start_time = Time.now + end_time = rand(1..10).days.from_now + + issue = create(:issue, project: project) + list_label = create(:label, lists: [create(:list)]) + + Timecop.freeze(start_time) { issue.update(label_ids: [list_label.id]) } + create_commit_referencing_issue(issue, time: end_time) + + end_time - start_time + end + + median_time_difference = time_differences.sort[2] + + # Use `be_within` to account for time lost between Rails invoking CLI git + # and the commit being created, which Timecop can't freeze. + expect(subject.plan).to be_within(2).of(median_time_difference) + end + end + + it "does not make a calculation for regular labels" do + 5.times do + regular_label = create(:label) + issue = create(:issue, project: project) + issue.update(label_ids: [regular_label.id]) + + create_commit_referencing_issue(issue) + end + + expect(subject.plan).to be_nil + end + end + end + + context "when a milestone and list-label are both added to the issue" do + context "when the issue is mentioned in a commit" do + it "calculates the median of available durations between the two (using milestone addition as the 'start_time')" do + time_differences = Array.new(5) do + label_addition_time = Time.now + milestone_addition_time = rand(2..12).hours.from_now + end_time = rand(1..10).days.from_now + + issue = create(:issue, project: project) + milestone = create(:milestone, project: project) + list_label = create(:label, lists: [create(:list)]) + + Timecop.freeze(label_addition_time) { issue.update(label_ids: [list_label.id]) } + Timecop.freeze(milestone_addition_time) { issue.update(milestone: milestone) } + create_commit_referencing_issue(issue, time: end_time) + + end_time - milestone_addition_time + end + + median_time_difference = time_differences.sort[2] + + # Use `be_within` to account for time lost between Rails invoking CLI git + # and the commit being created, which Timecop can't freeze. + expect(subject.plan).to be_within(2).of(median_time_difference) + end + + it "does not include issues from other projects" do + other_project = create(:project) + + list_label = create(:label, lists: [create(:list)]) + issue = create(:issue, project: other_project) + issue.update(milestone: create(:milestone)) + issue.update(label_ids: [list_label.id]) + create_commit_referencing_issue(issue) + + expect(subject.issue).to be_nil + end + + it "excludes issues created before the 'from' date" do + before_from_date = from_date - 5.days + + milestone = create(:milestone, project: project) + list_label = create(:label, lists: [create(:list)]) + issue = Timecop.freeze(before_from_date) { create(:issue, project: project)} + issue.update(milestone: milestone) + issue.update(label_ids: [list_label.id]) + create_commit_referencing_issue(issue) + + expect(subject.issue).to be_nil + end + end + end +end