| # Licensed to the Apache Software Foundation (ASF) under one or more |
| # contributor license agreements. See the NOTICE file distributed with this |
| # work for additional information regarding copyright ownership. The ASF |
| # licenses this file to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations under |
| # the License. |
| |
| |
| require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helpers')) |
| |
| |
| describe URI, '#download' do |
| before do |
| write @source = File.expand_path('source'), @content = 'A file to download' |
| @uri = URI(URI.escape("file://#{@source}")) |
| @target = File.expand_path('target') |
| rm_f @target |
| end |
| |
| it 'should download file if found' do |
| @uri.download @target |
| file(@target).should contain(@content) |
| end |
| |
| it 'should fail if file not found' do |
| lambda { (@uri + 'missing').download @target }.should raise_error(URI::NotFoundError) |
| file(@target).should_not exist |
| end |
| |
| it 'should work the same way from static method with URI' do |
| URI.download @uri, @target |
| file(@target).should contain(@content) |
| end |
| |
| it 'should work the same way from static method with String' do |
| URI.download @uri.to_s, @target |
| file(@target).should contain(@content) |
| end |
| |
| it 'should download to a task' do |
| @uri.download file(@target) |
| file(@target).should contain(@content) |
| end |
| |
| it 'should download to a file' do |
| File.open(@target, 'w') { |file| @uri.download file } |
| file(@target).should contain(@content) |
| end |
| end |
| |
| |
| describe URI, '#upload' do |
| before do |
| write @source = 'source', @content = 'Just a file' |
| @target = File.expand_path('target') |
| rm_rf @target |
| @uri = URI(URI.escape("file://#{@target}")) |
| end |
| |
| it 'should preserve file permissions if uploading to a file' do |
| File.chmod(0666, @source) |
| s = File.stat(@source).mode |
| @uri.upload @source |
| File.stat(@target).mode.should eql(s) |
| end |
| |
| it 'should upload file if found' do |
| @uri.upload @source |
| file(@target).should contain(@content) |
| end |
| |
| it 'should fail if file not found' do |
| lambda { @uri.upload @source.ext('missing') }.should raise_error(URI::NotFoundError) |
| file(@target).should_not exist |
| end |
| |
| it 'should work the same way from static method with URI' do |
| URI.upload @uri, @source |
| file(@target).should contain(@content) |
| end |
| |
| it 'should work the same way from static method with String' do |
| URI.upload @uri.to_s, @source |
| file(@target).should contain(@content) |
| end |
| |
| it 'should upload from a task' do |
| @uri.upload file(@source) |
| file(@target).should contain(@content) |
| end |
| |
| it 'should create MD5 hash' do |
| @uri.upload file(@source) |
| file(@target.ext('.md5')).should contain(Digest::MD5.hexdigest(@content)) |
| end |
| |
| it 'should create SHA1 hash' do |
| @uri.upload file(@source) |
| file(@target.ext('.sha1')).should contain(Digest::SHA1.hexdigest(@content)) |
| end |
| |
| it 'should upload an entire directory' do |
| mkpath 'dir' ; write 'dir/test', 'in directory' |
| mkpath 'dir/nested' ; write 'dir/nested/test', 'in nested directory' |
| @uri.upload 'dir' |
| file(@target).should contain('test', 'nested/test') |
| file(@target + '/test').should contain('in directory') |
| file(@target + '/nested/test').should contain('in nested directory') |
| end |
| end |
| |
| |
| describe URI::FILE do |
| |
| it 'should accept file:something as file:///something' do |
| URI('file:something').should eql(URI('file:///something')) |
| end |
| |
| it 'should accept file:/ as file:///' do |
| URI('file:/').should eql(URI('file:///')) |
| end |
| |
| it 'should accept file:/something as file:///something' do |
| URI('file:/something').should eql(URI('file:///something')) |
| end |
| |
| it 'should accept file://something as file://something/' do |
| URI('file://something').should eql(URI('file://something/')) |
| end |
| |
| it 'should accept file:///something' do |
| URI('file:///something').should be_kind_of(URI::FILE) |
| URI('file:///something').to_s.should eql('file:///something') |
| URI('file:///something').path.should eql('/something') |
| end |
| |
| it 'should treat host as path when host name is a Windows drive' do |
| URI('file://c:/something').should eql(URI('file:///c:/something')) |
| end |
| end |
| |
| |
| describe URI::FILE, '#read' do |
| before do |
| @filename = 'readme' |
| @uri = URI(URI.escape("file:///#{File.expand_path(@filename)}")) |
| @content = 'Readme. Please!' |
| write 'readme', @content |
| end |
| |
| it 'should not complain about excessive options' do |
| @uri.read :proxy=>[], :lovely=>true |
| end |
| |
| it 'should read the file' do |
| @uri.read.should eql(@content) |
| end |
| |
| it 'should read the file and yield to block' do |
| @uri.read { |content| content.should eql(@content) } |
| end |
| |
| it 'should raise NotFoundError if file doesn\'t exist' do |
| lambda { (@uri + 'notme').read }.should raise_error(URI::NotFoundError) |
| end |
| |
| it 'should raise NotFoundError if file is actually a directory' do |
| mkpath 'dir' |
| lambda { (@uri + 'dir').read }.should raise_error(URI::NotFoundError) |
| end |
| end |
| |
| |
| describe URI::FILE, '#write' do |
| before do |
| @filename = 'readme' |
| @uri = URI(URI.escape("file:///#{File.expand_path(@filename)}")) |
| @content = 'Readme. Please!' |
| end |
| |
| it 'should not complain about excessive options' do |
| @uri.write @content, :proxy=>[], :lovely=>true |
| end |
| |
| it 'should write the file from a string' do |
| @uri.write @content |
| read(@filename).should eql(@content) |
| end |
| |
| it 'should write the file from a reader' do |
| reader = Object.new |
| class << reader |
| def read(bytes) ; @array.pop ; end |
| end |
| reader.instance_variable_set :@array, [@content] |
| @uri.write reader |
| read(@filename).should eql(@content) |
| end |
| |
| it 'should write the file from a block' do |
| array = [@content] |
| @uri.write { array.pop } |
| read(@filename).should eql(@content) |
| end |
| |
| it 'should not create file if read fails' do |
| @uri.write { fail } rescue nil |
| file(@filename).should_not exist |
| end |
| end |
| |
| |
| def default_http_headers |
| {"Cache-Control" => "no-cache", "User-Agent" => "Buildr-#{Buildr::VERSION}"} |
| end |
| |
| describe URI::HTTP, '#read' do |
| before do |
| @proxy = 'http://john:smith@myproxy:8080' |
| @domain = 'domain' |
| @host_domain = "host.#{@domain}" |
| @path = "/foo/bar/baz" |
| @query = "?query" |
| @uri = URI("http://#{@host_domain}#{@path}#{@query}") |
| @no_proxy_args = [@host_domain, 80] |
| @proxy_args = @no_proxy_args + ['myproxy', 8080, 'john', 'smith'] |
| @http = double('http') |
| @http.stub(:request).and_yield(Net::HTTPNotModified.new(nil, nil, nil)) |
| end |
| |
| it 'should not use proxy unless proxy is set' do |
| Net::HTTP.should_receive(:new).with(*@no_proxy_args).and_return(@http) |
| @uri.read |
| end |
| |
| it 'should use HTTPS if applicable' do |
| Net::HTTP.should_receive(:new).with(@host_domain, 443).and_return(@http) |
| @http.should_receive(:use_ssl=).with(true) |
| URI(@uri.to_s.sub(/http/, 'https')).read |
| end |
| |
| it 'should use custom SSL CA certificates if provided through the environment variable SSL_CA_CERTS' do |
| ENV['SSL_VERIFY_MODE'] = 'VERIFY_PEER' |
| Net::HTTP.should_receive(:new).with(@host_domain, 443).and_return(@http) |
| @http.should_receive(:use_ssl=).with(true) |
| @http.should_receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER) |
| URI(@uri.to_s.sub(/http/, 'https')).read |
| end |
| |
| it 'should use custom verify mode if provided through the environment variable SSL_VERIFY_MODE' do |
| ENV['SSL_CA_CERTS'] = 'tmp/certs' |
| Net::HTTP.should_receive(:new).with(@host_domain, 443).and_return(@http) |
| @http.should_receive(:use_ssl=).with(true) |
| @http.should_receive(:ca_path=).with('tmp/certs') |
| URI(@uri.to_s.sub(/http/, 'https')).read |
| end |
| |
| it 'should use proxy from environment variable HTTP_PROXY when using http' do |
| ENV['HTTP_PROXY'] = @proxy |
| Net::HTTP.should_receive(:new).with(*@proxy_args).and_return(@http) |
| @uri.read |
| end |
| |
| it 'should use proxy from environment variable HTTPS_PROXY when using https' do |
| ENV['HTTPS_PROXY'] = @proxy |
| Net::HTTP.should_receive(:new).with(@host_domain, 443, 'myproxy', 8080, 'john', 'smith').and_return(@http) |
| @http.should_receive(:use_ssl=).with(true) |
| URI(@uri.to_s.sub(/http/, 'https')).read |
| end |
| |
| it 'should not use proxy for hosts from environment variable NO_PROXY' do |
| ENV['HTTP_PROXY'] = @proxy |
| ENV['NO_PROXY'] = @host_domain |
| Net::HTTP.should_receive(:new).with(*@no_proxy_args).and_return(@http) |
| @uri.read |
| end |
| |
| it 'should use proxy for hosts other than those specified by NO_PROXY' do |
| ENV['HTTP_PROXY'] = @proxy |
| ENV['NO_PROXY'] = 'whatever' |
| Net::HTTP.should_receive(:new).with(*@proxy_args).and_return(@http) |
| @uri.read |
| end |
| |
| it 'should support comma separated list in environment variable NO_PROXY' do |
| ENV['HTTP_PROXY'] = @proxy |
| ENV['NO_PROXY'] = 'optimus,prime' |
| Net::HTTP.should_receive(:new).with('optimus', 80).and_return(@http) |
| URI('http://optimus').read |
| Net::HTTP.should_receive(:new).with('prime', 80).and_return(@http) |
| URI('http://prime').read |
| Net::HTTP.should_receive(:new).with('bumblebee', *@proxy_args[1..-1]).and_return(@http) |
| URI('http://bumblebee').read |
| end |
| |
| it 'should support glob pattern in NO_PROXY' do |
| ENV['HTTP_PROXY'] = @proxy |
| ENV['NO_PROXY'] = "*.#{@domain}" |
| Net::HTTP.should_receive(:new).once.with(*@no_proxy_args).and_return(@http) |
| @uri.read |
| end |
| |
| it 'should support specific port in NO_PROXY' do |
| ENV['HTTP_PROXY'] = @proxy |
| ENV['NO_PROXY'] = "#{@host_domain}:80" |
| Net::HTTP.should_receive(:new).with(*@no_proxy_args).and_return(@http) |
| @uri.read |
| ENV['NO_PROXY'] = "#{@host_domain}:800" |
| Net::HTTP.should_receive(:new).with(*@proxy_args).and_return(@http) |
| @uri.read |
| end |
| |
| it 'should not die if content size is zero' do |
| ok = Net::HTTPOK.new(nil, nil, nil) |
| ok.stub(:read_body) |
| @http.stub(:request).and_yield(ok) |
| Net::HTTP.should_receive(:new).and_return(@http) |
| $stdout.should_receive(:isatty).and_return(false) |
| @uri.read :progress=>true |
| end |
| |
| it 'should use HTTP Basic authentication' do |
| Net::HTTP.should_receive(:new).and_return(@http) |
| request = double('request') |
| Net::HTTP::Get.should_receive(:new).and_return(request) |
| request.should_receive(:basic_auth).with('john', 'secret') |
| URI("http://john:secret@#{@host_domain}").read |
| end |
| |
| it 'should preseve authentication information during a redirect' do |
| Net::HTTP.should_receive(:new).twice.and_return(@http) |
| |
| # The first request will produce a redirect |
| redirect = Net::HTTPRedirection.new(nil, nil, nil) |
| redirect['Location'] = "http://#{@host_domain}/asdf" |
| |
| request1 = double('request1') |
| Net::HTTP::Get.should_receive(:new).once.with('/', default_http_headers).and_return(request1) |
| request1.should_receive(:basic_auth).with('john', 'secret') |
| @http.should_receive(:request).with(request1).and_yield(redirect) |
| |
| # The second request will be ok |
| ok = Net::HTTPOK.new(nil, nil, nil) |
| ok.stub(:read_body) |
| |
| request2 = double('request2') |
| Net::HTTP::Get.should_receive(:new).once.with("/asdf", default_http_headers).and_return(request2) |
| request2.should_receive(:basic_auth).with('john', 'secret') |
| @http.should_receive(:request).with(request2).and_yield(ok) |
| |
| URI("http://john:secret@#{@host_domain}").read |
| end |
| |
| it 'should include the query part when performing HTTP GET' do |
| # should this test be generalized or shared with any other URI subtypes? |
| Net::HTTP.stub(:new).and_return(@http) |
| Net::HTTP::Get.should_receive(:new).with(/#{Regexp.escape(@query)}$/, default_http_headers) |
| @uri.read |
| end |
| |
| end |
| |
| |
| describe URI::HTTP, '#write' do |
| before do |
| @content = 'Readme. Please!' |
| @uri = URI('http://john:secret@host.domain/foo/bar/baz.jar') |
| @http = double('Net::HTTP') |
| @http.stub(:request).and_return(Net::HTTPOK.new(nil, nil, nil)) |
| @http.should_receive(:read_timeout=).with(500) |
| Net::HTTP.stub(:new).and_return(@http) |
| end |
| |
| it 'should open connection to HTTP server' do |
| Net::HTTP.should_receive(:new).with('host.domain', 80).and_return(@http) |
| @uri.write @content |
| end |
| |
| it 'should use HTTP basic authentication' do |
| @http.should_receive(:request) do |request| |
| request['authorization'].should == ('Basic ' + ['john:secret'].pack('m').delete("\r\n")) |
| Net::HTTPOK.new(nil, nil, nil) |
| end |
| @uri.write @content |
| end |
| |
| it 'should use HTTPS if applicable' do |
| Net::HTTP.should_receive(:new).with('host.domain', 443).and_return(@http) |
| @http.should_receive(:use_ssl=).with(true) |
| URI(@uri.to_s.sub(/http/, 'https')).write @content |
| end |
| |
| it 'should upload file with PUT request' do |
| @http.should_receive(:request) do |request| |
| request.should be_kind_of(Net::HTTP::Put) |
| Net::HTTPOK.new(nil, nil, nil) |
| end |
| @uri.write @content |
| end |
| |
| it 'should set Content-Length header' do |
| @http.should_receive(:request) do |request| |
| request.content_length.should == @content.size |
| Net::HTTPOK.new(nil, nil, nil) |
| end |
| @uri.write @content |
| end |
| |
| it 'should set Content-MD5 header' do |
| @http.should_receive(:request) do |request| |
| request['Content-MD5'].should == Digest::MD5.hexdigest(@content) |
| Net::HTTPOK.new(nil, nil, nil) |
| end |
| @uri.write @content |
| end |
| |
| it 'should set User-Agent header' do |
| @http.should_receive(:request) do |request| |
| request['User-Agent'].should == "Buildr-#{Buildr::VERSION}" |
| Net::HTTPOK.new(nil, nil, nil) |
| end |
| @uri.write @content |
| end |
| |
| it 'should send entire content' do |
| @http.should_receive(:request) do |request| |
| body_stream = request.body_stream |
| body_stream.read(1024).should == @content |
| body_stream.read(1024).should be_nil |
| Net::HTTPOK.new(nil, nil, nil) |
| end |
| @uri.write @content |
| end |
| |
| it 'should fail on 4xx response' do |
| @http.should_receive(:request).and_return(Net::HTTPBadRequest.new(nil, nil, nil)) |
| lambda { @uri.write @content }.should raise_error(RuntimeError, /failed to upload/i) |
| end |
| |
| it 'should fail on 5xx response' do |
| @http.should_receive(:request).and_return(Net::HTTPServiceUnavailable.new(nil, nil, nil)) |
| lambda { @uri.write @content }.should raise_error(RuntimeError, /failed to upload/i) |
| end |
| |
| end |
| |
| |
| describe URI::SFTP, '#read' do |
| before do |
| @uri = URI('sftp://john:secret@localhost/root/path/readme') |
| @content = 'Readme. Please!' |
| |
| @ssh_session = double('Net::SSH::Session') |
| @sftp_session = double('Net::SFTP::Session') |
| @file_factory = double('Net::SFTP::Operations::FileFactory') |
| Net::SSH.stub(:start).with('localhost', 'john', :password=>'secret', :port=>22).and_return(@ssh_session) do |
| Net::SFTP::Session.should_receive(:new).with(@ssh_session).and_yield(@sftp_session).and_return(@sftp_session) |
| @sftp_session.should_receive(:connect!).and_return(@sftp_session) |
| @sftp_session.should_receive(:loop) |
| @sftp_session.should_receive(:file).with.and_return(@file_factory) |
| @file_factory.stub(:open) |
| @ssh_session.should_receive(:close) |
| @ssh_session |
| end |
| end |
| |
| it 'should open connection to SFTP server' do |
| @uri.read |
| end |
| |
| it 'should open file for reading' do |
| @file_factory.should_receive(:open).with('/root/path/readme', 'r') |
| @uri.read |
| end |
| |
| it 'should read contents of file and return it' do |
| file = double('Net::SFTP::Operations::File') |
| file.should_receive(:read).with(URI::RW_CHUNK_SIZE).once.and_return(@content) |
| @file_factory.should_receive(:open).with('/root/path/readme', 'r').and_yield(file) |
| @uri.read.should eql(@content) |
| end |
| |
| it 'should read contents of file and pass it to block' do |
| file = double('Net::SFTP::Operations::File') |
| file.should_receive(:read).with(URI::RW_CHUNK_SIZE).once.and_return(@content) |
| @file_factory.should_receive(:open).with('/root/path/readme', 'r').and_yield(file) |
| content = '' |
| @uri.read do |chunk| |
| content << chunk |
| end |
| content.should eql(@content) |
| end |
| end |
| |
| |
| describe URI::SFTP, '#write' do |
| before do |
| @uri = URI('sftp://john:secret@localhost/root/path/readme') |
| @content = 'Readme. Please!' |
| |
| @ssh_session = double('Net::SSH::Session') |
| @sftp_session = double('Net::SFTP::Session') |
| @file_factory = double('Net::SFTP::Operations::FileFactory') |
| Net::SSH.stub(:start).with('localhost', 'john', :password=>'secret', :port=>22).and_return(@ssh_session) do |
| Net::SFTP::Session.should_receive(:new).with(@ssh_session).and_yield(@sftp_session).and_return(@sftp_session) |
| @sftp_session.should_receive(:connect!).and_return(@sftp_session) |
| @sftp_session.should_receive(:loop) |
| @sftp_session.stub(:opendir!).and_return { fail } |
| @sftp_session.stub(:close) |
| @sftp_session.stub(:mkdir!) |
| @sftp_session.should_receive(:file).with.and_return(@file_factory) |
| @file_factory.stub(:open) |
| @ssh_session.should_receive(:close) |
| @ssh_session |
| end |
| end |
| |
| it 'should open connection to SFTP server' do |
| @uri.write @content |
| end |
| |
| it 'should check that path exists on server' do |
| paths = ['/root', '/root/path'] |
| @sftp_session.should_receive(:opendir!).with(anything()).twice { |path| paths.shift.should == path } |
| @uri.write @content |
| end |
| |
| it 'should close all opened directories' do |
| @sftp_session.should_receive(:opendir!).with(anything()).twice do |path| |
| @sftp_session.should_receive(:close).with(handle = Object.new) |
| handle |
| end |
| @uri.write @content |
| end |
| |
| it 'should create missing paths on server' do |
| @sftp_session.should_receive(:opendir!) { |path| fail unless path == '/root' } |
| @sftp_session.should_receive(:mkdir!).once.with('/root/path', {}) |
| @uri.write @content |
| end |
| |
| it 'should create missing directories recursively' do |
| paths = ['/root', '/root/path'] |
| @sftp_session.should_receive(:mkdir!).with(anything(), {}).twice { |path, options| paths.shift.should == path } |
| @uri.write @content |
| end |
| |
| it 'should open file for writing' do |
| @file_factory.should_receive(:open).with('/root/path/readme', 'w') |
| @uri.write @content |
| end |
| |
| it 'should write contents to file' do |
| file = double('Net::SFTP::Operations::File') |
| file.should_receive(:write).with(@content) |
| @file_factory.should_receive(:open).with('/root/path/readme', 'w').and_yield(file) |
| @uri.write @content |
| end |
| |
| end |