Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-08-24 15:12:19 +00:00
parent 263baf70a1
commit 53ab147992
43 changed files with 406 additions and 406 deletions

View File

@ -569,6 +569,8 @@ RSpec/ImplicitSubject:
RSpec/ReceiveNever:
Enabled: false
# Already covered by `RSpec::Configuration#on_potential_false_positives = :raise`.
# See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86429
RSpec/UnspecifiedException:
Enabled: false

View File

@ -90,7 +90,6 @@ RSpec/VerifiedDoubles:
- ee/spec/lib/sidebars/groups/menus/analytics_menu_spec.rb
- ee/spec/lib/system_check/geo/geo_database_configured_check_spec.rb
- ee/spec/models/app_sec/fuzzing/api/ci_configuration_spec.rb
- ee/spec/models/ee/approvable_spec.rb
- ee/spec/models/concerns/geo/verification_state_spec.rb
- ee/spec/models/ee/ci/job_artifact_spec.rb
- ee/spec/models/ee/user_spec.rb

View File

@ -1,11 +1,8 @@
import OrbitControlsClass from 'three-orbit-controls';
import STLLoaderClass from 'three-stl-loader';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { STLLoader } from 'three/examples/jsm/loaders/STLLoader';
import * as THREE from 'three/build/three.module';
import MeshObject from './mesh_object';
const STLLoader = STLLoaderClass(THREE);
const OrbitControls = OrbitControlsClass(THREE);
export default class Renderer {
constructor(container) {
this.renderWrapper = this.render.bind(this);

View File

@ -22,7 +22,7 @@ export default class MeshObject extends Mesh {
if (this.geometry.boundingSphere.radius > 4) {
const scale = 4 / this.geometry.boundingSphere.radius;
this.geometry.applyMatrix(new Matrix4().makeScale(scale, scale, scale));
this.geometry.applyMatrix4(new Matrix4().makeScale(scale, scale, scale));
this.geometry.computeBoundingSphere();
this.position.x = -this.geometry.boundingSphere.center.x;

View File

@ -130,11 +130,16 @@ export default {
},
async fetchExpandedContent() {
this.isLoadingExpandedContent = true;
this.error = null;
try {
await this.fetch(this.fetchExpandedData, FETCH_TYPE_EXPANDED);
} catch {
this.error = this.errorText;
// Reset these values so that we allow refetching
this.isExpandedForTheFirstTime = true;
this.isCollapsed = true;
}
this.isLoadingExpandedContent = false;

View File

@ -51,7 +51,8 @@ module Projects
def test_suite
suite = builds.sum do |build|
build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new)
test_report = build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new)
test_report.get_suite(build.test_suite_name)
end
Gitlab::Ci::Reports::TestFailureHistory.new(suite.failed.values, project).load!

View File

@ -28,7 +28,8 @@ module Resolvers
def load_test_suite_data(builds)
suite = builds.sum do |build|
build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new)
test_report = build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new)
test_report.get_suite(build.test_suite_name)
end
Gitlab::Ci::Reports::TestFailureHistory.new(suite.failed.values, pipeline.project).load!

View File

@ -84,6 +84,8 @@ module IssueResolverArguments
end
def ready?(**args)
args[:not] = args[:not].to_h if args[:not].present?
params_not_mutually_exclusive(args, mutually_exclusive_assignee_username_args)
params_not_mutually_exclusive(args, mutually_exclusive_milestone_args)
params_not_mutually_exclusive(args.fetch(:not, {}), mutually_exclusive_milestone_args)
@ -114,7 +116,6 @@ module IssueResolverArguments
def prepare_finder_params(args)
params = super(args)
params[:not] = params[:not].to_h if params[:not].present?
params[:iids] ||= [params.delete(:iid)].compact if params[:iid]
params[:attempt_project_search_optimizations] = true if params[:search].present?

View File

@ -17,8 +17,6 @@ module Types
@requires_argument = !!kwargs.delete(:requires_argument)
@authorize = Array.wrap(kwargs.delete(:authorize))
kwargs[:complexity] = field_complexity(kwargs[:resolver_class], kwargs[:complexity])
@feature_flag = kwargs[:_deprecated_feature_flag]
kwargs = check_feature_flag(kwargs)
@deprecation = gitlab_deprecation(kwargs)
after_connection_extensions = kwargs.delete(:late_extensions) || []
@ -91,16 +89,8 @@ module Types
@constant_complexity
end
def visible?(context)
return false if feature_flag.present? && !Feature.enabled?(feature_flag)
super
end
private
attr_reader :feature_flag
def field_authorized?(object, ctx)
object = object.node if object.is_a?(GraphQL::Pagination::Connection::Edge)
@ -123,27 +113,6 @@ module Types
@authorization ||= ::Gitlab::Graphql::Authorize::ObjectAuthorization.new(@authorize)
end
def feature_documentation_message(key, description)
message_parts = ["#{description} Available only when feature flag `#{key}` is enabled."]
message_parts << if Feature::Definition.has_definition?(key) && Feature::Definition.default_enabled?(key)
"This flag is enabled by default."
else
"This flag is disabled by default, because the feature is experimental and is subject to change without notice."
end
message_parts.join(' ')
end
def check_feature_flag(args)
ff = args.delete(:_deprecated_feature_flag)
return args unless ff.present?
args[:description] = feature_documentation_message(ff, args[:description])
args
end
def field_complexity(resolver_class, current)
return current if current.present? && current > 0

View File

@ -0,0 +1,22 @@
# frozen_string_literal: true
module Types
module Ci
class ConfigVariableType < BaseObject # rubocop:disable Graphql/AuthorizeTypes
graphql_name 'CiConfigVariable'
description 'CI/CD config variables.'
field :key, GraphQL::Types::String,
null: true,
description: 'Name of the variable.'
field :description, GraphQL::Types::String,
null: true,
description: 'Description for the CI/CD config variable.'
field :value, GraphQL::Types::String,
null: true,
description: 'Value of the variable.'
end
end
end

View File

@ -10,16 +10,16 @@ module Types
implements(VariableInterface)
field :environment_scope, GraphQL::Types::String,
null: true,
description: 'Scope defining the environments that can use the variable.'
field :protected, GraphQL::Types::Boolean,
null: true,
description: 'Indicates whether the variable is protected.'
null: true,
description: 'Scope defining the environments that can use the variable.'
field :masked, GraphQL::Types::Boolean,
null: true,
description: 'Indicates whether the variable is masked.'
null: true,
description: 'Indicates whether the variable is masked.'
field :protected, GraphQL::Types::Boolean,
null: true,
description: 'Indicates whether the variable is protected.'
end
end
end

View File

@ -9,21 +9,29 @@ module Types
implements(VariableInterface)
field :id, GraphQL::Types::ID,
null: false,
description: 'ID of the variable.'
field :environment_scope, GraphQL::Types::String,
null: true,
deprecated: {
reason: 'No longer used, only available for GroupVariableType and ProjectVariableType',
milestone: '15.3'
},
description: 'Scope defining the environments that can use the variable.'
null: true,
deprecated: {
reason: 'No longer used, only available for GroupVariableType and ProjectVariableType',
milestone: '15.3'
},
description: 'Scope defining the environments that can use the variable.'
field :protected, GraphQL::Types::Boolean,
null: true,
description: 'Indicates whether the variable is protected.'
null: true,
description: 'Indicates whether the variable is protected.'
field :masked, GraphQL::Types::Boolean,
null: true,
description: 'Indicates whether the variable is masked.'
null: true,
description: 'Indicates whether the variable is masked.'
field :raw, GraphQL::Types::Boolean,
null: true,
description: 'Indicates whether the variable is raw.'
def environment_scope
nil

View File

@ -10,12 +10,12 @@ module Types
implements(VariableInterface)
field :environment_scope, GraphQL::Types::String,
null: true,
deprecated: {
reason: 'No longer used, only available for GroupVariableType and ProjectVariableType',
milestone: '15.3'
},
description: 'Scope defining the environments that can use the variable.'
null: true,
deprecated: {
reason: 'No longer used, only available for GroupVariableType and ProjectVariableType',
milestone: '15.3'
},
description: 'Scope defining the environments that can use the variable.'
def environment_scope
nil

View File

@ -10,16 +10,16 @@ module Types
implements(VariableInterface)
field :environment_scope, GraphQL::Types::String,
null: true,
description: 'Scope defining the environments that can use the variable.'
null: true,
description: 'Scope defining the environments that can use the variable.'
field :protected, GraphQL::Types::Boolean,
null: true,
description: 'Indicates whether the variable is protected.'
null: true,
description: 'Indicates whether the variable is protected.'
field :masked, GraphQL::Types::Boolean,
null: true,
description: 'Indicates whether the variable is masked.'
null: true,
description: 'Indicates whether the variable is masked.'
end
end
end

View File

@ -8,24 +8,24 @@ module Types
graphql_name 'CiVariable'
field :id, GraphQL::Types::ID,
null: false,
description: 'ID of the variable.'
null: false,
description: 'ID of the variable.'
field :key, GraphQL::Types::String,
null: true,
description: 'Name of the variable.'
field :value, GraphQL::Types::String,
null: true,
description: 'Value of the variable.'
field :variable_type, ::Types::Ci::VariableTypeEnum,
null: true,
description: 'Type of the variable.'
null: true,
description: 'Name of the variable.'
field :raw, GraphQL::Types::Boolean,
null: true,
description: 'Indicates whether the variable is raw.'
null: true,
description: 'Indicates whether the variable is raw.'
field :value, GraphQL::Types::String,
null: true,
description: 'Value of the variable.'
field :variable_type, ::Types::Ci::VariableTypeEnum,
null: true,
description: 'Type of the variable.'
end
end
end

View File

@ -18,6 +18,17 @@ module Types
null: false,
description: 'Path of the CI configuration file.'
field :ci_config_variables, [Types::Ci::ConfigVariableType],
null: true,
calls_gitaly: true,
authorize: :create_pipeline,
alpha: { milestone: '15.3' },
description: 'CI/CD config variable.' do
argument :sha, GraphQL::Types::String,
required: true,
description: 'Sha.'
end
field :full_path, GraphQL::Types::ID,
null: false,
description: 'Full path of the project.'
@ -549,6 +560,16 @@ module Types
project.container_repositories.size
end
def ci_config_variables(sha)
result = ::Ci::ListConfigVariablesService.new(object, context[:current_user]).execute(sha)
return if result.nil?
result.map do |var_key, var_config|
{ key: var_key, **var_config }
end
end
def sast_ci_configuration
return unless Ability.allowed?(current_user, :download_code, object)

View File

@ -996,15 +996,11 @@ module Ci
end
def collect_test_reports!(test_reports)
test_reports.get_suite(test_suite_name).tap do |test_suite|
each_report(Ci::JobArtifact.file_types_for_report(:test)) do |file_type, blob|
Gitlab::Ci::Parsers.fabricate!(file_type).parse!(
blob,
test_suite,
job: self
)
end
each_report(Ci::JobArtifact.file_types_for_report(:test)) do |file_type, blob|
Gitlab::Ci::Parsers.fabricate!(file_type).parse!(blob, test_reports, job: self)
end
test_reports
end
def collect_accessibility_reports!(accessibility_report)
@ -1181,6 +1177,14 @@ module Ci
job_artifacts.map(&:file_type)
end
def test_suite_name
if matrix_build?
name
else
group_name
end
end
protected
def run_status_commit_hooks!
@ -1191,14 +1195,6 @@ module Ci
private
def test_suite_name
if matrix_build?
name
else
group_name
end
end
def matrix_build?
options.dig(:parallel, :matrix).present?
end

View File

@ -22,7 +22,8 @@ module Ci
private
def generate_test_suite_report(build)
build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new)
test_report = build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new)
test_report.get_suite(build.test_suite_name)
end
def tests_params(test_suite)

