diff options
author | lamont-granquist <lamont@scriptkiddie.org> | 2014-04-21 15:51:04 -0700 |
---|---|---|
committer | lamont-granquist <lamont@scriptkiddie.org> | 2014-04-21 15:51:04 -0700 |
commit | 480aeb8b5cda4292411d0bc66e1f05614000acfe (patch) | |
tree | 8a7ad036246a5bb63a9b29b805df786436f3c5cc /spec | |
parent | 5e6a1ad1c477acda4d75437cd356be12ffa5989b (diff) | |
parent | 7c126d5e02e784c0f6a94e429830004ee6840260 (diff) | |
download | chef-480aeb8b5cda4292411d0bc66e1f05614000acfe.tar.gz |
Merge pull request #1377 from opscode/lcg/CHEF-5198-3-moar-func-tests
CHEF-5198: adding func tests for Chef::HTTP clients
Diffstat (limited to 'spec')
-rw-r--r-- | spec/functional/http/simple_spec.rb | 59 | ||||
-rw-r--r-- | spec/functional/resource/remote_file_spec.rb | 142 | ||||
-rw-r--r-- | spec/functional/rest_spec.rb | 57 | ||||
-rw-r--r-- | spec/support/shared/functional/http.rb | 219 |
4 files changed, 343 insertions, 134 deletions
diff --git a/spec/functional/http/simple_spec.rb b/spec/functional/http/simple_spec.rb new file mode 100644 index 0000000000..2df40b6272 --- /dev/null +++ b/spec/functional/http/simple_spec.rb @@ -0,0 +1,59 @@ +# +# Author:: Lamont Granquist (<lamont@getchef.com>) +# Copyright:: Copyright (c) 2014 Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed 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 'spec_helper' +require 'tiny_server' +require 'support/shared/functional/http' + +describe Chef::HTTP::Simple do + include ChefHTTPShared + + let(:http_client) { described_class.new(source) } + let(:http_client_disable_gzip) { described_class.new(source, { :disable_gzip => true } ) } + + before(:all) do + start_tiny_server + end + + after(:all) do + stop_tiny_server + end + + shared_examples_for "downloads requests correctly" do + it "successfully downloads a streaming request" do + tempfile = http_client.streaming_request(source, {}) + tempfile.close + Digest::MD5.hexdigest(binread(tempfile.path)).should == Digest::MD5.hexdigest(expected_content) + end + it "successfully does a non-streaming GET request" do + Digest::MD5.hexdigest(http_client.get(source)).should == Digest::MD5.hexdigest(expected_content) + end + end + + shared_examples_for "validates content length and throws an exception" do + it "successfully downloads a streaming request" do + expect { http_client.streaming_request(source) }.to raise_error(Chef::Exceptions::ContentLengthMismatch) + end + it "successfully does a non-streaming GET request" do + expect { http_client.get(source) }.to raise_error(Chef::Exceptions::ContentLengthMismatch) + end + end + + it_behaves_like "downloading all the things" +end + diff --git a/spec/functional/resource/remote_file_spec.rb b/spec/functional/resource/remote_file_spec.rb index 4468951a7a..6385efd563 100644 --- a/spec/functional/resource/remote_file_spec.rb +++ b/spec/functional/resource/remote_file_spec.rb @@ -18,8 +18,10 @@ require 'spec_helper' require 'tiny_server' +require 'support/shared/functional/http' describe Chef::Resource::RemoteFile do + include ChefHTTPShared let(:file_cache_path) { Dir.mktmpdir } @@ -52,107 +54,6 @@ describe Chef::Resource::RemoteFile do let(:default_mode) { ((0100666 - File.umask) & 07777).to_s(8) } - def start_tiny_server(server_opts={}) - nyan_uncompressed_filename = File.join(CHEF_SPEC_DATA, 'remote_file', 'nyan_cat.png') - nyan_compressed_filename = File.join(CHEF_SPEC_DATA, 'remote_file', 'nyan_cat.png.gz') - nyan_uncompressed_size = File::Stat.new(nyan_uncompressed_filename).size - nyan_compressed_size = File::Stat.new(nyan_compressed_filename).size - - @server = TinyServer::Manager.new(server_opts) - @server.start - @api = TinyServer::API.instance - @api.clear - - # - # trivial endpoints - # - - @api.get("/nyan_cat.png", 200) { - File.open(nyan_uncompressed_filename, "rb") do |f| - f.read - end - } - @api.get("/nyan_cat.png.gz", 200, nil, { 'Content-Type' => 'application/gzip', 'Content-Encoding' => 'gzip' } ) { - File.open(nyan_compressed_filename, "rb") do |f| - f.read - end - } - - # - # endpoints that set Content-Length correctly - # - - @api.get("/nyan_cat_content_length.png", 200, nil, - { - 'Content-Length' => nyan_uncompressed_size.to_s, - } - ) { - File.open(nyan_uncompressed_filename, "rb") do |f| - f.read - end - } - - # this is sent over the wire compressed by the server, but does not have a .gz extension - @api.get("/nyan_cat_content_length_compressed.png", 200, nil, - { - 'Content-Length' => nyan_compressed_size.to_s, - 'Content-Type' => 'application/gzip', - 'Content-Encoding' => 'gzip' - } - ) { - File.open(nyan_compressed_filename, "rb") do |f| - f.read - end - } - - # - # endpoints that simulate truncated downloads (bad content-length header) - # - - @api.get("/nyan_cat_truncated.png", 200, nil, - { - 'Content-Length' => (nyan_uncompressed_size + 1).to_s, - } - ) { - File.open(nyan_uncompressed_filename, "rb") do |f| - f.read - end - } - # this is sent over the wire compressed by the server, but does not have a .gz extension - @api.get("/nyan_cat_truncated_compressed.png", 200, nil, - { - 'Content-Length' => (nyan_compressed_size + 1).to_s, - 'Content-Type' => 'application/gzip', - 'Content-Encoding' => 'gzip' - } - ) { - File.open(nyan_compressed_filename, "rb") do |f| - f.read - end - } - - # - # in the presense of a transfer-encoding header, we must ignore the content-length (this bad content-length should work) - # - - @api.get("/nyan_cat_transfer_encoding.png", 200, nil, - { - 'Content-Length' => (nyan_uncompressed_size + 1).to_s, - 'Transfer-Encoding' => 'anything', - } - ) { - File.open(nyan_uncompressed_filename, "rb") do |f| - f.read - end - } - - end - - def stop_tiny_server - @server.stop - @server = @api = nil - end - context "when fetching files over HTTP" do before(:all) do start_tiny_server @@ -177,13 +78,7 @@ describe Chef::Resource::RemoteFile do context "when using normal encoding" do let(:source) { 'http://localhost:9000/nyan_cat.png' } - let(:expected_content) do - content = File.open(File.join(CHEF_SPEC_DATA, 'remote_file', 'nyan_cat.png'), "rb") do |f| - f.read - end - content.force_encoding(Encoding::BINARY) if content.respond_to?(:force_encoding) - content - end + let(:expected_content) { binread(nyan_uncompressed_filename) } it_behaves_like "a file resource" @@ -192,13 +87,7 @@ describe Chef::Resource::RemoteFile do context "when using gzip encoding" do let(:source) { 'http://localhost:9000/nyan_cat.png.gz' } - let(:expected_content) do - content = File.open(File.join(CHEF_SPEC_DATA, 'remote_file', 'nyan_cat.png.gz'), "rb") do |f| - f.read - end - content.force_encoding(Encoding::BINARY) if content.respond_to?(:force_encoding) - content - end + let(:expected_content) { binread(nyan_compressed_filename) } it_behaves_like "a file resource" @@ -229,28 +118,13 @@ describe Chef::Resource::RemoteFile do let(:source) { 'https://localhost:9000/nyan_cat.png' } - let(:expected_content) do - content = File.open(File.join(CHEF_SPEC_DATA, 'remote_file', 'nyan_cat.png'), "rb") do |f| - f.read - end - content.force_encoding(Encoding::BINARY) if content.respond_to?(:force_encoding) - content - end + let(:expected_content) { binread(nyan_uncompressed_filename) } it_behaves_like "a file resource" end context "when dealing with content length checking" do - - def binread(file) - content = File.open(file, "rb") do |f| - f.read - end - content.force_encoding(Encoding::BINARY) if "".respond_to?(:force_encoding) - content - end - before(:all) do start_tiny_server end @@ -260,7 +134,7 @@ describe Chef::Resource::RemoteFile do end context "when downloading compressed data" do - let(:expected_content) { binread( File.join(CHEF_SPEC_DATA, 'remote_file', 'nyan_cat.png') ) } + let(:expected_content) { binread(nyan_uncompressed_filename) } let(:source) { 'http://localhost:9000/nyan_cat_content_length_compressed.png' } before do @@ -282,7 +156,7 @@ describe Chef::Resource::RemoteFile do end context "when downloding uncompressed data" do - let(:expected_content) { binread( File.join(CHEF_SPEC_DATA, 'remote_file', 'nyan_cat.png') ) } + let(:expected_content) { binread(nyan_uncompressed_filename) } let(:source) { 'http://localhost:9000/nyan_cat_content_length.png' } before do @@ -330,7 +204,7 @@ describe Chef::Resource::RemoteFile do end context "when downloding data with transfer-encoding set" do - let(:expected_content) { binread( File.join(CHEF_SPEC_DATA, 'remote_file', 'nyan_cat.png') ) } + let(:expected_content) { binread(nyan_uncompressed_filename) } let(:source) { 'http://localhost:9000/nyan_cat_transfer_encoding.png' } before do diff --git a/spec/functional/rest_spec.rb b/spec/functional/rest_spec.rb new file mode 100644 index 0000000000..7b87705d77 --- /dev/null +++ b/spec/functional/rest_spec.rb @@ -0,0 +1,57 @@ +# +# Author:: Lamont Granquist (<lamont@getchef.com>) +# Copyright:: Copyright (c) 2014 Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed 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 'spec_helper' +require 'tiny_server' +require 'support/shared/functional/http' + +describe Chef::REST do + include ChefHTTPShared + + let(:http_client) { described_class.new(source) } + let(:http_client_disable_gzip) { described_class.new(source, Chef::Config[:node_name], Chef::Config[:client_key], { :disable_gzip => true } ) } + + shared_examples_for "downloads requests correctly" do + it "successfully downloads a streaming request" do + tempfile = http_client.streaming_request(source, {}) + tempfile.close + Digest::MD5.hexdigest(binread(tempfile.path)).should == Digest::MD5.hexdigest(expected_content) + end + end + + shared_examples_for "validates content length and throws an exception" do + it "fails validation on a streaming download" do + expect { http_client.streaming_request(source, {}) }.to raise_error(Chef::Exceptions::ContentLengthMismatch) + end + end + + before do + Chef::Config[:node_name] = "webmonkey.example.com" + Chef::Config[:client_key] = CHEF_SPEC_DATA + "/ssl/private_key.pem" + end + + before(:all) do + start_tiny_server + end + + after(:all) do + stop_tiny_server + end + + it_behaves_like "downloading all the things" +end diff --git a/spec/support/shared/functional/http.rb b/spec/support/shared/functional/http.rb new file mode 100644 index 0000000000..86fe467d92 --- /dev/null +++ b/spec/support/shared/functional/http.rb @@ -0,0 +1,219 @@ +# +# Author:: Lamont Granquist (<lamont@getchef.com>) +# Copyright:: Copyright (c) 2014 Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed 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. +# + +# +# shared code for Chef::REST and Chef::HTTP::Simple and other Chef::HTTP wrappers +# + +module ChefHTTPShared + def nyan_uncompressed_filename + File.join(CHEF_SPEC_DATA, 'remote_file', 'nyan_cat.png') + end + + def nyan_compressed_filename + File.join(CHEF_SPEC_DATA, 'remote_file', 'nyan_cat.png.gz') + end + + def binread(file) + content = File.open(file, "rb") do |f| + f.read + end + content.force_encoding(Encoding::BINARY) if "".respond_to?(:force_encoding) + content + end + + def start_tiny_server(server_opts={}) + nyan_uncompressed_size = File::Stat.new(nyan_uncompressed_filename).size + nyan_compressed_size = File::Stat.new(nyan_compressed_filename).size + + @server = TinyServer::Manager.new(server_opts) + @server.start + @api = TinyServer::API.instance + @api.clear + + # + # trivial endpoints + # + + # just a normal file + # (expected_content should be uncompressed) + @api.get("/nyan_cat.png", 200) { + File.open(nyan_uncompressed_filename, "rb") do |f| + f.read + end + } + + # this ends in .gz, we do not uncompress it and drop it on the filesystem as a .gz file (the internet often lies) + # (expected_content should be compressed) + @api.get("/nyan_cat.png.gz", 200, nil, { 'Content-Type' => 'application/gzip', 'Content-Encoding' => 'gzip' } ) { + File.open(nyan_compressed_filename, "rb") do |f| + f.read + end + } + + # this is an uncompressed file that was compressed by some mod_gzip-ish webserver thingy, so we will expand it + # (expected_content should be uncompressed) + @api.get("/nyan_cat_compressed.png", 200, nil, { 'Content-Type' => 'application/gzip', 'Content-Encoding' => 'gzip' } ) { + File.open(nyan_compressed_filename, "rb") do |f| + f.read + end + } + + # + # endpoints that set Content-Length correctly + # + + # (expected_content should be uncompressed) + @api.get("/nyan_cat_content_length.png", 200, nil, + { + 'Content-Length' => nyan_uncompressed_size.to_s, + } + ) { + File.open(nyan_uncompressed_filename, "rb") do |f| + f.read + end + } + + # (expected_content should be uncompressed) + @api.get("/nyan_cat_content_length_compressed.png", 200, nil, + { + 'Content-Length' => nyan_compressed_size.to_s, + 'Content-Type' => 'application/gzip', + 'Content-Encoding' => 'gzip' + } + ) { + File.open(nyan_compressed_filename, "rb") do |f| + f.read + end + } + + # + # endpoints that simulate truncated downloads (bad content-length header) + # + + # (expected_content should be uncompressed) + @api.get("/nyan_cat_truncated.png", 200, nil, + { + 'Content-Length' => (nyan_uncompressed_size + 1).to_s, + } + ) { + File.open(nyan_uncompressed_filename, "rb") do |f| + f.read + end + } + + # (expected_content should be uncompressed) + @api.get("/nyan_cat_truncated_compressed.png", 200, nil, + { + 'Content-Length' => (nyan_compressed_size + 1).to_s, + 'Content-Type' => 'application/gzip', + 'Content-Encoding' => 'gzip' + } + ) { + File.open(nyan_compressed_filename, "rb") do |f| + f.read + end + } + + # + # in the presense of a transfer-encoding header, we must ignore the content-length (this bad content-length should work) + # + + # (expected_content should be uncompressed) + @api.get("/nyan_cat_transfer_encoding.png", 200, nil, + { + 'Content-Length' => (nyan_uncompressed_size + 1).to_s, + 'Transfer-Encoding' => 'anything', + } + ) { + File.open(nyan_uncompressed_filename, "rb") do |f| + f.read + end + } + + end + + def stop_tiny_server + @server.stop + @server = @api = nil + end + +end + +shared_examples_for "downloading all the things" do + + describe "when downloading a simple uncompressed file" do + let(:source) { 'http://localhost:9000/nyan_cat.png' } + let(:expected_content) { binread(nyan_uncompressed_filename) } + + it_behaves_like "downloads requests correctly" + end + + describe "when downloading a compressed file that should be left compressed" do + let(:source) { 'http://localhost:9000/nyan_cat.png.gz' } + let(:expected_content) { binread(nyan_compressed_filename) } + + # its the callers responsibility to disable_gzip when downloading a .gz url + let(:http_client) { http_client_disable_gzip } + + it_behaves_like "downloads requests correctly" + end + + describe "when downloading a file that has been compressed by the webserver" do + let(:source) { 'http://localhost:9000/nyan_cat_compressed.png' } + let(:expected_content) { binread(nyan_uncompressed_filename) } + + it_behaves_like "downloads requests correctly" + end + + describe "when downloading an uncompressed file with a correct content_length" do + let(:source) { 'http://localhost:9000/nyan_cat_content_length.png' } + let(:expected_content) { binread(nyan_uncompressed_filename) } + + it_behaves_like "downloads requests correctly" + end + + describe "when downloading a file that has been compressed by the webserver with a correct content_length" do + let(:source) { 'http://localhost:9000/nyan_cat_content_length_compressed.png' } + let(:expected_content) { binread(nyan_uncompressed_filename) } + + it_behaves_like "downloads requests correctly" + end + + describe "when downloading an uncompressed file that is truncated" do + let(:source) { 'http://localhost:9000/nyan_cat_truncated.png' } + let(:expected_content) { binread(nyan_uncompressed_filename) } + + it_behaves_like "validates content length and throws an exception" + end + + describe "when downloading a file that has been compressed by the webserver that is truncated" do + let(:source) { 'http://localhost:9000/nyan_cat_truncated_compressed.png' } + let(:expected_content) { binread(nyan_uncompressed_filename) } + + it_behaves_like "validates content length and throws an exception" + end + + describe "when downloading a file that has transfer encoding set with a bad content length that should be ignored" do + let(:source) { 'http://localhost:9000/nyan_cat_transfer_encoding.png' } + let(:expected_content) { binread(nyan_uncompressed_filename) } + + it_behaves_like "downloads requests correctly" + end +end + |