Commit ac7a29ab authored by Dan Davison's avatar Dan Davison

Merge branch 'acunskis-record-fabrication-time' into 'master'

E2E: Track fabrication time of resources

See merge request gitlab-org/gitlab!78587
parents 51f6fdbf 18b79fd0
...@@ -31,7 +31,7 @@ module QA ...@@ -31,7 +31,7 @@ module QA
parents = options.fetch(:parents) { [] } parents = options.fetch(:parents) { [] }
do_fabricate!(resource: resource, prepare_block: prepare_block, parents: parents) do do_fabricate!(resource: resource, prepare_block: prepare_block, parents: parents) do
log_fabrication(:browser_ui, resource, parents, args) { resource.fabricate!(*args) } log_and_record_fabrication(:browser_ui, resource, parents, args) { resource.fabricate!(*args) }
current_url current_url
end end
...@@ -47,7 +47,7 @@ module QA ...@@ -47,7 +47,7 @@ module QA
resource.eager_load_api_client! resource.eager_load_api_client!
do_fabricate!(resource: resource, prepare_block: prepare_block, parents: parents) do do_fabricate!(resource: resource, prepare_block: prepare_block, parents: parents) do
log_fabrication(:api, resource, parents, args) { resource.fabricate_via_api! } log_and_record_fabrication(:api, resource, parents, args) { resource.fabricate_via_api! }
end end
end end
...@@ -59,7 +59,7 @@ module QA ...@@ -59,7 +59,7 @@ module QA
resource.eager_load_api_client! resource.eager_load_api_client!
do_fabricate!(resource: resource, prepare_block: prepare_block, parents: parents) do do_fabricate!(resource: resource, prepare_block: prepare_block, parents: parents) do
log_fabrication(:api, resource, parents, args) { resource.remove_via_api! } log_and_record_fabrication(:api, resource, parents, args) { resource.remove_via_api! }
end end
end end
...@@ -71,36 +71,19 @@ module QA ...@@ -71,36 +71,19 @@ module QA
resource_web_url = yield resource_web_url = yield
resource.web_url = resource_web_url resource.web_url = resource_web_url
QA::Tools::TestResourceDataProcessor.collect(resource, resource_identifier(resource))
resource resource
end end
def resource_identifier(resource) def log_and_record_fabrication(fabrication_method, resource, parents, args)
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 start = Time.now
Support::FabricationTracker.start_fabrication Support::FabricationTracker.start_fabrication
result = yield.tap do result = yield.tap do
fabrication_time = Time.now - start fabrication_time = Time.now - start
identifier = resource_identifier(resource)
fabrication_http_method = if resource.api_fabrication_http_method == :get fabrication_http_method = if resource.api_fabrication_http_method == :get
if self.include?(Reusable) if include?(Reusable)
"Retrieved for reuse" "Retrieved for reuse"
else else
"Retrieved" "Retrieved"
...@@ -109,16 +92,23 @@ module QA ...@@ -109,16 +92,23 @@ module QA
"Built" "Built"
end end
Support::FabricationTracker.save_fabrication(:"#{method}_fabrication", fabrication_time) Support::FabricationTracker.save_fabrication(:"#{fabrication_method}_fabrication", fabrication_time)
Tools::TestResourceDataProcessor.collect(
resource: resource,
info: identifier,
fabrication_method: fabrication_method,
fabrication_time: fabrication_time
)
Runtime::Logger.debug do Runtime::Logger.debug do
msg = ["==#{'=' * parents.size}>"] msg = ["==#{'=' * parents.size}>"]
msg << "#{fabrication_http_method} a #{name}" msg << "#{fabrication_http_method} a #{name}"
msg << resource_identifier(resource) if resource_identifier(resource) msg << identifier
msg << "as a dependency of #{parents.last}" if parents.any? msg << "as a dependency of #{parents.last}" if parents.any?
msg << "via #{method}" msg << "via #{fabrication_method}"
msg << "in #{fabrication_time} seconds" msg << "in #{fabrication_time} seconds"
msg.join(' ') msg.compact.join(' ')
end end
end end
Support::FabricationTracker.finish_fabrication Support::FabricationTracker.finish_fabrication
...@@ -126,6 +116,26 @@ module QA ...@@ -126,6 +116,26 @@ module QA
result result
end end
# Unique resource identifier
#
# @param [QA::Resource::Base] resource
# @return [String]
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
# Define custom attribute # Define custom attribute
# #
# @param [Symbol] name # @param [Symbol] name
......
...@@ -31,6 +31,8 @@ module QA ...@@ -31,6 +31,8 @@ module QA
end end
end end
delegate :path_with_namespace, to: :project
def fabricate! def fabricate!
populate(:upstream, :user) populate(:upstream, :user)
......
...@@ -50,12 +50,6 @@ module QA ...@@ -50,12 +50,6 @@ module QA
resource_web_url(api_get) resource_web_url(api_get)
rescue ResourceNotFoundError rescue ResourceNotFoundError
super super
Support::Retrier.retry_on_exception(sleep_interval: 5) do
resource = resource_web_url(api_get)
populate(:runners_token)
resource
end
end end
def api_get_path def api_get_path
......
...@@ -51,14 +51,6 @@ module QA ...@@ -51,14 +51,6 @@ module QA
resource_web_url(api_get) resource_web_url(api_get)
rescue ResourceNotFoundError rescue ResourceNotFoundError
super super
# If the group was just created the runners token might not be
# available via the API immediately.
Support::Retrier.retry_on_exception(sleep_interval: 5) do
resource = resource_web_url(api_get)
populate(:runners_token)
resource
end
end end
def api_get_path def api_get_path
......
...@@ -25,7 +25,7 @@ module QA ...@@ -25,7 +25,7 @@ module QA
Resource::Runner.fabricate! do |runner| Resource::Runner.fabricate! do |runner|
runner.name = executor runner.name = executor
runner.tags = [executor] runner.tags = [executor]
runner.token = group.sandbox.runners_token runner.token = group.reload!.runners_token
end end
end end
......
...@@ -50,7 +50,7 @@ module QA ...@@ -50,7 +50,7 @@ module QA
runner.name = "qa-runner-#{Time.now.to_i}" runner.name = "qa-runner-#{Time.now.to_i}"
runner.tags = ["runner-for-#{project.group.name}"] runner.tags = ["runner-for-#{project.group.name}"]
runner.executor = :docker runner.executor = :docker
runner.token = project.group.runners_token runner.token = project.group.reload!.runners_token
end end
end end
......
...@@ -54,7 +54,7 @@ module QA ...@@ -54,7 +54,7 @@ module QA
runner.name = "qa-runner-#{Time.now.to_i}" runner.name = "qa-runner-#{Time.now.to_i}"
runner.tags = ["runner-for-#{project.group.name}"] runner.tags = ["runner-for-#{project.group.name}"]
runner.executor = :docker runner.executor = :docker
runner.token = project.group.runners_token runner.token = project.group.reload!.runners_token
end end
end end
......
...@@ -23,7 +23,7 @@ module QA ...@@ -23,7 +23,7 @@ module QA
let!(:runner) do let!(:runner) do
Resource::Runner.fabricate! do |runner| Resource::Runner.fabricate! do |runner|
runner.project = upstream_project runner.project = upstream_project
runner.token = upstream_project.group.sandbox.runners_token runner.token = upstream_project.group.reload!.runners_token
runner.name = executor runner.name = executor
runner.tags = [executor] runner.tags = [executor]
end end
......
...@@ -23,7 +23,7 @@ module QA ...@@ -23,7 +23,7 @@ module QA
let!(:runner) do let!(:runner) do
Resource::Runner.fabricate_via_api! do |runner| Resource::Runner.fabricate_via_api! do |runner|
runner.token = group.sandbox.runners_token runner.token = group.reload!.runners_token
runner.name = executor runner.name = executor
runner.tags = [executor] runner.tags = [executor]
end end
......
...@@ -14,40 +14,38 @@ module QA ...@@ -14,40 +14,38 @@ module QA
return log(:warn, 'Missing QA_INFLUXDB_URL, skipping metrics export!') unless influxdb_url return log(:warn, 'Missing QA_INFLUXDB_URL, skipping metrics export!') unless influxdb_url
return log(:warn, 'Missing QA_INFLUXDB_TOKEN, skipping metrics export!') unless influxdb_token return log(:warn, 'Missing QA_INFLUXDB_TOKEN, skipping metrics export!') unless influxdb_token
data = notification.examples.map { |example| test_stats(example) }.compact push_test_stats(notification.examples)
influx_client.create_write_api.write(data: data) push_fabrication_stats
log(:info, "Pushed #{data.length} entries to influxdb")
rescue StandardError => e
log(:error, "Failed to push data to influxdb, error: #{e}")
end end
private private
# InfluxDb client # Push test execution stats to influxdb
# #
# @return [InfluxDB2::Client] # @param [Array<RSpec::Core::Example>] examples
def influx_client # @return [void]
@influx_client ||= InfluxDB2::Client.new( def push_test_stats(examples)
influxdb_url, data = examples.map { |example| test_stats(example) }.compact
influxdb_token,
bucket: 'e2e-test-stats',
org: 'gitlab-qa',
precision: InfluxDB2::WritePrecision::NANOSECOND
)
end
# InfluxDb instance url influx_client.write(data: data)
# log(:debug, "Pushed #{data.length} test execution entries to influxdb")
# @return [String] rescue StandardError => e
def influxdb_url log(:error, "Failed to push test execution stats to influxdb, error: #{e}")
@influxdb_url ||= env('QA_INFLUXDB_URL')
end end
# Influxdb token # Push resource fabrication stats to influxdb
# #
# @return [String] # @return [void]
def influxdb_token def push_fabrication_stats
@influxdb_token ||= env('QA_INFLUXDB_TOKEN') data = Tools::TestResourceDataProcessor.resources.flat_map do |resource, values|
values.map { |v| fabrication_stats(resource: resource, **v) }
end
return if data.empty?
influx_client.write(data: data)
log(:debug, "Pushed #{data.length} resource fabrication entries to influxdb")
rescue StandardError => e
log(:error, "Failed to push fabrication stats to influxdb, error: #{e}")
end end
# Transform example to influxdb compatible metrics data # Transform example to influxdb compatible metrics data
...@@ -93,6 +91,33 @@ module QA ...@@ -93,6 +91,33 @@ module QA
nil nil
end end
# Resource fabrication data point
#
# @param [String] resource
# @param [String] info
# @param [Symbol] fabrication_method
# @param [Symbol] http_method
# @param [Integer] fabrication_time
# @return [Hash]
def fabrication_stats(resource:, info:, fabrication_method:, http_method:, fabrication_time:, **)
{
name: 'fabrication-stats',
time: time,
tags: {
resource: resource,
fabrication_method: fabrication_method,
http_method: http_method,
run_type: env('QA_RUN_TYPE') || run_type,
merge_request: merge_request
},
fields: {
fabrication_time: fabrication_time,
info: info,
job_url: QA::Runtime::Env.ci_job_url
}
}
end
# Project name # Project name
# #
# @return [String] # @return [String]
...@@ -150,7 +175,7 @@ module QA ...@@ -150,7 +175,7 @@ module QA
# @param [String] message # @param [String] message
# @return [void] # @return [void]
def log(level, message) def log(level, message)
QA::Runtime::Logger.public_send(level, "influxdb exporter: #{message}") QA::Runtime::Logger.public_send(level, "[influxdb exporter]: #{message}")
end end
# Return non empty environment variable value # Return non empty environment variable value
...@@ -170,6 +195,33 @@ module QA ...@@ -170,6 +195,33 @@ module QA
def devops_stage(file_path) def devops_stage(file_path)
file_path.match(%r{\d{1,2}_(\w+)/})&.captures&.first file_path.match(%r{\d{1,2}_(\w+)/})&.captures&.first
end end
# InfluxDb client
#
# @return [InfluxDB2::WriteApi]
def influx_client
@influx_client ||= InfluxDB2::Client.new(
influxdb_url,
influxdb_token,
bucket: 'e2e-test-stats',
org: 'gitlab-qa',
precision: InfluxDB2::WritePrecision::NANOSECOND
).create_write_api
end
# InfluxDb instance url
#
# @return [String]
def influxdb_url
@influxdb_url ||= env('QA_INFLUXDB_URL')
end
# Influxdb token
#
# @return [String]
def influxdb_token
@influxdb_token ||= env('QA_INFLUXDB_TOKEN')
end
end end
end end
end end
......
...@@ -6,60 +6,80 @@ ...@@ -6,60 +6,80 @@
module QA module QA
module Tools module Tools
class TestResourceDataProcessor class TestResourceDataProcessor
@resources ||= Hash.new { |hsh, key| hsh[key] = [] } include Singleton
def initialize
@resources = Hash.new { |hsh, key| hsh[key] = [] }
end
class << self class << self
# Ignoring rspec-mocks, sandbox, user and fork resources delegate :collect, :write_to_file, :resources, to: :instance
# TODO: Will need to figure out which user resources can be collected, ignore for now end
#
# Collecting resources created in E2E tests
# Data is a Hash of resources with keys as resource type (group, project, issue, etc.)
# Each type contains an array of resource object (hash) of the same type
# E.g: { "QA::Resource::Project": [ { info: 'foo', api_path: '/foo'}, {...} ] }
def collect(resource, info)
return if resource.api_response.nil? ||
resource.is_a?(RSpec::Mocks::Double) ||
resource.is_a?(Resource::Sandbox) ||
resource.is_a?(Resource::User) ||
resource.is_a?(Resource::Fork)
api_path = if resource.respond_to?(:api_delete_path) # @return [Hash<String, Array>]
resource.api_delete_path.gsub('%2F', '/') attr_reader :resources
elsif resource.respond_to?(:api_get_path)
resource.api_get_path.gsub('%2F', '/')
else
'Cannot find resource API path'
end
type = resource.class.name # Collecting resources created in E2E tests
# Data is a Hash of resources with keys as resource type (group, project, issue, etc.)
# Each type contains an array of resource object (hash) of the same type
# E.g: { "QA::Resource::Project": [ { info: 'foo', api_path: '/foo'}, {...} ] }
#
# @param [QA::Resource::Base] resource fabricated resource
# @param [String] info resource info
# @param [Symbol] method fabrication method, api or browser_ui
# @param [Integer] time fabrication time
# @return [Hash]
def collect(resource:, info:, fabrication_method:, fabrication_time:)
api_path = resource_api_path(resource)
type = resource.class.name
@resources[type] << { info: info, api_path: api_path } resources[type] << {
end info: info,
api_path: api_path,
fabrication_method: fabrication_method,
fabrication_time: fabrication_time,
http_method: resource.api_fabrication_http_method
}
end
# If JSON file exists and not empty, read and load file content
# Merge what is saved in @resources into the content from file
# Overwrite file content with the new data hash
# Otherwise create file and write data hash to file for the first time
#
# @return [void]
def write_to_file
return if resources.empty?
# If JSON file exists and not empty, read and load file content file = Pathname.new(Runtime::Env.test_resources_created_filepath)
# Merge what is saved in @resources into the content from file FileUtils.mkdir_p(file.dirname)
# Overwrite file content with the new data hash
# Otherwise create file and write data hash to file for the first time
def write_to_file
return if @resources.empty?
file = Runtime::Env.test_resources_created_filepath data = resources.deep_dup
FileUtils.mkdir_p('tmp/') # merge existing json if present
FileUtils.touch(file) JSON.parse(File.read(file)).deep_merge!(data) { |key, val, other_val| val + other_val } if file.exist?
data = nil
File.write(file, JSON.pretty_generate(data))
end
if File.zero?(file) private
data = @resources
else
data = JSON.parse(File.read(file))
@resources.each_pair do |key, val| # Determine resource api path or return default value
data[key].nil? ? data[key] = val : val.each { |item| data[key] << item } # Some resources fabricated via UI can raise no attribute error
end #
end # @param [QA::Resource::Base] resource
# @return [String]
def resource_api_path(resource)
default = 'Cannot find resource API path'
File.open(file, 'w') { |f| f.write(JSON.pretty_generate(data.each_value(&:uniq!))) } if resource.respond_to?(:api_delete_path)
resource.api_delete_path.gsub('%2F', '/')
elsif resource.respond_to?(:api_get_path)
resource.api_get_path.gsub('%2F', '/')
else
default
end end
rescue QA::Resource::Base::NoValueError
default
end end
end end
end end
......
...@@ -7,6 +7,11 @@ RSpec.describe QA::Resource::Base do ...@@ -7,6 +7,11 @@ RSpec.describe QA::Resource::Base do
let(:location) { 'http://location' } let(:location) { 'http://location' }
let(:log_regex) { %r{==> Built a MyResource with username 'qa' via #{method} in [\d.\-e]+ seconds+} } let(:log_regex) { %r{==> Built a MyResource with username 'qa' via #{method} in [\d.\-e]+ seconds+} }
before do
allow(QA::Tools::TestResourceDataProcessor).to receive(:collect)
allow(QA::Tools::TestResourceDataProcessor).to receive(:write_to_file)
end
shared_context 'with fabrication context' do shared_context 'with fabrication context' do
subject do subject do
Class.new(described_class) do Class.new(described_class) do
......
...@@ -22,6 +22,7 @@ describe QA::Support::Formatters::TestStatsFormatter do ...@@ -22,6 +22,7 @@ describe QA::Support::Formatters::TestStatsFormatter do
let(:file_path) { "./qa/specs/features/#{stage}/subfolder/some_spec.rb" } let(:file_path) { "./qa/specs/features/#{stage}/subfolder/some_spec.rb" }
let(:ui_fabrication) { 0 } let(:ui_fabrication) { 0 }
let(:api_fabrication) { 0 } let(:api_fabrication) { 0 }
let(:fabrication_resources) { {} }
let(:influx_client_args) do let(:influx_client_args) do
{ {
...@@ -88,6 +89,7 @@ describe QA::Support::Formatters::TestStatsFormatter do ...@@ -88,6 +89,7 @@ describe QA::Support::Formatters::TestStatsFormatter do
before do before do
allow(InfluxDB2::Client).to receive(:new).with(url, token, **influx_client_args) { influx_client } allow(InfluxDB2::Client).to receive(:new).with(url, token, **influx_client_args) { influx_client }
allow(QA::Tools::TestResourceDataProcessor).to receive(:resources) { fabrication_resources }
end end
context "without influxdb variables configured" do context "without influxdb variables configured" do
...@@ -135,6 +137,7 @@ describe QA::Support::Formatters::TestStatsFormatter do ...@@ -135,6 +137,7 @@ describe QA::Support::Formatters::TestStatsFormatter do
it('spec', :reliable, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/1234') {} it('spec', :reliable, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/1234') {}
end end
expect(influx_write_api).to have_received(:write).once
expect(influx_write_api).to have_received(:write).with(data: [data]) expect(influx_write_api).to have_received(:write).with(data: [data])
end end
end end
...@@ -147,6 +150,7 @@ describe QA::Support::Formatters::TestStatsFormatter do ...@@ -147,6 +150,7 @@ describe QA::Support::Formatters::TestStatsFormatter do
it('spec', :quarantine, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/1234') {} it('spec', :quarantine, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/1234') {}
end end
expect(influx_write_api).to have_received(:write).once
expect(influx_write_api).to have_received(:write).with(data: [data]) expect(influx_write_api).to have_received(:write).with(data: [data])
end end
end end
...@@ -162,6 +166,7 @@ describe QA::Support::Formatters::TestStatsFormatter do ...@@ -162,6 +166,7 @@ describe QA::Support::Formatters::TestStatsFormatter do
it 'exports data to influxdb with correct run type' do it 'exports data to influxdb with correct run type' do
run_spec run_spec
expect(influx_write_api).to have_received(:write).once
expect(influx_write_api).to have_received(:write).with(data: [data]) expect(influx_write_api).to have_received(:write).with(data: [data])
end end
end end
...@@ -179,6 +184,7 @@ describe QA::Support::Formatters::TestStatsFormatter do ...@@ -179,6 +184,7 @@ describe QA::Support::Formatters::TestStatsFormatter do
it 'exports data to influxdb with correct run type' do it 'exports data to influxdb with correct run type' do
run_spec run_spec
expect(influx_write_api).to have_received(:write).once
expect(influx_write_api).to have_received(:write).with(data: [data]) expect(influx_write_api).to have_received(:write).with(data: [data])
end end
end end
...@@ -195,8 +201,48 @@ describe QA::Support::Formatters::TestStatsFormatter do ...@@ -195,8 +201,48 @@ describe QA::Support::Formatters::TestStatsFormatter do
it 'exports data to influxdb with fabrication times' do it 'exports data to influxdb with fabrication times' do
run_spec run_spec
expect(influx_write_api).to have_received(:write).once
expect(influx_write_api).to have_received(:write).with(data: [data]) expect(influx_write_api).to have_received(:write).with(data: [data])
end end
end end
context 'with fabrication resources' do
let(:fabrication_resources) do
{
'QA::Resource::Project' => [{
info: "with id '1'",
api_path: '/project',
fabrication_method: :api,
fabrication_time: 1,
http_method: :post
}]
}
end
let(:fabrication_data) do
{
name: 'fabrication-stats',
time: DateTime.strptime(ci_timestamp).to_time,
tags: {
resource: 'QA::Resource::Project',
fabrication_method: :api,
http_method: :post,
run_type: run_type,
merge_request: "false"
},
fields: {
fabrication_time: 1,
info: "with id '1'",
job_url: ci_job_url
}
}
end
it 'exports fabrication stats data to influxdb' do
run_spec
expect(influx_write_api).to have_received(:write).with(data: [fabrication_data])
end
end
end end
end end
...@@ -32,7 +32,7 @@ module QA ...@@ -32,7 +32,7 @@ module QA
runner.name = "qa-runner-#{Time.now.to_i}" runner.name = "qa-runner-#{Time.now.to_i}"
runner.tags = ["runner-for-#{package_project.group.name}"] runner.tags = ["runner-for-#{package_project.group.name}"]
runner.executor = :docker runner.executor = :docker
runner.token = package_project.group.runners_token runner.token = package_project.group.reload!.runners_token
end end
end end
......
# frozen_string_literal: true # frozen_string_literal: true
RSpec.describe QA::Tools::TestResourceDataProcessor do RSpec.describe QA::Tools::TestResourceDataProcessor do
include QA::Support::Helpers::StubEnv
subject(:processor) { Class.new(described_class).instance }
let(:info) { 'information' } let(:info) { 'information' }
let(:api_path) { '/foo' } let(:api_response) { {} }
let(:result) { [{ info: info, api_path: api_path }] } let(:method) { :api }
let(:time) { 2 }
let(:api_path) { resource.api_delete_path }
let(:resource) { QA::Resource::Project.init { |project| project.id = 1 } }
describe '.collect' do let(:result) do
context 'when resource is not restricted' do {
let(:resource) { instance_double(QA::Resource::Project, api_delete_path: '/foo', api_response: 'foo') } 'QA::Resource::Project' => [{
info: info,
api_path: api_path,
fabrication_method: method,
fabrication_time: time,
http_method: :post
}]
}
end
it 'collects resource' do before do
expect(described_class.collect(resource, info)).to eq(result) processor.collect(resource: resource, info: info, fabrication_method: method, fabrication_time: time)
end end
describe '.collect' do
it 'collects and stores resource' do
expect(processor.resources).to eq(result)
end end
end
describe '.write_to_file' do
let(:resources_file) { Pathname.new(Faker::File.file_name(dir: 'tmp', ext: 'json')) }
context 'when resource api response is nil' do before do
let(:resource) { double(QA::Resource::Project, api_delete_path: '/foo', api_response: nil) } stub_env('QA_TEST_RESOURCES_CREATED_FILEPATH', resources_file)
it 'does not collect resource' do allow(File).to receive(:write)
expect(described_class.collect(resource, info)).to eq(nil)
end
end end
context 'when resource is restricted' do it 'writes applicable resources to file' do
let(:resource) { double(QA::Resource::Sandbox, api_delete_path: '/foo', api_response: 'foo') } processor.write_to_file
it 'does not collect resource' do expect(File).to have_received(:write).with(resources_file, JSON.pretty_generate(result))
expect(described_class.collect(resource, info)).to eq(nil)
end
end end
end end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment