Commit ce79c6e6 authored by Erick Bajao's avatar Erick Bajao

Start tracking project ci feature usages

This starts tracking project CI feature usage
for code coverage through the new table project_ci_feature_usages.
This new table will be used to avoid directly joining to CI tables.

Changelog: added
parent bdefcb44
# frozen_string_literal: true
module Projects
class CiFeatureUsage < ApplicationRecord
self.table_name = 'project_ci_feature_usages'
belongs_to :project
validates :project, :feature, presence: true
enum feature: {
code_coverage: 1,
security_report: 2
}
def self.insert_usage(project_id:, feature:, default_branch:)
insert(
{
project_id: project_id,
feature: feature,
default_branch: default_branch
},
unique_by: 'index_project_ci_feature_usages_unique_columns'
)
end
end
end
......@@ -3,7 +3,13 @@
module Ci
class DailyBuildGroupReportResultService
def execute(pipeline)
DailyBuildGroupReportResult.upsert_reports(coverage_reports(pipeline))
if DailyBuildGroupReportResult.upsert_reports(coverage_reports(pipeline))
Projects::CiFeatureUsage.insert_usage(
project_id: pipeline.project_id,
feature: :code_coverage,
default_branch: pipeline.default_branch?
)
end
end
private
......
# frozen_string_literal: true
class CreateProjectCiFeatureUsages < ActiveRecord::Migration[6.1]
def change
create_table :project_ci_feature_usages do |t|
t.references :project, index: false, foreign_key: { on_delete: :cascade }, null: false
t.integer :feature, null: false, limit: 2
t.boolean :default_branch, default: false, null: false
t.index [:project_id, :feature, :default_branch], unique: true, name: 'index_project_ci_feature_usages_unique_columns'
end
end
end
7c62c47ebad110a343c1f9834ae34bd0fa2bad763025da06f911e127a7380542
\ No newline at end of file
......@@ -16933,6 +16933,22 @@ CREATE SEQUENCE project_ci_cd_settings_id_seq
ALTER SEQUENCE project_ci_cd_settings_id_seq OWNED BY project_ci_cd_settings.id;
CREATE TABLE project_ci_feature_usages (
id bigint NOT NULL,
project_id bigint NOT NULL,
feature smallint NOT NULL,
default_branch boolean DEFAULT false NOT NULL
);
CREATE SEQUENCE project_ci_feature_usages_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE project_ci_feature_usages_id_seq OWNED BY project_ci_feature_usages.id;
CREATE TABLE project_compliance_framework_settings (
project_id bigint NOT NULL,
framework_id bigint,
......@@ -20620,6 +20636,8 @@ ALTER TABLE ONLY project_auto_devops ALTER COLUMN id SET DEFAULT nextval('projec
ALTER TABLE ONLY project_ci_cd_settings ALTER COLUMN id SET DEFAULT nextval('project_ci_cd_settings_id_seq'::regclass);
ALTER TABLE ONLY project_ci_feature_usages ALTER COLUMN id SET DEFAULT nextval('project_ci_feature_usages_id_seq'::regclass);
ALTER TABLE ONLY project_compliance_framework_settings ALTER COLUMN project_id SET DEFAULT nextval('project_compliance_framework_settings_project_id_seq'::regclass);
ALTER TABLE ONLY project_custom_attributes ALTER COLUMN id SET DEFAULT nextval('project_custom_attributes_id_seq'::regclass);
......@@ -22208,6 +22226,9 @@ ALTER TABLE ONLY project_auto_devops
ALTER TABLE ONLY project_ci_cd_settings
ADD CONSTRAINT project_ci_cd_settings_pkey PRIMARY KEY (id);
ALTER TABLE ONLY project_ci_feature_usages
ADD CONSTRAINT project_ci_feature_usages_pkey PRIMARY KEY (id);
ALTER TABLE ONLY project_compliance_framework_settings
ADD CONSTRAINT project_compliance_framework_settings_pkey PRIMARY KEY (project_id);
......@@ -24772,6 +24793,8 @@ CREATE UNIQUE INDEX index_project_auto_devops_on_project_id ON project_auto_devo
CREATE UNIQUE INDEX index_project_ci_cd_settings_on_project_id ON project_ci_cd_settings USING btree (project_id);
CREATE UNIQUE INDEX index_project_ci_feature_usages_unique_columns ON project_ci_feature_usages USING btree (project_id, feature, default_branch);
CREATE INDEX index_project_compliance_framework_settings_on_framework_id ON project_compliance_framework_settings USING btree (framework_id);
CREATE INDEX index_project_compliance_framework_settings_on_project_id ON project_compliance_framework_settings USING btree (project_id);
......@@ -27013,6 +27036,9 @@ ALTER TABLE ONLY epic_user_mentions
ALTER TABLE ONLY approver_groups
ADD CONSTRAINT fk_rails_1cdcbd7723 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY project_ci_feature_usages
ADD CONSTRAINT fk_rails_1deedbf64b FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY packages_tags
ADD CONSTRAINT fk_rails_1dfc868911 FOREIGN KEY (package_id) REFERENCES packages_packages(id) ON DELETE CASCADE;
# frozen_string_literal: true
FactoryBot.define do
factory :project_ci_feature_usage, class: 'Projects::CiFeatureUsage' do
project factory: :project
feature { :code_coverage } # rubocop: disable RSpec/EmptyExampleGroup
default_branch { false }
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Projects::CiFeatureUsage, type: :model do
describe 'associations' do
it { is_expected.to belong_to(:project) }
end
it_behaves_like 'having unique enum values'
describe 'validations' do
it { is_expected.to validate_presence_of(:project) }
it { is_expected.to validate_presence_of(:feature) }
end
describe '.insert_usage' do
let_it_be(:project) { create(:project) }
context 'when data is not a duplicate' do
it 'creates a new record' do
expect { described_class.insert_usage(project_id: project.id, default_branch: false, feature: :code_coverage) }
.to change { described_class.count }
expect(described_class.first).to have_attributes(
project_id: project.id,
default_branch: false,
feature: 'code_coverage'
)
end
end
context 'when data is a duplicate' do
before do
create(:project_ci_feature_usage, project: project, default_branch: false, feature: :code_coverage)
end
it 'does not create a new record' do
expect { described_class.insert_usage(project_id: project.id, default_branch: false, feature: :code_coverage) }
.not_to change { described_class.count }
end
end
end
end
......@@ -41,6 +41,17 @@ RSpec.describe Ci::DailyBuildGroupReportResultService, '#execute' do
expect(Ci::DailyBuildGroupReportResult.find_by(group_name: 'extra')).to be_nil
end
it 'creates a project_ci_feature_usage record for the pipeline project' do
described_class.new.execute(pipeline)
expect(Projects::CiFeatureUsage.count).to eq(1)
expect(Projects::CiFeatureUsage.first).to have_attributes(
project_id: pipeline.project.id,
feature: 'code_coverage',
default_branch: false
)
end
context 'when there are multiple builds with the same group name that report coverage' do
let!(:test_job_1) { create(:ci_build, pipeline: pipeline, name: 'test 1/2', coverage: 70) }
let!(:test_job_2) { create(:ci_build, pipeline: pipeline, name: 'test 2/2', coverage: 80) }
......@@ -99,6 +110,16 @@ RSpec.describe Ci::DailyBuildGroupReportResultService, '#execute' do
data: { 'coverage' => new_karma_job.coverage }
)
end
it 'does not create a new project_ci_feature_usage record for the pipeline project' do
expect { described_class.new.execute(pipeline) }.not_to change { Projects::CiFeatureUsage.count }
expect(Projects::CiFeatureUsage.first).to have_attributes(
project_id: pipeline.project.id,
feature: 'code_coverage',
default_branch: false
)
end
end
context 'when the ID of the pipeline is older than the last_pipeline_id' do
......@@ -161,6 +182,8 @@ RSpec.describe Ci::DailyBuildGroupReportResultService, '#execute' do
it 'does nothing' do
expect { described_class.new.execute(new_pipeline) }.not_to raise_error
expect(Ci::DailyBuildGroupReportResult.count).to eq(0)
expect(Projects::CiFeatureUsage.count).to eq(0)
end
end
......@@ -178,6 +201,17 @@ RSpec.describe Ci::DailyBuildGroupReportResultService, '#execute' do
expect(coverage.default_branch).to be_truthy
end
end
it 'creates a project_ci_feature_usage record for the pipeline project for default branch' do
described_class.new.execute(pipeline)
expect(Projects::CiFeatureUsage.count).to eq(1)
expect(Projects::CiFeatureUsage.first).to have_attributes(
project_id: pipeline.project.id,
feature: 'code_coverage',
default_branch: true
)
end
end
context 'when pipeline ref_path is not the project default branch' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment