diff --git a/app/controllers/projects/cycle_analytics/events_controller.rb b/app/controllers/projects/cycle_analytics/events_controller.rb index fb43356ff10..6314d9f2a9f 100644 --- a/app/controllers/projects/cycle_analytics/events_controller.rb +++ b/app/controllers/projects/cycle_analytics/events_controller.rb @@ -50,7 +50,7 @@ module Projects end def cycle_analytics - @cycle_analytics ||= ::CycleAnalytics.new(project, options(events_params)) + @cycle_analytics ||= ::CycleAnalytics::ProjectLevel.new(project, options: options(events_params)) end def events_params diff --git a/app/controllers/projects/cycle_analytics_controller.rb b/app/controllers/projects/cycle_analytics_controller.rb index 8c071496ba9..2d46a71bf99 100644 --- a/app/controllers/projects/cycle_analytics_controller.rb +++ b/app/controllers/projects/cycle_analytics_controller.rb @@ -9,7 +9,7 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController before_action :authorize_read_cycle_analytics! def show - @cycle_analytics = ::CycleAnalytics.new(@project, options(cycle_analytics_params)) + @cycle_analytics = ::CycleAnalytics::ProjectLevel.new(@project, options: options(cycle_analytics_params)) @cycle_analytics_no_data = @cycle_analytics.no_stats? diff --git a/app/models/cycle_analytics.rb b/app/models/cycle_analytics.rb deleted file mode 100644 index d0f5b6970b1..00000000000 --- a/app/models/cycle_analytics.rb +++ /dev/null @@ -1,46 +0,0 @@ -# frozen_string_literal: true - -class CycleAnalytics - STAGES = %i[issue plan code test review staging production].freeze - - def initialize(project, options) - @project = project - @options = options - end - - def all_medians_per_stage - STAGES.each_with_object({}) do |stage_name, medians_per_stage| - medians_per_stage[stage_name] = self[stage_name].median - end - end - - def summary - @summary ||= ::Gitlab::CycleAnalytics::StageSummary.new(@project, - from: @options[:from], - current_user: @options[:current_user]).data - end - - def stats - @stats ||= stats_per_stage - end - - def no_stats? - stats.all? { |hash| hash[:value].nil? } - end - - def permissions(user:) - Gitlab::CycleAnalytics::Permissions.get(user: user, project: @project) - end - - def [](stage_name) - Gitlab::CycleAnalytics::Stage[stage_name].new(project: @project, options: @options) - end - - private - - def stats_per_stage - STAGES.map do |stage_name| - self[stage_name].as_json - end - end -end diff --git a/app/models/cycle_analytics/base.rb b/app/models/cycle_analytics/base.rb new file mode 100644 index 00000000000..d7b28cd1b67 --- /dev/null +++ b/app/models/cycle_analytics/base.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module CycleAnalytics + class Base + STAGES = %i[issue plan code test review staging production].freeze + + def all_medians_by_stage + STAGES.each_with_object({}) do |stage_name, medians_per_stage| + medians_per_stage[stage_name] = self[stage_name].median + end + end + + def stats + @stats ||= STAGES.map do |stage_name| + self[stage_name].as_json + end + end + + def no_stats? + stats.all? { |hash| hash[:value].nil? } + end + + def [](stage_name) + Gitlab::CycleAnalytics::Stage[stage_name].new(project: @project, options: @options) + end + end +end diff --git a/app/models/cycle_analytics/project_level.rb b/app/models/cycle_analytics/project_level.rb new file mode 100644 index 00000000000..22631cc7d41 --- /dev/null +++ b/app/models/cycle_analytics/project_level.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module CycleAnalytics + class ProjectLevel < Base + attr_reader :project, :options + + def initialize(project, options:) + @project = project + @options = options + end + + def summary + @summary ||= ::Gitlab::CycleAnalytics::StageSummary.new(project, + from: options[:from], + current_user: options[:current_user]).data + end + + def permissions(user:) + Gitlab::CycleAnalytics::Permissions.get(user: user, project: project) + end + end +end diff --git a/changelogs/unreleased/prepare-cycle-analytics-for-group-level.yml b/changelogs/unreleased/prepare-cycle-analytics-for-group-level.yml new file mode 100644 index 00000000000..d7bfc67b208 --- /dev/null +++ b/changelogs/unreleased/prepare-cycle-analytics-for-group-level.yml @@ -0,0 +1,5 @@ +--- +title: Modify cycle analytics on project level +merge_request: 30356 +author: +type: changed diff --git a/lib/gitlab/cycle_analytics/base_event_fetcher.rb b/lib/gitlab/cycle_analytics/base_event_fetcher.rb index 304d60996a6..0cacef5b278 100644 --- a/lib/gitlab/cycle_analytics/base_event_fetcher.rb +++ b/lib/gitlab/cycle_analytics/base_event_fetcher.rb @@ -5,11 +5,11 @@ module Gitlab class BaseEventFetcher include BaseQuery - attr_reader :projections, :query, :stage, :order + attr_reader :projections, :query, :stage, :order, :project, :options MAX_EVENTS = 50 - def initialize(project:, stage:, options:) + def initialize(project: nil, stage:, options:) @project = project @stage = stage @options = options @@ -40,13 +40,13 @@ module Gitlab end def events_query - diff_fn = subtract_datetimes_diff(base_query, @options[:start_time_attrs], @options[:end_time_attrs]) + diff_fn = subtract_datetimes_diff(base_query, options[:start_time_attrs], options[:end_time_attrs]) base_query.project(extract_diff_epoch(diff_fn).as('total_time'), *projections).order(order.desc).take(MAX_EVENTS) end def default_order - [@options[:start_time_attrs]].flatten.first + [options[:start_time_attrs]].flatten.first end def serialize(_event) @@ -59,13 +59,21 @@ module Gitlab def allowed_ids @allowed_ids ||= allowed_ids_finder_class - .new(@options[:current_user], project_id: @project.id) + .new(options[:current_user], allowed_ids_source) .execute.where(id: event_result_ids).pluck(:id) end def event_result_ids event_result.map { |event| event['id'] } end + + def allowed_ids_source + { project_id: project.id } + end + + def projects + [project] + end end end end diff --git a/lib/gitlab/cycle_analytics/base_query.rb b/lib/gitlab/cycle_analytics/base_query.rb index 36231b187cd..39fc1759cfc 100644 --- a/lib/gitlab/cycle_analytics/base_query.rb +++ b/lib/gitlab/cycle_analytics/base_query.rb @@ -10,7 +10,7 @@ module Gitlab private def base_query - @base_query ||= stage_query(@project.id) # rubocop:disable Gitlab/ModuleWithInstanceVariables + @base_query ||= stage_query(projects.map(&:id)) end def stage_query(project_ids) @@ -18,7 +18,7 @@ module Gitlab .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_table[:created_at].gteq(options[:from])) # Load merge_requests query = query.join(mr_table, Arel::Nodes::OuterJoin) diff --git a/lib/gitlab/cycle_analytics/base_stage.rb b/lib/gitlab/cycle_analytics/base_stage.rb index e2d6a301734..98b86e54340 100644 --- a/lib/gitlab/cycle_analytics/base_stage.rb +++ b/lib/gitlab/cycle_analytics/base_stage.rb @@ -5,7 +5,9 @@ module Gitlab class BaseStage include BaseQuery - def initialize(project:, options:) + attr_reader :project, :options + + def initialize(project: nil, options:) @project = project @options = options end @@ -14,8 +16,8 @@ module Gitlab event_fetcher.fetch end - def as_json - AnalyticsStageSerializer.new.represent(self) + def as_json(serializer: AnalyticsStageSerializer) + serializer.new.represent(self) end def title @@ -23,21 +25,14 @@ module Gitlab end def median - BatchLoader.for(@project.id).batch(key: name) do |project_ids, loader| - cte_table = Arel::Table.new("cte_table_for_#{name}") - - # Build a `SELECT` query. We find the first of the `end_time_attrs` that isn't `NULL` (call this end_time). - # Next, we find the first of the start_time_attrs that isn't `NULL` (call this start_time). - # We compute the (end_time - start_time) interval, and give it an alias based on the current - # cycle analytics stage. - interval_query = Arel::Nodes::As.new(cte_table, - subtract_datetimes(stage_query(project_ids), start_time_attrs, end_time_attrs, name.to_s)) + return if project.nil? + BatchLoader.for(project.id).batch(key: name) do |project_ids, loader| if project_ids.one? - loader.call(@project.id, median_datetime(cte_table, interval_query, name)) + loader.call(project.id, median_query(project_ids)) else begin - median_datetimes(cte_table, interval_query, name, :project_id)&.each do |project_id, median| + median_datetimes(cte_table, interval_query(project_ids), name, :project_id)&.each do |project_id, median| loader.call(project_id, median) end rescue NotSupportedError @@ -47,20 +42,42 @@ module Gitlab end end + def median_query(project_ids) + # Build a `SELECT` query. We find the first of the `end_time_attrs` that isn't `NULL` (call this end_time). + # Next, we find the first of the start_time_attrs that isn't `NULL` (call this start_time). + # We compute the (end_time - start_time) interval, and give it an alias based on the current + # cycle analytics stage. + + median_datetime(cte_table, interval_query(project_ids), name) + end + def name raise NotImplementedError.new("Expected #{self.name} to implement name") end + def cte_table + Arel::Table.new("cte_table_for_#{name}") + end + + def interval_query(project_ids) + Arel::Nodes::As.new(cte_table, + subtract_datetimes(stage_query(project_ids), start_time_attrs, end_time_attrs, name.to_s)) + end + private def event_fetcher - @event_fetcher ||= Gitlab::CycleAnalytics::EventFetcher[name].new(project: @project, + @event_fetcher ||= Gitlab::CycleAnalytics::EventFetcher[name].new(project: project, stage: name, options: event_options) end def event_options - @options.merge(start_time_attrs: start_time_attrs, end_time_attrs: end_time_attrs) + options.merge(start_time_attrs: start_time_attrs, end_time_attrs: end_time_attrs) + end + + def projects + [project] end end end diff --git a/lib/gitlab/cycle_analytics/code_event_fetcher.rb b/lib/gitlab/cycle_analytics/code_event_fetcher.rb index 6c348f1862d..9e7ca529579 100644 --- a/lib/gitlab/cycle_analytics/code_event_fetcher.rb +++ b/lib/gitlab/cycle_analytics/code_event_fetcher.rb @@ -20,7 +20,7 @@ module Gitlab private def serialize(event) - AnalyticsMergeRequestSerializer.new(project: @project).represent(event) + AnalyticsMergeRequestSerializer.new(project: project).represent(event) end def allowed_ids_finder_class diff --git a/lib/gitlab/cycle_analytics/issue_event_fetcher.rb b/lib/gitlab/cycle_analytics/issue_event_fetcher.rb index 8a870f2e2a3..bb3520ae920 100644 --- a/lib/gitlab/cycle_analytics/issue_event_fetcher.rb +++ b/lib/gitlab/cycle_analytics/issue_event_fetcher.rb @@ -18,7 +18,7 @@ module Gitlab private def serialize(event) - AnalyticsIssueSerializer.new(project: @project).represent(event) + AnalyticsIssueSerializer.new(project: project).represent(event) end def allowed_ids_finder_class diff --git a/lib/gitlab/cycle_analytics/issue_helper.rb b/lib/gitlab/cycle_analytics/issue_helper.rb index c9266341378..ac836b8bf0f 100644 --- a/lib/gitlab/cycle_analytics/issue_helper.rb +++ b/lib/gitlab/cycle_analytics/issue_helper.rb @@ -7,7 +7,7 @@ module Gitlab 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_table[:created_at].gteq(options[:from])) .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 diff --git a/lib/gitlab/cycle_analytics/permissions.rb b/lib/gitlab/cycle_analytics/permissions.rb index afefd09b614..03ba98b4dfb 100644 --- a/lib/gitlab/cycle_analytics/permissions.rb +++ b/lib/gitlab/cycle_analytics/permissions.rb @@ -23,7 +23,7 @@ module Gitlab end def get - ::CycleAnalytics::STAGES.each do |stage| + ::CycleAnalytics::Base::STAGES.each do |stage| @stage_permission_hash[stage] = authorized_stage?(stage) end diff --git a/lib/gitlab/cycle_analytics/plan_event_fetcher.rb b/lib/gitlab/cycle_analytics/plan_event_fetcher.rb index d924f956dcd..49a6b099f34 100644 --- a/lib/gitlab/cycle_analytics/plan_event_fetcher.rb +++ b/lib/gitlab/cycle_analytics/plan_event_fetcher.rb @@ -18,7 +18,7 @@ module Gitlab private def serialize(event) - AnalyticsIssueSerializer.new(project: @project).represent(event) + AnalyticsIssueSerializer.new(project: project).represent(event) end def allowed_ids_finder_class diff --git a/lib/gitlab/cycle_analytics/plan_helper.rb b/lib/gitlab/cycle_analytics/plan_helper.rb index 30fc2ce6d40..ae578d45ad5 100644 --- a/lib/gitlab/cycle_analytics/plan_helper.rb +++ b/lib/gitlab/cycle_analytics/plan_helper.rb @@ -7,7 +7,7 @@ module Gitlab 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_table[:created_at].gteq(options[:from])) .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)) diff --git a/lib/gitlab/cycle_analytics/production_event_fetcher.rb b/lib/gitlab/cycle_analytics/production_event_fetcher.rb index 6bcbe0412a9..949119d69a0 100644 --- a/lib/gitlab/cycle_analytics/production_event_fetcher.rb +++ b/lib/gitlab/cycle_analytics/production_event_fetcher.rb @@ -18,7 +18,7 @@ module Gitlab private def serialize(event) - AnalyticsIssueSerializer.new(project: @project).represent(event) + AnalyticsIssueSerializer.new(project: project).represent(event) end def allowed_ids_finder_class diff --git a/lib/gitlab/cycle_analytics/production_helper.rb b/lib/gitlab/cycle_analytics/production_helper.rb index aff65b150fb..778757a9ede 100644 --- a/lib/gitlab/cycle_analytics/production_helper.rb +++ b/lib/gitlab/cycle_analytics/production_helper.rb @@ -6,7 +6,7 @@ module Gitlab def stage_query(project_ids) super(project_ids) .where(mr_metrics_table[:first_deployed_to_production_at] - .gteq(@options[:from])) # rubocop:disable Gitlab/ModuleWithInstanceVariables + .gteq(options[:from])) end end end diff --git a/lib/gitlab/cycle_analytics/review_event_fetcher.rb b/lib/gitlab/cycle_analytics/review_event_fetcher.rb index b6354b5ffad..d31736e755d 100644 --- a/lib/gitlab/cycle_analytics/review_event_fetcher.rb +++ b/lib/gitlab/cycle_analytics/review_event_fetcher.rb @@ -19,7 +19,7 @@ module Gitlab private def serialize(event) - AnalyticsMergeRequestSerializer.new(project: @project).represent(event) + AnalyticsMergeRequestSerializer.new(project: project).represent(event) end def allowed_ids_finder_class diff --git a/lib/gitlab/cycle_analytics/test_helper.rb b/lib/gitlab/cycle_analytics/test_helper.rb index 32fca7fa898..d9124d62c7c 100644 --- a/lib/gitlab/cycle_analytics/test_helper.rb +++ b/lib/gitlab/cycle_analytics/test_helper.rb @@ -14,7 +14,7 @@ module Gitlab private def branch - @branch ||= @options[:branch] # rubocop:disable Gitlab/ModuleWithInstanceVariables + @branch ||= options[:branch] end end end diff --git a/lib/gitlab/cycle_analytics/usage_data.rb b/lib/gitlab/cycle_analytics/usage_data.rb index 913ee373f54..644300caead 100644 --- a/lib/gitlab/cycle_analytics/usage_data.rb +++ b/lib/gitlab/cycle_analytics/usage_data.rb @@ -32,7 +32,7 @@ module Gitlab def medians_per_stage projects.each_with_object({}) do |project, hsh| - ::CycleAnalytics.new(project, options).all_medians_per_stage.each do |stage_name, median| + ::CycleAnalytics::ProjectLevel.new(project, options: options).all_medians_by_stage.each do |stage_name, median| hsh[stage_name] ||= [] hsh[stage_name] << median end diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index f8b103c0fab..5ee02650e49 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -7,7 +7,7 @@ describe 'cycle analytics events' do let!(:context) { create(:issue, project: project, created_at: 2.days.ago) } let(:events) do - CycleAnalytics.new(project, { from: from_date, current_user: user })[stage].events + CycleAnalytics::ProjectLevel.new(project, options: { from: from_date, current_user: user })[stage].events end before do diff --git a/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb b/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb index a785b17f682..8122e85a981 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::STAGES.each do |stage| + CycleAnalytics::Base::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 b22a0340015..db6e70973ae 100644 --- a/spec/models/cycle_analytics/code_spec.rb +++ b/spec/models/cycle_analytics/code_spec.rb @@ -8,7 +8,8 @@ describe 'CycleAnalytics#code' do let(:project) { create(:project, :repository) } let(:from_date) { 10.days.ago } let(:user) { create(:user, :admin) } - subject { CycleAnalytics.new(project, from: from_date) } + + subject { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) } context 'with deployment' do generate_cycle_analytics_spec( diff --git a/spec/models/cycle_analytics/issue_spec.rb b/spec/models/cycle_analytics/issue_spec.rb index 07d60be091a..7b18e24a351 100644 --- a/spec/models/cycle_analytics/issue_spec.rb +++ b/spec/models/cycle_analytics/issue_spec.rb @@ -8,7 +8,8 @@ describe 'CycleAnalytics#issue' do let(:project) { create(:project, :repository) } let(:from_date) { 10.days.ago } let(:user) { create(:user, :admin) } - subject { CycleAnalytics.new(project, from: from_date) } + + subject { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) } generate_cycle_analytics_spec( phase: :issue, diff --git a/spec/models/cycle_analytics/plan_spec.rb b/spec/models/cycle_analytics/plan_spec.rb index 3d22a284264..73fc98ba6cf 100644 --- a/spec/models/cycle_analytics/plan_spec.rb +++ b/spec/models/cycle_analytics/plan_spec.rb @@ -8,7 +8,8 @@ describe 'CycleAnalytics#plan' do let(:project) { create(:project, :repository) } let(:from_date) { 10.days.ago } let(:user) { create(:user, :admin) } - subject { CycleAnalytics.new(project, from: from_date) } + + subject { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) } generate_cycle_analytics_spec( phase: :plan, diff --git a/spec/models/cycle_analytics/production_spec.rb b/spec/models/cycle_analytics/production_spec.rb index 383727cd8f7..ddd199362d1 100644 --- a/spec/models/cycle_analytics/production_spec.rb +++ b/spec/models/cycle_analytics/production_spec.rb @@ -8,7 +8,8 @@ describe 'CycleAnalytics#production' do let(:project) { create(:project, :repository) } let(:from_date) { 10.days.ago } let(:user) { create(:user, :admin) } - subject { CycleAnalytics.new(project, from: from_date) } + + subject { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) } generate_cycle_analytics_spec( phase: :production, diff --git a/spec/models/cycle_analytics_spec.rb b/spec/models/cycle_analytics/project_level_spec.rb similarity index 83% rename from spec/models/cycle_analytics_spec.rb rename to spec/models/cycle_analytics/project_level_spec.rb index 5d8b5b573cf..77bd0bfeb9c 100644 --- a/spec/models/cycle_analytics_spec.rb +++ b/spec/models/cycle_analytics/project_level_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe CycleAnalytics do +describe CycleAnalytics::ProjectLevel do let(:project) { create(:project, :repository) } let(:from_date) { 10.days.ago } let(:user) { create(:user, :admin) } @@ -11,9 +11,9 @@ describe CycleAnalytics do 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(project, from: from_date) } + subject { described_class.new(project, options: { from: from_date }) } - describe '#all_medians_per_stage' do + describe '#all_medians_by_stage' do before do allow_any_instance_of(Gitlab::ReferenceExtractor).to receive(:issues).and_return([issue]) @@ -26,7 +26,7 @@ describe CycleAnalytics do hsh[stage_name] = subject[stage_name].median.presence end - expect(subject.all_medians_per_stage).to eq(values) + expect(subject.all_medians_by_stage).to eq(values) end end end diff --git a/spec/models/cycle_analytics/review_spec.rb b/spec/models/cycle_analytics/review_spec.rb index 1af5f9cc1f4..63c481ed465 100644 --- a/spec/models/cycle_analytics/review_spec.rb +++ b/spec/models/cycle_analytics/review_spec.rb @@ -8,7 +8,8 @@ describe 'CycleAnalytics#review' do let(:project) { create(:project, :repository) } let(:from_date) { 10.days.ago } let(:user) { create(:user, :admin) } - subject { CycleAnalytics.new(project, from: from_date) } + + subject { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) } generate_cycle_analytics_spec( phase: :review, diff --git a/spec/models/cycle_analytics/staging_spec.rb b/spec/models/cycle_analytics/staging_spec.rb index 8375944f03c..c134b97553f 100644 --- a/spec/models/cycle_analytics/staging_spec.rb +++ b/spec/models/cycle_analytics/staging_spec.rb @@ -9,7 +9,7 @@ describe 'CycleAnalytics#staging' do let(:from_date) { 10.days.ago } let(:user) { create(:user, :admin) } - subject { CycleAnalytics.new(project, from: from_date) } + subject { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) } generate_cycle_analytics_spec( phase: :staging, diff --git a/spec/models/cycle_analytics/test_spec.rb b/spec/models/cycle_analytics/test_spec.rb index b78258df564..a6ea73b2699 100644 --- a/spec/models/cycle_analytics/test_spec.rb +++ b/spec/models/cycle_analytics/test_spec.rb @@ -8,7 +8,8 @@ describe 'CycleAnalytics#test' do let(:project) { create(:project, :repository) } let(:from_date) { 10.days.ago } let(:user) { create(:user, :admin) } - subject { CycleAnalytics.new(project, from: from_date) } + + subject { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) } generate_cycle_analytics_spec( phase: :test,