View File

@ -80,8 +80,8 @@ module Ci
end
def generate_test_suite!(build)
# Returns an instance of Gitlab::Ci::Reports::TestSuite
build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new)
test_report = build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new)
test_report.get_suite(build.test_suite_name)
end
def ci_unit_test_attrs(batch)

View File

@ -10098,6 +10098,18 @@ Represents the total number of issues and their weights for a particular day.
| <a id="ciconfigstagegroups"></a>`groups` | [`CiConfigGroupConnection`](#ciconfiggroupconnection) | Groups of jobs for the stage. (see [Connections](#connections)) |
| <a id="ciconfigstagename"></a>`name` | [`String`](#string) | Name of the stage. |
### `CiConfigVariable`
CI/CD config variables.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="ciconfigvariabledescription"></a>`description` | [`String`](#string) | Description for the CI/CD config variable. |
| <a id="ciconfigvariablekey"></a>`key` | [`String`](#string) | Name of the variable. |
| <a id="ciconfigvariablevalue"></a>`value` | [`String`](#string) | Value of the variable. |
### `CiGroup`
#### Fields
@ -15801,6 +15813,22 @@ four standard [pagination arguments](#connection-pagination-arguments):
| ---- | ---- | ----------- |
| <a id="projectboardsid"></a>`id` | [`BoardID`](#boardid) | Find a board by its ID. |
##### `Project.ciConfigVariables`
CI/CD config variable.
WARNING:
**Introduced** in 15.3.
This feature is in Alpha. It can be changed or removed at any time.
Returns [`[CiConfigVariable!]`](#ciconfigvariable).
###### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="projectciconfigvariablessha"></a>`sha` | [`String!`](#string) | Sha. |
##### `Project.ciTemplate`
Find a single CI/CD template by name.

View File

@ -546,12 +546,6 @@ def resolve(id: )
end
```
### `feature_flag` property (deprecated)
NOTE:
This property is deprecated and should no longer be used. The property
has been temporarily renamed to `_deprecated_feature_flag` and support for it will be removed in [#369202](https://gitlab.com/gitlab-org/gitlab/-/issues/369202).
## Deprecating schema items
The GitLab GraphQL API is versionless, which means we maintain backwards

View File

@ -8,7 +8,9 @@ module Gitlab
JunitParserError = Class.new(Gitlab::Ci::Parsers::ParserError)
ATTACHMENT_TAG_REGEX = /\[\[ATTACHMENT\|(?<path>.+?)\]\]/.freeze
def parse!(xml_data, test_suite, job:)
def parse!(xml_data, test_report, job:)
test_suite = test_report.get_suite(job.test_suite_name)
root = Hash.from_xml(xml_data)
total_parsed = 0
max_test_cases = job.max_test_cases_per_report

View File

@ -32,14 +32,11 @@ included_attributes:
- :created_at
- :updated_at
- :start_date
- :last_run_date
- :duration_in_weeks
- :iterations_in_advance
- :active
- :automatic
- :roll_over
- :title
- :description
- :sequence
iterations_cadences: *iterations_cadence_definition
iteration: &iteration_definition
- :iid
@ -69,6 +66,10 @@ excluded_attributes:
- :state_id
iterations_cadence: &iterations_cadence_definition
- :id
- :last_run_date
- :duration_in_weeks
- :iterations_in_advance
- :automatic
iterations_cadences: *iterations_cadence_definition
iteration: &iteration_excluded_definition
- :id

View File

@ -26,7 +26,10 @@ module Gitlab
private
def setup_models
setup_note if @relation_name == :notes
case @relation_name
when :notes then setup_note
when :'Iterations::Cadence' then setup_iterations_cadence
end
update_group_references
end
@ -45,6 +48,10 @@ module Gitlab
def use_attributes_permitter?
false
end
def setup_iterations_cadence
@relation_hash['automatic'] = false
end
end
end
end

View File

@ -168,9 +168,7 @@
"string-hash": "1.1.3",
"style-loader": "^2.0.0",
"swagger-ui-dist": "4.12.0",
"three": "^0.84.0",
"three-orbit-controls": "^82.1.0",
"three-stl-loader": "^1.0.4",
"three": "^0.143.0",
"timeago.js": "^4.0.2",
"unified": "^10.1.2",
"unist-util-visit-parents": "^5.1.0",

View File

@ -81,12 +81,6 @@ module RuboCop
in_app_directory?(node, 'graphql')
end
# Returns true if the given node resides in app/graphql/types,
# ee/app/graphql/types, or ee/app/graphql/ee/types.
def in_graphql_types?(node)
in_graphql_directory?(node, 'types')
end
# Returns true if the given node resides in lib/api or ee/lib/api.
def in_api?(node)
in_lib_directory?(node, 'api')

View File

@ -26,9 +26,6 @@ module RuboCop
data_consistency
deduplicate
].freeze
GRAPHQL_METHODS = %i[
field
].freeze
SELF_METHODS = %i[
push_frontend_feature_flag
push_force_frontend_feature_flag
@ -36,7 +33,7 @@ module RuboCop
limit_feature_flag_for_override=
].freeze + EXPERIMENT_METHODS + RUGGED_METHODS + WORKER_METHODS
RESTRICT_ON_SEND = FEATURE_METHODS + EXPERIMENTATION_METHODS + GRAPHQL_METHODS + SELF_METHODS
RESTRICT_ON_SEND = FEATURE_METHODS + EXPERIMENTATION_METHODS + SELF_METHODS
USAGE_DATA_COUNTERS_EVENTS_YAML_GLOBS = [
File.expand_path("../../../config/metrics/aggregates/*.yml", __dir__),
@ -138,15 +135,6 @@ module RuboCop
node.children[3].each_pair.find do |pair|
pair.key.value == :feature_flag
end&.value
elsif graphql_method?(node)
return unless node.children.size > 3
opts_index = node.children[3].hash_type? ? 3 : 4
return unless node.children[opts_index]
node.children[opts_index].each_pair.find do |pair|
pair.key.value == :_deprecated_feature_flag
end&.value
else
arg_index = rugged_method?(node) ? 3 : 2
@ -209,16 +197,12 @@ module RuboCop
WORKER_METHODS.include?(method_name(node))
end
def graphql_method?(node)
GRAPHQL_METHODS.include?(method_name(node)) && in_graphql_types?(node)
end
def self_method?(node)
SELF_METHODS.include?(method_name(node)) && class_caller(node).empty?
end
def trackable_flag?(node)
feature_method?(node) || experimentation_method?(node) || graphql_method?(node) || self_method?(node)
feature_method?(node) || experimentation_method?(node) || self_method?(node)
end
# Marking all event's feature flags as used as Gitlab::UsageDataCounters::HLLRedisCounter.track_event{,context}

View File

@ -236,16 +236,17 @@ describe('MR Widget', () => {
data: { vulnerabilities: [{ vuln: 2 }] },
};
const fetchExpandedData = jest.fn().mockResolvedValue(mockDataExpanded);
createComponent({
propsData: {
isCollapsible: true,
fetchCollapsedData: () => Promise.resolve(mockDataCollapsed),
fetchExpandedData: () => Promise.resolve(mockDataExpanded),
fetchExpandedData,
},
});
findToggleButton().vm.$emit('click');
await waitForPromises();
// First fetches the collapsed data
@ -259,6 +260,62 @@ describe('MR Widget', () => {
collapsed: null,
expanded: mockDataExpanded.data,
});
// Triggering a click does not call the expanded data again
findToggleButton().vm.$emit('click');
await waitForPromises();
expect(fetchExpandedData).toHaveBeenCalledTimes(1);
});
it('allows refetching when fetch expanded data returns an error', async () => {
const fetchExpandedData = jest.fn().mockRejectedValue({ error: true });
createComponent({
propsData: {
isCollapsible: true,
fetchCollapsedData: () => Promise.resolve([]),
fetchExpandedData,
},
});
findToggleButton().vm.$emit('click');
await waitForPromises();
// First fetches the collapsed data
expect(wrapper.emitted('input')[0][0]).toEqual({
collapsed: undefined,
expanded: null,
});
expect(fetchExpandedData).toHaveBeenCalledTimes(1);
expect(wrapper.emitted('input')).toHaveLength(1); // Should not an emit an input call because request failed
findToggleButton().vm.$emit('click');
await waitForPromises();
expect(fetchExpandedData).toHaveBeenCalledTimes(2);
});
it('resets the error message when another request is fetched', async () => {
const fetchExpandedData = jest.fn().mockRejectedValue({ error: true });
createComponent({
propsData: {
isCollapsible: true,
fetchCollapsedData: () => Promise.resolve([]),
fetchExpandedData,
},
});
findToggleButton().vm.$emit('click');
await waitForPromises();
expect(wrapper.findByText('Failed to load').exists()).toBe(true);
fetchExpandedData.mockImplementation(() => new Promise(() => {}));
findToggleButton().vm.$emit('click');
await nextTick();
expect(wrapper.findByText('Failed to load').exists()).toBe(false);
});
});
});

View File

@ -1,52 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Graphql Field feature flags' do
include GraphqlHelpers
include Graphql::ResolverFactories
let_it_be(:user) { create(:user) }
let(:feature_flag) { 'test_feature' }
let(:test_object) { double(name: 'My name') }
let(:query_string) { '{ item { name } }' }
let(:result) { execute_query(query_type)['data'] }
before do
skip_feature_flags_yaml_validation
end
subject { result }
describe 'Feature flagged field' do
let(:type) { type_factory }
let(:query_type) do
query_factory do |query|
query.field :item, type, null: true, _deprecated_feature_flag: feature_flag, resolver: new_resolver(test_object)
end
end
it 'checks YAML definition for default_enabled' do
# Exception is indicative of a check for YAML definition
expect { subject }.to raise_error(Feature::InvalidFeatureFlagError, /The feature flag YAML definition for '#{feature_flag}' does not exist/)
end
context 'skipping YAML check' do
before do
skip_default_enabled_yaml_check
end
it 'returns the value when feature is enabled' do
expect(subject['item']).to eq('name' => test_object.name)
end
it 'returns nil when the feature is disabled' do
stub_feature_flags(feature_flag => false)
expect(subject).to be_nil
end
end
end
end

View File

@ -205,39 +205,6 @@ RSpec.describe Types::BaseField do
end
end
end
describe '#visible?' do
context 'and has a feature_flag' do
let(:flag) { :test_feature }
let(:field) { described_class.new(name: 'test', type: GraphQL::Types::String, _deprecated_feature_flag: flag, null: false) }
let(:context) { {} }
before do
skip_feature_flags_yaml_validation
end
it 'checks YAML definition for default_enabled' do
# Exception is indicative of a check for YAML definition
expect { field.visible?(context) }.to raise_error(Feature::InvalidFeatureFlagError, /The feature flag YAML definition for '#{flag}' does not exist/)
end
context 'skipping YAML check' do
before do
skip_default_enabled_yaml_check
end
it 'returns false if the feature is not enabled' do
stub_feature_flags(flag => false)
expect(field.visible?(context)).to eq(false)
end
it 'returns true if the feature is enabled' do
expect(field.visible?(context)).to eq(true)
end
end
end
end
end
describe '#resolve' do
@ -251,77 +218,11 @@ RSpec.describe Types::BaseField do
end
end
describe '#description' do
context 'feature flag given' do
let(:field) { described_class.new(name: 'test', type: GraphQL::Types::String, _deprecated_feature_flag: flag, null: false, description: 'Test description.') }
let(:flag) { :test_flag }
it 'prepends the description' do
expect(field.description).to start_with 'Test description. Available only when feature flag `test_flag` is enabled.'
end
context 'falsey feature_flag values' do
using RSpec::Parameterized::TableSyntax
where(:flag, :feature_value, :default_enabled) do
'' | false | false
'' | true | false
nil | false | true
nil | true | false
end
with_them do
it 'returns the correct description' do
expect(field.description).to eq('Test description.')
end
end
end
context 'with different default_enabled values' do
using RSpec::Parameterized::TableSyntax
where(:feature_value, :default_enabled, :expected_description) do
disabled_ff_description = "Test description. Available only when feature flag `test_flag` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice."
enabled_ff_description = "Test description. Available only when feature flag `test_flag` is enabled. This flag is enabled by default."
false | false | disabled_ff_description
true | false | disabled_ff_description
false | true | enabled_ff_description
true | true | enabled_ff_description
end
with_them do
before do
stub_feature_flags("#{flag}": feature_value)
allow(Feature::Definition).to receive(:has_definition?).with(flag).and_return(true)
allow(Feature::Definition).to receive(:default_enabled?).and_return(default_enabled)
end
it 'returns the correct availability in the description' do
expect(field.description).to eq expected_description
end
end
end
end
end
include_examples 'Gitlab-style deprecations' do
def subject(args = {})
base_args = { name: 'test', type: GraphQL::Types::String, null: true }
described_class.new(**base_args.merge(args))
end
it 'interacts well with the `_deprecated_feature_flag` property' do
field = subject(
deprecated: { milestone: '1.10', reason: 'Deprecation reason' },
description: 'Field description.',
_deprecated_feature_flag: 'foo_flag'
)
expect(field.description).to start_with('Field description. Available only when feature flag `foo_flag` is enabled.')
expect(field.description).to end_with('Deprecated in 1.10: Deprecation reason.')
end
end
end

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['CiConfigVariable'] do
specify { expect(described_class).to have_graphql_fields(:key, :description, :value).at_least }
end

View File

@ -5,5 +5,5 @@ require 'spec_helper'
RSpec.describe GitlabSchema.types['CiInstanceVariable'] do
specify { expect(described_class.interfaces).to contain_exactly(Types::Ci::VariableInterface) }
specify { expect(described_class).to have_graphql_fields(:masked, :protected).at_least }
specify { expect(described_class).to have_graphql_fields(:environment_scope, :masked, :protected).at_least }
end

View File

@ -4,4 +4,6 @@ require 'spec_helper'
RSpec.describe GitlabSchema.types['CiManualVariable'] do
specify { expect(described_class.interfaces).to contain_exactly(Types::Ci::VariableInterface) }
specify { expect(described_class).to have_graphql_fields(:environment_scope).at_least }
end

View File

@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe GitlabSchema.types['CiVariable'] do
specify do
expect(described_class).to have_graphql_fields(
:id, :key, :value, :variable_type, :raw
:id, :key, :raw, :value, :variable_type
).at_least
end
end

View File

@ -37,6 +37,7 @@ RSpec.describe GitlabSchema.types['Project'] do
ci_template timelogs merge_commit_template squash_commit_template work_item_types
recent_issue_boards ci_config_path_or_default packages_cleanup_policy ci_variables
timelog_categories fork_targets
ci_config_variables
]
expect(described_class).to include_graphql_fields(*expected_fields)

View File

@ -4,11 +4,13 @@ require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Parsers::Test::Junit do
describe '#parse!' do
subject { described_class.new.parse!(junit, test_suite, job: job) }
subject { described_class.new.parse!(junit, test_report, job: job) }
let(:test_suite) { Gitlab::Ci::Reports::TestSuite.new('rspec') }
let(:job) { double(test_suite_name: 'rspec', max_test_cases_per_report: max_test_cases) }
let(:test_report) { Gitlab::Ci::Reports::TestReport.new }
let(:test_suite) { test_report.get_suite(job.test_suite_name) }
let(:test_cases) { flattened_test_cases(test_suite) }
let(:job) { double(max_test_cases_per_report: max_test_cases) }
let(:max_test_cases) { 0 }
context 'when data is JUnit style XML' do

View File

@ -4366,9 +4366,7 @@ RSpec.describe Ci::Build do
end
describe '#collect_test_reports!' do
subject { build.collect_test_reports!(test_reports) }
let(:test_reports) { Gitlab::Ci::Reports::TestReport.new }
subject(:test_reports) { build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new) }
it { expect(test_reports.get_suite(build.name).total_count).to eq(0) }
@ -4416,56 +4414,6 @@ RSpec.describe Ci::Build do
end
end
end
context 'when build is part of parallel build' do
let(:build_1) { create(:ci_build, name: 'build 1/2') }
let(:test_report) { Gitlab::Ci::Reports::TestReport.new }
before do
build_1.collect_test_reports!(test_report)
end
it 'uses the group name for test suite name' do
expect(test_report.test_suites.keys).to contain_exactly('build')
end
context 'when there are more than one parallel builds' do
let(:build_2) { create(:ci_build, name: 'build 2/2') }
before do
build_2.collect_test_reports!(test_report)
end
it 'merges the test suite from parallel builds' do
expect(test_report.test_suites.keys).to contain_exactly('build')
end
end
end
context 'when build is part of matrix build' do
let(:test_report) { Gitlab::Ci::Reports::TestReport.new }
let(:matrix_build_1) { create(:ci_build, :matrix) }
before do
matrix_build_1.collect_test_reports!(test_report)
end
it 'uses the job name for the test suite' do
expect(test_report.test_suites.keys).to contain_exactly(matrix_build_1.name)
end
context 'when there are more than one matrix builds' do
let(:matrix_build_2) { create(:ci_build, :matrix) }
before do
matrix_build_2.collect_test_reports!(test_report)
end
it 'keeps separate test suites' do
expect(test_report.test_suites.keys).to match_array([matrix_build_1.name, matrix_build_2.name])
end
end
end
end
describe '#collect_accessibility_reports!' do
@ -5662,4 +5610,28 @@ RSpec.describe Ci::Build do
end
end
end
describe '#test_suite_name' do
let(:build) { create(:ci_build, name: 'test') }
it 'uses the group name for test suite name' do
expect(build.test_suite_name).to eq('test')
end
context 'when build is part of parallel build' do
let(:build) { create(:ci_build, name: 'build 1/2') }
it 'uses the group name for test suite name' do
expect(build.test_suite_name).to eq('build')
end
end
context 'when build is part of matrix build' do
let!(:matrix_build) { create(:ci_build, :matrix) }
it 'uses the job name for the test suite' do
expect(matrix_build.test_suite_name).to eq(matrix_build.name)
end
end
end
end

View File

@ -4370,6 +4370,10 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
create(:ci_job_artifact, :junit_with_ant, job: build_java)
end
it 'has a test suite for each job' do
expect(subject.test_suites.keys).to contain_exactly('rspec', 'java')
end
it 'returns test reports with collected data' do
expect(subject.total_count).to be(7)
expect(subject.success_count).to be(5)
@ -4388,6 +4392,34 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
context 'when the pipeline has parallel builds with test reports' do
let!(:parallel_build_1) { create(:ci_build, name: 'build 1/2', pipeline: pipeline) }
let!(:parallel_build_2) { create(:ci_build, name: 'build 2/2', pipeline: pipeline) }
before do
create(:ci_job_artifact, :junit, job: parallel_build_1)
create(:ci_job_artifact, :junit, job: parallel_build_2)
end
it 'merges the test suite from parallel builds' do
expect(subject.test_suites.keys).to contain_exactly('build')
end
end
context 'the pipeline has matrix builds with test reports' do
let!(:matrix_build_1) { create(:ci_build, :matrix, pipeline: pipeline) }
let!(:matrix_build_2) { create(:ci_build, :matrix, pipeline: pipeline) }
before do
create(:ci_job_artifact, :junit, job: matrix_build_1)
create(:ci_job_artifact, :junit, job: matrix_build_2)
end
it 'keeps separate test suites for each matrix build' do
expect(subject.test_suites.keys).to contain_exactly(matrix_build_1.name, matrix_build_2.name)
end
end
context 'when pipeline does not have any builds with test reports' do
it 'returns empty test reports' do
expect(subject.total_count).to be(0)

View File

@ -0,0 +1,92 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Query.project(fullPath).ciConfigVariables(sha)' do
include GraphqlHelpers
include ReactiveCachingHelpers
let_it_be(:project) { create(:project, :repository, :public) }
let_it_be(:user) { create(:user) }
let_it_be(:content) do
File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
end
let(:sha) { project.commit.sha }
let(:service) { Ci::ListConfigVariablesService.new(project, user) }
let(:query) do
%(
query {
project(fullPath: "#{project.full_path}") {
ciConfigVariables(sha: "#{sha}") {
key
value
description
}
}
}
)
end
context 'when the user has the correct permissions' do
before do
project.add_maintainer(user)
stub_ci_pipeline_yaml_file(content)
allow(Ci::ListConfigVariablesService)
.to receive(:new)
.and_return(service)
end
context 'when the cache is not empty' do
before do
synchronous_reactive_cache(service)
end
it 'returns the CI variables for the config' do
post_graphql(query, current_user: user)
expect(graphql_data.dig('project', 'ciConfigVariables')).to contain_exactly(
{
'key' => 'DB_NAME',
'value' => 'postgres',
'description' => nil
},
{
'key' => 'ENVIRONMENT_VAR',
'value' => 'env var value',
'description' => 'env var description'
}
)
end
end
context 'when the cache is empty' do
let(:sha) { 'main' }
it 'returns nothing' do
post_graphql(query, current_user: user)
expect(graphql_data.dig('project', 'ciConfigVariables')).to be_nil
end
end
end
context 'when the user is not authorized' do
before do
project.add_guest(user)
stub_ci_pipeline_yaml_file(content)
allow(Ci::ListConfigVariablesService)
.to receive(:new)
.and_return(service)
synchronous_reactive_cache(service)
end
it 'returns nothing' do
post_graphql(query, current_user: user)
expect(graphql_data.dig('project', 'ciConfigVariables')).to be_nil
end
end
end

View File

@ -172,31 +172,6 @@ RSpec.describe RuboCop::CodeReuseHelpers do
end
end
describe '#in_graphql_types?' do
%w[
app/graphql/types
ee/app/graphql/ee/types
ee/app/graphql/types
].each do |path|
it "returns true for a node in #{path}" do
node = build_and_parse_source('10', rails_root_join(path, 'foo.rb'))
expect(cop.in_graphql_types?(node)).to eq(true)
end
end
%w[
app/graphql/resolvers
app/foo
].each do |path|
it "returns false for a node in #{path}" do
node = build_and_parse_source('10', rails_root_join(path, 'foo.rb'))
expect(cop.in_graphql_types?(node)).to eq(false)
end
end
end
describe '#in_api?' do
it 'returns true for a node in the API directory' do
node = build_and_parse_source('10', rails_root_join('lib', 'api', 'foo.rb'))

View File

@ -212,19 +212,6 @@ RSpec.describe RuboCop::Cop::Gitlab::MarkUsedFeatureFlags do
include_examples 'does not set any flags as used', 'deduplicate :delayed'
end
describe 'GraphQL `field` method' do
before do
allow(cop).to receive(:in_graphql_types?).and_return(true)
end
include_examples 'sets flag as used', 'field :runners, Types::Ci::RunnerType.connection_type, null: true, _deprecated_feature_flag: :foo', 'foo'
include_examples 'sets flag as used', 'field :runners, null: true, _deprecated_feature_flag: :foo', 'foo'
include_examples 'does not set any flags as used', 'field :solution'
include_examples 'does not set any flags as used', 'field :runners, Types::Ci::RunnerType.connection_type'
include_examples 'does not set any flags as used', 'field :runners, Types::Ci::RunnerType.connection_type, null: true, description: "hello world"'
include_examples 'does not set any flags as used', 'field :solution, type: GraphQL::Types::String, null: true, description: "URL to the vulnerabilitys details page."'
end
describe "tracking of usage data metrics known events happens at the beginning of inspection" do
let(:usage_data_counters_known_event_feature_flags) { ['an_event_feature_flag'] }

View File

@ -8,6 +8,9 @@ before_script:
variables:
DB_NAME: postgres
ENVIRONMENT_VAR:
value: 'env var value'
description: 'env var description'
stages:
- test

View File

@ -11286,20 +11286,10 @@ textextensions@2:
resolved "https://registry.yarnpkg.com/textextensions/-/textextensions-2.2.0.tgz#38ac676151285b658654581987a0ce1a4490d286"
integrity sha512-j5EMxnryTvKxwH2Cq+Pb43tsf6sdEgw6Pdwxk83mPaq0ToeFJt6WE4J3s5BqY7vmjlLgkgXvhtXUxo80FyBhCA==
three-orbit-controls@^82.1.0:
version "82.1.0"
resolved "https://registry.yarnpkg.com/three-orbit-controls/-/three-orbit-controls-82.1.0.tgz#11a7f33d0a20ecec98f098b37780f6537374fab4"
integrity sha1-EafzPQog7OyY8Jizd4D2U3N0+rQ=
three-stl-loader@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/three-stl-loader/-/three-stl-loader-1.0.4.tgz#6b3319a31e3b910aab1883d19b00c81a663c3e03"
integrity sha1-azMZox47kQqrGIPRmwDIGmY8PgM=
three@^0.84.0:
version "0.84.0"
resolved "https://registry.yarnpkg.com/three/-/three-0.84.0.tgz#95be85a55a0fa002aa625ed559130957dcffd918"
integrity sha1-lb6FpVoPoAKqYl7VWRMJV9z/2Rg=
three@^0.143.0:
version "0.143.0"
resolved "https://registry.yarnpkg.com/three/-/three-0.143.0.tgz#1455bca132cc2b20beb7f41d313e10c29e5ed9df"
integrity sha512-oKcAGYHhJ46TGEuHjodo2n6TY2R6lbvrkp+feKZxqsUL/WkH7GKKaeu6RHeyb2Xjfk2dPLRKLsOP0KM2VgT8Zg==
throat@^6.0.1:
version "6.0.1"