Commit cbe21937 authored by Maxime Orefice's avatar Maxime Orefice Committed by Shinya Maeda

Expose show tests endpoint

This endpoint will be used to fetch individual test suite
data. This is part of the work to improve the performance
of our junit feature.
parent 54828d89
...@@ -2,35 +2,58 @@ ...@@ -2,35 +2,58 @@
module Projects module Projects
module Pipelines module Pipelines
class TestsController < Projects::ApplicationController class TestsController < Projects::Pipelines::ApplicationController
before_action :pipeline
before_action :authorize_read_pipeline!
before_action :authorize_read_build!
before_action :validate_feature_flag! before_action :validate_feature_flag!
before_action :authorize_read_build!
before_action :builds, only: [:show]
def summary def summary
respond_to do |format| respond_to do |format|
format.json do format.json do
render json: TestReportSerializer render json: TestReportSummarySerializer
.new(project: project, current_user: @current_user) .new(project: project, current_user: @current_user)
.represent(pipeline.test_report_summary) .represent(pipeline.test_report_summary)
end end
end end
end end
def show
respond_to do |format|
format.json do
render json: TestSuiteSerializer
.new(project: project, current_user: @current_user)
.represent(test_suite, details: true)
end
end
end
private private
def validate_feature_flag! def validate_feature_flag!
render_404 unless Feature.enabled?(:build_report_summary, project) render_404 unless Feature.enabled?(:build_report_summary, project)
end end
def pipeline # rubocop: disable CodeReuse/ActiveRecord
project.all_pipelines.find(tests_params[:id]) def builds
pipeline.latest_builds.where(id: build_params)
end
def build_params
return [] unless params[:build_ids]
params[:build_ids].split(",")
end end
def tests_params def test_suite
params.permit(:id) if builds.present?
builds.map do |build|
build.collect_test_reports!(Gitlab::Ci::Reports::TestReports.new)
end.sum
else
render_404
end
end end
# rubocop: enable CodeReuse/ActiveRecord
end end
end end
end end
# frozen_string_literal: true
class TestReportSummaryEntity < TestReportEntity
expose :test_suites, using: TestSuiteSummaryEntity do |summary|
summary.test_suites.values
end
end
# frozen_string_literal: true
class TestReportSummarySerializer < BaseSerializer
entity TestReportSummaryEntity
end
# frozen_string_literal: true
class TestSuiteSerializer < BaseSerializer
entity TestSuiteEntity
end
# frozen_string_literal: true
class TestSuiteSummaryEntity < TestSuiteEntity
expose :build_ids do |summary|
summary.build_ids
end
end
...@@ -26,11 +26,11 @@ resources :pipelines, only: [:index, :new, :create, :show, :destroy] do ...@@ -26,11 +26,11 @@ resources :pipelines, only: [:index, :new, :create, :show, :destroy] do
resources :stages, only: [], param: :name do resources :stages, only: [], param: :name do
post :play_manual post :play_manual
end end
end
resources :tests, only: [], controller: 'pipelines/tests' do resources :tests, only: [:show], param: :suite_name, controller: 'pipelines/tests' do
collection do collection do
get :summary get :summary
end
end end
end end
end end
......
...@@ -4,9 +4,9 @@ module Gitlab ...@@ -4,9 +4,9 @@ module Gitlab
module Ci module Ci
module Reports module Reports
class TestSuite class TestSuite
attr_reader :name attr_accessor :name
attr_reader :test_cases attr_accessor :test_cases
attr_reader :total_time attr_accessor :total_time
attr_reader :suite_error attr_reader :suite_error
def initialize(name = nil) def initialize(name = nil)
...@@ -70,6 +70,14 @@ module Gitlab ...@@ -70,6 +70,14 @@ module Gitlab
@suite_error = msg @suite_error = msg
end end
def +(other)
self.class.new.tap do |test_suite|
test_suite.name = self.name
test_suite.test_cases = self.test_cases.deep_merge(other.test_cases)
test_suite.total_time = self.total_time + other.total_time
end
end
private private
def existing_key?(test_case) def existing_key?(test_case)
......
...@@ -15,6 +15,10 @@ module Gitlab ...@@ -15,6 +15,10 @@ module Gitlab
end end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def build_ids
results.pluck(:build_id)
end
def total_time def total_time
@total_time ||= results.sum(&:tests_duration) @total_time ||= results.sum(&:tests_duration)
end end
......
...@@ -46,12 +46,66 @@ RSpec.describe Projects::Pipelines::TestsController do ...@@ -46,12 +46,66 @@ RSpec.describe Projects::Pipelines::TestsController do
end end
end end
describe 'GET #show.json' do
context 'when pipeline has build report results' do
let(:pipeline) { create(:ci_pipeline, :with_report_results, project: project) }
let(:suite_name) { 'test' }
let(:build_ids) { pipeline.latest_builds.pluck(:id) }
it 'renders test suite data' do
get_tests_show_json(build_ids)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['name']).to eq('test')
end
end
context 'when pipeline does not have build report results' do
let(:pipeline) { create(:ci_empty_pipeline) }
let(:suite_name) { 'test' }
it 'renders 404' do
get_tests_show_json([])
expect(response).to have_gitlab_http_status(:not_found)
expect(response.body).to be_empty
end
end
context 'when feature is disabled' do
let(:suite_name) { 'test' }
before do
stub_feature_flags(build_report_summary: false)
end
it 'returns 404' do
get_tests_show_json([])
expect(response).to have_gitlab_http_status(:not_found)
expect(response.body).to be_empty
end
end
end
def get_tests_summary_json def get_tests_summary_json
get :summary, get :summary,
params: { params: {
namespace_id: project.namespace, namespace_id: project.namespace,
project_id: project, project_id: project,
id: pipeline.id pipeline_id: pipeline.id
},
format: :json
end
def get_tests_show_json(build_ids)
get :show,
params: {
namespace_id: project.namespace,
project_id: project,
pipeline_id: pipeline.id,
suite_name: suite_name,
build_ids: build_ids
}, },
format: :json format: :json
end end
......
...@@ -139,6 +139,41 @@ RSpec.describe Gitlab::Ci::Reports::TestSuite do ...@@ -139,6 +139,41 @@ RSpec.describe Gitlab::Ci::Reports::TestSuite do
end end
end end
describe '#+' do
let(:test_suite_2) { described_class.new('Rspec') }
subject { test_suite + test_suite_2 }
context 'when adding multiple suites together' do
before do
test_suite.add_test_case(test_case_success)
test_suite.add_test_case(test_case_failed)
end
it 'returns a new test suite' do
expect(subject).to be_an_instance_of(described_class)
end
it 'returns the suite name' do
expect(subject.name).to eq('Rspec')
end
it 'returns the sum for total_time' do
expect(subject.total_time).to eq(3.33)
end
it 'merges tests cases hash', :aggregate_failures do
test_suite_2.add_test_case(create_test_case_java_success)
failed_keys = test_suite.test_cases['failed'].keys
success_keys = test_suite.test_cases['success'].keys + test_suite_2.test_cases['success'].keys
expect(subject.test_cases['failed'].keys).to contain_exactly(*failed_keys)
expect(subject.test_cases['success'].keys).to contain_exactly(*success_keys)
end
end
end
Gitlab::Ci::Reports::TestCase::STATUS_TYPES.each do |status_type| Gitlab::Ci::Reports::TestCase::STATUS_TYPES.each do |status_type|
describe "##{status_type}" do describe "##{status_type}" do
subject { test_suite.public_send("#{status_type}") } subject { test_suite.public_send("#{status_type}") }
......
...@@ -17,6 +17,16 @@ RSpec.describe Gitlab::Ci::Reports::TestSuiteSummary do ...@@ -17,6 +17,16 @@ RSpec.describe Gitlab::Ci::Reports::TestSuiteSummary do
end end
end end
describe '#build_ids' do
subject { test_suite_summary.build_ids }
context 'when test suite summary has several build report results' do
it 'returns the build ids' do
expect(subject).to contain_exactly(build_report_result_1.build_id, build_report_result_2.build_id)
end
end
end
describe '#total_time' do describe '#total_time' do
subject { test_suite_summary.total_time } subject { test_suite_summary.total_time }
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe TestReportSummaryEntity do
let(:pipeline) { create(:ci_pipeline, :with_report_results) }
let(:entity) { described_class.new(pipeline.test_report_summary) }
describe '#as_json' do
subject(:as_json) { entity.as_json }
it 'contains the total time' do
expect(as_json).to include(:total_time)
end
it 'contains the counts' do
expect(as_json).to include(:total_count, :success_count, :failed_count, :skipped_count, :error_count)
end
context 'when summary has test suites' do
it 'contains the test suites' do
expect(as_json).to include(:test_suites)
expect(as_json[:test_suites].count).to eq(1)
end
it 'contains build_ids' do
expect(as_json[:test_suites].first).to include(:build_ids)
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe TestSuiteSummaryEntity do
let(:pipeline) { create(:ci_pipeline, :with_report_results) }
let(:entity) { described_class.new(pipeline.test_report_summary.total) }
describe '#as_json' do
subject(:as_json) { entity.as_json }
it 'contains the total time' do
expect(as_json).to include(:total_time)
end
it 'contains the counts' do
expect(as_json).to include(:total_count, :success_count, :failed_count, :skipped_count, :error_count)
end
it 'contains the build_ids' do
expect(as_json).to include(:build_ids)
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