Return-Path: X-Original-To: apmail-incubator-deltacloud-dev-archive@minotaur.apache.org Delivered-To: apmail-incubator-deltacloud-dev-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 6EC15939D for ; Wed, 2 Nov 2011 09:35:31 +0000 (UTC) Received: (qmail 27845 invoked by uid 500); 2 Nov 2011 09:35:31 -0000 Delivered-To: apmail-incubator-deltacloud-dev-archive@incubator.apache.org Received: (qmail 27826 invoked by uid 500); 2 Nov 2011 09:35:31 -0000 Mailing-List: contact deltacloud-dev-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: deltacloud-dev@incubator.apache.org Delivered-To: mailing list deltacloud-dev@incubator.apache.org Received: (qmail 27818 invoked by uid 99); 2 Nov 2011 09:35:31 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 02 Nov 2011 09:35:31 +0000 X-ASF-Spam-Status: No, hits=-5.0 required=5.0 tests=RCVD_IN_DNSWL_HI,SPF_HELO_PASS,SPF_PASS X-Spam-Check-By: apache.org Received-SPF: pass (athena.apache.org: domain of marios@redhat.com designates 209.132.183.28 as permitted sender) Received: from [209.132.183.28] (HELO mx1.redhat.com) (209.132.183.28) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 02 Nov 2011 09:35:26 +0000 Received: from int-mx02.intmail.prod.int.phx2.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id pA29Z5YK010119 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Wed, 2 Nov 2011 05:35:05 -0400 Received: from name.redhat.com (ovpn-112-16.ams2.redhat.com [10.36.112.16]) by int-mx02.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id pA29Yuij012418 for ; Wed, 2 Nov 2011 05:35:03 -0400 From: marios@redhat.com To: deltacloud-dev@incubator.apache.org Subject: [PATCH 2/2] Unit tests for google storage driver Date: Wed, 2 Nov 2011 11:34:53 +0200 Message-Id: <1320226493-13155-3-git-send-email-marios@redhat.com> In-Reply-To: <1320226493-13155-1-git-send-email-marios@redhat.com> References: <1320226493-13155-1-git-send-email-marios@redhat.com> X-Scanned-By: MIMEDefang 2.67 on 10.5.11.12 From: marios Signed-off-by: marios --- server/Rakefile | 4 +- server/tests/common.rb | 37 +++++++ .../common_fixtures/deltacloud_blob_test.png | Bin 0 -> 24436 bytes server/tests/drivers/google/api_test.rb | 35 ++++++ server/tests/drivers/google/buckets_test.rb | 115 ++++++++++++++++++++ ...te-997d394df9ccfde81ea3073a68d674f173faa56f.yml | 22 ++++ ...te-fdd72e5f5a3576ca37a34483de5f623c1c08a7f9.yml | 22 ++++ ...et-4c5647f476f144847319d51b3c5cebe0d82b5995.yml | 36 ++++++ ...et-ff2e867e891fe52fd64cd797d594ac100e15b61a.yml | 44 ++++++++ ...ad-997d394df9ccfde81ea3073a68d674f173faa56f.yml | 44 ++++++++ ...st-2babb3d81fcf440c1f6999fec2c9e471cc544758.yml | 32 ++++++ ...st-9543e39820e2be0b739d4bbdf2a4dd3cb7e4e411.yml | 42 +++++++ ...st-997d394df9ccfde81ea3073a68d674f173faa56f.yml | 42 +++++++ server/tests/drivers/google/setup.rb | 13 +++ 14 files changed, 486 insertions(+), 2 deletions(-) create mode 100644 server/tests/drivers/common_fixtures/deltacloud_blob_test.png create mode 100644 server/tests/drivers/google/api_test.rb create mode 100644 server/tests/drivers/google/buckets_test.rb create mode 100644 server/tests/drivers/google/fixtures/delete-997d394df9ccfde81ea3073a68d674f173faa56f.yml create mode 100644 server/tests/drivers/google/fixtures/delete-fdd72e5f5a3576ca37a34483de5f623c1c08a7f9.yml create mode 100644 server/tests/drivers/google/fixtures/get-4c5647f476f144847319d51b3c5cebe0d82b5995.yml create mode 100644 server/tests/drivers/google/fixtures/get-ff2e867e891fe52fd64cd797d594ac100e15b61a.yml create mode 100644 server/tests/drivers/google/fixtures/head-997d394df9ccfde81ea3073a68d674f173faa56f.yml create mode 100644 server/tests/drivers/google/fixtures/post-2babb3d81fcf440c1f6999fec2c9e471cc544758.yml create mode 100644 server/tests/drivers/google/fixtures/post-9543e39820e2be0b739d4bbdf2a4dd3cb7e4e411.yml create mode 100644 server/tests/drivers/google/fixtures/post-997d394df9ccfde81ea3073a68d674f173faa56f.yml create mode 100644 server/tests/drivers/google/setup.rb diff --git a/server/Rakefile b/server/Rakefile index f07a6ce..0ec0f34 100644 --- a/server/Rakefile +++ b/server/Rakefile @@ -53,7 +53,7 @@ rescue LoadError end namespace :test do - %w(mock rackspace rhevm openstack).each do |driver| + %w(mock rackspace rhevm openstack google).each do |driver| desc "Run #{driver} unit tests" Rake::TestTask.new(driver) { |t| t.test_files = ['tests/common.rb', "tests/drivers/#{driver}/setup.rb"] + FileList.new("tests/drivers/#{driver}/*_test.rb") + FileList.new('tests/rabbit_test.rb') @@ -75,7 +75,7 @@ end desc "Call our Test::Unit suite" task :test do - %w(mock rackspace rhevm).each do |driver| + %w(mock rackspace rhevm openstack google).each do |driver| Rake::Task["test:#{driver}"].reenable Rake::Task["test:#{driver}"].invoke end diff --git a/server/tests/common.rb b/server/tests/common.rb index fc39819..7de5640 100644 --- a/server/tests/common.rb +++ b/server/tests/common.rb @@ -229,6 +229,43 @@ module DeltacloudTestCommon $created_instances << id end + #common variables used by the bucket/blob unit tests across clouds + @@created_bucket_name="testbucki2rpux3wdelme" + @@created_blob_name="testblobk1ds91kVdelme" + @@created_blob_local_file="#{File.dirname(__FILE__)}/drivers/common_fixtures/deltacloud_blob_test.png" + + def check_bucket_basics(bucket, cloud) + (bucket/'bucket/name').first.text.should == "#{@@created_bucket_name}#{cloud}" + (bucket/'bucket').attribute("id").text.should == "#{@@created_bucket_name}#{cloud}" + (bucket/'bucket').length.should > 0 + (bucket/'bucket/name').first.text.should_not == nil + (bucket/'bucket').attribute("href").text.should_not == nil + end + + def check_blob_basics(blob, cloud) + (blob/'blob').length.should == 1 + (blob/'blob').attribute("id").text.should_not == nil + (blob/'blob').attribute("href").text.should_not==nil + (blob/'blob/bucket').text.should_not == nil + (blob/'blob/content_length').text.should_not == nil + (blob/'blob/content_type').text.should_not == nil + (blob/'blob').attribute("id").text.should == "#{@@created_blob_name}#{cloud}" + (blob/'blob/bucket').text.should == "#{@@created_bucket_name}#{cloud}" + (blob/'blob/content_length').text.to_i.should == File.size(@@created_blob_local_file) + end + + def check_blob_metadata(blob, metadata_hash) + meta_from_blob = {} + #extract metadata from nokogiri blob xml + (0.. (((blob/'blob/user_metadata').first).elements.size - 1) ).each do |i| + meta_from_blob[(((blob/'blob/user_metadata').first).elements[i].attribute("key").value)] = + (((blob/'blob/user_metadata').first).elements[i].children[1].text) + end + #remove any 'x-goog-meta-' prefixes (problem for google blobs and vcr...) + meta_from_blob.gsub_keys(/x-.*-meta-/i, "") + meta_from_blob.eql?(metadata_hash).should == true + end + end include DeltacloudTestCommon diff --git a/server/tests/drivers/common_fixtures/deltacloud_blob_test.png b/server/tests/drivers/common_fixtures/deltacloud_blob_test.png new file mode 100644 index 0000000000000000000000000000000000000000..bf1b766689138e64236b7f8341001ba05abbef6e GIT binary patch literal 24436 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 literal 0 HcmV?d00001 diff --git a/server/tests/drivers/google/api_test.rb b/server/tests/drivers/google/api_test.rb new file mode 100644 index 0000000..bd86d95 --- /dev/null +++ b/server/tests/drivers/google/api_test.rb @@ -0,0 +1,35 @@ +$:.unshift File.join(File.dirname(__FILE__), '..', '..', '..') +require 'tests/common' + +module GoogleTest + + class ApiTest < Test::Unit::TestCase + include Rack::Test::Methods + + def app + Sinatra::Application + end + + def test_01_it_returns_entry_points + get_auth_url '/api;driver=google/?force_auth=1' + (last_xml_response/'/api').first[:driver].should == 'google' + (last_xml_response/'/api/link').length.should > 0 + end + + def test_02_it_has_google_features + get_url '/api;driver=google' + features = (last_xml_response/'/api/link[@rel="buckets"]/feature').collect { |f| f[:name] } + features.include?('bucket_location').should == true + features.length.should == 1 + end + + def test_03_it_has_google_collections + get_url '/api;driver=google' + collections = (last_xml_response/'/api/link').collect { |f| f[:rel] } + collections.include?('buckets').should == true + collections.include?('drivers').should == true + collections.length.should == 2 + end + + end +end diff --git a/server/tests/drivers/google/buckets_test.rb b/server/tests/drivers/google/buckets_test.rb new file mode 100644 index 0000000..07f33cc --- /dev/null +++ b/server/tests/drivers/google/buckets_test.rb @@ -0,0 +1,115 @@ +$:.unshift File.join(File.dirname(__FILE__), '..', '..', '..') +require 'tests/common' +#require 'webmock/test_unit' +module GoogleTest + + class BucketsTest < Test::Unit::TestCase + include Rack::Test::Methods + + def app + Sinatra::Application + end + + @@bucket_name_google="#{@@created_bucket_name}googel" + @@blob_name_google="#{@@created_blob_name}googel" +#intentional typos here - bucket names cannot contain 'google' +#see http://code.google.com/apis/storage/docs/reference/v1/developer-guidev1.html + + def test_01_it_can_create_new_bucket + params = { + :name => @@bucket_name_google, + :'api[driver]' => 'google' + } + post_url '/api/buckets', params + last_response.status.should == 201 # Created + bucket = last_xml_response + check_bucket_basics(bucket, "googel") + (bucket/'bucket/size').first.text.should == "0" + end + + def test_02_it_can_post_new_blob + temp_file=File.open(@@created_blob_local_file) + params = { + 'blob_id' => @@blob_name_google, + :meta_params=>"2", + :meta_name1=>"Author", + :meta_value1=>"deltacloud", + :meta_name2=>"foo", + :meta_value2=>"bar", + :'api[driver]' => 'google' + } + uri="/api/buckets/#{@@bucket_name_google}" + vcr_cassette = "post-"+Digest::SHA1.hexdigest("#{uri}-#{params.sort_by {|k,v| k.to_s}}") + params.merge!({'blob_data' => Rack::Test::UploadedFile.new(temp_file.path, "text/html")}) + post_url uri, params, {'vcr_cassette'=>vcr_cassette} + last_response.status.should == 200 + blob= last_xml_response + check_blob_basics(blob, "googel") + end + + def test_03_it_can_retrieve_named_bucket_details + params = { + :'api[driver]' => 'google' + } + get_url "/api/buckets/#{@@bucket_name_google}", params + last_response.status.should == 200 + bucket = last_xml_response + check_bucket_basics(bucket, "googel") + (bucket/'bucket/size').first.text.should == "1" #assuming blob created succesfully right? + end + + def test_04_it_can_retrieve_named_blob_details + params = { + :'api[driver]' => 'google' + } + get_url "/api/buckets/#{@@bucket_name_google}/#{@@blob_name_google}", params + last_response.status.should == 200 + blob = last_xml_response + check_blob_basics(blob, "googel") + check_blob_metadata(blob, {"author"=>"deltacloud", "foo"=>"bar"}) + end + + def test_05_it_can_retrieve_blob_metadata + params = { + :'api[driver]' => 'google' + } + head_url "/api/buckets/#{@@bucket_name_google}/#{@@blob_name_google}", params + last_response.status.should == 204 + blob_meta_hash = last_response.headers.inject({}){|result, (k,v)| result[k]=v if k=~/^X-Deltacloud-Blobmeta-/i ; result} + blob_meta_hash.gsub_keys(/x-.*meta-/i, "") + ({"author"=>"deltacloud", "foo"=>"bar"}.eql?(blob_meta_hash)).should == true + end + + def test_06_it_can_update_blob_metadata + params = { + :'api[driver]' => 'google' + } + new_meta = {"X-Deltacloud-Blobmeta-author" => "ApacheDeltacloud", "X-Deltacloud-Blobmeta-oof" => "rab"} + new_meta.each_pair do |k,v| + header k, v + end + post_url "/api/buckets/#{@@bucket_name_google}/#{@@blob_name_google}", params + last_response.status.should == 204 + new_meta.each_pair do |k,v| + (last_response.headers[k]==v).should == true + end + end + + def test_07_it_can_delete_blob + params = { + :'api[driver]' => 'google' + } + delete_url "/api/buckets/#{@@bucket_name_google}/#{@@blob_name_google}", params + last_response.status.should == 204 + end + + def test_08_it_can_delete_bucket + params = { + :'api[driver]' => 'google' + } + delete_url "/api/buckets/#{@@bucket_name_google}", params + last_response.status.should == 204 + end + + end +end diff --git a/server/tests/drivers/google/fixtures/delete-997d394df9ccfde81ea3073a68d674f173faa56f.yml b/server/tests/drivers/google/fixtures/delete-997d394df9ccfde81ea3073a68d674f173faa56f.yml new file mode 100644 index 0000000..2cb9082 --- /dev/null +++ b/server/tests/drivers/google/fixtures/delete-997d394df9ccfde81ea3073a68d674f173faa56f.yml @@ -0,0 +1,22 @@ +--- +- !ruby/struct:VCR::HTTPInteraction + request: !ruby/struct:VCR::Request + method: DELETE + uri: https://testbucki2rpux3wdelmegoogel.commondatastorage.googleapis.com:443/testblobk1ds91kVdelmegoogel + body: + headers: + date: + - Mon, 31 Oct 2011 16:08:19 +0000 + authorization: + - GOOG1 GOOGTEWOMQJ5SPYXOBWW:hO/e9+l2SNp7OxeNrOYs8wUpvMQ= + response: !ruby/struct:VCR::Response + status: !ruby/struct:VCR::ResponseStatus + code: 204 + message: + headers: + server: + - GSE + date: + - Mon, 31 Oct 2011 16:08:21 GMT + body: + http_version: diff --git a/server/tests/drivers/google/fixtures/delete-fdd72e5f5a3576ca37a34483de5f623c1c08a7f9.yml b/server/tests/drivers/google/fixtures/delete-fdd72e5f5a3576ca37a34483de5f623c1c08a7f9.yml new file mode 100644 index 0000000..b765d26 --- /dev/null +++ b/server/tests/drivers/google/fixtures/delete-fdd72e5f5a3576ca37a34483de5f623c1c08a7f9.yml @@ -0,0 +1,22 @@ +--- +- !ruby/struct:VCR::HTTPInteraction + request: !ruby/struct:VCR::Request + method: DELETE + uri: https://testbucki2rpux3wdelmegoogel.commondatastorage.googleapis.com:443/ + body: + headers: + date: + - Mon, 31 Oct 2011 16:08:21 +0000 + authorization: + - GOOG1 GOOGTEWOMQJ5SPYXOBWW:YM/XhYfrUQpBljguGmK+5/Yrj+8= + response: !ruby/struct:VCR::Response + status: !ruby/struct:VCR::ResponseStatus + code: 204 + message: + headers: + server: + - GSE + date: + - Mon, 31 Oct 2011 16:08:24 GMT + body: + http_version: diff --git a/server/tests/drivers/google/fixtures/get-4c5647f476f144847319d51b3c5cebe0d82b5995.yml b/server/tests/drivers/google/fixtures/get-4c5647f476f144847319d51b3c5cebe0d82b5995.yml new file mode 100644 index 0000000..777ff26 --- /dev/null +++ b/server/tests/drivers/google/fixtures/get-4c5647f476f144847319d51b3c5cebe0d82b5995.yml @@ -0,0 +1,36 @@ +--- +- !ruby/struct:VCR::HTTPInteraction + request: !ruby/struct:VCR::Request + method: GET + uri: https://testbucki2rpux3wdelmegoogel.commondatastorage.googleapis.com:443/ + body: + headers: + date: + - Mon, 31 Oct 2011 16:08:14 +0000 + authorization: + - GOOG1 GOOGTEWOMQJ5SPYXOBWW:O+oHdcbVDoIPzRPKSv7zDGzHwEs= + response: !ruby/struct:VCR::Response + status: !ruby/struct:VCR::ResponseStatus + code: 200 + message: + headers: + x-content-type-options: + - nosniff + x-frame-options: + - SAMEORIGIN + expires: + - Mon, 31 Oct 2011 16:08:15 GMT + content-type: + - application/xml; charset=UTF-8 + server: + - GSE + date: + - Mon, 31 Oct 2011 16:08:15 GMT + x-xss-protection: + - 1; mode=block + content-length: + - "530" + cache-control: + - private, max-age=0 + body: testbucki2rpux3wdelmegoogelfalsetestblobk1ds91kVdelmegoogel2011-10-31T16:08:13.953Z"6051b502c4281855ff32dc3e924fe3b0"24436STANDARD00b4903a9772c5a80c691066edef0ac07b267f576a2d11bbfc5bf1d2c7a2b8fd + http_version: diff --git a/server/tests/drivers/google/fixtures/get-ff2e867e891fe52fd64cd797d594ac100e15b61a.yml b/server/tests/drivers/google/fixtures/get-ff2e867e891fe52fd64cd797d594ac100e15b61a.yml new file mode 100644 index 0000000..b4a0075 --- /dev/null +++ b/server/tests/drivers/google/fixtures/get-ff2e867e891fe52fd64cd797d594ac100e15b61a.yml @@ -0,0 +1,44 @@ +--- +- !ruby/struct:VCR::HTTPInteraction + request: !ruby/struct:VCR::Request + method: HEAD + uri: https://testbucki2rpux3wdelmegoogel.commondatastorage.googleapis.com:443/testblobk1ds91kVdelmegoogel + body: + headers: + date: + - Mon, 31 Oct 2011 16:08:15 +0000 + authorization: + - GOOG1 GOOGTEWOMQJ5SPYXOBWW:4uOxP9utyDWMztB6ttbzxtvEl+c= + response: !ruby/struct:VCR::Response + status: !ruby/struct:VCR::ResponseStatus + code: 200 + message: + headers: + x-content-type-options: + - nosniff + x-goog-meta-foo: + - bar + etag: + - "\"6051b502c4281855ff32dc3e924fe3b0\"" + expires: + - Fri, 01 Jan 1990 00:00:00 GMT + last-modified: + - Mon, 31 Oct 2011 16:08:13 GMT + content-type: + - text/html + server: + - GSE + date: + - Mon, 31 Oct 2011 16:08:16 GMT + x-goog-meta-author: + - deltacloud + x-xss-protection: + - 1; mode=block + content-length: + - "24436" + cache-control: + - no-cache, no-store, max-age=0, must-revalidate + pragma: + - no-cache + body: + http_version: diff --git a/server/tests/drivers/google/fixtures/head-997d394df9ccfde81ea3073a68d674f173faa56f.yml b/server/tests/drivers/google/fixtures/head-997d394df9ccfde81ea3073a68d674f173faa56f.yml new file mode 100644 index 0000000..8a3cd2b --- /dev/null +++ b/server/tests/drivers/google/fixtures/head-997d394df9ccfde81ea3073a68d674f173faa56f.yml @@ -0,0 +1,44 @@ +--- +- !ruby/struct:VCR::HTTPInteraction + request: !ruby/struct:VCR::Request + method: HEAD + uri: https://testbucki2rpux3wdelmegoogel.commondatastorage.googleapis.com:443/testblobk1ds91kVdelmegoogel + body: + headers: + date: + - Mon, 31 Oct 2011 16:08:16 +0000 + authorization: + - GOOG1 GOOGTEWOMQJ5SPYXOBWW:hX43qnnUbX6Rv5kx1W6I0JYLQXc= + response: !ruby/struct:VCR::Response + status: !ruby/struct:VCR::ResponseStatus + code: 200 + message: + headers: + x-content-type-options: + - nosniff + x-goog-meta-foo: + - bar + etag: + - "\"6051b502c4281855ff32dc3e924fe3b0\"" + expires: + - Fri, 01 Jan 1990 00:00:00 GMT + last-modified: + - Mon, 31 Oct 2011 16:08:13 GMT + content-type: + - text/html + server: + - GSE + date: + - Mon, 31 Oct 2011 16:08:17 GMT + x-goog-meta-author: + - deltacloud + x-xss-protection: + - 1; mode=block + content-length: + - "24436" + cache-control: + - no-cache, no-store, max-age=0, must-revalidate + pragma: + - no-cache + body: + http_version: diff --git a/server/tests/drivers/google/fixtures/post-2babb3d81fcf440c1f6999fec2c9e471cc544758.yml b/server/tests/drivers/google/fixtures/post-2babb3d81fcf440c1f6999fec2c9e471cc544758.yml new file mode 100644 index 0000000..d1984bc --- /dev/null +++ b/server/tests/drivers/google/fixtures/post-2babb3d81fcf440c1f6999fec2c9e471cc544758.yml @@ -0,0 +1,32 @@ +--- +- !ruby/struct:VCR::HTTPInteraction + request: !ruby/struct:VCR::Request + method: PUT + uri: https://testbucki2rpux3wdelmegoogel.commondatastorage.googleapis.com:443/ + body: + headers: + date: + - Mon, 31 Oct 2011 16:08:10 +0000 + authorization: + - GOOG1 GOOGTEWOMQJ5SPYXOBWW:bRTiSvTVRqKfJ7gP56IH+mAuQbQ= + response: !ruby/struct:VCR::Response + status: !ruby/struct:VCR::ResponseStatus + code: 200 + message: + headers: + expires: + - Fri, 01 Jan 1990 00:00:00 GMT + content-type: + - text/html + server: + - HTTP Upload Server Built on Oct 25 2011 12:43:05 (1319571785) + date: + - Mon, 31 Oct 2011 16:08:12 GMT + content-length: + - "0" + cache-control: + - no-cache, no-store, must-revalidate + pragma: + - no-cache + body: + http_version: diff --git a/server/tests/drivers/google/fixtures/post-9543e39820e2be0b739d4bbdf2a4dd3cb7e4e411.yml b/server/tests/drivers/google/fixtures/post-9543e39820e2be0b739d4bbdf2a4dd3cb7e4e411.yml new file mode 100644 index 0000000..7bda7aa --- /dev/null +++ b/server/tests/drivers/google/fixtures/post-9543e39820e2be0b739d4bbdf2a4dd3cb7e4e411.yml @@ -0,0 +1,42 @@ +--- +- !ruby/struct:VCR::HTTPInteraction + request: !ruby/struct:VCR::Request + method: PUT + uri: https://testbucki2rpux3wdelmegoogel.commondatastorage.googleapis.com:443/testblobk1ds91kVdelmegoogel + body: + headers: + x-goog-meta-foo: + - bar + content-type: + - text/html + x-goog-meta-author: + - deltacloud + date: + - Mon, 31 Oct 2011 16:08:12 +0000 + authorization: + - GOOG1 GOOGTEWOMQJ5SPYXOBWW:JNALirDG6a6xuk01lcWVVFa1eAA= + content-length: + - 24436 + response: !ruby/struct:VCR::Response + status: !ruby/struct:VCR::ResponseStatus + code: 200 + message: + headers: + etag: + - "\"6051b502c4281855ff32dc3e924fe3b0\"" + expires: + - Fri, 01 Jan 1990 00:00:00 GMT + content-type: + - text/html + server: + - HTTP Upload Server Built on Oct 25 2011 12:43:05 (1319571785) + date: + - Mon, 31 Oct 2011 16:08:14 GMT + content-length: + - "0" + cache-control: + - no-cache, no-store, must-revalidate + pragma: + - no-cache + body: + http_version: diff --git a/server/tests/drivers/google/fixtures/post-997d394df9ccfde81ea3073a68d674f173faa56f.yml b/server/tests/drivers/google/fixtures/post-997d394df9ccfde81ea3073a68d674f173faa56f.yml new file mode 100644 index 0000000..e3d7149 --- /dev/null +++ b/server/tests/drivers/google/fixtures/post-997d394df9ccfde81ea3073a68d674f173faa56f.yml @@ -0,0 +1,42 @@ +--- +- !ruby/struct:VCR::HTTPInteraction + request: !ruby/struct:VCR::Request + method: PUT + uri: https://testbucki2rpux3wdelmegoogel.commondatastorage.googleapis.com:443/testblobk1ds91kVdelmegoogel + body: + headers: + x-goog-meta-oof: + - rab + x-goog-copy-source: + - /testbucki2rpux3wdelmegoogel/testblobk1ds91kVdelmegoogel + x-goog-meta-author: + - ApacheDeltacloud + date: + - Mon, 31 Oct 2011 16:08:17 +0000 + authorization: + - GOOG1 GOOGTEWOMQJ5SPYXOBWW:vYNMJg7yvwLXIBtugEQh2phd+18= + x-goog-metadata-directive: + - REPLACE + response: !ruby/struct:VCR::Response + status: !ruby/struct:VCR::ResponseStatus + code: 200 + message: + headers: + etag: + - "\"6051b502c4281855ff32dc3e924fe3b0\"" + expires: + - Fri, 01 Jan 1990 00:00:00 GMT + content-type: + - text/html + server: + - HTTP Upload Server Built on Oct 25 2011 12:43:05 (1319571785) + date: + - Mon, 31 Oct 2011 16:08:19 GMT + content-length: + - "175" + cache-control: + - no-cache, no-store, must-revalidate + pragma: + - no-cache + body: 2011-10-31T16:08:13.953Z"6051b502c4281855ff32dc3e924fe3b0" + http_version: diff --git a/server/tests/drivers/google/setup.rb b/server/tests/drivers/google/setup.rb new file mode 100644 index 0000000..ac6bacd --- /dev/null +++ b/server/tests/drivers/google/setup.rb @@ -0,0 +1,13 @@ +ENV['API_DRIVER'] = "google" +ENV['API_USER'] = 'GOOGTEWOMQJ5SPYXOBWW' +ENV['API_PASSWORD'] = 'fMUsAllQJH1rnwKEsTb8gVl6Bt0GB5yqpPgoXcGP' + +require 'vcr' + +DeltacloudTestCommon::record! + +VCR.config do |c| + c.cassette_library_dir = "#{File.dirname(__FILE__)}/fixtures/" + c.stub_with :excon + c.default_cassette_options = { :record => :new_episodes} +end -- 1.7.6.4