Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/support/helpers')
-rw-r--r--spec/support/helpers/after_next_helpers.rb53
-rw-r--r--spec/support/helpers/database_helpers.rb13
-rw-r--r--spec/support/helpers/dependency_proxy_helpers.rb18
-rw-r--r--spec/support/helpers/features/merge_request_helpers.rb25
-rw-r--r--spec/support/helpers/features/web_ide_spec_helpers.rb35
-rw-r--r--spec/support/helpers/file_read_helpers.rb48
-rw-r--r--spec/support/helpers/filtered_search_helpers.rb4
-rw-r--r--spec/support/helpers/git_http_helpers.rb34
-rw-r--r--spec/support/helpers/gitlab_verify_helpers.rb2
-rw-r--r--spec/support/helpers/gpg_helpers.rb139
-rw-r--r--spec/support/helpers/graphql_helpers.rb206
-rw-r--r--spec/support/helpers/login_helpers.rb16
-rw-r--r--spec/support/helpers/multipart_helpers.rb2
-rw-r--r--spec/support/helpers/next_instance_of.rb27
-rw-r--r--spec/support/helpers/services_helper.rb11
-rw-r--r--spec/support/helpers/snowplow_helpers.rb26
-rw-r--r--spec/support/helpers/stub_experiments.rb16
-rw-r--r--spec/support/helpers/stub_object_storage.rb17
-rw-r--r--spec/support/helpers/test_env.rb48
-rw-r--r--spec/support/helpers/usage_data_helpers.rb2
-rw-r--r--spec/support/helpers/workhorse_helpers.rb6
21 files changed, 564 insertions, 184 deletions
diff --git a/spec/support/helpers/after_next_helpers.rb b/spec/support/helpers/after_next_helpers.rb
new file mode 100644
index 00000000000..0a7844fdd8f
--- /dev/null
+++ b/spec/support/helpers/after_next_helpers.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+require_relative './next_instance_of'
+
+module AfterNextHelpers
+ class DeferredExpectation
+ include ::NextInstanceOf
+ include ::RSpec::Matchers
+ include ::RSpec::Mocks::ExampleMethods
+
+ def initialize(klass, args, level:)
+ @klass = klass
+ @args = args
+ @level = level.to_sym
+ end
+
+ def to(condition)
+ run_condition(condition, asserted: true)
+ end
+
+ def not_to(condition)
+ run_condition(condition, asserted: false)
+ end
+
+ private
+
+ attr_reader :klass, :args, :level
+
+ def run_condition(condition, asserted:)
+ msg = asserted ? :to : :not_to
+ case level
+ when :expect
+ if asserted
+ expect_next_instance_of(klass, *args) { |instance| expect(instance).send(msg, condition) }
+ else
+ allow_next_instance_of(klass, *args) { |instance| expect(instance).send(msg, condition) }
+ end
+ when :allow
+ allow_next_instance_of(klass, *args) { |instance| allow(instance).send(msg, condition) }
+ else
+ raise "Unknown level: #{level}"
+ end
+ end
+ end
+
+ def allow_next(klass, *args)
+ DeferredExpectation.new(klass, args, level: :allow)
+ end
+
+ def expect_next(klass, *args)
+ DeferredExpectation.new(klass, args, level: :expect)
+ end
+end
diff --git a/spec/support/helpers/database_helpers.rb b/spec/support/helpers/database_helpers.rb
new file mode 100644
index 00000000000..e9f0a74a8d1
--- /dev/null
+++ b/spec/support/helpers/database_helpers.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module DatabaseHelpers
+ # In order to directly work with views using factories,
+ # we can swapout the view for a table of identical structure.
+ def swapout_view_for_table(view)
+ ActiveRecord::Base.connection.execute(<<~SQL)
+ CREATE TABLE #{view}_copy (LIKE #{view});
+ DROP VIEW #{view};
+ ALTER TABLE #{view}_copy RENAME TO #{view};
+ SQL
+ end
+end
diff --git a/spec/support/helpers/dependency_proxy_helpers.rb b/spec/support/helpers/dependency_proxy_helpers.rb
index 545b9d1f4d0..ebb849628bf 100644
--- a/spec/support/helpers/dependency_proxy_helpers.rb
+++ b/spec/support/helpers/dependency_proxy_helpers.rb
@@ -11,11 +11,18 @@ module DependencyProxyHelpers
.to_return(status: status, body: body || auth_body)
end
- def stub_manifest_download(image, tag, status = 200, body = nil)
+ def stub_manifest_download(image, tag, status: 200, body: nil, headers: {})
manifest_url = registry.manifest_url(image, tag)
stub_full_request(manifest_url)
- .to_return(status: status, body: body || manifest)
+ .to_return(status: status, body: body || manifest, headers: headers)
+ end
+
+ def stub_manifest_head(image, tag, status: 200, body: nil, digest: '123456')
+ manifest_url = registry.manifest_url(image, tag)
+
+ stub_full_request(manifest_url, method: :head)
+ .to_return(status: status, body: body, headers: { 'docker-content-digest' => digest } )
end
def stub_blob_download(image, blob_sha, status = 200, body = '123456')
@@ -25,6 +32,13 @@ module DependencyProxyHelpers
.to_return(status: status, body: body)
end
+ def build_jwt(user = nil, expire_time: nil)
+ JSONWebToken::HMACToken.new(::Auth::DependencyProxyAuthenticationService.secret).tap do |jwt|
+ jwt['user_id'] = user.id if user
+ jwt.expire_time = expire_time || jwt.issued_at + 1.minute
+ end
+ end
+
private
def registry
diff --git a/spec/support/helpers/features/merge_request_helpers.rb b/spec/support/helpers/features/merge_request_helpers.rb
new file mode 100644
index 00000000000..53896e1fe12
--- /dev/null
+++ b/spec/support/helpers/features/merge_request_helpers.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Spec
+ module Support
+ module Helpers
+ module Features
+ module MergeRequestHelpers
+ def preload_view_requirements(merge_request, note)
+ # This will load the status fields of the author of the note and merge request
+ # to avoid queries when rendering the view being tested.
+ #
+ merge_request.author.status
+ note.author.status
+ end
+
+ def serialize_issuable_sidebar(user, project, merge_request)
+ MergeRequestSerializer
+ .new(current_user: user, project: project)
+ .represent(merge_request, serializer: 'sidebar')
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/support/helpers/features/web_ide_spec_helpers.rb b/spec/support/helpers/features/web_ide_spec_helpers.rb
index 12d3cecd052..358bfacce05 100644
--- a/spec/support/helpers/features/web_ide_spec_helpers.rb
+++ b/spec/support/helpers/features/web_ide_spec_helpers.rb
@@ -22,8 +22,6 @@ module WebIdeSpecHelpers
click_link('Web IDE')
wait_for_requests
-
- save_monaco_editor_reference
end
def ide_tree_body
@@ -65,17 +63,6 @@ module WebIdeSpecHelpers
ide_set_editor_value(content)
end
- def ide_rename_file(path, new_path)
- container = ide_traverse_to_file(path)
-
- click_file_action(container, 'Rename/Move')
-
- within '#ide-new-entry' do
- find('input').fill_in(with: new_path)
- click_button('Rename file')
- end
- end
-
# Deletes a file by traversing to `path`
# then clicking the 'Delete' action.
#
@@ -103,20 +90,6 @@ module WebIdeSpecHelpers
container
end
- def ide_close_file(name)
- within page.find('.multi-file-tabs') do
- click_button("Close #{name}")
- end
- end
-
- def ide_open_file(path)
- row = ide_traverse_to_file(path)
-
- ide_open_file_row(row)
-
- wait_for_requests
- end
-
def ide_open_file_row(row)
return if ide_folder_row_open?(row)
@@ -130,10 +103,6 @@ module WebIdeSpecHelpers
execute_script("monaco.editor.getModel('#{uri}').setValue('#{escape_javascript(value)}')")
end
- def ide_set_editor_position(line, col)
- execute_script("TEST_EDITOR.setPosition(#{{ lineNumber: line, column: col }.to_json})")
- end
-
def ide_editor_value
editor = find('.monaco-editor')
uri = editor['data-uri']
@@ -180,8 +149,4 @@ module WebIdeSpecHelpers
wait_for_requests
end
end
-
- def save_monaco_editor_reference
- evaluate_script("monaco.editor.onDidCreateEditor(editor => { window.TEST_EDITOR = editor; })")
- end
end
diff --git a/spec/support/helpers/file_read_helpers.rb b/spec/support/helpers/file_read_helpers.rb
new file mode 100644
index 00000000000..c30a9e6466f
--- /dev/null
+++ b/spec/support/helpers/file_read_helpers.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+module FileReadHelpers
+ def stub_file_read(file, content: nil, error: nil)
+ allow_original_file_read
+
+ expectation = allow(File).to receive(:read).with(file)
+
+ if error
+ expectation.and_raise(error)
+ elsif content
+ expectation.and_return(content)
+ else
+ expectation
+ end
+ end
+
+ def expect_file_read(file, content: nil, error: nil)
+ allow_original_file_read
+
+ expectation = expect(File).to receive(:read).with(file)
+
+ if error
+ expectation.and_raise(error)
+ elsif content
+ expectation.and_return(content)
+ else
+ expectation
+ end
+ end
+
+ def expect_file_not_to_read(file)
+ allow_original_file_read
+
+ expect(File).not_to receive(:read).with(file)
+ end
+
+ private
+
+ def allow_original_file_read
+ # Don't set this mock twice, otherwise subsequent calls will clobber
+ # previous mocks
+ return if @allow_original_file_read
+
+ @allow_original_file_read = true
+ allow(File).to receive(:read).and_call_original
+ end
+end
diff --git a/spec/support/helpers/filtered_search_helpers.rb b/spec/support/helpers/filtered_search_helpers.rb
index d203ff60cc9..10068b9c508 100644
--- a/spec/support/helpers/filtered_search_helpers.rb
+++ b/spec/support/helpers/filtered_search_helpers.rb
@@ -113,6 +113,10 @@ module FilteredSearchHelpers
create_token('Assignee', assignee_name)
end
+ def reviewer_token(reviewer_name = nil)
+ create_token('Reviewer', reviewer_name)
+ end
+
def milestone_token(milestone_name = nil, has_symbol = true, operator = '=')
symbol = has_symbol ? '%' : nil
create_token('Milestone', milestone_name, symbol, operator)
diff --git a/spec/support/helpers/git_http_helpers.rb b/spec/support/helpers/git_http_helpers.rb
index c9c1c4dcfc9..c9b4e06f067 100644
--- a/spec/support/helpers/git_http_helpers.rb
+++ b/spec/support/helpers/git_http_helpers.rb
@@ -5,45 +5,45 @@ require_relative 'workhorse_helpers'
module GitHttpHelpers
include WorkhorseHelpers
- def clone_get(project, **options)
- get "/#{project}/info/refs", params: { service: 'git-upload-pack' }, headers: auth_env(*options.values_at(:user, :password, :spnego_request_token))
+ def clone_get(repository_path, **options)
+ get "/#{repository_path}/info/refs", params: { service: 'git-upload-pack' }, headers: auth_env(*options.values_at(:user, :password, :spnego_request_token))
end
- def clone_post(project, **options)
- post "/#{project}/git-upload-pack", headers: auth_env(*options.values_at(:user, :password, :spnego_request_token))
+ def clone_post(repository_path, **options)
+ post "/#{repository_path}/git-upload-pack", headers: auth_env(*options.values_at(:user, :password, :spnego_request_token))
end
- def push_get(project, **options)
- get "/#{project}/info/refs", params: { service: 'git-receive-pack' }, headers: auth_env(*options.values_at(:user, :password, :spnego_request_token))
+ def push_get(repository_path, **options)
+ get "/#{repository_path}/info/refs", params: { service: 'git-receive-pack' }, headers: auth_env(*options.values_at(:user, :password, :spnego_request_token))
end
- def push_post(project, **options)
- post "/#{project}/git-receive-pack", headers: auth_env(*options.values_at(:user, :password, :spnego_request_token))
+ def push_post(repository_path, **options)
+ post "/#{repository_path}/git-receive-pack", headers: auth_env(*options.values_at(:user, :password, :spnego_request_token))
end
- def download(project, user: nil, password: nil, spnego_request_token: nil)
+ def download(repository_path, user: nil, password: nil, spnego_request_token: nil)
args = { user: user, password: password, spnego_request_token: spnego_request_token }
- clone_get(project, **args)
+ clone_get(repository_path, **args)
yield response
- clone_post(project, **args)
+ clone_post(repository_path, **args)
yield response
end
- def upload(project, user: nil, password: nil, spnego_request_token: nil)
+ def upload(repository_path, user: nil, password: nil, spnego_request_token: nil)
args = { user: user, password: password, spnego_request_token: spnego_request_token }
- push_get(project, **args)
+ push_get(repository_path, **args)
yield response
- push_post(project, **args)
+ push_post(repository_path, **args)
yield response
end
- def download_or_upload(project, **args, &block)
- download(project, **args, &block)
- upload(project, **args, &block)
+ def download_or_upload(repository_path, **args, &block)
+ download(repository_path, **args, &block)
+ upload(repository_path, **args, &block)
end
def auth_env(user, password, spnego_request_token)
diff --git a/spec/support/helpers/gitlab_verify_helpers.rb b/spec/support/helpers/gitlab_verify_helpers.rb
index 9901ce374ed..2a6ba8aaff4 100644
--- a/spec/support/helpers/gitlab_verify_helpers.rb
+++ b/spec/support/helpers/gitlab_verify_helpers.rb
@@ -2,7 +2,7 @@
module GitlabVerifyHelpers
def collect_ranges(args = {})
- verifier = described_class.new(args.merge(batch_size: 1))
+ verifier = described_class.new(**args.merge(batch_size: 1))
collect_results(verifier).map { |range, _| range }
end
diff --git a/spec/support/helpers/gpg_helpers.rb b/spec/support/helpers/gpg_helpers.rb
index f4df1cf601c..389e5818dbe 100644
--- a/spec/support/helpers/gpg_helpers.rb
+++ b/spec/support/helpers/gpg_helpers.rb
@@ -144,6 +144,145 @@ module GpgHelpers
'5F7EA3981A5845B141ABD522CCFBE19F00AC8B1D'
end
+ def secret_key2
+ <<~KEY.strip
+ -----BEGIN PGP PRIVATE KEY BLOCK-----
+
+ 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
+ 4f6xMfFu2A==
+ =3YL6
+ -----END PGP PRIVATE KEY BLOCK-----
+ KEY
+ end
+
+ def public_key2
+ <<~KEY.strip
+ -----BEGIN PGP PUBLIC KEY BLOCK-----
+
+ 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
+ =RAwd
+ -----END PGP PUBLIC KEY BLOCK-----
+ KEY
+ end
+
+ def fingerprint2
+ 'C447A6F6BFD9CEF8FB371785571625A930241179'
+ end
+
def names
['Nannie Bernhard']
end
diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb
index a1b4e6eee92..b20801bd3c4 100644
--- a/spec/support/helpers/graphql_helpers.rb
+++ b/spec/support/helpers/graphql_helpers.rb
@@ -4,6 +4,11 @@ module GraphqlHelpers
MutationDefinition = Struct.new(:query, :variables)
NoData = Class.new(StandardError)
+ UnauthorizedObject = Class.new(StandardError)
+
+ def graphql_args(**values)
+ ::Graphql::Arguments.new(values)
+ end
# makes an underscored string look like a fieldname
# "merge_request" => "mergeRequest"
@@ -17,7 +22,10 @@ module GraphqlHelpers
# ready, then the early return is returned instead.
#
# Then the resolve method is called.
- def resolve(resolver_class, args: {}, **resolver_args)
+ def resolve(resolver_class, args: {}, lookahead: :not_given, parent: :not_given, **resolver_args)
+ args = aliased_args(resolver_class, args)
+ args[:parent] = parent unless parent == :not_given
+ args[:lookahead] = lookahead unless lookahead == :not_given
resolver = resolver_instance(resolver_class, **resolver_args)
ready, early_return = sync_all { resolver.ready?(**args) }
@@ -26,6 +34,16 @@ module GraphqlHelpers
resolver.resolve(**args)
end
+ # TODO: Remove this method entirely when GraphqlHelpers uses real resolve_field
+ # see: https://gitlab.com/gitlab-org/gitlab/-/issues/287791
+ def aliased_args(resolver, args)
+ definitions = resolver.arguments
+
+ args.transform_keys do |k|
+ definitions[GraphqlHelpers.fieldnamerize(k)]&.keyword || k
+ end
+ end
+
def resolver_instance(resolver_class, obj: nil, ctx: {}, field: nil, schema: GitlabSchema)
if ctx.is_a?(Hash)
q = double('Query', schema: schema)
@@ -111,24 +129,33 @@ module GraphqlHelpers
def variables_for_mutation(name, input)
graphql_input = prepare_input_for_mutation(input)
- result = { input_variable_name_for_mutation(name) => graphql_input }
+ { input_variable_name_for_mutation(name) => graphql_input }
+ end
- # Avoid trying to serialize multipart data into JSON
- if graphql_input.values.none? { |value| io_value?(value) }
- result.to_json
- else
- result
- end
+ def serialize_variables(variables)
+ return unless variables
+ return variables if variables.is_a?(String)
+
+ ::Gitlab::Utils::MergeHash.merge(Array.wrap(variables).map(&:to_h)).to_json
end
- def resolve_field(name, object, args = {})
- context = double("Context",
- schema: GitlabSchema,
- query: GraphQL::Query.new(GitlabSchema),
- parent: nil)
- field = described_class.fields[name]
+ def resolve_field(name, object, args = {}, current_user: nil)
+ q = GraphQL::Query.new(GitlabSchema)
+ context = GraphQL::Query::Context.new(query: q, object: object, values: { current_user: current_user })
+ allow(context).to receive(:parent).and_return(nil)
+ field = described_class.fields.fetch(GraphqlHelpers.fieldnamerize(name))
instance = described_class.authorized_new(object, context)
- field.resolve_field(instance, {}, context)
+ raise UnauthorizedObject unless instance
+
+ field.resolve_field(instance, args, context)
+ end
+
+ def simple_resolver(resolved_value = 'Resolved value')
+ Class.new(Resolvers::BaseResolver) do
+ define_method :resolve do |**_args|
+ resolved_value
+ end
+ end
end
# Recursively convert a Hash with Ruby-style keys to GraphQL fieldname-style keys
@@ -165,10 +192,32 @@ module GraphqlHelpers
end
def query_graphql_field(name, attributes = {}, fields = nil)
- <<~QUERY
- #{field_with_params(name, attributes)}
- #{wrap_fields(fields || all_graphql_fields_for(name.to_s.classify))}
- QUERY
+ attributes, fields = [nil, attributes] if fields.nil? && !attributes.is_a?(Hash)
+
+ field = field_with_params(name, attributes)
+
+ field + wrap_fields(fields || all_graphql_fields_for(name.to_s.classify)).to_s
+ end
+
+ def page_info_selection
+ "pageInfo { hasNextPage hasPreviousPage endCursor startCursor }"
+ end
+
+ def query_nodes(name, fields = nil, args: nil, of: name, include_pagination_info: false, max_depth: 1)
+ fields ||= all_graphql_fields_for(of.to_s.classify, max_depth: max_depth)
+ node_selection = include_pagination_info ? "#{page_info_selection} nodes" : :nodes
+ query_graphql_path([[name, args], node_selection], fields)
+ end
+
+ # e.g:
+ # query_graphql_path(%i[foo bar baz], all_graphql_fields_for('Baz'))
+ # => foo { bar { baz { x y z } } }
+ def query_graphql_path(segments, fields = nil)
+ # we really want foldr here...
+ segments.reverse.reduce(fields) do |tail, segment|
+ name, args = Array.wrap(segment)
+ query_graphql_field(name, args, tail)
+ end
end
def wrap_fields(fields)
@@ -203,50 +252,22 @@ module GraphqlHelpers
type = GitlabSchema.types[class_name.to_s]
return "" unless type
- type.fields.map do |name, field|
- # We can't guess arguments, so skip fields that require them
- next if required_arguments?(field)
- next if excluded.include?(name)
-
- singular_field_type = field_type(field)
-
- # If field type is the same as parent type, then we're hitting into
- # mutual dependency. Break it from infinite recursion
- next if parent_types.include?(singular_field_type)
+ # We can't guess arguments, so skip fields that require them
+ skip = ->(name, field) { excluded.include?(name) || required_arguments?(field) }
- if nested_fields?(field)
- fields =
- all_graphql_fields_for(singular_field_type, parent_types | [type], max_depth: max_depth - 1)
-
- "#{name} { #{fields} }" unless fields.blank?
- else
- name
- end
- end.compact.join("\n")
+ ::Graphql::FieldSelection.select_fields(type, skip, parent_types, max_depth)
end
- def attributes_to_graphql(attributes)
- attributes.map do |name, value|
- value_str = as_graphql_literal(value)
+ def with_signature(variables, query)
+ %Q[query(#{variables.map(&:sig).join(', ')}) #{query}]
+ end
- "#{GraphqlHelpers.fieldnamerize(name.to_s)}: #{value_str}"
- end.join(", ")
+ def var(type)
+ ::Graphql::Var.new(generate(:variable), type)
end
- # Fairly dumb Ruby => GraphQL rendering function. Only suitable for testing.
- # Use symbol for Enum values
- def as_graphql_literal(value)
- case value
- when Array then "[#{value.map { |v| as_graphql_literal(v) }.join(',')}]"
- when Hash then "{#{attributes_to_graphql(value)}}"
- when Integer, Float then value.to_s
- when String then "\"#{value.gsub(/"/, '\\"')}\""
- when Symbol then value
- when nil then 'null'
- when true then 'true'
- when false then 'false'
- else raise ArgumentError, "Cannot represent #{value} as GraphQL literal"
- end
+ def attributes_to_graphql(arguments)
+ ::Graphql::Arguments.new(arguments).to_s
end
def post_multiplex(queries, current_user: nil, headers: {})
@@ -254,7 +275,7 @@ module GraphqlHelpers
end
def post_graphql(query, current_user: nil, variables: nil, headers: {})
- params = { query: query, variables: variables&.to_json }
+ params = { query: query, variables: serialize_variables(variables) }
post api('/', current_user, version: 'graphql'), params: params, headers: headers
end
@@ -320,36 +341,47 @@ module GraphqlHelpers
{ operations: operations.to_json, map: map.to_json }.merge(extracted_files)
end
+ def fresh_response_data
+ Gitlab::Json.parse(response.body)
+ end
+
# Raises an error if no data is found
- def graphql_data
+ def graphql_data(body = json_response)
# Note that `json_response` is defined as `let(:json_response)` and
# therefore, in a spec with multiple queries, will only contain data
# from the _first_ query, not subsequent ones
- json_response['data'] || (raise NoData, graphql_errors)
+ body['data'] || (raise NoData, graphql_errors(body))
end
def graphql_data_at(*path)
graphql_dig_at(graphql_data, *path)
end
+ # Slightly more powerful than just `dig`:
+ # - also supports implicit flat-mapping (.e.g. :foo :nodes :bar :nodes)
def graphql_dig_at(data, *path)
keys = path.map { |segment| segment.is_a?(Integer) ? segment : GraphqlHelpers.fieldnamerize(segment) }
# Allows for array indexing, like this
# ['project', 'boards', 'edges', 0, 'node', 'lists']
keys.reduce(data) do |memo, key|
- memo.is_a?(Array) ? memo[key] : memo&.dig(key)
+ if memo.is_a?(Array)
+ key.is_a?(Integer) ? memo[key] : memo.flat_map { |e| Array.wrap(e[key]) }
+ else
+ memo&.dig(key)
+ end
end
end
- def graphql_errors
- case json_response
+ # See note at graphql_data about memoization and multiple requests
+ def graphql_errors(body = json_response)
+ case body
when Hash # regular query
- json_response['errors']
+ body['errors']
when Array # multiplexed queries
- json_response.map { |response| response['errors'] }
+ body.map { |response| response['errors'] }
else
- raise "Unknown GraphQL response type #{json_response.class}"
+ raise "Unknown GraphQL response type #{body.class}"
end
end
@@ -392,19 +424,29 @@ module GraphqlHelpers
end
def nested_fields?(field)
- !scalar?(field) && !enum?(field)
+ ::Graphql::FieldInspection.new(field).nested_fields?
end
def scalar?(field)
- field_type(field).kind.scalar?
+ ::Graphql::FieldInspection.new(field).scalar?
end
def enum?(field)
- field_type(field).kind.enum?
+ ::Graphql::FieldInspection.new(field).enum?
end
+ # There are a few non BaseField fields in our schema (pageInfo for one).
+ # None of them require arguments.
def required_arguments?(field)
- field.arguments.values.any? { |argument| argument.type.non_null? }
+ return field.requires_argument? if field.is_a?(::Types::BaseField)
+
+ if (meta = field.try(:metadata)) && meta[:type_class]
+ required_arguments?(meta[:type_class])
+ elsif args = field.try(:arguments)
+ args.values.any? { |argument| argument.type.non_null? }
+ else
+ false
+ end
end
def io_value?(value)
@@ -412,15 +454,7 @@ module GraphqlHelpers
end
def field_type(field)
- field_type = field.type.respond_to?(:to_graphql) ? field.type.to_graphql : field.type
-
- # The type could be nested. For example `[GraphQL::STRING_TYPE]`:
- # - List
- # - String!
- # - String
- field_type = field_type.of_type while field_type.respond_to?(:of_type)
-
- field_type
+ ::Graphql::FieldInspection.new(field).type
end
# for most tests, we want to allow unlimited complexity
@@ -498,6 +532,20 @@ module GraphqlHelpers
variables: {}
)
end
+
+ # A lookahead that selects everything
+ def positive_lookahead
+ double(selects?: true).tap do |selection|
+ allow(selection).to receive(:selection).and_return(selection)
+ end
+ end
+
+ # A lookahead that selects nothing
+ def negative_lookahead
+ double(selects?: false).tap do |selection|
+ allow(selection).to receive(:selection).and_return(selection)
+ end
+ end
end
# This warms our schema, doing this as part of loading the helpers to avoid
diff --git a/spec/support/helpers/login_helpers.rb b/spec/support/helpers/login_helpers.rb
index e21d4497cda..86022e16d71 100644
--- a/spec/support/helpers/login_helpers.rb
+++ b/spec/support/helpers/login_helpers.rb
@@ -138,13 +138,15 @@ module LoginHelpers
secret: 'mock_secret'
},
extra: {
- raw_info: {
- info: {
- name: 'mockuser',
- email: email,
- image: 'mock_user_thumbnail_url'
+ raw_info: OneLogin::RubySaml::Attributes.new(
+ {
+ info: {
+ name: 'mockuser',
+ email: email,
+ image: 'mock_user_thumbnail_url'
+ }
}
- },
+ ),
response_object: response_object
}
}).merge(additional_info) { |_, old_hash, new_hash| old_hash.merge(new_hash) }
@@ -198,7 +200,7 @@ module LoginHelpers
env['omniauth.error.strategy'] = strategy
end
- def stub_omniauth_saml_config(messages, context: Rails.application)
+ def stub_omniauth_saml_config(context: Rails.application, **messages)
set_devise_mapping(context: context)
routes = Rails.application.routes
routes.disable_clear_and_finalize = true
diff --git a/spec/support/helpers/multipart_helpers.rb b/spec/support/helpers/multipart_helpers.rb
index 2e8db0e9e42..bcb184f84c5 100644
--- a/spec/support/helpers/multipart_helpers.rb
+++ b/spec/support/helpers/multipart_helpers.rb
@@ -37,7 +37,7 @@ module MultipartHelpers
# *without* the "#{key}." prefix
result.deep_transform_keys! { |k| k.remove("#{key}.") }
{
- "#{key}.gitlab-workhorse-upload" => jwt_token('upload' => result)
+ "#{key}.gitlab-workhorse-upload" => jwt_token(data: { 'upload' => result })
}
end
diff --git a/spec/support/helpers/next_instance_of.rb b/spec/support/helpers/next_instance_of.rb
index 83c788c3d38..a8e9ab2bafe 100644
--- a/spec/support/helpers/next_instance_of.rb
+++ b/spec/support/helpers/next_instance_of.rb
@@ -1,28 +1,31 @@
# frozen_string_literal: true
module NextInstanceOf
- def expect_next_instance_of(klass, *new_args)
- stub_new(expect(klass), *new_args) do |expectation|
- yield(expectation)
- end
+ def expect_next_instance_of(klass, *new_args, &blk)
+ stub_new(expect(klass), nil, *new_args, &blk)
end
- def allow_next_instance_of(klass, *new_args)
- stub_new(allow(klass), *new_args) do |allowance|
- yield(allowance)
- end
+ def expect_next_instances_of(klass, number, *new_args, &blk)
+ stub_new(expect(klass), number, *new_args, &blk)
+ end
+
+ def allow_next_instance_of(klass, *new_args, &blk)
+ stub_new(allow(klass), nil, *new_args, &blk)
+ end
+
+ def allow_next_instances_of(klass, number, *new_args, &blk)
+ stub_new(allow(klass), number, *new_args, &blk)
end
private
- def stub_new(target, *new_args)
+ def stub_new(target, number, *new_args, &blk)
receive_new = receive(:new)
+ receive_new.exactly(number).times if number
receive_new.with(*new_args) if new_args.any?
target.to receive_new.and_wrap_original do |method, *original_args|
- method.call(*original_args).tap do |instance|
- yield(instance)
- end
+ method.call(*original_args).tap(&blk)
end
end
end
diff --git a/spec/support/helpers/services_helper.rb b/spec/support/helpers/services_helper.rb
new file mode 100644
index 00000000000..bf007815551
--- /dev/null
+++ b/spec/support/helpers/services_helper.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require_relative './after_next_helpers'
+
+module ServicesHelper
+ include AfterNextHelpers
+
+ def expect_execution_of(service_class, *args)
+ expect_next(service_class, *args).to receive(:execute)
+ end
+end
diff --git a/spec/support/helpers/snowplow_helpers.rb b/spec/support/helpers/snowplow_helpers.rb
index 15eac1b24fc..70a4eadd8de 100644
--- a/spec/support/helpers/snowplow_helpers.rb
+++ b/spec/support/helpers/snowplow_helpers.rb
@@ -31,7 +31,31 @@ module SnowplowHelpers
# )
# end
# end
- def expect_snowplow_event(category:, action:, **kwargs)
+ #
+ # Passing context:
+ #
+ # Simply provide a hash that has the schema and data expected.
+ #
+ # expect_snowplow_event(
+ # category: 'Experiment',
+ # action: 'created',
+ # context: [
+ # {
+ # schema: 'iglu:com.gitlab/.../0-3-0',
+ # data: { key: 'value' }
+ # }
+ # ]
+ # )
+ def expect_snowplow_event(category:, action:, context: nil, **kwargs)
+ if context
+ kwargs[:context] = []
+ context.each do |c|
+ expect(SnowplowTracker::SelfDescribingJson).to have_received(:new)
+ .with(c[:schema], c[:data]).at_least(:once)
+ kwargs[:context] << an_instance_of(SnowplowTracker::SelfDescribingJson)
+ end
+ end
+
expect(Gitlab::Tracking).to have_received(:event) # rubocop:disable RSpec/ExpectGitlabTracking
.with(category, action, **kwargs).at_least(:once)
end
diff --git a/spec/support/helpers/stub_experiments.rb b/spec/support/helpers/stub_experiments.rb
index 7a6154d5ef9..247692d83ee 100644
--- a/spec/support/helpers/stub_experiments.rb
+++ b/spec/support/helpers/stub_experiments.rb
@@ -3,15 +3,15 @@
module StubExperiments
# Stub Experiment with `key: true/false`
#
- # @param [Hash] experiment where key is feature name and value is boolean whether enabled or not.
+ # @param [Hash] experiment where key is feature name and value is boolean whether active or not.
#
# Examples
- # - `stub_experiment(signup_flow: false)` ... Disable `signup_flow` experiment globally.
+ # - `stub_experiment(signup_flow: false)` ... Disables `signup_flow` experiment.
def stub_experiment(experiments)
- allow(Gitlab::Experimentation).to receive(:enabled?).and_call_original
+ allow(Gitlab::Experimentation).to receive(:active?).and_call_original
experiments.each do |experiment_key, enabled|
- allow(Gitlab::Experimentation).to receive(:enabled?).with(experiment_key) { enabled }
+ allow(Gitlab::Experimentation).to receive(:active?).with(experiment_key) { enabled }
end
end
@@ -20,12 +20,12 @@ module StubExperiments
# @param [Hash] experiment where key is feature name and value is boolean whether enabled or not.
#
# Examples
- # - `stub_experiment_for_user(signup_flow: false)` ... Disable `signup_flow` experiment for user.
- def stub_experiment_for_user(experiments)
- allow(Gitlab::Experimentation).to receive(:enabled_for_value?).and_call_original
+ # - `stub_experiment_for_subject(signup_flow: false)` ... Disable `signup_flow` experiment for user.
+ def stub_experiment_for_subject(experiments)
+ allow(Gitlab::Experimentation).to receive(:in_experiment_group?).and_call_original
experiments.each do |experiment_key, enabled|
- allow(Gitlab::Experimentation).to receive(:enabled_for_value?).with(experiment_key, anything) { enabled }
+ allow(Gitlab::Experimentation).to receive(:in_experiment_group?).with(experiment_key, anything) { enabled }
end
end
end
diff --git a/spec/support/helpers/stub_object_storage.rb b/spec/support/helpers/stub_object_storage.rb
index dba3d2b137e..dc54a21d0fa 100644
--- a/spec/support/helpers/stub_object_storage.rb
+++ b/spec/support/helpers/stub_object_storage.rb
@@ -22,6 +22,16 @@ module StubObjectStorage
background_upload: false,
direct_upload: false
)
+ new_config = config.to_h.deep_symbolize_keys.merge({
+ enabled: enabled,
+ proxy_download: proxy_download,
+ background_upload: background_upload,
+ direct_upload: direct_upload
+ })
+
+ # Needed for ObjectStorage::Config compatibility
+ allow(config).to receive(:to_hash).and_return(new_config)
+ allow(config).to receive(:to_h).and_return(new_config)
allow(config).to receive(:enabled) { enabled }
allow(config).to receive(:proxy_download) { proxy_download }
allow(config).to receive(:background_upload) { background_upload }
@@ -84,13 +94,6 @@ module StubObjectStorage
def stub_terraform_state_object_storage(**params)
stub_object_storage_uploader(config: Gitlab.config.terraform_state.object_store,
- uploader: Terraform::VersionedStateUploader,
- remote_directory: 'terraform',
- **params)
- end
-
- def stub_terraform_state_version_object_storage(**params)
- stub_object_storage_uploader(config: Gitlab.config.terraform_state.object_store,
uploader: Terraform::StateUploader,
remote_directory: 'terraform',
**params)
diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb
index 4c78ca0117c..01571277a1d 100644
--- a/spec/support/helpers/test_env.rb
+++ b/spec/support/helpers/test_env.rb
@@ -168,6 +168,11 @@ module TestEnv
version: Gitlab::GitalyClient.expected_server_version,
task: "gitlab:gitaly:install[#{install_gitaly_args}]") do
Gitlab::SetupHelper::Gitaly.create_configuration(gitaly_dir, { 'default' => repos_path }, force: true)
+ Gitlab::SetupHelper::Gitaly.create_configuration(
+ gitaly_dir,
+ { 'default' => repos_path }, force: true,
+ options: { gitaly_socket: "gitaly2.socket", config_filename: "gitaly2.config.toml" }
+ )
Gitlab::SetupHelper::Praefect.create_configuration(gitaly_dir, { 'praefect' => repos_path }, force: true)
end
@@ -241,15 +246,38 @@ module TestEnv
end
def setup_workhorse
- install_workhorse_args = [workhorse_dir, workhorse_url].compact.join(',')
-
- component_timed_setup(
- 'GitLab Workhorse',
- install_dir: workhorse_dir,
- version: Gitlab::Workhorse.version,
- task: "gitlab:workhorse:install[#{install_workhorse_args}]") do
- Gitlab::SetupHelper::Workhorse.create_configuration(workhorse_dir, nil)
- end
+ start = Time.now
+ return if skip_compile_workhorse?
+
+ puts "\n==> Setting up GitLab Workhorse..."
+
+ FileUtils.rm_rf(workhorse_dir)
+ Gitlab::SetupHelper::Workhorse.compile_into(workhorse_dir)
+ Gitlab::SetupHelper::Workhorse.create_configuration(workhorse_dir, nil)
+
+ File.write(workhorse_tree_file, workhorse_tree) if workhorse_source_clean?
+
+ puts " GitLab Workhorse set up in #{Time.now - start} seconds...\n"
+ end
+
+ def skip_compile_workhorse?
+ File.directory?(workhorse_dir) &&
+ workhorse_source_clean? &&
+ File.exist?(workhorse_tree_file) &&
+ workhorse_tree == File.read(workhorse_tree_file)
+ end
+
+ def workhorse_source_clean?
+ out = IO.popen(%w[git status --porcelain workhorse], &:read)
+ $?.success? && out.empty?
+ end
+
+ def workhorse_tree
+ IO.popen(%w[git rev-parse HEAD:workhorse], &:read)
+ end
+
+ def workhorse_tree_file
+ File.join(workhorse_dir, 'WORKHORSE_TREE')
end
def workhorse_dir
@@ -260,7 +288,7 @@ module TestEnv
host = "[#{host}]" if host.include?(':')
listen_addr = [host, port].join(':')
- config_path = Gitlab::SetupHelper::Workhorse.get_config_path(workhorse_dir)
+ config_path = Gitlab::SetupHelper::Workhorse.get_config_path(workhorse_dir, {})
# This should be set up in setup_workhorse, but since
# component_needs_update? only checks that versions are consistent,
diff --git a/spec/support/helpers/usage_data_helpers.rb b/spec/support/helpers/usage_data_helpers.rb
index 8e8aeea2ea1..df79049123d 100644
--- a/spec/support/helpers/usage_data_helpers.rb
+++ b/spec/support/helpers/usage_data_helpers.rb
@@ -85,6 +85,7 @@ module UsageDataHelpers
projects
projects_imported_from_github
projects_asana_active
+ projects_jenkins_active
projects_jira_active
projects_jira_server_active
projects_jira_cloud_active
@@ -161,7 +162,6 @@ module UsageDataHelpers
git
gitaly
database
- avg_cycle_analytics
prometheus_metrics_enabled
web_ide_clientside_preview_enabled
ingress_modsecurity_enabled
diff --git a/spec/support/helpers/workhorse_helpers.rb b/spec/support/helpers/workhorse_helpers.rb
index 7e95f49aea2..cd8387de686 100644
--- a/spec/support/helpers/workhorse_helpers.rb
+++ b/spec/support/helpers/workhorse_helpers.rb
@@ -85,15 +85,15 @@ module WorkhorseHelpers
return {} if upload_params.empty?
- { "#{key}.gitlab-workhorse-upload" => jwt_token('upload' => upload_params) }
+ { "#{key}.gitlab-workhorse-upload" => jwt_token(data: { 'upload' => upload_params }) }
end
- def jwt_token(data = {}, issuer: 'gitlab-workhorse', secret: Gitlab::Workhorse.secret, algorithm: 'HS256')
+ def jwt_token(data: {}, issuer: 'gitlab-workhorse', secret: Gitlab::Workhorse.secret, algorithm: 'HS256')
JWT.encode({ 'iss' => issuer }.merge(data), secret, algorithm)
end
def workhorse_rewritten_fields_header(fields)
- { Gitlab::Middleware::Multipart::RACK_ENV_KEY => jwt_token('rewritten_fields' => fields) }
+ { Gitlab::Middleware::Multipart::RACK_ENV_KEY => jwt_token(data: { 'rewritten_fields' => fields }) }
end
def workhorse_disk_accelerated_file_params(key, file)