Commit 85c6168f authored by Bob Van Landuyt's avatar Bob Van Landuyt

Merge branch '32773-include-test-results-within-release-evidence-4' into 'master'

Move release evidence generation to service

See merge request gitlab-org/gitlab!32927
parents 1a651214 f9d1e485
...@@ -6,8 +6,6 @@ class Releases::Evidence < ApplicationRecord ...@@ -6,8 +6,6 @@ class Releases::Evidence < ApplicationRecord
belongs_to :release, inverse_of: :evidences belongs_to :release, inverse_of: :evidences
before_validation :generate_summary_and_sha
default_scope { order(created_at: :asc) } default_scope { order(created_at: :asc) }
sha_attribute :summary_sha sha_attribute :summary_sha
...@@ -31,14 +29,4 @@ class Releases::Evidence < ApplicationRecord ...@@ -31,14 +29,4 @@ class Releases::Evidence < ApplicationRecord
safe_summary safe_summary
end end
private
def generate_summary_and_sha
summary = Evidences::EvidenceSerializer.new.represent(self) # rubocop: disable CodeReuse/Serializer
return unless summary
self.summary = summary
self.summary_sha = Gitlab::CryptoHelper.sha256(summary)
end
end end
# frozen_string_literal: true
module Releases
class CreateEvidenceService
def initialize(release)
@release = release
end
def execute
evidence = release.evidences.build
summary = Evidences::EvidenceSerializer.new.represent(evidence) # rubocop: disable CodeReuse/Serializer
evidence.summary = summary
# TODO: fix the sha generating https://gitlab.com/gitlab-org/gitlab/-/issues/209000
evidence.summary_sha = Gitlab::CryptoHelper.sha256(summary)
evidence.save!
end
private
attr_reader :release
end
end
...@@ -10,6 +10,6 @@ class CreateEvidenceWorker # rubocop:disable Scalability/IdempotentWorker ...@@ -10,6 +10,6 @@ class CreateEvidenceWorker # rubocop:disable Scalability/IdempotentWorker
release = Release.find_by_id(release_id) release = Release.find_by_id(release_id)
return unless release return unless release
Releases::Evidence.create!(release: release) ::Releases::CreateEvidenceService.new(release).execute
end end
end end
...@@ -31,8 +31,8 @@ describe Projects::Releases::EvidencesController do ...@@ -31,8 +31,8 @@ describe Projects::Releases::EvidencesController do
end end
describe 'GET #show' do describe 'GET #show' do
let_it_be(:tag_name) { "v1.1.0-evidence" } let(:tag_name) { "v1.1.0-evidence" }
let!(:release) { create(:release, :with_evidence, project: project, tag: tag_name) } let!(:release) { create(:release, project: project, tag: tag_name) }
let(:evidence) { release.evidences.first } let(:evidence) { release.evidences.first }
let(:tag) { CGI.escape(release.tag) } let(:tag) { CGI.escape(release.tag) }
let(:format) { :json } let(:format) { :json }
...@@ -48,6 +48,8 @@ describe Projects::Releases::EvidencesController do ...@@ -48,6 +48,8 @@ describe Projects::Releases::EvidencesController do
end end
before do before do
::Releases::CreateEvidenceService.new(release).execute
sign_in(user) sign_in(user)
end end
...@@ -84,14 +86,9 @@ describe Projects::Releases::EvidencesController do ...@@ -84,14 +86,9 @@ describe Projects::Releases::EvidencesController do
end end
context 'when release is associated to a milestone which includes an issue' do context 'when release is associated to a milestone which includes an issue' do
let_it_be(:project) { create(:project, :repository, :public) } let(:issue) { create(:issue, project: project) }
let_it_be(:issue) { create(:issue, project: project) } let(:milestone) { create(:milestone, project: project, issues: [issue]) }
let_it_be(:milestone) { create(:milestone, project: project, issues: [issue]) } let(:release) { create(:release, project: project, tag: tag_name, milestones: [milestone]) }
let_it_be(:release) { create(:release, project: project, tag: tag_name, milestones: [milestone]) }
before do
create(:evidence, release: release)
end
shared_examples_for 'does not show the issue in evidence' do shared_examples_for 'does not show the issue in evidence' do
it do it do
...@@ -111,7 +108,9 @@ describe Projects::Releases::EvidencesController do ...@@ -111,7 +108,9 @@ describe Projects::Releases::EvidencesController do
end end
end end
shared_examples_for 'safely expose evidence' do context 'when user is non-project member' do
let(:user) { create(:user) }
it_behaves_like 'does not show the issue in evidence' it_behaves_like 'does not show the issue in evidence'
context 'when the issue is confidential' do context 'when the issue is confidential' do
...@@ -127,28 +126,50 @@ describe Projects::Releases::EvidencesController do ...@@ -127,28 +126,50 @@ describe Projects::Releases::EvidencesController do
end end
context 'when project is private' do context 'when project is private' do
let!(:project) { create(:project, :repository, :private) } let(:project) { create(:project, :repository, :private) }
it_behaves_like 'evidence not found' it_behaves_like 'evidence not found'
end end
context 'when project restricts the visibility of issues to project members only' do context 'when project restricts the visibility of issues to project members only' do
let!(:project) { create(:project, :repository, :issues_private) } let(:project) { create(:project, :repository, :issues_private) }
it_behaves_like 'evidence not found' it_behaves_like 'evidence not found'
end end
end end
context 'when user is non-project member' do
let(:user) { create(:user) }
it_behaves_like 'safely expose evidence'
end
context 'when user is auditor', if: Gitlab.ee? do context 'when user is auditor', if: Gitlab.ee? do
let(:user) { create(:user, :auditor) } let(:user) { create(:user, :auditor) }
it_behaves_like 'safely expose evidence' it_behaves_like 'does not show the issue in evidence'
context 'when the issue is confidential' do
let(:issue) { create(:issue, :confidential, project: project) }
it_behaves_like 'does not show the issue in evidence'
end
context 'when the user is the author of the confidential issue' do
let(:issue) { create(:issue, :confidential, project: project, author: user) }
it_behaves_like 'does not show the issue in evidence'
end
context 'when project is private' do
let(:project) { create(:project, :repository, :private) }
it 'returns evidence ' do
subject
expect(json_response).to eq(evidence.summary)
end
end
context 'when project restricts the visibility of issues to project members only' do
let(:project) { create(:project, :repository, :issues_private) }
it_behaves_like 'evidence not found'
end
end end
context 'when external authorization control is enabled' do context 'when external authorization control is enabled' do
......
...@@ -80,7 +80,7 @@ describe 'User views releases', :js do ...@@ -80,7 +80,7 @@ describe 'User views releases', :js do
context 'with a tag containing a slash' do context 'with a tag containing a slash' do
it 'sees the release' do it 'sees the release' do
release = create :release, :with_evidence, project: project, tag: 'debian/2.4.0-1' release = create :release, project: project, tag: 'debian/2.4.0-1'
visit project_releases_path(project) visit project_releases_path(project)
expect(page).to have_content(release.name) expect(page).to have_content(release.name)
......
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
"id", "id",
"title", "title",
"description", "description",
"author",
"state", "state",
"iid", "iid",
"confidential", "confidential",
......
...@@ -7,7 +7,8 @@ ...@@ -7,7 +7,8 @@
"state", "state",
"iid", "iid",
"created_at", "created_at",
"due_date" "due_date",
"issues"
], ],
"properties": { "properties": {
"id": { "type": "integer" }, "id": { "type": "integer" },
...@@ -16,7 +17,11 @@ ...@@ -16,7 +17,11 @@
"state": { "type": "string" }, "state": { "type": "string" },
"iid": { "type": "integer" }, "iid": { "type": "integer" },
"created_at": { "type": "date" }, "created_at": { "type": "date" },
"due_date": { "type": ["date", "null"] } "due_date": { "type": ["date", "null"] },
"issues": {
"type": "array",
"items": { "$ref": "issue.json" }
}
}, },
"additionalProperties": false "additionalProperties": false
} }
...@@ -4,11 +4,15 @@ require 'spec_helper' ...@@ -4,11 +4,15 @@ require 'spec_helper'
describe API::Entities::Release do describe API::Entities::Release do
let_it_be(:project) { create(:project) } let_it_be(:project) { create(:project) }
let_it_be(:release) { create(:release, :with_evidence, project: project) } let(:release) { create(:release, project: project) }
let(:evidence) { release.evidences.first } let(:evidence) { release.evidences.first }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:entity) { described_class.new(release, current_user: user).as_json } let(:entity) { described_class.new(release, current_user: user).as_json }
before do
::Releases::CreateEvidenceService.new(release).execute
end
describe 'evidences' do describe 'evidences' do
context 'when the current user can download code' do context 'when the current user can download code' do
let(:entity_evidence) { entity[:evidences].first } let(:entity_evidence) { entity[:evidences].first }
......
...@@ -94,14 +94,6 @@ RSpec.describe Release do ...@@ -94,14 +94,6 @@ RSpec.describe Release do
describe 'evidence' do describe 'evidence' do
let(:release_with_evidence) { create(:release, :with_evidence, project: project) } let(:release_with_evidence) { create(:release, :with_evidence, project: project) }
describe '#create_evidence!' do
context 'when a release is created' do
it 'creates one Evidence object too' do
expect { release_with_evidence }.to change(Releases::Evidence, :count).by(1)
end
end
end
context 'when a release is deleted' do context 'when a release is deleted' do
it 'also deletes the associated evidence' do it 'also deletes the associated evidence' do
release_with_evidence release_with_evidence
......
...@@ -5,83 +5,21 @@ require 'spec_helper' ...@@ -5,83 +5,21 @@ require 'spec_helper'
describe Releases::Evidence do describe Releases::Evidence do
let_it_be(:project) { create(:project) } let_it_be(:project) { create(:project) }
let(:release) { create(:release, project: project) } let(:release) { create(:release, project: project) }
let(:schema_file) { 'evidences/evidence' }
let(:summary_json) { described_class.last.summary.to_json }
describe 'associations' do describe 'associations' do
it { is_expected.to belong_to(:release) } it { is_expected.to belong_to(:release) }
end end
describe 'summary_sha' do it 'filters out issues from summary json' do
it 'returns nil if summary is nil' do milestone = create(:milestone, project: project, due_date: nil)
expect(build(:evidence, summary: nil).summary_sha).to be_nil issue = create(:issue, project: project, description: nil, state: 'closed')
end milestone.issues << issue
end release.milestones << milestone
describe '#generate_summary_and_sha' do
before do
described_class.create!(release: release)
end
context 'when a release name is not provided' do
let(:release) { create(:release, project: project, name: nil) }
it 'creates a valid JSON object' do
expect(release.name).to eq(release.tag)
expect(summary_json).to match_schema(schema_file)
end
end
context 'when a release is associated to a milestone' do
let(:milestone) { create(:milestone, project: project) }
let(:release) { create(:release, project: project, milestones: [milestone]) }
context 'when a milestone has no issue associated with it' do
it 'creates a valid JSON object' do
expect(milestone.issues).to be_empty
expect(summary_json).to match_schema(schema_file)
end
end
context 'when a milestone has no description' do
let(:milestone) { create(:milestone, project: project, description: nil) }
it 'creates a valid JSON object' do
expect(milestone.description).to be_nil
expect(summary_json).to match_schema(schema_file)
end
end
context 'when a milestone has no due_date' do
let(:milestone) { create(:milestone, project: project, due_date: nil) }
it 'creates a valid JSON object' do
expect(milestone.due_date).to be_nil
expect(summary_json).to match_schema(schema_file)
end
end
context 'when a milestone has an issue' do
context 'when the issue has no description' do
let(:issue) { create(:issue, project: project, description: nil, state: 'closed') }
before do
milestone.issues << issue
end
it 'creates a valid JSON object' do ::Releases::CreateEvidenceService.new(release).execute
expect(milestone.issues.first.description).to be_nil evidence = release.evidences.last
expect(summary_json).to match_schema(schema_file)
end
end
end
end
context 'when a release is not associated to any milestone' do expect(evidence.read_attribute(:summary)["release"]["milestones"].first["issues"].first["title"]).to be_present
it 'creates a valid JSON object' do expect(evidence.summary["release"]["milestones"].first["issues"]).to be_nil
expect(release.milestones).to be_empty
expect(summary_json).to match_schema(schema_file)
end
end
end end
end end
...@@ -3,12 +3,66 @@ ...@@ -3,12 +3,66 @@
require 'spec_helper' require 'spec_helper'
describe Evidences::EvidenceEntity do describe Evidences::EvidenceEntity do
let(:evidence) { build(:evidence) } let_it_be(:project) { create(:project) }
let(:entity) { described_class.new(evidence) } let(:release) { create(:release, project: project) }
let(:evidence) { build(:evidence, release: release) }
let(:schema_file) { 'evidences/evidence' }
subject { entity.as_json } subject { described_class.new(evidence).as_json }
it 'exposes the expected fields' do it 'exposes the expected fields' do
expect(subject.keys).to contain_exactly(:release) expect(subject.keys).to contain_exactly(:release)
end end
context 'when a release is associated to a milestone' do
let(:milestone) { create(:milestone, project: project) }
let(:release) { create(:release, project: project, milestones: [milestone]) }
context 'when a milestone has no issue associated with it' do
it 'creates a valid JSON object' do
expect(subject[:release][:milestones].first[:issues]).to be_empty
expect(subject.to_json).to match_schema(schema_file)
end
end
context 'when a milestone has no description' do
let(:milestone) { create(:milestone, project: project, description: nil) }
it 'creates a valid JSON object' do
expect(subject[:release][:milestones].first[:description]).to be_nil
expect(subject.to_json).to match_schema(schema_file)
end
end
context 'when a milestone has no due_date' do
let(:milestone) { create(:milestone, project: project, due_date: nil) }
it 'creates a valid JSON object' do
expect(subject[:release][:milestones].first[:due_date]).to be_nil
expect(subject.to_json).to match_schema(schema_file)
end
end
context 'when a milestone has an issue' do
context 'when the issue has no description' do
let(:issue) { create(:issue, project: project, description: nil, state: 'closed') }
before do
milestone.issues << issue
end
it 'creates a valid JSON object' do
expect(subject[:release][:milestones].first[:issues].first[:title]).to be_present
expect(subject.to_json).to match_schema(schema_file)
end
end
end
end
context 'when a release is not associated to any milestone' do
it 'creates a valid JSON object' do
expect(subject[:release][:milestones]).to be_empty
expect(subject.to_json).to match_schema(schema_file)
end
end
end end
# frozen_string_literal: true
require 'spec_helper'
describe Releases::CreateEvidenceService do
let_it_be(:project) { create(:project) }
let(:release) { create(:release, project: project) }
let(:service) { described_class.new(release) }
it 'creates evidence' do
expect { service.execute }.to change { release.reload.evidences.count }.by(1)
end
it 'saves evidence summary' do
service.execute
evidence = Releases::Evidence.last
expect(release.tag).not_to be_nil
expect(evidence.summary["release"]["tag_name"]).to eq(release.tag)
end
it 'saves sha' do
service.execute
evidence = Releases::Evidence.last
expect(evidence.summary_sha).not_to be_nil
end
end
...@@ -3,9 +3,13 @@ ...@@ -3,9 +3,13 @@
require 'spec_helper' require 'spec_helper'
describe CreateEvidenceWorker do describe CreateEvidenceWorker do
let!(:release) { create(:release) } let(:release) { create(:release) }
it 'creates a new Evidence record' do it 'creates a new Evidence record' do
expect_next_instance_of(::Releases::CreateEvidenceService, release) do |service|
expect(service).to receive(:execute).and_call_original
end
expect { described_class.new.perform(release.id) }.to change(Releases::Evidence, :count).by(1) expect { described_class.new.perform(release.id) }.to change(Releases::Evidence, :count).by(1)
end end
end end
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