summaryrefslogtreecommitdiff
path: root/qa/qa/resource
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-01-20 09:16:11 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2022-01-20 09:16:11 +0000
commitedaa33dee2ff2f7ea3fac488d41558eb5f86d68c (patch)
tree11f143effbfeba52329fb7afbd05e6e2a3790241 /qa/qa/resource
parentd8a5691316400a0f7ec4f83832698f1988eb27c1 (diff)
downloadgitlab-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.rb6
-rw-r--r--qa/qa/resource/base.rb75
-rw-r--r--qa/qa/resource/bulk_import_group.rb19
-rw-r--r--qa/qa/resource/group_badge.rb17
-rw-r--r--qa/qa/resource/group_base.rb12
-rw-r--r--qa/qa/resource/group_milestone.rb17
-rw-r--r--qa/qa/resource/issue.rb22
-rw-r--r--qa/qa/resource/label_base.rb17
-rw-r--r--qa/qa/resource/merge_request.rb71
-rw-r--r--qa/qa/resource/project.rb69
-rw-r--r--qa/qa/resource/repository/commit.rb99
-rw-r--r--qa/qa/resource/reusable_group.rb54
-rw-r--r--qa/qa/resource/reusable_project.rb6
-rw-r--r--qa/qa/resource/user.rb15
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