|
| 1 | +require 'katello_test_helper' |
| 2 | + |
| 3 | +module ::Actions::Pulp3::CapsuleContent |
| 4 | + class DistributionConflictTest < ActiveSupport::TestCase |
| 5 | + it 'matches both async task and direct api race messages' do |
| 6 | + async_error = ::Katello::Errors::Pulp3Error.new( |
| 7 | + "{'base_path': [ErrorDetail(string='This field must be unique.', code='unique')]}" |
| 8 | + ) |
| 9 | + api_error = RuntimeError.new('{"base_path":["Overlaps with existing distribution"]}') |
| 10 | + |
| 11 | + assert ::Katello::Pulp3::DistributionConflict.create_race?(async_error) |
| 12 | + assert ::Katello::Pulp3::DistributionConflict.create_race?(api_error) |
| 13 | + refute ::Katello::Pulp3::DistributionConflict.create_race?("Remote artifacts cannot be exported") |
| 14 | + end |
| 15 | + end |
| 16 | + |
| 17 | + class GenerateMetadataTest < ActiveSupport::TestCase |
| 18 | + include Dynflow::Testing |
| 19 | + include Support::Actions::Fixtures |
| 20 | + include Support::CapsuleSupport |
| 21 | + include Support::Actions::RemoteAction |
| 22 | + |
| 23 | + let(:proxy) { capsule_content.smart_proxy } |
| 24 | + |
| 25 | + before do |
| 26 | + set_user |
| 27 | + SmartProxy.any_instance.stubs(:ping_pulp).returns({}) |
| 28 | + SmartProxy.any_instance.stubs(:ping_pulp3).returns({}) |
| 29 | + SmartProxy.any_instance.stubs(:pulp3_configuration).returns(nil) |
| 30 | + end |
| 31 | + |
| 32 | + # pulp3_skip_publication: true - plan_self is skipped, tree is empty |
| 33 | + it 'does not plan RefreshDistribution inline for publication-less repos (e.g. docker)' do |
| 34 | + repo = katello_repositories(:pulp3_docker_1) |
| 35 | + tree = plan_action_tree(::Actions::Pulp3::CapsuleContent::GenerateMetadata, |
| 36 | + repo, proxy) |
| 37 | + |
| 38 | + refute_tree_planned(tree, ::Actions::Pulp3::CapsuleContent::RefreshDistribution) |
| 39 | + end |
| 40 | + |
| 41 | + # pulp3_skip_publication: false - plan_self IS called to create a publication |
| 42 | + it 'does not plan RefreshDistribution inline for publication-based repos (e.g. file)' do |
| 43 | + repo = katello_repositories(:pulp3_file_1) |
| 44 | + tree = plan_action_tree(::Actions::Pulp3::CapsuleContent::GenerateMetadata, |
| 45 | + repo, proxy) |
| 46 | + |
| 47 | + refute_tree_planned(tree, ::Actions::Pulp3::CapsuleContent::RefreshDistribution) |
| 48 | + end |
| 49 | + end |
| 50 | + |
| 51 | + class RefreshDistributionTest < ActiveSupport::TestCase |
| 52 | + include Dynflow::Testing |
| 53 | + include Support::Actions::Fixtures |
| 54 | + include Support::CapsuleSupport |
| 55 | + include Support::Actions::RemoteAction |
| 56 | + |
| 57 | + let(:proxy) { capsule_content.smart_proxy } |
| 58 | + let(:repo) { katello_repositories(:pulp3_docker_1) } |
| 59 | + |
| 60 | + before do |
| 61 | + set_user |
| 62 | + SmartProxy.any_instance.stubs(:ping_pulp).returns({}) |
| 63 | + SmartProxy.any_instance.stubs(:ping_pulp3).returns({}) |
| 64 | + SmartProxy.any_instance.stubs(:pulp3_configuration).returns(nil) |
| 65 | + end |
| 66 | + |
| 67 | + def build_action_with_output |
| 68 | + action = create_action(::Actions::Pulp3::CapsuleContent::RefreshDistribution) |
| 69 | + action.stubs(:input).returns('repository_id' => repo.id, 'smart_proxy_id' => proxy.id) |
| 70 | + action_output = {} |
| 71 | + action.stubs(:output).returns(action_output) |
| 72 | + [action, action_output] |
| 73 | + end |
| 74 | + |
| 75 | + it 'recovers from a concurrent distribution creation by retrying as an update' do |
| 76 | + action, action_output = build_action_with_output |
| 77 | + |
| 78 | + mock_task = mock('pulp_task') |
| 79 | + action.expects(:invoke_external_task).returns(mock_task) |
| 80 | + action.expects(:external_task=).with(mock_task) |
| 81 | + |
| 82 | + error = ::Katello::Errors::Pulp3Error.new( |
| 83 | + "{'base_path': [ErrorDetail(string='This field must be unique.', code='unique')]}" |
| 84 | + ) |
| 85 | + action.rescue_external_task(error) |
| 86 | + assert action_output[:retried_distribution_refresh] |
| 87 | + end |
| 88 | + |
| 89 | + it 'recovers when both name and base_path are reported as non-unique (real Pulp error format)' do |
| 90 | + action, action_output = build_action_with_output |
| 91 | + |
| 92 | + mock_task = mock('pulp_task') |
| 93 | + action.expects(:invoke_external_task).returns(mock_task) |
| 94 | + action.expects(:external_task=).with(mock_task) |
| 95 | + |
| 96 | + error = ::Katello::Errors::Pulp3Error.new( |
| 97 | + "{'name': [ErrorDetail(string='This field must be unique.', code='unique')], " \ |
| 98 | + "'base_path': [ErrorDetail(string='This field must be unique.', code='unique')]}" |
| 99 | + ) |
| 100 | + action.rescue_external_task(error) |
| 101 | + assert action_output[:retried_distribution_refresh] |
| 102 | + end |
| 103 | + |
| 104 | + # Non-Pulp3Errors fall through to the parent's rescue_external_task, |
| 105 | + # which delegates to Dynflow's default handler. We verify here that |
| 106 | + # RefreshDistribution does not intercept them for its own retry logic. |
| 107 | + it 'does not attempt recovery for non-Pulp3Errors' do |
| 108 | + action, _action_output = build_action_with_output |
| 109 | + action.expects(:invoke_external_task).never |
| 110 | + |
| 111 | + action.rescue_external_task(RuntimeError.new("connection refused")) |
| 112 | + end |
| 113 | + |
| 114 | + it 'recovers from an overlap conflict (base_path overlaps existing distribution)' do |
| 115 | + action, action_output = build_action_with_output |
| 116 | + |
| 117 | + mock_task = mock('pulp_task') |
| 118 | + action.expects(:invoke_external_task).returns(mock_task) |
| 119 | + action.expects(:external_task=).with(mock_task) |
| 120 | + |
| 121 | + error = ::Katello::Errors::Pulp3Error.new( |
| 122 | + "{'base_path': ['Overlaps with existing distribution']}" |
| 123 | + ) |
| 124 | + action.rescue_external_task(error) |
| 125 | + assert action_output[:retried_distribution_refresh] |
| 126 | + end |
| 127 | + |
| 128 | + it 're-raises the conflict after the one allowed retry is exhausted' do |
| 129 | + action, action_output = build_action_with_output |
| 130 | + action_output[:retried_distribution_refresh] = true |
| 131 | + action.expects(:invoke_external_task).never |
| 132 | + |
| 133 | + error = ::Katello::Errors::Pulp3Error.new( |
| 134 | + "{'base_path': [ErrorDetail(string='This field must be unique.', code='unique')]}" |
| 135 | + ) |
| 136 | + |
| 137 | + assert_raises(::Katello::Errors::Pulp3Error) { action.rescue_external_task(error) } |
| 138 | + end |
| 139 | + |
| 140 | + it 're-raises Pulp3Errors unrelated to distribution uniqueness' do |
| 141 | + action, _action_output = build_action_with_output |
| 142 | + action.expects(:invoke_external_task).never |
| 143 | + |
| 144 | + error = ::Katello::Errors::Pulp3Error.new("Remote artifacts cannot be exported") |
| 145 | + assert_raises(::Katello::Errors::Pulp3Error) { action.rescue_external_task(error) } |
| 146 | + end |
| 147 | + end |
| 148 | +end |
0 commit comments