diff options
Diffstat (limited to 'spec/lib/api')
-rw-r--r-- | spec/lib/api/entities/snippet_repository_storage_move_spec.rb | 25 | ||||
-rw-r--r-- | spec/lib/api/helpers/authentication_spec.rb | 207 | ||||
-rw-r--r-- | spec/lib/api/helpers_spec.rb | 47 |
3 files changed, 278 insertions, 1 deletions
diff --git a/spec/lib/api/entities/snippet_repository_storage_move_spec.rb b/spec/lib/api/entities/snippet_repository_storage_move_spec.rb new file mode 100644 index 00000000000..8086be3ffa7 --- /dev/null +++ b/spec/lib/api/entities/snippet_repository_storage_move_spec.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe API::Entities::SnippetRepositoryStorageMove do + describe '#as_json' do + subject { entity.as_json } + + let(:default_storage) { 'default' } + let(:second_storage) { 'test_second_storage' } + let(:storage_move) { create(:snippet_repository_storage_move, :scheduled, destination_storage_name: second_storage) } + let(:entity) { described_class.new(storage_move) } + + it 'includes basic fields' do + allow(Gitlab.config.repositories.storages).to receive(:keys).and_return(%W[#{default_storage} #{second_storage}]) + + is_expected.to include( + state: 'scheduled', + source_storage_name: default_storage, + destination_storage_name: second_storage, + snippet: a_kind_of(Hash) + ) + end + end +end diff --git a/spec/lib/api/helpers/authentication_spec.rb b/spec/lib/api/helpers/authentication_spec.rb new file mode 100644 index 00000000000..461b0d2f6f9 --- /dev/null +++ b/spec/lib/api/helpers/authentication_spec.rb @@ -0,0 +1,207 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe API::Helpers::Authentication do + let_it_be(:user) { create(:user) } + let_it_be(:project, reload: true) { create(:project, :public) } + let_it_be(:personal_access_token) { create(:personal_access_token, user: user) } + let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) } + + describe 'class methods' do + subject { Class.new.include(described_class::ClassMethods).new } + + describe '.authenticate_with' do + it 'sets namespace_inheritable :authentication to correctly when body is empty' do + expect(subject).to receive(:namespace_inheritable).with(:authentication, {}) + + subject.authenticate_with { |allow| } + end + + it 'sets namespace_inheritable :authentication to correctly when body is not empty' do + expect(subject).to receive(:namespace_inheritable).with(:authentication, { basic: [:pat, :job], oauth: [:pat, :job] }) + + subject.authenticate_with { |allow| allow.token_type(:pat, :job).sent_through(:basic, :oauth) } + end + end + end + + describe 'helper methods' do + let(:object) do + cls = Class.new + + class << cls + def helpers(*modules, &block) + modules.each { |m| include m } + include Module.new.tap { |m| m.class_eval(&block) } if block_given? + end + end + + cls.define_method(:unauthorized!) { raise '401' } + cls.define_method(:bad_request!) { |m| raise "400 - #{m}" } + + # Include the helper class methods, as instance methods + cls.include described_class::ClassMethods + + # Include the methods under test + cls.include described_class + + cls.new + end + + describe '#token_from_namespace_inheritable' do + let(:object) do + o = super() + + o.instance_eval do + # It doesn't matter what this returns as long as the method is defined + def current_request + nil + end + + # Spoof Grape's namespace inheritable system + def namespace_inheritable(key, value = nil) + return unless key == :authentication + + if value + @authentication = value + else + @authentication + end + end + end + + o + end + + let(:authentication) do + object.authenticate_with { |allow| allow.token_types(*resolvers).sent_through(*locators) } + end + + subject { object.token_from_namespace_inheritable } + + before do + # Skip validation of token transports and types to simplify testing + allow(Gitlab::APIAuthentication::TokenLocator).to receive(:new) { |type| type } + allow(Gitlab::APIAuthentication::TokenResolver).to receive(:new) { |type| type } + + authentication + end + + shared_examples 'stops early' do |response_method| + it "calls ##{response_method}" do + errcls = Class.new(StandardError) + expect(object).to receive(response_method).and_raise(errcls) + expect { subject }.to raise_error(errcls) + end + end + + shared_examples 'an anonymous request' do + it 'returns nil' do + expect(subject).to be(nil) + end + end + + shared_examples 'an authenticated request' do + it 'returns the token' do + expect(subject).to be(token) + end + end + + shared_examples 'an unauthorized request' do + it_behaves_like 'stops early', :unauthorized! + end + + context 'with no allowed authentication strategies' do + let(:authentication) { nil } + + it_behaves_like 'an anonymous request' + end + + context 'with no located credentials' do + let(:locators) { [double(extract: nil)] } + let(:resolvers) { [] } + + it_behaves_like 'an anonymous request' + end + + context 'with one set of located credentials' do + let(:locators) { [double(extract: true)] } + + context 'when the credentials contain a valid token' do + let(:token) { double } + let(:resolvers) { [double(resolve: token)] } + + it_behaves_like 'an authenticated request' + end + + context 'when the credentials do not contain a valid token' do + let(:resolvers) { [double(resolve: nil)] } + + it_behaves_like 'an unauthorized request' + end + end + + context 'with multiple located credentials' do + let(:locators) { [double(extract: true), double(extract: true)] } + let(:resolvers) { [] } + + it_behaves_like 'stops early', :bad_request! + end + + context 'when a resolver raises UnauthorizedError' do + let(:locators) { [double(extract: true)] } + let(:resolvers) do + r = double + expect(r).to receive(:resolve).and_raise(Gitlab::Auth::UnauthorizedError) + r + end + + it_behaves_like 'an unauthorized request' + end + end + + describe '#access_token_from_namespace_inheritable' do + subject { object.access_token_from_namespace_inheritable } + + it 'returns #token_from_namespace_inheritable if it is a personal access token' do + expect(object).to receive(:token_from_namespace_inheritable).and_return(personal_access_token) + expect(subject).to be(personal_access_token) + end + + it 'returns nil if #token_from_namespace_inheritable is not a personal access token' do + token = double + expect(object).to receive(:token_from_namespace_inheritable).and_return(token) + expect(subject).to be(nil) + end + end + + describe '#user_from_namespace_inheritable' do + subject { object.user_from_namespace_inheritable } + + it 'returns #token_from_namespace_inheritable if it is a deploy token' do + expect(object).to receive(:token_from_namespace_inheritable).and_return(deploy_token) + expect(subject).to be(deploy_token) + end + + it 'returns #token_from_namespace_inheritable.user if the token is not a deploy token' do + user = double + token = double(user: user) + expect(object).to receive(:token_from_namespace_inheritable).and_return(token) + + expect(subject).to be(user) + end + + it 'falls back to #find_user_from_warden if #token_from_namespace_inheritable.user is nil' do + token = double(user: nil) + expect(object).to receive(:token_from_namespace_inheritable).and_return(token) + subject + end + + it 'falls back to #find_user_from_warden if #token_from_namespace_inheritable is nil' do + expect(object).to receive(:token_from_namespace_inheritable).and_return(nil) + subject + end + end + end +end diff --git a/spec/lib/api/helpers_spec.rb b/spec/lib/api/helpers_spec.rb index be5f0cc9f9a..bdf04fafaae 100644 --- a/spec/lib/api/helpers_spec.rb +++ b/spec/lib/api/helpers_spec.rb @@ -205,7 +205,7 @@ RSpec.describe API::Helpers do end it 'tracks redis hll event' do - expect(Gitlab::UsageDataCounters::HLLRedisCounter).to receive(:track_event).with(value, event_name) + expect(Gitlab::UsageDataCounters::HLLRedisCounter).to receive(:track_event).with(event_name, values: value) subject.increment_unique_values(event_name, value) end @@ -363,4 +363,49 @@ RSpec.describe API::Helpers do end end end + + describe '#present_disk_file!' do + let_it_be(:dummy_class) do + Class.new do + attr_reader :headers + alias_method :header, :headers + + def initialize + @headers = {} + end + end + end + + let(:dummy_instance) { dummy_class.include(described_class).new } + let(:path) { '/tmp/file.txt' } + let(:filename) { 'file.txt' } + + subject { dummy_instance.present_disk_file!(path, filename) } + + before do + expect(dummy_instance).to receive(:content_type).with('application/octet-stream') + end + + context 'with X-Sendfile supported' do + before do + dummy_instance.headers['X-Sendfile-Type'] = 'X-Sendfile' + end + + it 'sends the file using X-Sendfile' do + expect(dummy_instance).to receive(:body).with('') + + subject + + expect(dummy_instance.headers['X-Sendfile']).to eq(path) + end + end + + context 'without X-Sendfile supported' do + it 'sends the file' do + expect(dummy_instance).to receive(:sendfile).with(path) + + subject + end + end + end end |