Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
7d4e6bdbfb
commit
06bd645177
|
@ -0,0 +1,44 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# Monkey patch of Enumerator#next to get better stack traces
|
||||||
|
# when an error is raised from within a Fiber.
|
||||||
|
# https://bugs.ruby-lang.org/issues/16829
|
||||||
|
module EnumeratorNextPatch
|
||||||
|
%w(next next_values peek peek_values).each do |name|
|
||||||
|
define_method(name) do |*args|
|
||||||
|
gitlab_patch_backtrace_marker { super(*args) }
|
||||||
|
rescue Exception => err # rubocop: disable Lint/RescueException
|
||||||
|
err.set_backtrace(err.backtrace + caller) unless
|
||||||
|
has_gitlab_patch_backtrace_marker?(err.backtrace) && backtrace_matches_caller?(err.backtrace)
|
||||||
|
|
||||||
|
raise
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def gitlab_patch_backtrace_marker
|
||||||
|
yield
|
||||||
|
end
|
||||||
|
|
||||||
|
# This function tells us whether the exception was generated by #next itself or by something in
|
||||||
|
# the Fiber that it invokes. If it's generated by #next, then the backtrace will have
|
||||||
|
# #gitlab_patch_backtrace_marker as the third item down the trace (since
|
||||||
|
# #gitlab_patch_backtrace_marker calls a block, which in turn calls #next.) If it's generated
|
||||||
|
# by the Fiber that #next invokes, then it won't contain this marker.
|
||||||
|
def has_gitlab_patch_backtrace_marker?(backtrace)
|
||||||
|
match = %r(^(.*):[0-9]+:in `gitlab_patch_backtrace_marker'$).match(backtrace[2])
|
||||||
|
|
||||||
|
!!match && match[1] == __FILE__
|
||||||
|
end
|
||||||
|
|
||||||
|
# This function makes sure that the rest of the stack trace matches in order to avoid missing
|
||||||
|
# an exception that was generated by calling #next on another Enumerator inside the Fiber.
|
||||||
|
# This might miss some *very* contrived scenarios involving recursion, but exceptions don't
|
||||||
|
# provide Fiber information, so it's the best we can do.
|
||||||
|
def backtrace_matches_caller?(backtrace)
|
||||||
|
backtrace[3..] == caller[1..]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Enumerator.prepend(EnumeratorNextPatch)
|
|
@ -0,0 +1,9 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddVulnerabilityReadsCastedClusterAgentColumn < Gitlab::Database::Migration[2.0]
|
||||||
|
enable_lock_retries!
|
||||||
|
|
||||||
|
def change
|
||||||
|
add_column :vulnerability_reads, :casted_cluster_agent_id, :bigint
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,81 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class UpdateVulnerabilityReadsTriggerFunctions < Gitlab::Database::Migration[2.0]
|
||||||
|
AGENT_ID_VALUE = "NEW.location->'kubernetes_resource'->>'agent_id'"
|
||||||
|
CASTED_AGENT_ID_VALUE = "CAST(#{AGENT_ID_VALUE} AS bigint)"
|
||||||
|
|
||||||
|
def up
|
||||||
|
update_insert_or_update_vulnerability_reads_function(with_casted_cluster_agent_id: true)
|
||||||
|
update_update_location_from_vulnerability_occurrences_function(with_casted_cluster_agent_id: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
update_insert_or_update_vulnerability_reads_function(with_casted_cluster_agent_id: false)
|
||||||
|
update_update_location_from_vulnerability_occurrences_function(with_casted_cluster_agent_id: false)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def update_insert_or_update_vulnerability_reads_function(with_casted_cluster_agent_id: false)
|
||||||
|
insert_fields = with_casted_cluster_agent_id ? 'cluster_agent_id, casted_cluster_agent_id' : 'cluster_agent_id'
|
||||||
|
insert_values = with_casted_cluster_agent_id ? [AGENT_ID_VALUE, CASTED_AGENT_ID_VALUE].join(', ') : AGENT_ID_VALUE
|
||||||
|
|
||||||
|
execute(<<~SQL)
|
||||||
|
CREATE OR REPLACE FUNCTION insert_or_update_vulnerability_reads()
|
||||||
|
RETURNS TRIGGER
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $$
|
||||||
|
DECLARE
|
||||||
|
severity smallint;
|
||||||
|
state smallint;
|
||||||
|
report_type smallint;
|
||||||
|
resolved_on_default_branch boolean;
|
||||||
|
BEGIN
|
||||||
|
IF (NEW.vulnerability_id IS NULL AND (TG_OP = 'INSERT' OR TG_OP = 'UPDATE')) THEN
|
||||||
|
RETURN NULL;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF (TG_OP = 'UPDATE' AND OLD.vulnerability_id IS NOT NULL AND NEW.vulnerability_id IS NOT NULL) THEN
|
||||||
|
RETURN NULL;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
vulnerabilities.severity, vulnerabilities.state, vulnerabilities.report_type, vulnerabilities.resolved_on_default_branch
|
||||||
|
INTO
|
||||||
|
severity, state, report_type, resolved_on_default_branch
|
||||||
|
FROM
|
||||||
|
vulnerabilities
|
||||||
|
WHERE
|
||||||
|
vulnerabilities.id = NEW.vulnerability_id;
|
||||||
|
|
||||||
|
INSERT INTO vulnerability_reads (vulnerability_id, project_id, scanner_id, report_type, severity, state, resolved_on_default_branch, uuid, location_image, #{insert_fields})
|
||||||
|
VALUES (NEW.vulnerability_id, NEW.project_id, NEW.scanner_id, report_type, severity, state, resolved_on_default_branch, NEW.uuid::uuid, NEW.location->>'image', #{insert_values})
|
||||||
|
ON CONFLICT(vulnerability_id) DO NOTHING;
|
||||||
|
RETURN NULL;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
|
SQL
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_update_location_from_vulnerability_occurrences_function(with_casted_cluster_agent_id: false)
|
||||||
|
execute(<<~SQL)
|
||||||
|
CREATE OR REPLACE FUNCTION update_location_from_vulnerability_occurrences()
|
||||||
|
RETURNS trigger
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $$
|
||||||
|
BEGIN
|
||||||
|
UPDATE
|
||||||
|
vulnerability_reads
|
||||||
|
SET
|
||||||
|
location_image = NEW.location->>'image',
|
||||||
|
#{with_casted_cluster_agent_id ? "casted_cluster_agent_id = #{CASTED_AGENT_ID_VALUE}," : ''}
|
||||||
|
cluster_agent_id = #{AGENT_ID_VALUE}
|
||||||
|
WHERE
|
||||||
|
vulnerability_id = NEW.vulnerability_id;
|
||||||
|
RETURN NULL;
|
||||||
|
|
||||||
|
END
|
||||||
|
$$;
|
||||||
|
SQL
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,16 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddForeignKeyToVulnerabilityReadsCastedClusterAgentId < Gitlab::Database::Migration[2.0]
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def up
|
||||||
|
add_concurrent_foreign_key :vulnerability_reads, :cluster_agents,
|
||||||
|
column: :casted_cluster_agent_id, on_delete: :nullify
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
with_lock_retries do
|
||||||
|
remove_foreign_key :vulnerability_reads, :cluster_agents, column: :casted_cluster_agent_id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,15 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddIndexToVulnerabilityReadsCastedClusterAgentId < Gitlab::Database::Migration[2.0]
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
INDEX_NAME = 'index_cis_vulnerability_reads_on_cluster_agent_id'
|
||||||
|
|
||||||
|
def up
|
||||||
|
add_concurrent_index :vulnerability_reads, :casted_cluster_agent_id, name: INDEX_NAME, where: 'report_type = 7'
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
remove_concurrent_index_by_name :vulnerability_reads, INDEX_NAME
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1 @@
|
||||||
|
af6f954426b714649a3b19e80a20cf99475cdc8496c23add8032cda27072d31d
|
|
@ -0,0 +1 @@
|
||||||
|
02821039feda457c1fb61dc6ff62756752c0c1c0ad01e12ecf28c265462529d4
|
|
@ -0,0 +1 @@
|
||||||
|
ef078bbcf8415a7bb49ed919739005d22c21199da1a0e5e5c0971d2a8e1b2a40
|
|
@ -0,0 +1 @@
|
||||||
|
89a03d69c44ed95133446275bb9b39dfe91ad3022cefdfa438ea3c96ab4f8e69
|
|
@ -71,8 +71,8 @@ BEGIN
|
||||||
WHERE
|
WHERE
|
||||||
vulnerabilities.id = NEW.vulnerability_id;
|
vulnerabilities.id = NEW.vulnerability_id;
|
||||||
|
|
||||||
INSERT INTO vulnerability_reads (vulnerability_id, project_id, scanner_id, report_type, severity, state, resolved_on_default_branch, uuid, location_image, cluster_agent_id)
|
INSERT INTO vulnerability_reads (vulnerability_id, project_id, scanner_id, report_type, severity, state, resolved_on_default_branch, uuid, location_image, cluster_agent_id, casted_cluster_agent_id)
|
||||||
VALUES (NEW.vulnerability_id, NEW.project_id, NEW.scanner_id, report_type, severity, state, resolved_on_default_branch, NEW.uuid::uuid, NEW.location->>'image', NEW.location->'kubernetes_resource'->>'agent_id')
|
VALUES (NEW.vulnerability_id, NEW.project_id, NEW.scanner_id, report_type, severity, state, resolved_on_default_branch, NEW.uuid::uuid, NEW.location->>'image', NEW.location->'kubernetes_resource'->>'agent_id', CAST(NEW.location->'kubernetes_resource'->>'agent_id' AS bigint))
|
||||||
ON CONFLICT(vulnerability_id) DO NOTHING;
|
ON CONFLICT(vulnerability_id) DO NOTHING;
|
||||||
RETURN NULL;
|
RETURN NULL;
|
||||||
END
|
END
|
||||||
|
@ -206,6 +206,7 @@ UPDATE
|
||||||
vulnerability_reads
|
vulnerability_reads
|
||||||
SET
|
SET
|
||||||
location_image = NEW.location->>'image',
|
location_image = NEW.location->>'image',
|
||||||
|
casted_cluster_agent_id = CAST(NEW.location->'kubernetes_resource'->>'agent_id' AS bigint),
|
||||||
cluster_agent_id = NEW.location->'kubernetes_resource'->>'agent_id'
|
cluster_agent_id = NEW.location->'kubernetes_resource'->>'agent_id'
|
||||||
WHERE
|
WHERE
|
||||||
vulnerability_id = NEW.vulnerability_id;
|
vulnerability_id = NEW.vulnerability_id;
|
||||||
|
@ -22211,6 +22212,7 @@ CREATE TABLE vulnerability_reads (
|
||||||
uuid uuid NOT NULL,
|
uuid uuid NOT NULL,
|
||||||
location_image text,
|
location_image text,
|
||||||
cluster_agent_id text,
|
cluster_agent_id text,
|
||||||
|
casted_cluster_agent_id bigint,
|
||||||
CONSTRAINT check_380451bdbe CHECK ((char_length(location_image) <= 2048)),
|
CONSTRAINT check_380451bdbe CHECK ((char_length(location_image) <= 2048)),
|
||||||
CONSTRAINT check_a105eb825a CHECK ((char_length(cluster_agent_id) <= 10))
|
CONSTRAINT check_a105eb825a CHECK ((char_length(cluster_agent_id) <= 10))
|
||||||
);
|
);
|
||||||
|
@ -27655,6 +27657,8 @@ CREATE UNIQUE INDEX index_ci_variables_on_project_id_and_key_and_environment_sco
|
||||||
|
|
||||||
CREATE INDEX index_cicd_settings_on_namespace_id_where_stale_pruning_enabled ON namespace_ci_cd_settings USING btree (namespace_id) WHERE (allow_stale_runner_pruning = true);
|
CREATE INDEX index_cicd_settings_on_namespace_id_where_stale_pruning_enabled ON namespace_ci_cd_settings USING btree (namespace_id) WHERE (allow_stale_runner_pruning = true);
|
||||||
|
|
||||||
|
CREATE INDEX index_cis_vulnerability_reads_on_cluster_agent_id ON vulnerability_reads USING btree (casted_cluster_agent_id) WHERE (report_type = 7);
|
||||||
|
|
||||||
CREATE INDEX index_cluster_agent_tokens_on_agent_id_status_last_used_at ON cluster_agent_tokens USING btree (agent_id, status, last_used_at DESC NULLS LAST);
|
CREATE INDEX index_cluster_agent_tokens_on_agent_id_status_last_used_at ON cluster_agent_tokens USING btree (agent_id, status, last_used_at DESC NULLS LAST);
|
||||||
|
|
||||||
CREATE INDEX index_cluster_agent_tokens_on_created_by_user_id ON cluster_agent_tokens USING btree (created_by_user_id);
|
CREATE INDEX index_cluster_agent_tokens_on_created_by_user_id ON cluster_agent_tokens USING btree (created_by_user_id);
|
||||||
|
@ -32030,6 +32034,9 @@ ALTER TABLE ONLY merge_requests
|
||||||
ALTER TABLE ONLY merge_request_metrics
|
ALTER TABLE ONLY merge_request_metrics
|
||||||
ADD CONSTRAINT fk_ae440388cc FOREIGN KEY (latest_closed_by_id) REFERENCES users(id) ON DELETE SET NULL;
|
ADD CONSTRAINT fk_ae440388cc FOREIGN KEY (latest_closed_by_id) REFERENCES users(id) ON DELETE SET NULL;
|
||||||
|
|
||||||
|
ALTER TABLE ONLY vulnerability_reads
|
||||||
|
ADD CONSTRAINT fk_aee839e611 FOREIGN KEY (casted_cluster_agent_id) REFERENCES cluster_agents(id) ON DELETE SET NULL;
|
||||||
|
|
||||||
ALTER TABLE ONLY dast_profile_schedules
|
ALTER TABLE ONLY dast_profile_schedules
|
||||||
ADD CONSTRAINT fk_aef03d62e5 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL;
|
ADD CONSTRAINT fk_aef03d62e5 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL;
|
||||||
|
|
||||||
|
|
|
@ -16719,8 +16719,10 @@ Represents the scan result policy.
|
||||||
| ---- | ---- | ----------- |
|
| ---- | ---- | ----------- |
|
||||||
| <a id="scanresultpolicydescription"></a>`description` | [`String!`](#string) | Description of the policy. |
|
| <a id="scanresultpolicydescription"></a>`description` | [`String!`](#string) | Description of the policy. |
|
||||||
| <a id="scanresultpolicyenabled"></a>`enabled` | [`Boolean!`](#boolean) | Indicates whether this policy is enabled. |
|
| <a id="scanresultpolicyenabled"></a>`enabled` | [`Boolean!`](#boolean) | Indicates whether this policy is enabled. |
|
||||||
|
| <a id="scanresultpolicygroupapprovers"></a>`groupApprovers` | [`[Group!]`](#group) | Approvers of the group type. |
|
||||||
| <a id="scanresultpolicyname"></a>`name` | [`String!`](#string) | Name of the policy. |
|
| <a id="scanresultpolicyname"></a>`name` | [`String!`](#string) | Name of the policy. |
|
||||||
| <a id="scanresultpolicyupdatedat"></a>`updatedAt` | [`Time!`](#time) | Timestamp of when the policy YAML was last updated. |
|
| <a id="scanresultpolicyupdatedat"></a>`updatedAt` | [`Time!`](#time) | Timestamp of when the policy YAML was last updated. |
|
||||||
|
| <a id="scanresultpolicyuserapprovers"></a>`userApprovers` | [`[UserCore!]`](#usercore) | Approvers of the user type. |
|
||||||
| <a id="scanresultpolicyyaml"></a>`yaml` | [`String!`](#string) | YAML definition of the policy. |
|
| <a id="scanresultpolicyyaml"></a>`yaml` | [`String!`](#string) | YAML definition of the policy. |
|
||||||
|
|
||||||
### `ScannedResource`
|
### `ScannedResource`
|
||||||
|
|
|
@ -16,7 +16,7 @@ module QA
|
||||||
|
|
||||||
NotRespondingError = Class.new(RuntimeError)
|
NotRespondingError = Class.new(RuntimeError)
|
||||||
|
|
||||||
CAPYBARA_MAX_WAIT_TIME = 10
|
CAPYBARA_MAX_WAIT_TIME = Env.max_capybara_wait_time
|
||||||
|
|
||||||
def self.blank_page?
|
def self.blank_page?
|
||||||
['', 'about:blank', 'data:,'].include?(Capybara.current_session.driver.browser.current_url)
|
['', 'about:blank', 'data:,'].include?(Capybara.current_session.driver.browser.current_url)
|
||||||
|
|
|
@ -461,6 +461,10 @@ module QA
|
||||||
enabled?(ENV['QA_SKIP_SMOKE_RELIABLE'], default: false)
|
enabled?(ENV['QA_SKIP_SMOKE_RELIABLE'], default: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def max_capybara_wait_time
|
||||||
|
ENV.fetch('MAX_CAPYBARA_WAIT_TIME', 10).to_i
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def remote_grid_credentials
|
def remote_grid_credentials
|
||||||
|
|
|
@ -0,0 +1,167 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe 'Enumerator#next patch fix' do
|
||||||
|
describe 'Enumerator' do
|
||||||
|
RSpec::Matchers.define :contain_unique_method_calls_in_order do |expected|
|
||||||
|
attr_reader :actual
|
||||||
|
|
||||||
|
match do |actual|
|
||||||
|
@actual_err = actual
|
||||||
|
regexps = expected.map { |method_name| { name: method_name, regexp: make_regexp(method_name) } }
|
||||||
|
@actual = actual.backtrace.filter_map do |line|
|
||||||
|
regexp = regexps.find { |r| r[:regexp].match? line }
|
||||||
|
|
||||||
|
regexp[:name] if regexp
|
||||||
|
end
|
||||||
|
|
||||||
|
expected == @actual
|
||||||
|
end
|
||||||
|
|
||||||
|
diffable
|
||||||
|
|
||||||
|
failure_message do
|
||||||
|
"#{super()}\n\nFull error backtrace:\n #{@actual_err.backtrace.join("\n ")}"
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def make_regexp(method_name)
|
||||||
|
Regexp.new("/spec/initializers/enumerator_next_patch_spec\\.rb:[0-9]+:in `#{method_name}'$")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def have_been_raised_by_next_and_not_fixed_up
|
||||||
|
contain_unique_method_calls_in_order %w(call_enum_method)
|
||||||
|
end
|
||||||
|
|
||||||
|
def have_been_raised_by_enum_object_and_fixed_up
|
||||||
|
contain_unique_method_calls_in_order %w(make_error call_enum_method)
|
||||||
|
end
|
||||||
|
|
||||||
|
def have_been_raised_by_nested_next_and_fixed_up
|
||||||
|
contain_unique_method_calls_in_order %w(call_nested_next call_enum_method)
|
||||||
|
end
|
||||||
|
|
||||||
|
methods = [
|
||||||
|
{
|
||||||
|
name: 'next',
|
||||||
|
expected_value: 'Test value'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'next_values',
|
||||||
|
expected_value: ['Test value']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'peek',
|
||||||
|
expected_value: 'Test value'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'peek_values',
|
||||||
|
expected_value: ['Test value']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
methods.each do |method|
|
||||||
|
describe "##{method[:name]}" do
|
||||||
|
def call_enum_method
|
||||||
|
enumerator.send(method_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:method_name) { method[:name] }
|
||||||
|
|
||||||
|
subject { call_enum_method }
|
||||||
|
|
||||||
|
describe 'normal yield' do
|
||||||
|
let(:enumerator) { Enumerator.new { |yielder| yielder << 'Test value' } }
|
||||||
|
|
||||||
|
it 'returns yielded value' do
|
||||||
|
is_expected.to eq(method[:expected_value])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'end of iteration' do
|
||||||
|
let(:enumerator) { Enumerator.new { |_| } }
|
||||||
|
|
||||||
|
it 'does not fix up StopIteration' do
|
||||||
|
expect { subject }.to raise_error do |err|
|
||||||
|
expect(err).to be_a(StopIteration)
|
||||||
|
expect(err).to have_been_raised_by_next_and_not_fixed_up
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'nested enum object' do
|
||||||
|
def call_nested_next
|
||||||
|
nested_enumerator.next
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:nested_enumerator) { Enumerator.new { |_| } }
|
||||||
|
let(:enumerator) { Enumerator.new { |yielder| yielder << call_nested_next } }
|
||||||
|
|
||||||
|
it 'fixes up StopIteration thrown by another instance of #next' do
|
||||||
|
expect { subject }.to raise_error do |err|
|
||||||
|
expect(err).to be_a(StopIteration)
|
||||||
|
expect(err).to have_been_raised_by_nested_next_and_fixed_up
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'arguments error' do
|
||||||
|
def call_enum_method
|
||||||
|
enumerator.send(method_name, 'extra argument')
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:enumerator) { Enumerator.new { |_| } }
|
||||||
|
|
||||||
|
it 'does not fix up ArgumentError' do
|
||||||
|
expect { subject }.to raise_error do |err|
|
||||||
|
expect(err).to be_a(ArgumentError)
|
||||||
|
expect(err).to have_been_raised_by_next_and_not_fixed_up
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'error' do
|
||||||
|
let(:enumerator) { Enumerator.new { |_| raise error } }
|
||||||
|
let(:error) { make_error }
|
||||||
|
|
||||||
|
it 'fixes up StopIteration' do
|
||||||
|
def make_error
|
||||||
|
StopIteration.new.tap { |err| err.set_backtrace(caller) }
|
||||||
|
end
|
||||||
|
|
||||||
|
expect { subject }.to raise_error do |err|
|
||||||
|
expect(err).to be(error)
|
||||||
|
expect(err).to have_been_raised_by_enum_object_and_fixed_up
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'fixes up ArgumentError' do
|
||||||
|
def make_error
|
||||||
|
ArgumentError.new.tap { |err| err.set_backtrace(caller) }
|
||||||
|
end
|
||||||
|
|
||||||
|
expect { subject }.to raise_error do |err|
|
||||||
|
expect(err).to be(error)
|
||||||
|
expect(err).to have_been_raised_by_enum_object_and_fixed_up
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'adds backtrace from other errors' do
|
||||||
|
def make_error
|
||||||
|
StandardError.new('This is a test').tap { |err| err.set_backtrace(caller) }
|
||||||
|
end
|
||||||
|
|
||||||
|
expect { subject }.to raise_error do |err|
|
||||||
|
expect(err).to be(error)
|
||||||
|
expect(err).to have_been_raised_by_enum_object_and_fixed_up
|
||||||
|
expect(err.message).to eq('This is a test')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue