diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-01-20 09:16:11 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-01-20 09:16:11 +0000 |
commit | edaa33dee2ff2f7ea3fac488d41558eb5f86d68c (patch) | |
tree | 11f143effbfeba52329fb7afbd05e6e2a3790241 /qa/qa/resource | |
parent | d8a5691316400a0f7ec4f83832698f1988eb27c1 (diff) | |
download | gitlab-ce-edaa33dee2ff2f7ea3fac488d41558eb5f86d68c.tar.gz |
Add latest changes from gitlab-org/gitlab@14-7-stable-eev14.7.0-rc42
Diffstat (limited to 'qa/qa/resource')
-rw-r--r-- | qa/qa/resource/api_fabricator.rb | 6 | ||||
-rw-r--r-- | qa/qa/resource/base.rb | 75 | ||||
-rw-r--r-- | qa/qa/resource/bulk_import_group.rb | 19 | ||||
-rw-r--r-- | qa/qa/resource/group_badge.rb | 17 | ||||
-rw-r--r-- | qa/qa/resource/group_base.rb | 12 | ||||
-rw-r--r-- | qa/qa/resource/group_milestone.rb | 17 | ||||
-rw-r--r-- | qa/qa/resource/issue.rb | 22 | ||||
-rw-r--r-- | qa/qa/resource/label_base.rb | 17 | ||||
-rw-r--r-- | qa/qa/resource/merge_request.rb | 71 | ||||
-rw-r--r-- | qa/qa/resource/project.rb | 69 | ||||
-rw-r--r-- | qa/qa/resource/repository/commit.rb | 99 | ||||
-rw-r--r-- | qa/qa/resource/reusable_group.rb | 54 | ||||
-rw-r--r-- | qa/qa/resource/reusable_project.rb | 6 | ||||
-rw-r--r-- | qa/qa/resource/user.rb | 15 |
14 files changed, 288 insertions, 211 deletions
diff --git a/qa/qa/resource/api_fabricator.rb b/qa/qa/resource/api_fabricator.rb index 6315ef0bd22..4c77c515cfd 100644 --- a/qa/qa/resource/api_fabricator.rb +++ b/qa/qa/resource/api_fabricator.rb @@ -63,6 +63,10 @@ module QA process_api_response(parse_body(response)) end + def api_fabrication_http_method + @api_fabrication_http_method ||= :post + end + private def resource_web_url(resource) @@ -85,6 +89,8 @@ module QA raise ResourceNotFoundError, "Resource at #{request.mask_url} could not be found (#{response.code}): `#{response}`." end + @api_fabrication_http_method = :get # rubocop:disable Gitlab/ModuleWithInstanceVariables + response end diff --git a/qa/qa/resource/base.rb b/qa/qa/resource/base.rb index 640d2a8f06e..0112e766cf0 100644 --- a/qa/qa/resource/base.rb +++ b/qa/qa/resource/base.rb @@ -71,34 +71,49 @@ module QA resource_web_url = yield resource.web_url = resource_web_url + QA::Tools::TestResourceDataProcessor.collect(resource, resource_identifier(resource)) + resource end + def resource_identifier(resource) + if resource.respond_to?(:username) && resource.username + "with username '#{resource.username}'" + elsif resource.respond_to?(:full_path) && resource.full_path + "with full_path '#{resource.full_path}'" + elsif resource.respond_to?(:name) && resource.name + "with name '#{resource.name}'" + elsif resource.respond_to?(:id) && resource.id + "with id '#{resource.id}'" + elsif resource.respond_to?(:iid) && resource.iid + "with iid '#{resource.iid}'" + end + rescue QA::Resource::Base::NoValueError + nil + end + def log_fabrication(method, resource, parents, args) start = Time.now Support::FabricationTracker.start_fabrication result = yield.tap do fabrication_time = Time.now - start - resource_identifier = begin - if resource.respond_to?(:username) && resource.username - "with username '#{resource.username}'" - elsif resource.respond_to?(:full_path) && resource.full_path - "with full_path '#{resource.full_path}'" - elsif resource.respond_to?(:name) && resource.name - "with name '#{resource.name}'" - elsif resource.respond_to?(:id) && resource.id - "with id '#{resource.id}'" - end - rescue QA::Resource::Base::NoValueError - nil - end + + fabrication_http_method = if resource.api_fabrication_http_method == :get + if self.include?(Reusable) + "Retrieved for reuse" + else + "Retrieved" + end + else + "Built" + end Support::FabricationTracker.save_fabrication(:"#{method}_fabrication", fabrication_time) Runtime::Logger.debug do msg = ["==#{'=' * parents.size}>"] - msg << "Built a #{name}" - msg << resource_identifier if resource_identifier + msg << "#{fabrication_http_method} a #{name}" + msg << resource_identifier(resource) if resource_identifier(resource) msg << "as a dependency of #{parents.last}" if parents.any? msg << "via #{method}" msg << "in #{fabrication_time} seconds" @@ -156,11 +171,11 @@ module QA raise NotImplementedError end - def visit! + def visit!(skip_resp_code_check: false) Runtime::Logger.debug(%(Visiting #{self.class.name} at "#{web_url}")) # Just in case an async action is not yet complete - Support::WaitForRequests.wait_for_requests + Support::WaitForRequests.wait_for_requests(skip_resp_code_check: skip_resp_code_check) Support::Retrier.retry_until do visit(web_url) @@ -168,7 +183,7 @@ module QA end # Wait until the new page is ready for us to interact with it - Support::WaitForRequests.wait_for_requests + Support::WaitForRequests.wait_for_requests(skip_resp_code_check: skip_resp_code_check) end def populate(*attribute_names) @@ -179,6 +194,30 @@ module QA QA::Support::Waiter.wait_until(max_duration: max_duration, sleep_interval: sleep_interval, &block) end + # Object comparison + # + # @param [QA::Resource::Base] other + # @return [Boolean] + def ==(other) + other.is_a?(self.class) && comparable == other.comparable + end + + # Override inspect for a better rspec failure diff output + # + # @return [String] + def inspect + JSON.pretty_generate(comparable) + end + + protected + + # Custom resource comparison logic using resource attributes from api_resource + # + # @return [Hash] + def comparable + raise("comparable method needs to be implemented in order to compare resources via '=='") + end + private def attribute_value(name, block) diff --git a/qa/qa/resource/bulk_import_group.rb b/qa/qa/resource/bulk_import_group.rb index e8dc2d291b8..a22529152e1 100644 --- a/qa/qa/resource/bulk_import_group.rb +++ b/qa/qa/resource/bulk_import_group.rb @@ -3,18 +3,21 @@ module QA module Resource class BulkImportGroup < Group - attributes :source_group_path, + attributes :source_group, + :destination_group, :import_id - attribute :destination_group_path do - source_group_path - end - attribute :access_token do api_client.personal_access_token end - alias_method :path, :source_group_path + # In most cases we will want to set path the same as source group + # but it can be set to a custom name as well when imported via API + attribute :destination_group_path do + source_group.path + end + # Can't define path as attribue since @path is set in base class initializer + alias_method :path, :destination_group_path delegate :gitlab_address, to: 'QA::Runtime::Scenario' @@ -51,9 +54,9 @@ module QA entities: [ { source_type: 'group_entity', - source_full_path: source_group_path, + source_full_path: source_group.full_path, destination_name: destination_group_path, - destination_namespace: sandbox.path + destination_namespace: sandbox.full_path } ] } diff --git a/qa/qa/resource/group_badge.rb b/qa/qa/resource/group_badge.rb index fd76f066e8b..3719b502b93 100644 --- a/qa/qa/resource/group_badge.rb +++ b/qa/qa/resource/group_badge.rb @@ -39,27 +39,12 @@ module QA # @return [String] def resource_web_url(_resource); end - # Object comparison - # - # @param [QA::Resource::GroupBadge] other - # @return [Boolean] - def ==(other) - other.is_a?(GroupBadge) && comparable_badge == other.comparable_badge - end - - # Override inspect for a better rspec failure diff output - # - # @return [String] - def inspect - JSON.pretty_generate(comparable_badge) - end - protected # Return subset of fields for comparing badges # # @return [Hash] - def comparable_badge + def comparable reload! unless api_response api_response.slice( diff --git a/qa/qa/resource/group_base.rb b/qa/qa/resource/group_base.rb index 19bb5ea00d7..9f492a046db 100644 --- a/qa/qa/resource/group_base.rb +++ b/qa/qa/resource/group_base.rb @@ -123,18 +123,12 @@ module QA end # Object comparison + # Override to make sure we are comparing descendands of GroupBase # # @param [QA::Resource::GroupBase] other # @return [Boolean] def ==(other) - other.is_a?(GroupBase) && comparable_group == other.comparable_group - end - - # Override inspect for a better rspec failure diff output - # - # @return [String] - def inspect - JSON.pretty_generate(comparable_group) + other.is_a?(GroupBase) && comparable == other.comparable end protected @@ -142,7 +136,7 @@ module QA # Return subset of fields for comparing groups # # @return [Hash] - def comparable_group + def comparable reload! if api_response.nil? api_resource.slice( diff --git a/qa/qa/resource/group_milestone.rb b/qa/qa/resource/group_milestone.rb index 880ca2b9721..b9ec53e929c 100644 --- a/qa/qa/resource/group_milestone.rb +++ b/qa/qa/resource/group_milestone.rb @@ -56,27 +56,12 @@ module QA end end - # Object comparison - # - # @param [QA::Resource::GroupMilestone] other - # @return [Boolean] - def ==(other) - other.is_a?(GroupMilestone) && comparable_milestone == other.comparable_milestone - end - - # Override inspect for a better rspec failure diff output - # - # @return [String] - def inspect - JSON.pretty_generate(comparable_milestone) - end - protected # Return subset of fields for comparing milestones # # @return [Hash] - def comparable_milestone + def comparable reload! unless api_response api_response.slice( diff --git a/qa/qa/resource/issue.rb b/qa/qa/resource/issue.rb index 344f177932f..1e38de97c1e 100644 --- a/qa/qa/resource/issue.rb +++ b/qa/qa/resource/issue.rb @@ -3,7 +3,7 @@ module QA module Resource class Issue < Base - attr_writer :description, :milestone, :template, :weight + attr_writer :milestone, :template, :weight attribute :project do Project.fabricate! do |resource| @@ -95,19 +95,13 @@ module QA ) end - # Object comparison + # Create a new comment # - # @param [QA::Resource::Issue] other - # @return [Boolean] - def ==(other) - other.is_a?(Issue) && comparable_issue == other.comparable_issue - end - - # Override inspect for a better rspec failure diff output - # - # @return [String] - def inspect - JSON.pretty_generate(comparable_issue) + # @param [String] body + # @param [Boolean] confidential + # @return [Hash] + def add_comment(body:, confidential: false) + api_post_to(api_comments_path, body: body, confidential: confidential) end protected @@ -115,7 +109,7 @@ module QA # Return subset of fields for comparing issues # # @return [Hash] - def comparable_issue + def comparable reload! if api_response.nil? api_resource.slice( diff --git a/qa/qa/resource/label_base.rb b/qa/qa/resource/label_base.rb index b1af0e23561..855e41af8bb 100644 --- a/qa/qa/resource/label_base.rb +++ b/qa/qa/resource/label_base.rb @@ -49,27 +49,12 @@ module QA } end - # Object comparison - # - # @param [QA::Resource::GroupBase] other - # @return [Boolean] - def ==(other) - other.is_a?(LabelBase) && comparable_label == other.comparable_label - end - - # Override inspect for a better rspec failure diff output - # - # @return [String] - def inspect - JSON.pretty_generate(comparable_label) - end - protected # Return subset of fields for comparing labels # # @return [Hash] - def comparable_label + def comparable reload! unless api_response api_response.slice( diff --git a/qa/qa/resource/merge_request.rb b/qa/qa/resource/merge_request.rb index ba63e0823f0..685cccea02d 100644 --- a/qa/qa/resource/merge_request.rb +++ b/qa/qa/resource/merge_request.rb @@ -6,11 +6,13 @@ module QA attr_accessor :approval_rules, :source_branch, :target_new_branch, + :update_existing_file, :assignee, :milestone, :labels, :file_name, :file_content + attr_writer :no_preparation, :wait_for_merge, :template @@ -25,6 +27,8 @@ module QA attribute :project do Project.fabricate_via_api! do |resource| resource.name = 'project-with-merge-request' + resource.initialize_with_readme = true + resource.api_client = api_client end end @@ -33,22 +37,27 @@ module QA end attribute :target do - Repository::ProjectPush.fabricate! do |resource| + Repository::Commit.fabricate_via_api! do |resource| resource.project = project - resource.branch_name = target_branch - resource.new_branch = target_new_branch - resource.remote_branch = target_branch + resource.api_client = api_client + resource.commit_message = 'This is a test commit' + resource.add_files([{ 'file_path': "file-#{SecureRandom.hex(8)}.txt", 'content': 'MR init' }]) + resource.branch = target_branch + + resource.start_branch = project.default_branch if target_branch != project.default_branch end end attribute :source do - Repository::ProjectPush.fabricate! do |resource| + Repository::Commit.fabricate_via_api! do |resource| resource.project = project - resource.branch_name = target_branch - resource.remote_branch = source_branch - resource.new_branch = false - resource.file_name = file_name - resource.file_content = file_content + resource.api_client = api_client + resource.commit_message = 'This is a test commit' + resource.branch = source_branch + resource.start_branch = target_branch + + files = [{ 'file_path': file_name, 'content': file_content }] + update_existing_file ? resource.update_files(files) : resource.add_files(files) end end @@ -63,6 +72,7 @@ module QA @file_name = "added_file-#{SecureRandom.hex(8)}.txt" @file_content = "File Added" @target_new_branch = true + @update_existing_file = false @no_preparation = false @wait_for_merge = true end @@ -168,27 +178,18 @@ module QA ) end - # Object comparison - # - # @param [QA::Resource::MergeRequest] other - # @return [Boolean] - def ==(other) - other.is_a?(MergeRequest) && comparable_mr == other.comparable_mr - end - - # Override inspect for a better rspec failure diff output + # Add mr comment # - # @return [String] - def inspect - JSON.pretty_generate(comparable_mr) + # @param [String] body + # @return [Hash] + def add_comment(body) + api_post_to(api_comments_path, body: body) end - protected - # Return subset of fields for comparing merge requests # # @return [Hash] - def comparable_mr + def comparable reload! if api_response.nil? api_resource.except( @@ -197,7 +198,9 @@ module QA :project_id, :source_project_id, :target_project_id, + :merge_status, # these can differ depending on user fetching mr + :user, :subscribed, :first_contribution ).merge({ references: api_resource[:references].except(:full) }) @@ -211,8 +214,24 @@ module QA super(api_resource) end + # Create source and target and commits if necessary + # + # @return [void] def populate_target_and_source_if_required - populate(:target, :source) unless @no_preparation + return if @no_preparation + + populate(:target) if create_target? + populate(:source) + end + + # Check if target needs to be created + # + # Return false if project was already initialized and mr target is default branch + # Return false if target_new_branch is explicitly set to false + # + # @return [Boolean] + def create_target? + !(project.initialize_with_readme && target_branch == project.default_branch) && target_new_branch end end end diff --git a/qa/qa/resource/project.rb b/qa/qa/resource/project.rb index d3c1e91f358..0750ea49224 100644 --- a/qa/qa/resource/project.rb +++ b/qa/qa/resource/project.rb @@ -107,32 +107,6 @@ module QA super end - def has_file?(file_path) - response = repository_tree - - raise ResourceNotFoundError, (response[:message]).to_s if response.is_a?(Hash) && response.has_key?(:message) - - response.any? { |file| file[:path] == file_path } - end - - def has_branch?(branch) - has_branches?(Array(branch)) - end - - def has_branches?(branches) - branches.all? do |branch| - response = get(request_url("#{api_repository_branches_path}/#{branch}")) - response.code == HTTP_STATUS_OK - end - end - - def has_tags?(tags) - tags.all? do |tag| - response = get(request_url("#{api_repository_tags_path}/#{tag}")) - response.code == HTTP_STATUS_OK - end - end - def api_get_path "/projects/#{CGI.escape(path_with_namespace)}" end @@ -237,6 +211,32 @@ module QA post_body end + def has_file?(file_path) + response = repository_tree + + raise ResourceNotFoundError, (response[:message]).to_s if response.is_a?(Hash) && response.has_key?(:message) + + response.any? { |file| file[:path] == file_path } + end + + def has_branch?(branch) + has_branches?(Array(branch)) + end + + def has_branches?(branches) + branches.all? do |branch| + response = get(request_url("#{api_repository_branches_path}/#{branch}")) + response.code == HTTP_STATUS_OK + end + end + + def has_tags?(tags) + tags.all? do |tag| + response = get(request_url("#{api_repository_tags_path}/#{tag}")) + response.code == HTTP_STATUS_OK + end + end + def change_repository_storage(new_storage) response = put(request_url(api_put_path), repository_storage: new_storage) @@ -372,27 +372,12 @@ module QA api_post_to(api_wikis_path, title: title, content: content) end - # Object comparison - # - # @param [QA::Resource::Project] other - # @return [Boolean] - def ==(other) - other.is_a?(Project) && comparable_project == other.comparable_project - end - - # Override inspect for a better rspec failure diff output - # - # @return [String] - def inspect - JSON.pretty_generate(comparable_project) - end - protected # Return subset of fields for comparing projects # # @return [Hash] - def comparable_project + def comparable reload! if api_response.nil? api_resource.slice( diff --git a/qa/qa/resource/repository/commit.rb b/qa/qa/resource/repository/commit.rb index f5dba164de6..54093a5c195 100644 --- a/qa/qa/resource/repository/commit.rb +++ b/qa/qa/resource/repository/commit.rb @@ -22,42 +22,7 @@ module QA def initialize @commit_message = 'QA Test - Commit message' - end - - def add_files(files) - validate_files!(files) - - @add_files = files - end - - def add_directory(dir) - raise "Must set directory as a Pathname" unless dir.is_a?(Pathname) - - files_to_add = [] - - dir.each_child do |child| - case child.ftype? - when "file" - files_to_add.append({ - file_path: child.to_s, - content: child.read - }) - when "directory" - add_directory(child) - else - continue - end - end - - validate_files!(files_to_add) - - @add_files.merge(files_to_add) - end - - def update_files(files) - validate_files!(files) - - @update_files = files + @actions = [] end # If `actions` are specified, it performs the actions to create, @@ -72,32 +37,74 @@ module QA end def api_get_path - api_post_path + "/projects/#{CGI.escape(project.path_with_namespace)}/repository/commits" end def api_post_path - "/projects/#{CGI.escape(project.path_with_namespace)}/repository/commits" + api_get_path end def api_post_body { - branch: @branch || project.default_branch, - author_email: @author_email || Runtime::User.default_email, - author_name: @author_name || Runtime::User.username, + branch: branch || project.default_branch, + author_email: author_email || api_client.user&.email || Runtime::User.default_email, + author_name: author_name || api_client.user&.name || Runtime::User.username, commit_message: commit_message, actions: actions }.merge(new_branch) end - def actions - pending_actions = [] - pending_actions << @add_files.map { |file| file.merge({ action: "create" }) } if @add_files - pending_actions << @update_files.map { |file| file.merge({ action: "update" }) } if @update_files - pending_actions.flatten + # Add files + # Pass in array of new files like, example: + # [{ "file_path": "foo/bar", "content": "some content" }] + # + # @param [Array<Hash>] files + # @return [void] + def add_files(files) + validate_files!(files) + + actions.push(*files.map { |file| file.merge({ action: "create" }) }) + end + + # Update files + # Pass in array of files and it's contents, example: + # [{ "file_path": "foo/bar", "content": "some content" }] + # + # @param [Array<Hash>] files + # @return [void] + def update_files(files) + validate_files!(files) + + actions.push(*files.map { |file| file.merge({ action: "update" }) }) + end + + # Add all files from directory + # + # @param [Pathname] dir + # @return [void] + def add_directory(dir) + raise "Must set directory as a Pathname" unless dir.is_a?(Pathname) + + files_to_add = [] + + dir.each_child do |child| + case child.ftype + when "directory" + add_directory(child) + when "file" + files_to_add.push({ file_path: child.basename, content: child.read }) + else + continue + end + end + + add_files(files_to_add) end private + attr_reader :actions + def validate_files!(files) if !files.is_a?(Array) || files.empty? || diff --git a/qa/qa/resource/reusable_group.rb b/qa/qa/resource/reusable_group.rb new file mode 100644 index 00000000000..a4bd799e85c --- /dev/null +++ b/qa/qa/resource/reusable_group.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module QA + module Resource + class ReusableGroup < Group + prepend Reusable + + def initialize + super + + @path = "reusable_group" + @description = "QA reusable group" + @reuse_as = :default_group + end + + # Confirms that the group can be reused + # + # @return [nil] returns nil unless an error is raised + def validate_reuse_preconditions + unless reused_path_unique? + raise ResourceReuseError, + "Reusable groups must have the same name. The group reused as #{reuse_as} has the path '#{path}' but it should be '#{self.class.resources[reuse_as].path}'" + end + end + + # Confirms that reuse of the resource did not change it in a way that breaks later reuse. This raises an error if + # the current group path doesn't match the original path. + def validate_reuse + reload! + + if api_resource[:path] != @path + raise ResourceReuseError, "The group now has the path '#{api_resource[:path]}' but it should be '#{path}'" + end + end + + # Checks if the group is being reused with the same path. + # + # @return [Boolean] true if the group's path is different from another group with the same reuse symbol (reuse_as) + def reused_path_unique? + return true unless self.class.resources.key?(reuse_as) + + self.class.resources[reuse_as].path == path + end + + # Overrides QA::Resource::Group#remove_via_api! to log a debug message stating that removal will happen after + # the suite completes rather than now. + # + # @return [nil] + def remove_via_api! + QA::Runtime::Logger.debug("#{self.class.name} - deferring removal until after suite") + end + end + end +end diff --git a/qa/qa/resource/reusable_project.rb b/qa/qa/resource/reusable_project.rb index 6b33bb9d65d..d2dfff8ad56 100644 --- a/qa/qa/resource/reusable_project.rb +++ b/qa/qa/resource/reusable_project.rb @@ -5,6 +5,12 @@ module QA class ReusableProject < Project prepend Reusable + attribute :group do + ReusableGroup.fabricate_via_api! do |resource| + resource.api_client = api_client + end + end + def initialize super diff --git a/qa/qa/resource/user.rb b/qa/qa/resource/user.rb index ed4ea057484..eab428a61e7 100644 --- a/qa/qa/resource/user.rb +++ b/qa/qa/resource/user.rb @@ -98,6 +98,12 @@ module QA super end + def exists? + api_get + rescue ResourceNotFoundError + false + end + def api_delete super @@ -181,6 +187,15 @@ module QA ) end + protected + + # Compare users by username and password + # + # @return [Array] + def comparable + [username, password] + end + private def ldap_post_body |