From 3e0383b318c9db5e1e9c2cc0de462122c0d8122b Mon Sep 17 00:00:00 2001 From: Klaus-Dieter Gundermann Date: Tue, 29 Aug 2017 22:21:23 +0200 Subject: [PATCH 01/28] Move internal specs to internal directory --- spec/{ => internal}/association_spec.rb | 0 spec/{ => internal}/class_builder_spec.rb | 0 spec/internal/keys_spec.rb | 93 +++++++++++++++++++ spec/{ => internal}/property_metadata_spec.rb | 0 spec/{ => internal}/query_builder_spec.rb | 0 5 files changed, 93 insertions(+) rename spec/{ => internal}/association_spec.rb (100%) rename spec/{ => internal}/class_builder_spec.rb (100%) create mode 100644 spec/internal/keys_spec.rb rename spec/{ => internal}/property_metadata_spec.rb (100%) rename spec/{ => internal}/query_builder_spec.rb (100%) diff --git a/spec/association_spec.rb b/spec/internal/association_spec.rb similarity index 100% rename from spec/association_spec.rb rename to spec/internal/association_spec.rb diff --git a/spec/class_builder_spec.rb b/spec/internal/class_builder_spec.rb similarity index 100% rename from spec/class_builder_spec.rb rename to spec/internal/class_builder_spec.rb diff --git a/spec/internal/keys_spec.rb b/spec/internal/keys_spec.rb new file mode 100644 index 0000000..643ef21 --- /dev/null +++ b/spec/internal/keys_spec.rb @@ -0,0 +1,93 @@ +require 'spec_helper' +require 'base64' + +module OData + describe "Keys" do + describe "Collection with an int64 key Named 'id'" do + + before(:all) do + stub_request(:get, "http://test.com/test.svc/$metadata"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/int64_ids/edmx_car_service.xml", __FILE__)), :headers => {}) + @service = OData::Service.new "http://test.com/test.svc/" + end + + context "has the correct metadata" do + before(:all) do + @id_meta = @service.class_metadata['Car']['id'] + end + + subject { @id_meta } + + its(:name) { should eq('id') } + its(:type) { should eq('Edm.Int64') } + its(:nullable) { should eq false } + its(:fc_target_path) { should be_nil } + its(:fc_keep_in_content) { should be_nil } + end + + context "can parse Id correctly" do + before(:each) do + stub_request(:get, "http://test.com/test.svc/Cars(213L)"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/int64_ids/result_cars.xml", __FILE__)), :headers => {}) + + @service.Cars(213) + results = @service.execute + @car = results.first + end + + subject { @car } + + its(:id) { should eq(213) } + its(:color) { should eq('peach') } + end + end + + describe "Collection with an int64 key named 'KeyId'" do + + before(:all) do + stub_request(:get, "http://test.com/test.svc/$metadata"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/int64_ids/edmx_boat_service.xml", __FILE__)), :headers => {}) + @service = OData::Service.new "http://test.com/test.svc/" + end + + context "has the correct metadata" do + before(:all) do + @id_meta = @service.class_metadata['Boat']['KeyId'] + end + + subject { @id_meta } + + its(:name) { should eq('KeyId') } + its(:type) { should eq('Edm.Int64') } + its(:nullable) { should eq(false) } + its(:fc_target_path) { should be_nil } + its(:fc_keep_in_content) { should be_nil } + end + + context "can parse Id correctly" do + before(:each) do + stub_request(:get, "http://test.com/test.svc/Boats(213L)"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/int64_ids/result_boats.xml", __FILE__)), :headers => {}) + + @service.Boats(213) + results = @service.execute + @boat = results.first + end + + subject { @boat } + + its(:id) { should eq(213) } + its(:color) { should eq('blue') } + end + end + + describe "Collection with a string key named 'xxx" do + end + + end + +end diff --git a/spec/property_metadata_spec.rb b/spec/internal/property_metadata_spec.rb similarity index 100% rename from spec/property_metadata_spec.rb rename to spec/internal/property_metadata_spec.rb diff --git a/spec/query_builder_spec.rb b/spec/internal/query_builder_spec.rb similarity index 100% rename from spec/query_builder_spec.rb rename to spec/internal/query_builder_spec.rb From 26b9d654c04b046004bc0fd94392643f7f5fd8b5 Mon Sep 17 00:00:00 2001 From: Klaus-Dieter Gundermann Date: Tue, 29 Aug 2017 22:25:51 +0200 Subject: [PATCH 02/28] Update class_builder_spec --- spec/internal/class_builder_spec.rb | 64 ++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 10 deletions(-) diff --git a/spec/internal/class_builder_spec.rb b/spec/internal/class_builder_spec.rb index ac66d11..a9bf7f6 100644 --- a/spec/internal/class_builder_spec.rb +++ b/spec/internal/class_builder_spec.rb @@ -2,21 +2,65 @@ module OData describe ClassBuilder do - describe "#initialize" do - before(:each) do - @methods = [] - @nav_props = [] - @svc = nil - @namespace = nil - end - it "handles lowercase entities" do + + before(:each) do + @methods = [] + @nav_props = [] + @svc = nil + @namespace = nil + end + + after(:each) do + Object.send(:remove_const, 'Product') if Object.const_defined? 'Product' + Object.send(:remove_const, 'Namespace') if Object.const_defined? 'Namespace' + end + + context "Building the class" do + subject { ClassBuilder.new('Product', @methods, @nav_props, @svc, @namespace).build } + + it "should take in an instance of the service" do + subject.should eq Product + end + + it "creates the :first class method" do + expect(subject).to respond_to(:first) + end + + it "creates :__metadata method" do + expect(subject.new).to respond_to(:__metadata) + end + + it "creates :as_json method" do + expect(subject.new).to respond_to(:as_json) + end + end + + context "with additional params" do + + it "handles lowercase entities" do klass = ClassBuilder.new 'product', @methods, @nav_props, @svc, @namespace result = klass.build result.should eq Product end - it "should take in an instance of the service" do - klass = ClassBuilder.new 'product', @methods, @nav_props, @svc, @namespace + + it "creates additional methods" do + klass = ClassBuilder.new 'Product', [:method1], @nav_props, @svc, @namespace + result = klass.build + expect(result.new).to respond_to(:method1) end + + it "creates nav_props" do + klass = ClassBuilder.new 'Product', @methods, [:navigate], @svc, @namespace + result = klass.build + expect(result.new).to respond_to(:navigate) + end + + it "creates the class within a namespace" do + klass = ClassBuilder.new 'Namespace::Product', @methods, @nav_props, @svc, "Namespace" + result = klass.build + expect(result).to eq Namespace::Product + end + end end end \ No newline at end of file From 40052271a606f78cc53aacb8beddd788affaef18 Mon Sep 17 00:00:00 2001 From: Klaus-Dieter Gundermann Date: Tue, 29 Aug 2017 22:27:45 +0200 Subject: [PATCH 03/28] Move specs for Services to services directory --- spec/{ => services}/dynamics_nav_spec.rb | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename spec/{ => services}/dynamics_nav_spec.rb (100%) diff --git a/spec/dynamics_nav_spec.rb b/spec/services/dynamics_nav_spec.rb similarity index 100% rename from spec/dynamics_nav_spec.rb rename to spec/services/dynamics_nav_spec.rb From 8ba3dde5d436cd2d68f3e67315ece89dcb318d87 Mon Sep 17 00:00:00 2001 From: Klaus-Dieter Gundermann Date: Thu, 7 Sep 2017 21:03:54 +0200 Subject: [PATCH 04/28] Extracted to keys_spec --- spec/internal/keys_spec.rb | 8 ++-- spec/revised_service_spec.rb | 83 ------------------------------------ 2 files changed, 4 insertions(+), 87 deletions(-) diff --git a/spec/internal/keys_spec.rb b/spec/internal/keys_spec.rb index 643ef21..86ddb67 100644 --- a/spec/internal/keys_spec.rb +++ b/spec/internal/keys_spec.rb @@ -8,7 +8,7 @@ module OData before(:all) do stub_request(:get, "http://test.com/test.svc/$metadata"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/int64_ids/edmx_car_service.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => File.new( FIXTURES + "/int64_ids/edmx_car_service.xml"), :headers => {}) @service = OData::Service.new "http://test.com/test.svc/" end @@ -30,7 +30,7 @@ module OData before(:each) do stub_request(:get, "http://test.com/test.svc/Cars(213L)"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/int64_ids/result_cars.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => File.new( FIXTURES + "/int64_ids/result_cars.xml"), :headers => {}) @service.Cars(213) results = @service.execute @@ -49,7 +49,7 @@ module OData before(:all) do stub_request(:get, "http://test.com/test.svc/$metadata"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/int64_ids/edmx_boat_service.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => File.new( FIXTURES + "/int64_ids/edmx_boat_service.xml"), :headers => {}) @service = OData::Service.new "http://test.com/test.svc/" end @@ -71,7 +71,7 @@ module OData before(:each) do stub_request(:get, "http://test.com/test.svc/Boats(213L)"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/int64_ids/result_boats.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => File.new( FIXTURES + "/int64_ids/result_boats.xml"), :headers => {}) @service.Boats(213) results = @service.execute diff --git a/spec/revised_service_spec.rb b/spec/revised_service_spec.rb index 5a2b8d4..1a06731 100644 --- a/spec/revised_service_spec.rb +++ b/spec/revised_service_spec.rb @@ -201,89 +201,6 @@ module OData end end - describe "Keys" do - describe "Collection with an int64 key Named 'id'" do - - before(:all) do - stub_request(:get, "http://test.com/test.svc/$metadata"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/int64_ids/edmx_car_service.xml", __FILE__)), :headers => {}) - @svc = OData::Service.new "http://test.com/test.svc/" - end - - context "has the correct metadata" do - before(:all) do - @id_meta = @svc.class_metadata['Car']['id'] - end - - subject { @id_meta } - - its(:name) { should eq('id') } - its(:type) { should eq('Edm.Int64') } - its(:nullable) { should eq false } - its(:fc_target_path) { should be_nil } - its(:fc_keep_in_content) { should be_nil } - end - - context "can parse Id correctly" do - before(:each) do - stub_request(:get, "http://test.com/test.svc/Cars(213L)"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/int64_ids/result_cars.xml", __FILE__)), :headers => {}) - - @svc.Cars(213) - results = @svc.execute - @car = results.first - end - - subject { @car } - - its(:id) { should eq(213) } - its(:color) { should eq('peach') } - end - end - - describe "Collection with an int64 key named 'KeyId'" do - - before(:all) do - stub_request(:get, "http://test.com/test.svc/$metadata"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/int64_ids/edmx_boat_service.xml", __FILE__)), :headers => {}) - @svc = OData::Service.new "http://test.com/test.svc/" - end - - context "has the correct metadata" do - before(:all) do - @id_meta = @svc.class_metadata['Boat']['KeyId'] - end - - subject { @id_meta } - - its(:name) { should eq('KeyId') } - its(:type) { should eq('Edm.Int64') } - its(:nullable) { should eq(false) } - its(:fc_target_path) { should be_nil } - its(:fc_keep_in_content) { should be_nil } - end - - context "can parse Id correctly" do - before(:each) do - stub_request(:get, "http://test.com/test.svc/Boats(213L)"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/int64_ids/result_boats.xml", __FILE__)), :headers => {}) - - @svc.Boats(213) - results = @svc.execute - @boat = results.first - end - - subject { @boat } - - its(:id) { should eq(213) } - its(:color) { should eq('blue') } - end - end - end describe "Dual Namespaces" do before(:all) do From b720b4796945abc9b8088852fa98eb95b8f4bab0 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 7 Sep 2017 21:40:14 +0200 Subject: [PATCH 05/28] Extract to sap_spec --- spec/service_spec.rb | 19 ------------------- spec/services/sap_spec.rb | 25 +++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 19 deletions(-) create mode 100644 spec/services/sap_spec.rb diff --git a/spec/service_spec.rb b/spec/service_spec.rb index 42d6790..52d10a3 100644 --- a/spec/service_spec.rb +++ b/spec/service_spec.rb @@ -161,25 +161,6 @@ module OData end end - describe "handling of SAP results" do - before(:each) do - # Required for the build_classes method - stub_request(:get, "http://test.com/test.svc/$metadata"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sap/edmx_sap_demo_flight.xml", __FILE__)), :headers => {}) - - stub_request(:get, "http://test.com/test.svc/z_demo_flightCollection"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sap/result_sap_demo_flight_missing_category.xml", __FILE__)), :headers => {}) - end - - it "should handle entities without a category element" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.z_demo_flightCollection - results = svc.execute - results.first.should be_a_kind_of(ZDemoFlight) - end - end describe "collections, objects, metadata etc" do before(:each) do diff --git a/spec/services/sap_spec.rb b/spec/services/sap_spec.rb new file mode 100644 index 0000000..08e9247 --- /dev/null +++ b/spec/services/sap_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper' + +module OData + + describe "handling of SAP results" do + before(:each) do + # Required for the build_classes method + stub_request(:get, "http://test.com/test.svc/$metadata"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new( FIXTURES + "/sap/edmx_sap_demo_flight.xml"), :headers => {}) + + stub_request(:get, "http://test.com/test.svc/z_demo_flightCollection"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new( FIXTURES + "/sap/result_sap_demo_flight_missing_category.xml"), :headers => {}) + end + + it "should handle entities without a category element" do + svc = OData::Service.new "http://test.com/test.svc/" + svc.z_demo_flightCollection + results = svc.execute + results.first.should be_a_kind_of(ZDemoFlight) + end + end + +end \ No newline at end of file From 0b87509b6dd0effb7090262321bcf9916cbc26ec Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 7 Sep 2017 21:40:58 +0200 Subject: [PATCH 06/28] Extract to sample_service_spec --- spec/service_spec.rb | 483 --------------------------- spec/services/sample_service_spec.rb | 371 ++++++++++++++++++++ 2 files changed, 371 insertions(+), 483 deletions(-) create mode 100644 spec/services/sample_service_spec.rb diff --git a/spec/service_spec.rb b/spec/service_spec.rb index 52d10a3..a750372 100644 --- a/spec/service_spec.rb +++ b/spec/service_spec.rb @@ -429,491 +429,8 @@ module OData end end - describe "sample service" do - before(:each) do - # Required for the build_classes method - stub_request(:get, /http:\/\/test\.com\/test\.svc\/\$metadata(?:\?.+)?/). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/edmx_categories_products.xml", __FILE__)), :headers => {}) - - stub_request(:get, /http:\/\/test\.com\/test\.svc\/Products\(\d\)/). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_single_product.xml", __FILE__)), :headers => {}) - - stub_request(:get, /http:\/\/test\.com\/test\.svc\/Products\(\d{2,}\)/). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_single_product_not_found.xml", __FILE__)), :headers => {}) - - stub_request(:get, "http://test.com/test.svc/Products(1)/Category"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_single_category.xml", __FILE__)), :headers => {}) - - stub_request(:get, "http://test.com/test.svc/Categories(1)"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_single_category.xml", __FILE__)), :headers => {}) - - stub_request(:get, "http://test.com/test.svc/Categories(1)/Products"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_multiple_category_products.xml", __FILE__)), :headers => {}) - - stub_request(:post, "http://test.com/test.svc/Categories(1)/$links/Products").to_return(:status => 204) - stub_request(:post, "http://test.com/test.svc/$batch").to_return(:status => 202) - end - - describe "lazy loading" do - after(:each) do - Object.send(:remove_const, 'Product') if Object.const_defined? 'Product' - Object.send(:remove_const, 'Category') if Object.const_defined? 'Category' - end - - it "should have a load property method" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.should respond_to(:load_property) - end - - it "should throw an exception if the object isn't tracked" do - svc = OData::Service.new "http://test.com/test.svc/" - new_object = Product.new - lambda { svc.load_property(new_object, "Category") }.should raise_error(NotSupportedError, "You cannot load a property on an entity that isn't tracked") - end - - it "should throw an exception if there isn't a method matching the navigation property passed in" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.Products(1) - product = svc.execute.first - lambda { svc.load_property(product, "NoMatchingMethod") }.should raise_error(ArgumentError, "'NoMatchingMethod' is not a valid navigation property") - end - - it "should throw an exception if the method passed in is a standard property (non-navigation)" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.Products(1) - product = svc.execute.first - lambda { svc.load_property(product, "Name") }.should raise_error(ArgumentError, "'Name' is not a valid navigation property") - end - it "should fill a single navigation property" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.Products(1) - product = svc.execute.first - svc.load_property(product, "Category") - product.Category.should_not be_nil - product.Category.Id.should eq 1 - product.Category.Name.should eq 'Category 1' - end - it "should fill a collection navigation property" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.Categories(1) - category = svc.execute.first - svc.load_property(category, "Products") - category.Products.first.should be_a Product - category.Products[0].Id.should eq 1 - category.Products[1].Id.should eq 2 - end - - it "should not mutate the object's metadata" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.Products(1) - product = svc.execute.first - original_metadata = product.__metadata.to_json - svc.load_property(product, "Category") - product.__metadata.to_json.should == original_metadata - end - end - - describe "find, create, add, update, and delete" do - after(:each) do - Object.send(:remove_const, 'Product') if Object.const_defined? 'Product' - Object.send(:remove_const, 'Category') if Object.const_defined? 'Category' - end - - it "should implement an AddTo method for collection" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.should respond_to :AddToCategories - svc.should respond_to :AddToProducts - end - - it "should create objects with an initialize method that can build the object from a hash" do - svc = OData::Service.new "http://test.com/test.svc/" - product = Product.new 'Id' => 1000, 'Name' => 'New Product' - product.Id.should eq 1000 - product.Name.should eq 'New Product' - end - - it "should create objects that rejects keys that don't have corresponding methods" do - svc = OData::Service.new "http://test.com/test.svc/" - lambda { Product.new 'NotAProperty' => true }.should raise_error NoMethodError - end - - it "should create objects that expose a properties class method that lists the properties for the object" do - svc = OData::Service.new "http://test.com/test.svc/" - Product.properties.should include 'Id' - Product.properties.should include 'Name' - Product.properties.should include 'Category' - end - - it "should have full metadata for a property returned from the properties method" do - svc = OData::Service.new "http://test.com/test.svc/" - Product.properties['Category'].should be_a PropertyMetadata - expect(Product.properties['Category'].nav_prop).to eq true - end - - it "should create objects that expose an id property" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.Products(1) - product = svc.execute.first - expect(product).to respond_to :id - end - - it "should extract the id from the metadata" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.Products(1) - product = svc.execute.first - expect(product.id).to eq 1 - end - - describe "Class.first method" do - it "should exist on the create server objects" do - svc = OData::Service.new "http://test.com/test.svc/" - expect(Product).to respond_to :first - end - it "should return nil if an id isn't passed in" do - svc = OData::Service.new "http://test.com/test.svc/" - product = Product.first(nil) - product.should be_nil - end - it "should return nil if an id isn't found" do - svc = OData::Service.new "http://test.com/test.svc/" - product = Product.first(1234567890) - product.should be_nil - end - it "should return a product if an id is found" do - svc = OData::Service.new "http://test.com/test.svc/" - product = Product.first(1) - product.should_not be_nil - end - end - end - - describe "namespaces" do - after(:each) do - VisoftInc::Sample::Models.send(:remove_const, 'Product') if VisoftInc::Sample::Models.const_defined? 'Product' - VisoftInc::Sample::Models.send(:remove_const, 'Category') if VisoftInc::Sample::Models.const_defined? 'Category' - - VisoftInc::Sample.send(:remove_const, 'Models') if VisoftInc::Sample.const_defined? 'Models' - VisoftInc.send(:remove_const, 'Sample') if VisoftInc.const_defined? 'Sample' - Object.send(:remove_const, 'VisoftInc') if Object.const_defined? 'VisoftInc' - end - - it "should create models in the specified namespace if the option is set (using a .NET style namespace with dots)" do - svc = OData::Service.new "http://test.com/test.svc/", { :namespace => 'VisoftInc.Sample.Models' } - expect(defined?(VisoftInc::Sample::Models::Product).nil?).to eq false - expect(defined?(VisoftInc::Sample::Models::Category).nil?).to eq false - end - - it "should create models in the specified namespace if the option is set (using Ruby style namespaces with double colons)" do - svc = OData::Service.new "http://test.com/test.svc/", { :namespace => 'VisoftInc::Sample::Models' } - defined?(VisoftInc::Sample::Models::Product).nil?.should eq false - defined?(VisoftInc::Sample::Models::Category).nil?.should eq false - end - - it "should fill object defined in a namespace" do - svc = OData::Service.new "http://test.com/test.svc/", { :namespace => 'VisoftInc::Sample::Models' } - svc.Categories(1) - categories = svc.execute - categories.should_not be_nil - category = categories.first - category.Id.should eq 1 - category.Name.should eq 'Category 1' - end - - it "should fill the class_metadata hash" do - svc = OData::Service.new "http://test.com/test.svc/", { :namespace => 'VisoftInc::Sample::Models' } - svc.class_metadata.should_not be_empty - end - - it "should add a key (based on the name) for each property class_metadata hash" do - svc = OData::Service.new "http://test.com/test.svc/", { :namespace => 'VisoftInc::Sample::Models' } - svc.class_metadata['VisoftInc::Sample::Models::Product'].should have_key 'Id' - svc.class_metadata['VisoftInc::Sample::Models::Product'].should have_key 'Name' - svc.class_metadata['VisoftInc::Sample::Models::Product'].should have_key 'Description' - end - - it "should lazy load objects defined in a namespace" do - svc = OData::Service.new "http://test.com/test.svc/", { :namespace => 'VisoftInc::Sample::Models' } - svc.Categories(1) - category = svc.execute.first - svc.load_property category, 'Products' - category.Products.should_not be_nil - category.Products.first.Id.should eq 1 - category.Products.first.Name.should eq 'Widget 1' - end - end - - describe "add_link method" do - it "should exist as a method on the service" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.should respond_to(:add_link) - end - - it "shouldn't be allowed if a parent isn't tracked" do - svc = OData::Service.new "http://test.com/test.svc/" - category = Category.new :Name => 'New Category' - property = nil # Not needed for this test - product = nil # Not needed for this test - lambda { svc.add_link(category, property, product) }.should raise_error(NotSupportedError, "You cannot add a link on an entity that isn't tracked (Category)") - end - - it "shouldn't be allowed if a property isn't found on the parent" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.Categories(1) - category = svc.execute.first - property = 'NotAProperty' - product = nil # Not needed for this test - lambda { svc.add_link(category, property, product) }.should raise_error(ArgumentError, "'NotAProperty' is not a valid navigation property for Category") - end - - it "shouldn't be allowed if a property isn't a navigation property on the parent" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.Categories(1) - category = svc.execute.first - property = 'Name' - product = nil # Not needed for this test - lambda { svc.add_link(category, property, product) }.should raise_error(ArgumentError, "'Name' is not a valid navigation property for Category") - end - - it "shouldn't be allowed if a child isn't tracked" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.Categories(1) - category = svc.execute.first - property = 'Products' - product = Product.new :Name => 'Widget 1' - lambda { svc.add_link(category, property, product) }.should raise_error(NotSupportedError, "You cannot add a link on a child entity that isn't tracked (Product)") - end - - it "should perform a post against the correct URL with the correct body on a single_save" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.Categories(1) - category = svc.execute.first - svc.Products(1) - product = svc.execute.first - property = 'Products' - svc.add_link(category, property, product) - svc.save_changes - - if RUBY_VERSION.start_with? '2.3' - a_request(:post, "http://test.com/test.svc/Categories(1)/$links/Products"). - with(:body => '{"uri":"http://test.com/test.svc/Products(1)"}', - :headers => DEFAULT_HEADERS.merge({'Content-Type' => 'application/json'})).should have_been_made - else - a_request(:post, "http://test.com/test.svc/Categories(1)/$links/Products"). - with(:body => '"{\"uri\":\"http://test.com/test.svc/Products(1)\"}"', - :headers => DEFAULT_HEADERS.merge({'Content-Type' => 'application/json'})).should have_been_made - end - end - - it "should add the child to the parent's navigation property on a single_save" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.Categories(1) - category = svc.execute.first - svc.Products(1) - product = svc.execute.first - property = 'Products' - svc.add_link(category, property, product) - svc.save_changes - category.Products.should include product - end - - it "should add the parent to the child's navigation property on a single_save" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.Categories(1) - category = svc.execute.first - svc.Products(1) - product = svc.execute.first - property = 'Products' - svc.add_link(category, property, product) - svc.save_changes - product.Category.should eq category - end - - describe "batch_save" do - before(:each) do - @svc = OData::Service.new "http://test.com/test.svc/" - @category = Category.first(1) - @product = Product.first(1) - new_category = Category.new - @svc.AddToCategories(new_category) - @svc.add_link(@category, 'Products', @product) - @svc.save_changes - end - - it "should perform a post with the correct URL and body on a batch_save" do - if RUBY_VERSION.start_with? '2.3' - WebMock.should have_requested(:post, "http://test.com/test.svc/$batch").with { |request| - request.body.include? "POST http://test.com/test.svc/Categories(1)/$links/Products HTTP/1.1" - request.body.include? '{"uri":"http://test.com/test.svc/Products(1)"}' - } - else - WebMock.should have_requested(:post, "http://test.com/test.svc/$batch").with { |request| - request.body.include? "POST http://test.com/test.svc/Categories(1)/$links/Products HTTP/1.1" - request.body.include? '{\"uri\":\"http://test.com/test.svc/Products(1)\"}' - } - end - end - context "child is a part of the parent's collection" do - subject { @category.Products } - it { should include @product } - end - context "parent object should be filled in on the child" do - subject { @product.Category } - it { should eq @category } - end - end - - describe "serializes nested collections" do - # Compy with oData Deep Insert capabilities - # http://docs.oasis-open.org/odata/odata-json-format/v4.0/os/odata-json-format-v4.0-os.html#_Toc372793073 - - before :each do - category = Category.new - category.Products = [Product.new(Name: "First"), Product.new(Name: "Last")] - @json = JSON.parse(category.to_json(type: :add)) - end - - it "should have an array for the Products key" do - @json["Products"].should be_a_kind_of Array - end - - it "should have a hash for each product" do - @json["Products"].each{|x| x.should be_a_kind_of Hash} - end - - it "should have the same data we put into the products" do - @json["Products"].first["Name"].should eq "First" - @json["Products"].last["Name"].should eq "Last" - end - end - end - end - - describe "JSON serialization of objects" do - let(:username) { "blabla" } - let(:password) { "" } - - before(:each) do - # Required for the build_classes method - stub_request(:get, "http://test.com/test.svc/$metadata"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_system_center/edmx_ms_system_center.xml", __FILE__)), :headers => {}) - - stub_request(:get, "http://test.com/test.svc/VirtualMachines"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_system_center/virtual_machines.xml", __FILE__)), :headers => {}) - svc = OData::Service.new "http://test.com/test.svc/", { :username => username, :password => password, :verify_ssl => false, :namespace => "VMM" } - svc.VirtualMachines - results = svc.execute - @json = results.first.as_json - end - - it "Should quote Edm.Int64 properties" do - @json["PerfDiskBytesWrite"].should be_a(String) - end - - it "Should output collections with metadata" do - @json["VMNetworkAssignments"].should be_a(Hash) - @json["VMNetworkAssignments"].should have_key("__metadata") - @json["VMNetworkAssignments"]["__metadata"].should be_a(Hash) - @json["VMNetworkAssignments"]["__metadata"].should have_key("type") - @json["VMNetworkAssignments"]["__metadata"]["type"].should eq("Collection(VMM.VMNetworkAssignment)") - @json["VMNetworkAssignments"].should have_key("results") - @json["VMNetworkAssignments"]["results"].should be_a(Array) - @json["VMNetworkAssignments"]["results"].should eq([]) - end - end - - describe "handling of Microsoft System Center 2012" do - let(:username) { "blabla" } - let(:password) { "" } - - before(:each) do - auth_string = "#{username}:#{password}" - authorization_header = { authorization: "Basic #{Base64::encode64(auth_string).strip}" } - headers = DEFAULT_HEADERS.merge(authorization_header) - - # Required for the build_classes method - stub_request(:get, "http://test.com/test.svc/$metadata"). - with(:headers => headers). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_system_center/edmx_ms_system_center.xml", __FILE__)), :headers => {}) - - stub_request(:get, "http://test.com/test.svc/VirtualMachines"). - with(:headers => headers). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_system_center/virtual_machines.xml", __FILE__)), :headers => {}) - - stub_request(:get, "http://test.com/test.svc/HardwareProfiles?$filter=Memory%20eq%203500"). - with(:headers => headers). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_system_center/hardware_profiles.xml", __FILE__)), :headers => {}) - - stub_request(:get, "http://test.com/test.svc/VMTemplates"). - with(:headers => headers). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_system_center/vm_templates.xml", __FILE__)), :headers => {}) - end - - after(:all) do - VMM.constants.each do |constant| - VMM.send :remove_const, constant - end - end - - it "should successfully parse null valued string properties" do - svc = OData::Service.new "http://test.com/test.svc/", { :username => username, :password => password, :verify_ssl => false, :namespace => "VMM" } - svc.VirtualMachines - results = svc.execute - results.first.should be_a_kind_of(VMM::VirtualMachine) - results.first.CostCenter.should be_nil - end - - it "should successfully return a virtual machine" do - svc = OData::Service.new "http://test.com/test.svc/", { :username => username, :password => password, :verify_ssl => false, :namespace => "VMM" } - svc.VirtualMachines - results = svc.execute - results.first.should be_a_kind_of(VMM::VirtualMachine) - end - - it "should successfully return a hardware profile for results that include a collection of complex types" do - svc = OData::Service.new "http://test.com/test.svc/", { :username => username, :password => password, :verify_ssl => false, :namespace => "VMM" } - svc.HardwareProfiles.filter("Memory eq 3500") - results = svc.execute - results.first.should be_a_kind_of(VMM::HardwareProfile) - end - - it "should successfully return a collection of complex types" do - svc = OData::Service.new "http://test.com/test.svc/", { :username => username, :password => password, :verify_ssl => false, :namespace => "VMM" } - svc.HardwareProfiles.filter("Memory eq 3500") - results = svc.execute - granted_list = results.first.GrantedToList - granted_list.should be_a_kind_of(Array) - granted_list.first.should be_a_kind_of(VMM::UserAndRole) - granted_list.first.RoleName.should == "Important Tenant" - end - - - it "should successfully return results that include a collection of Edm types" do - svc = OData::Service.new "http://test.com/test.svc/", { :username => username, :password => password, :verify_ssl => false, :namespace => "VMM" } - svc.VMTemplates - results = svc.execute - results.first.should be_a_kind_of(VMM::VMTemplate) - end - - it "should successfully return a collection of Edm types" do - svc = OData::Service.new "http://test.com/test.svc/", { :username => username, :password => password, :verify_ssl => false, :namespace => "VMM" } - svc.VMTemplates - results = svc.execute - boot_order = results.first.BootOrder - boot_order.should be_a_kind_of(Array) - boot_order.first.should be_a_kind_of(String) - boot_order.should eq ['CD', 'IdeHardDrive', 'PxeBoot', 'Floppy'] - end - end describe "handling of nested expands" do before(:each) do diff --git a/spec/services/sample_service_spec.rb b/spec/services/sample_service_spec.rb new file mode 100644 index 0000000..42f7d31 --- /dev/null +++ b/spec/services/sample_service_spec.rb @@ -0,0 +1,371 @@ +require 'spec_helper' + +module OData + describe "sample service" do + + before(:each) do + # Required for the build_classes method + stub_request(:get, /http:\/\/test\.com\/test\.svc\/\$metadata(?:\?.+)?/). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new( FIXTURES + "/sample_service/edmx_categories_products.xml"), :headers => {}) + + stub_request(:get, /http:\/\/test\.com\/test\.svc\/Products\(\d\)/). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new( FIXTURES + "/sample_service/result_single_product.xml"), :headers => {}) + + stub_request(:get, /http:\/\/test\.com\/test\.svc\/Products\(\d{2,}\)/). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(FIXTURES + "/sample_service/result_single_product_not_found.xml"), :headers => {}) + + stub_request(:get, "http://test.com/test.svc/Products(1)/Category"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(FIXTURES + "/sample_service/result_single_category.xml"), :headers => {}) + + stub_request(:get, "http://test.com/test.svc/Categories(1)"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(FIXTURES + "/sample_service/result_single_category.xml"), :headers => {}) + + stub_request(:get, "http://test.com/test.svc/Categories(1)/Products"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(FIXTURES + "/sample_service/result_multiple_category_products.xml"), :headers => {}) + + stub_request(:post, "http://test.com/test.svc/Categories(1)/$links/Products").to_return(:status => 204) + stub_request(:post, "http://test.com/test.svc/$batch").to_return(:status => 202) + end + + describe "lazy loading" do + after(:each) do + Object.send(:remove_const, 'Product') if Object.const_defined? 'Product' + Object.send(:remove_const, 'Category') if Object.const_defined? 'Category' + end + + it "should have a load property method" do + svc = OData::Service.new "http://test.com/test.svc/" + svc.should respond_to(:load_property) + end + + it "should throw an exception if the object isn't tracked" do + svc = OData::Service.new "http://test.com/test.svc/" + new_object = Product.new + lambda { svc.load_property(new_object, "Category") }.should raise_error(NotSupportedError, "You cannot load a property on an entity that isn't tracked") + end + + it "should throw an exception if there isn't a method matching the navigation property passed in" do + svc = OData::Service.new "http://test.com/test.svc/" + svc.Products(1) + product = svc.execute.first + lambda { svc.load_property(product, "NoMatchingMethod") }.should raise_error(ArgumentError, "'NoMatchingMethod' is not a valid navigation property") + end + + it "should throw an exception if the method passed in is a standard property (non-navigation)" do + svc = OData::Service.new "http://test.com/test.svc/" + svc.Products(1) + product = svc.execute.first + lambda { svc.load_property(product, "Name") }.should raise_error(ArgumentError, "'Name' is not a valid navigation property") + end + + it "should fill a single navigation property" do + svc = OData::Service.new "http://test.com/test.svc/" + svc.Products(1) + product = svc.execute.first + svc.load_property(product, "Category") + product.Category.should_not be_nil + product.Category.Id.should eq 1 + product.Category.Name.should eq 'Category 1' + end + + it "should fill a collection navigation property" do + svc = OData::Service.new "http://test.com/test.svc/" + svc.Categories(1) + category = svc.execute.first + svc.load_property(category, "Products") + category.Products.first.should be_a Product + category.Products[0].Id.should eq 1 + category.Products[1].Id.should eq 2 + end + + it "should not mutate the object's metadata" do + svc = OData::Service.new "http://test.com/test.svc/" + svc.Products(1) + product = svc.execute.first + original_metadata = product.__metadata.to_json + svc.load_property(product, "Category") + product.__metadata.to_json.should == original_metadata + end + end + + describe "find, create, add, update, and delete" do + after(:each) do + Object.send(:remove_const, 'Product') if Object.const_defined? 'Product' + Object.send(:remove_const, 'Category') if Object.const_defined? 'Category' + end + + it "should implement an AddTo method for collection" do + svc = OData::Service.new "http://test.com/test.svc/" + svc.should respond_to :AddToCategories + svc.should respond_to :AddToProducts + end + + it "should create objects with an initialize method that can build the object from a hash" do + svc = OData::Service.new "http://test.com/test.svc/" + product = Product.new 'Id' => 1000, 'Name' => 'New Product' + product.Id.should eq 1000 + product.Name.should eq 'New Product' + end + + it "should create objects that rejects keys that don't have corresponding methods" do + svc = OData::Service.new "http://test.com/test.svc/" + lambda { Product.new 'NotAProperty' => true }.should raise_error NoMethodError + end + + it "should create objects that expose a properties class method that lists the properties for the object" do + svc = OData::Service.new "http://test.com/test.svc/" + Product.properties.should include 'Id' + Product.properties.should include 'Name' + Product.properties.should include 'Category' + end + + it "should have full metadata for a property returned from the properties method" do + svc = OData::Service.new "http://test.com/test.svc/" + Product.properties['Category'].should be_a PropertyMetadata + expect(Product.properties['Category'].nav_prop).to eq true + end + + it "should create objects that expose an id property" do + svc = OData::Service.new "http://test.com/test.svc/" + svc.Products(1) + product = svc.execute.first + expect(product).to respond_to :id + end + + it "should extract the id from the metadata" do + svc = OData::Service.new "http://test.com/test.svc/" + svc.Products(1) + product = svc.execute.first + expect(product.id).to eq 1 + end + + describe "Class.first method" do + it "should exist on the create server objects" do + svc = OData::Service.new "http://test.com/test.svc/" + expect(Product).to respond_to :first + end + it "should return nil if an id isn't passed in" do + svc = OData::Service.new "http://test.com/test.svc/" + product = Product.first(nil) + product.should be_nil + end + it "should return nil if an id isn't found" do + svc = OData::Service.new "http://test.com/test.svc/" + product = Product.first(1234567890) + product.should be_nil + end + it "should return a product if an id is found" do + svc = OData::Service.new "http://test.com/test.svc/" + product = Product.first(1) + product.should_not be_nil + end + end + end + + describe "namespaces" do + after(:each) do + VisoftInc::Sample::Models.send(:remove_const, 'Product') if VisoftInc::Sample::Models.const_defined? 'Product' + VisoftInc::Sample::Models.send(:remove_const, 'Category') if VisoftInc::Sample::Models.const_defined? 'Category' + + VisoftInc::Sample.send(:remove_const, 'Models') if VisoftInc::Sample.const_defined? 'Models' + VisoftInc.send(:remove_const, 'Sample') if VisoftInc.const_defined? 'Sample' + Object.send(:remove_const, 'VisoftInc') if Object.const_defined? 'VisoftInc' + end + + it "should create models in the specified namespace if the option is set (using a .NET style namespace with dots)" do + svc = OData::Service.new "http://test.com/test.svc/", { :namespace => 'VisoftInc.Sample.Models' } + expect(defined?(VisoftInc::Sample::Models::Product).nil?).to eq false + expect(defined?(VisoftInc::Sample::Models::Category).nil?).to eq false + end + + it "should create models in the specified namespace if the option is set (using Ruby style namespaces with double colons)" do + svc = OData::Service.new "http://test.com/test.svc/", { :namespace => 'VisoftInc::Sample::Models' } + defined?(VisoftInc::Sample::Models::Product).nil?.should eq false + defined?(VisoftInc::Sample::Models::Category).nil?.should eq false + end + + it "should fill object defined in a namespace" do + svc = OData::Service.new "http://test.com/test.svc/", { :namespace => 'VisoftInc::Sample::Models' } + svc.Categories(1) + categories = svc.execute + categories.should_not be_nil + category = categories.first + category.Id.should eq 1 + category.Name.should eq 'Category 1' + end + + it "should fill the class_metadata hash" do + svc = OData::Service.new "http://test.com/test.svc/", { :namespace => 'VisoftInc::Sample::Models' } + svc.class_metadata.should_not be_empty + end + + it "should add a key (based on the name) for each property class_metadata hash" do + svc = OData::Service.new "http://test.com/test.svc/", { :namespace => 'VisoftInc::Sample::Models' } + svc.class_metadata['VisoftInc::Sample::Models::Product'].should have_key 'Id' + svc.class_metadata['VisoftInc::Sample::Models::Product'].should have_key 'Name' + svc.class_metadata['VisoftInc::Sample::Models::Product'].should have_key 'Description' + end + + it "should lazy load objects defined in a namespace" do + svc = OData::Service.new "http://test.com/test.svc/", { :namespace => 'VisoftInc::Sample::Models' } + svc.Categories(1) + category = svc.execute.first + svc.load_property category, 'Products' + category.Products.should_not be_nil + category.Products.first.Id.should eq 1 + category.Products.first.Name.should eq 'Widget 1' + end + end + + describe "add_link method" do + it "should exist as a method on the service" do + svc = OData::Service.new "http://test.com/test.svc/" + svc.should respond_to(:add_link) + end + + it "shouldn't be allowed if a parent isn't tracked" do + svc = OData::Service.new "http://test.com/test.svc/" + category = Category.new :Name => 'New Category' + property = nil # Not needed for this test + product = nil # Not needed for this test + lambda { svc.add_link(category, property, product) }.should raise_error(NotSupportedError, "You cannot add a link on an entity that isn't tracked (Category)") + end + + it "shouldn't be allowed if a property isn't found on the parent" do + svc = OData::Service.new "http://test.com/test.svc/" + svc.Categories(1) + category = svc.execute.first + property = 'NotAProperty' + product = nil # Not needed for this test + lambda { svc.add_link(category, property, product) }.should raise_error(ArgumentError, "'NotAProperty' is not a valid navigation property for Category") + end + + it "shouldn't be allowed if a property isn't a navigation property on the parent" do + svc = OData::Service.new "http://test.com/test.svc/" + svc.Categories(1) + category = svc.execute.first + property = 'Name' + product = nil # Not needed for this test + lambda { svc.add_link(category, property, product) }.should raise_error(ArgumentError, "'Name' is not a valid navigation property for Category") + end + + it "shouldn't be allowed if a child isn't tracked" do + svc = OData::Service.new "http://test.com/test.svc/" + svc.Categories(1) + category = svc.execute.first + property = 'Products' + product = Product.new :Name => 'Widget 1' + lambda { svc.add_link(category, property, product) }.should raise_error(NotSupportedError, "You cannot add a link on a child entity that isn't tracked (Product)") + end + + it "should perform a post against the correct URL with the correct body on a single_save" do + svc = OData::Service.new "http://test.com/test.svc/" + svc.Categories(1) + category = svc.execute.first + svc.Products(1) + product = svc.execute.first + property = 'Products' + svc.add_link(category, property, product) + svc.save_changes + + if RUBY_VERSION.start_with? '2.3' + a_request(:post, "http://test.com/test.svc/Categories(1)/$links/Products"). + with(:body => '{"uri":"http://test.com/test.svc/Products(1)"}', + :headers => DEFAULT_HEADERS.merge({'Content-Type' => 'application/json'})).should have_been_made + else + a_request(:post, "http://test.com/test.svc/Categories(1)/$links/Products"). + with(:body => '"{\"uri\":\"http://test.com/test.svc/Products(1)\"}"', + :headers => DEFAULT_HEADERS.merge({'Content-Type' => 'application/json'})).should have_been_made + end + end + + it "should add the child to the parent's navigation property on a single_save" do + svc = OData::Service.new "http://test.com/test.svc/" + svc.Categories(1) + category = svc.execute.first + svc.Products(1) + product = svc.execute.first + property = 'Products' + svc.add_link(category, property, product) + svc.save_changes + category.Products.should include product + end + + it "should add the parent to the child's navigation property on a single_save" do + svc = OData::Service.new "http://test.com/test.svc/" + svc.Categories(1) + category = svc.execute.first + svc.Products(1) + product = svc.execute.first + property = 'Products' + svc.add_link(category, property, product) + svc.save_changes + product.Category.should eq category + end + + describe "batch_save" do + before(:each) do + @svc = OData::Service.new "http://test.com/test.svc/" + @category = Category.first(1) + @product = Product.first(1) + new_category = Category.new + @svc.AddToCategories(new_category) + @svc.add_link(@category, 'Products', @product) + @svc.save_changes + end + + it "should perform a post with the correct URL and body on a batch_save" do + if RUBY_VERSION.start_with? '2.3' + WebMock.should have_requested(:post, "http://test.com/test.svc/$batch").with { |request| + request.body.include? "POST http://test.com/test.svc/Categories(1)/$links/Products HTTP/1.1" + request.body.include? '{"uri":"http://test.com/test.svc/Products(1)"}' + } + else + WebMock.should have_requested(:post, "http://test.com/test.svc/$batch").with { |request| + request.body.include? "POST http://test.com/test.svc/Categories(1)/$links/Products HTTP/1.1" + request.body.include? '{\"uri\":\"http://test.com/test.svc/Products(1)\"}' + } + end + end + context "child is a part of the parent's collection" do + subject { @category.Products } + it { should include @product } + end + context "parent object should be filled in on the child" do + subject { @product.Category } + it { should eq @category } + end + end + + describe "serializes nested collections" do + # Compy with oData Deep Insert capabilities + # http://docs.oasis-open.org/odata/odata-json-format/v4.0/os/odata-json-format-v4.0-os.html#_Toc372793073 + + before :each do + category = Category.new + category.Products = [Product.new(Name: "First"), Product.new(Name: "Last")] + @json = JSON.parse(category.to_json(type: :add)) + end + + it "should have an array for the Products key" do + @json["Products"].should be_a_kind_of Array + end + + it "should have a hash for each product" do + @json["Products"].each{|x| x.should be_a_kind_of Hash} + end + + it "should have the same data we put into the products" do + @json["Products"].first["Name"].should eq "First" + @json["Products"].last["Name"].should eq "Last" + end + end + end + end +end From 67c38dd3ea83bf4336d1eb7d2af8c3884a3e4c2c Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 7 Sep 2017 21:43:31 +0200 Subject: [PATCH 07/28] Extract to microsoft_system_center_spec --- spec/services/microsoft_system_center_spec.rb | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 spec/services/microsoft_system_center_spec.rb diff --git a/spec/services/microsoft_system_center_spec.rb b/spec/services/microsoft_system_center_spec.rb new file mode 100644 index 0000000..f0bbe92 --- /dev/null +++ b/spec/services/microsoft_system_center_spec.rb @@ -0,0 +1,88 @@ +require 'spec_helper' +require 'base64' + +module OData + describe "handling of Microsoft System Center 2012" do + let(:username) { "blabla" } + let(:password) { "" } + + before(:each) do + auth_string = "#{username}:#{password}" + authorization_header = { authorization: "Basic #{Base64::encode64(auth_string).strip}" } + headers = DEFAULT_HEADERS.merge(authorization_header) + + # Required for the build_classes method + stub_request(:get, "http://test.com/test.svc/$metadata"). + with(:headers => headers). + to_return(:status => 200, :body => File.new( FIXTURES + "/ms_system_center/edmx_ms_system_center.xml"), :headers => {}) + + stub_request(:get, "http://test.com/test.svc/VirtualMachines"). + with(:headers => headers). + to_return(:status => 200, :body => File.new( FIXTURES + "/ms_system_center/virtual_machines.xml"), :headers => {}) + + stub_request(:get, "http://test.com/test.svc/HardwareProfiles?$filter=Memory%20eq%203500"). + with(:headers => headers). + to_return(:status => 200, :body => File.new( FIXTURES + "/ms_system_center/hardware_profiles.xml"), :headers => {}) + + stub_request(:get, "http://test.com/test.svc/VMTemplates"). + with(:headers => headers). + to_return(:status => 200, :body => File.new( FIXTURES + "/ms_system_center/vm_templates.xml"), :headers => {}) + end + + after(:all) do + VMM.constants.each do |constant| + VMM.send :remove_const, constant + end + end + + it "should successfully parse null valued string properties" do + svc = OData::Service.new "http://test.com/test.svc/", { :username => username, :password => password, :verify_ssl => false, :namespace => "VMM" } + svc.VirtualMachines + results = svc.execute + results.first.should be_a_kind_of(VMM::VirtualMachine) + results.first.CostCenter.should be_nil + end + + it "should successfully return a virtual machine" do + svc = OData::Service.new "http://test.com/test.svc/", { :username => username, :password => password, :verify_ssl => false, :namespace => "VMM" } + svc.VirtualMachines + results = svc.execute + results.first.should be_a_kind_of(VMM::VirtualMachine) + end + + it "should successfully return a hardware profile for results that include a collection of complex types" do + svc = OData::Service.new "http://test.com/test.svc/", { :username => username, :password => password, :verify_ssl => false, :namespace => "VMM" } + svc.HardwareProfiles.filter("Memory eq 3500") + results = svc.execute + results.first.should be_a_kind_of(VMM::HardwareProfile) + end + + it "should successfully return a collection of complex types" do + svc = OData::Service.new "http://test.com/test.svc/", { :username => username, :password => password, :verify_ssl => false, :namespace => "VMM" } + svc.HardwareProfiles.filter("Memory eq 3500") + results = svc.execute + granted_list = results.first.GrantedToList + granted_list.should be_a_kind_of(Array) + granted_list.first.should be_a_kind_of(VMM::UserAndRole) + granted_list.first.RoleName.should == "Important Tenant" + end + + + it "should successfully return results that include a collection of Edm types" do + svc = OData::Service.new "http://test.com/test.svc/", { :username => username, :password => password, :verify_ssl => false, :namespace => "VMM" } + svc.VMTemplates + results = svc.execute + results.first.should be_a_kind_of(VMM::VMTemplate) + end + + it "should successfully return a collection of Edm types" do + svc = OData::Service.new "http://test.com/test.svc/", { :username => username, :password => password, :verify_ssl => false, :namespace => "VMM" } + svc.VMTemplates + results = svc.execute + boot_order = results.first.BootOrder + boot_order.should be_a_kind_of(Array) + boot_order.first.should be_a_kind_of(String) + boot_order.should eq ['CD', 'IdeHardDrive', 'PxeBoot', 'Floppy'] + end + end +end From 921a5a2f3ed19a3f4981229461a93422dc571411 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 7 Sep 2017 21:59:51 +0200 Subject: [PATCH 08/28] Create remove_classes --- spec/support/remove_classes.rb | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 spec/support/remove_classes.rb diff --git a/spec/support/remove_classes.rb b/spec/support/remove_classes.rb new file mode 100644 index 0000000..8f8ed5c --- /dev/null +++ b/spec/support/remove_classes.rb @@ -0,0 +1,26 @@ +# +# Removes the classes which were created by the odata_service +# +def remove_classes(service) + service.class_metadata.each_key do |klass| + next unless (String === klass ) + + namespaces = klass.split(/\.|::/) + (0..namespaces.count).each do |index| + index = namespaces.count-index-1 + name = namespaces[index] + if index == 0 + Object.send(:remove_const,name) if Object.const_defined? name + else + current_ns = namespaces[0..index-1].join '::' + if !current_ns.blank? and Object.const_defined? current_ns + if eval "#{current_ns}.const_defined? '#{name}'" + eval "#{current_ns}.send(:remove_const, '#{name}')" + end + end + end + end + + # Object.send(:remove_const, klass) if (String === klass and Object.const_defined? klass) + end +end From b864222a154ceae813e07b29507309e676eae757 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 7 Sep 2017 22:48:04 +0200 Subject: [PATCH 09/28] Use remove_classes in specs --- spec/internal/association_spec.rb | 4 ++ spec/revised_service_spec.rb | 12 +++-- spec/service_v4_spec.rb | 5 ++ spec/services/dynamics_nav_spec.rb | 46 +++++++++---------- spec/services/microsoft_system_center_spec.rb | 36 ++++++--------- spec/support/remove_classes.rb | 8 ++-- 6 files changed, 58 insertions(+), 53 deletions(-) diff --git a/spec/internal/association_spec.rb b/spec/internal/association_spec.rb index ab7b5aa..0ad3c24 100644 --- a/spec/internal/association_spec.rb +++ b/spec/internal/association_spec.rb @@ -12,6 +12,10 @@ module OData end describe "#initialize singlular navigation property" do before { @association = Association.new @product_category, @svc.edmx } + + after(:all) do + remove_classes @service + end subject { @association } it "sets the association name" do diff --git a/spec/revised_service_spec.rb b/spec/revised_service_spec.rb index 1a06731..7277d27 100644 --- a/spec/revised_service_spec.rb +++ b/spec/revised_service_spec.rb @@ -12,6 +12,9 @@ module OData @cat_prod_service = OData::Service.new "http://test.com/test.svc" end subject { @cat_prod_service } + after(:each) do + remove_classes @service + end context "methods" do it { should respond_to :update_object } @@ -211,15 +214,14 @@ module OData to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_system_center/edmx_ms_system_center_v2.xml", __FILE__)), :headers => {}) end - after(:all) do - VMM.constants.each do |constant| - VMM.send :remove_const, constant - end + after(:each) do + remove_classes @service end it "should parse the service without errors" do - lambda { OData::Service.new "http://test.com/test.svc/", { :username => "xxxx\\yyyy", :password=> "zzzz", :verify_ssl => false, :namespace => "VMM" } }.should_not raise_error + lambda { @service = OData::Service.new "http://test.com/test.svc/", { :username => "xxxx\\yyyy", :password=> "zzzz", :verify_ssl => false, :namespace => "VMM" } }.should_not raise_error end + end describe "Dual Services" do diff --git a/spec/service_v4_spec.rb b/spec/service_v4_spec.rb index bb5f603..dc3159c 100644 --- a/spec/service_v4_spec.rb +++ b/spec/service_v4_spec.rb @@ -11,6 +11,11 @@ @service = OData::Service.new "http://test.com/test.svc" end + + after(:all) do + remove_classes @service + end + subject { @service } context "methods" do diff --git a/spec/services/dynamics_nav_spec.rb b/spec/services/dynamics_nav_spec.rb index def25e7..8fa7fc3 100644 --- a/spec/services/dynamics_nav_spec.rb +++ b/spec/services/dynamics_nav_spec.rb @@ -4,68 +4,66 @@ module OData describe Service do describe "handling of Microsoft Dynamics Nav OData WebService" do - let(:username) { "blabla" } - let(:password) { "" } before(:each) do + # Required for the build_classes method + username = "blabla" + password = "" auth_string = "#{username}:#{password}" authorization_header = { authorization: "Basic #{Base64::encode64(auth_string).strip}" } headers = DEFAULT_HEADERS.merge(authorization_header) - # Required for the build_classes method stub_request(:get, "http://test.com/nav.svc/$metadata"). with(:headers => headers). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_dynamics_nav/edmx_ms_dynamics_nav.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => File.new( FIXTURES + "/ms_dynamics_nav/edmx_ms_dynamics_nav.xml"), :headers => {}) stub_request(:get, "http://test.com/nav.svc/Customer"). with(:headers => headers). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_dynamics_nav/result_customer.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => File.new( FIXTURES + "/ms_dynamics_nav/result_customer.xml"), :headers => {}) stub_request(:get, "http://test.com/nav.svc/Customer('100013')"). with(:headers => headers). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_dynamics_nav/result_customer.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => File.new( FIXTURES + "/ms_dynamics_nav/result_customer.xml"), :headers => {}) stub_request(:get, "http://test.com/nav.svc/Customer(100013)"). with(:headers => headers). - to_return(:status => 400, :body => File.new(File.expand_path("../fixtures/ms_dynamics_nav/result_customer_error.xml", __FILE__)), :headers => {}) + to_return(:status => 400, :body => File.new( FIXTURES + "/ms_dynamics_nav/result_customer_error.xml"), :headers => {}) stub_request(:get, "http://test.com/nav.svc/SalesOrder(Document_Type='Order',No='AB-1600013')"). with(:headers => headers). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_dynamics_nav/result_sales_order.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => File.new( FIXTURES + "/ms_dynamics_nav/result_sales_order.xml"), :headers => {}) + + @service = OData::Service.new "http://test.com/nav.svc/", { :username => username, :password => password, :verify_ssl => false } + end - after(:all) do - Object.send(:remove_const, 'Customer') - Object.send(:remove_const, 'SalesOrder') + after(:each) do + remove_classes @service end - it "should successfully parse null valued string properties" do - svc = OData::Service.new "http://test.com/nav.svc/", { :username => username, :password => password, :verify_ssl => false } - svc.Customer - results = svc.execute + it "should successfully parse null valued string properties" do + @service.Customer + results = @service.execute results.first.should be_a_kind_of(Customer) end it "should successfully return a customer by its string id" do - svc = OData::Service.new "http://test.com/nav.svc/", { :username => username, :password => password, :verify_ssl => false } - svc.Customer('100013') - results = svc.execute + @service.Customer('100013') + results = @service.execute results.first.should be_a_kind_of(Customer) results.first.Name.should eq 'Contoso AG' end it "should cast to string if a customer is accessed with integer id" do - svc = OData::Service.new "http://test.com/nav.svc/", { :username => username, :password => password, :verify_ssl => false } - svc.Customer(100013) - results = svc.execute + @service.Customer(100013) + results = @service.execute results.first.should be_a_kind_of(Customer) results.first.Name.should eq 'Contoso AG' end it "should successfully return a sales_order by its composite string ids" do - svc = OData::Service.new "http://test.com/nav.svc/", { :username => username, :password => password, :verify_ssl => false } - svc.SalesOrder(Document_Type: 'Order', No: 'AB-1600013') - results = svc.execute + @service.SalesOrder(Document_Type: 'Order', No: 'AB-1600013') + results = @service.execute results.first.should be_a_kind_of(SalesOrder) results.first.No.should eq 'AB-1600013' end diff --git a/spec/services/microsoft_system_center_spec.rb b/spec/services/microsoft_system_center_spec.rb index f0bbe92..30a0e65 100644 --- a/spec/services/microsoft_system_center_spec.rb +++ b/spec/services/microsoft_system_center_spec.rb @@ -27,40 +27,36 @@ module OData stub_request(:get, "http://test.com/test.svc/VMTemplates"). with(:headers => headers). to_return(:status => 200, :body => File.new( FIXTURES + "/ms_system_center/vm_templates.xml"), :headers => {}) + + @service = OData::Service.new "http://test.com/test.svc/", { :username => username, :password => password, :verify_ssl => false, :namespace => "VMM" } end after(:all) do - VMM.constants.each do |constant| - VMM.send :remove_const, constant - end + remove_classes @service end it "should successfully parse null valued string properties" do - svc = OData::Service.new "http://test.com/test.svc/", { :username => username, :password => password, :verify_ssl => false, :namespace => "VMM" } - svc.VirtualMachines - results = svc.execute + @service.VirtualMachines + results = @service.execute results.first.should be_a_kind_of(VMM::VirtualMachine) results.first.CostCenter.should be_nil end it "should successfully return a virtual machine" do - svc = OData::Service.new "http://test.com/test.svc/", { :username => username, :password => password, :verify_ssl => false, :namespace => "VMM" } - svc.VirtualMachines - results = svc.execute + @service.VirtualMachines + results = @service.execute results.first.should be_a_kind_of(VMM::VirtualMachine) end it "should successfully return a hardware profile for results that include a collection of complex types" do - svc = OData::Service.new "http://test.com/test.svc/", { :username => username, :password => password, :verify_ssl => false, :namespace => "VMM" } - svc.HardwareProfiles.filter("Memory eq 3500") - results = svc.execute + @service.HardwareProfiles.filter("Memory eq 3500") + results = @service.execute results.first.should be_a_kind_of(VMM::HardwareProfile) end it "should successfully return a collection of complex types" do - svc = OData::Service.new "http://test.com/test.svc/", { :username => username, :password => password, :verify_ssl => false, :namespace => "VMM" } - svc.HardwareProfiles.filter("Memory eq 3500") - results = svc.execute + @service.HardwareProfiles.filter("Memory eq 3500") + results = @service.execute granted_list = results.first.GrantedToList granted_list.should be_a_kind_of(Array) granted_list.first.should be_a_kind_of(VMM::UserAndRole) @@ -69,16 +65,14 @@ module OData it "should successfully return results that include a collection of Edm types" do - svc = OData::Service.new "http://test.com/test.svc/", { :username => username, :password => password, :verify_ssl => false, :namespace => "VMM" } - svc.VMTemplates - results = svc.execute + @service.VMTemplates + results = @service.execute results.first.should be_a_kind_of(VMM::VMTemplate) end it "should successfully return a collection of Edm types" do - svc = OData::Service.new "http://test.com/test.svc/", { :username => username, :password => password, :verify_ssl => false, :namespace => "VMM" } - svc.VMTemplates - results = svc.execute + @service.VMTemplates + results = @service.execute boot_order = results.first.BootOrder boot_order.should be_a_kind_of(Array) boot_order.first.should be_a_kind_of(String) diff --git a/spec/support/remove_classes.rb b/spec/support/remove_classes.rb index 8f8ed5c..b270a05 100644 --- a/spec/support/remove_classes.rb +++ b/spec/support/remove_classes.rb @@ -2,10 +2,12 @@ # Removes the classes which were created by the odata_service # def remove_classes(service) + return if service.nil? + service.class_metadata.each_key do |klass| - next unless (String === klass ) - - namespaces = klass.split(/\.|::/) + next unless (String === klass ) + + namespaces = klass.split(/\.|::/) (0..namespaces.count).each do |index| index = namespaces.count-index-1 name = namespaces[index] From 8466105a37d352ff1db7202a16392ff615db9013 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 7 Sep 2017 22:48:54 +0200 Subject: [PATCH 10/28] rename @cat_prod_service to @service --- spec/revised_service_spec.rb | 48 ++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/spec/revised_service_spec.rb b/spec/revised_service_spec.rb index 7277d27..470b1c7 100644 --- a/spec/revised_service_spec.rb +++ b/spec/revised_service_spec.rb @@ -4,32 +4,36 @@ module OData describe Service do - before(:all) do + before(:each) do stub_request(:get, /http:\/\/test\.com\/test\.svc\/\$metadata(?:\?.+)?/). with(:headers => DEFAULT_HEADERS). to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/edmx_categories_products.xml", __FILE__)), :headers => {}) - @cat_prod_service = OData::Service.new "http://test.com/test.svc" + @service = OData::Service.new "http://test.com/test.svc" end - subject { @cat_prod_service } + after(:each) do remove_classes @service end + subject { @service } + context "methods" do + it { should respond_to :collections } + it { should respond_to :class_metadata } + it { should respond_to :function_imports } + it { should respond_to :classes } + + it { should respond_to :execute } + it { should respond_to :update_object } it { should respond_to :delete_object } it { should respond_to :save_changes } it { should respond_to :load_property } it { should respond_to :add_link } - it { should respond_to :execute } it { should respond_to :partial? } it { should respond_to :next } - it { should respond_to :classes } - it { should respond_to :class_metadata } - it { should respond_to :collections } it { should respond_to :options } - it { should respond_to :function_imports } context "after parsing metadata" do it { should respond_to :Products } @@ -38,8 +42,9 @@ module OData it { should respond_to :AddToCategories } end end + context "collections method" do - subject { @cat_prod_service.collections } + subject { @service.collections } it { should include 'Products' } it { should include 'Categories' } it "should expose the edmx type of objects" do @@ -51,14 +56,15 @@ module OData subject['Categories'][:type].should eq Category end end + context "class metadata" do - subject { @cat_prod_service.class_metadata } + subject { @service.class_metadata } it { should_not be_empty} it { should have_key 'Product' } it { should have_key 'Category' } context "should have keys for each property" do - subject { @cat_prod_service.class_metadata['Category'] } + subject { @service.class_metadata['Category'] } it { should have_key 'Id' } it { should have_key 'Name' } it { should have_key 'Products' } @@ -100,8 +106,9 @@ module OData end end end + context "function_imports method" do - subject { @cat_prod_service.function_imports } + subject { @service.function_imports } it { should_not be_empty} it { should have_key 'CleanDatabaseForTesting' } it { should have_key 'EntityCategoryWebGet' } @@ -126,14 +133,14 @@ module OData subject['EntitySingleCategoryWebGet'][:parameters]['id'].should eq 'Edm.Int32' end context "after parsing function imports" do - subject { @cat_prod_service } + subject { @service } it { should respond_to :CleanDatabaseForTesting } it { should respond_to :EntityCategoryWebGet } it { should respond_to :EntitySingleCategoryWebGet } it { should respond_to :CategoryNames } end context "error checking" do - subject { @cat_prod_service } + subject { @service } it "should throw an exception if a parameter is passed in to a method that doesn't require one" do lambda { subject.EntityCategoryWebGet(1) }.should raise_error(ArgumentError, "wrong number of arguments (1 for 0)") end @@ -142,7 +149,7 @@ module OData end end context "url and http method checks" do - subject { @cat_prod_service } + subject { @service } before { stub_request(:any, /http:\/\/test\.com\/test\.svc\/(.*)/) } it "should call the correct url with the correct http method for a post with no parameters" do subject.CleanDatabaseForTesting @@ -158,7 +165,7 @@ module OData end end context "function import result parsing" do - subject { @cat_prod_service } + subject { @service } before(:each) do stub_request(:post, "http://test.com/test.svc/CleanDatabaseForTesting").to_return(:status => 204) @@ -206,7 +213,7 @@ module OData describe "Dual Namespaces" do - before(:all) do + before(:each) do auth_string = "xxxx\\yyyy:zzzz" authorization_header = { authorization: "Basic #{Base64::encode64(auth_string).strip}" } stub_request(:get, "http://test.com/test.svc/$metadata"). @@ -225,7 +232,7 @@ module OData end describe "Dual Services" do - before(:all) do + before(:each) do stub_request(:get, "http://service1.com/test.svc/$metadata"). with(:headers => DEFAULT_HEADERS). to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/edmx_categories_products.xml", __FILE__)), :headers => {}) @@ -239,6 +246,11 @@ module OData @service2 = OData::Service.new "http://service2.com/test.svc" end + after(:each) do + remove_classes @service1 + remove_classes @service2 + end + it "should use the correct service uri" do expect(@service1.class_metadata[:uri]).to eq 'http://service1.com/test.svc' expect(@service2.class_metadata[:uri]).to eq 'http://service2.com/test.svc' From 78dc5f090f803269b7fb093d1f804ec8302e8882 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 7 Sep 2017 22:54:57 +0200 Subject: [PATCH 11/28] rename svc to @service in service_spec --- spec/service_spec.rb | 299 +++++++++++++++++++++++-------------------- 1 file changed, 159 insertions(+), 140 deletions(-) diff --git a/spec/service_spec.rb b/spec/service_spec.rb index a750372..50c9b29 100644 --- a/spec/service_spec.rb +++ b/spec/service_spec.rb @@ -2,6 +2,11 @@ module OData describe Service do + + after(:each) do + remove_classes @service + end + describe "#initialize" do it "truncates passed in end slash from uri when making the request" do # Required for the build_classes method @@ -9,7 +14,7 @@ module OData with(:headers => DEFAULT_HEADERS). to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/edmx_empty.xml", __FILE__)), :headers => {}) - svc = OData::Service.new "http://test.com/test.svc/" + @service =OData::Service.new "http://test.com/test.svc/" end it "doesn't error with lowercase entities" do # Required for the build_classes method @@ -17,7 +22,7 @@ module OData with(:headers => DEFAULT_HEADERS). to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/edmx_lowercase.xml", __FILE__)), :headers => {}) - lambda { OData::Service.new "http://test.com/test.svc" }.should_not raise_error + expect { @service = OData::Service.new "http://test.com/test.svc" }.not_to raise_error end describe "additional query string parameters" do @@ -28,11 +33,11 @@ module OData to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/edmx_empty.xml", __FILE__)), :headers => {}) end it "should accept additional query string parameters" do - svc = OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } - svc.options[:additional_params].should eq Hash[:x=>1, :y=>2] + @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } + @service.options[:additional_params].should eq Hash[:x=>1, :y=>2] end it "should call the correct metadata uri when additional_params are passed in" do - svc = OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x => 1, :y => 2 } } + @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x => 1, :y => 2 } } a_request(:get, "http://test.com/test.svc/$metadata?x=1&y=2").should have_been_made end end @@ -45,15 +50,16 @@ module OData to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/edmx_empty.xml", __FILE__)), :headers => {}) end it "should accept in options that will be passed to the rest-client lib" do - svc = OData::Service.new "http://test.com/test.svc/", { :rest_options => { :ssl_ca_file => "ca_certificate.pem" } } - svc.options[:rest_options].should eq Hash[:ssl_ca_file => "ca_certificate.pem"] + @service =OData::Service.new "http://test.com/test.svc/", { :rest_options => { :ssl_ca_file => "ca_certificate.pem" } } + @service.options[:rest_options].should eq Hash[:ssl_ca_file => "ca_certificate.pem"] end it "should merge the rest options with the built in options" do - svc = OData::Service.new "http://test.com/test.svc/", { :rest_options => { :ssl_ca_file => "ca_certificate.pem" } } - svc.instance_variable_get(:@rest_options).should eq Hash[:verify_ssl => 1, :user => nil, :password => nil, :ssl_ca_file => "ca_certificate.pem"] + @service =OData::Service.new "http://test.com/test.svc/", { :rest_options => { :ssl_ca_file => "ca_certificate.pem" } } + @service.instance_variable_get(:@rest_options).should eq Hash[:verify_ssl => 1, :user => nil, :password => nil, :ssl_ca_file => "ca_certificate.pem"] end end end + describe "additional query string parameters" do before(:each) do # Required for the build_classes method @@ -62,61 +68,61 @@ module OData to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sap/edmx_sap_demo_flight.xml", __FILE__)), :headers => {}) end it "should pass the parameters as part of a query" do - svc = OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } - svc.flight_dataCollection - svc.execute + @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } + @service.flight_dataCollection + @service.execute a_request(:get, "http://test.com/test.svc/flight_dataCollection?x=1&y=2").should have_been_made end it "should pass the parameters as part of a save" do - svc = OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } + @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } new_flight = ZDemoFlight.new - svc.AddToflight_dataCollection(new_flight) - svc.save_changes + @service.AddToflight_dataCollection(new_flight) + @service.save_changes a_request(:post, "http://test.com/test.svc/flight_dataCollection?x=1&y=2").should have_been_made end it "should pass the parameters as part of an update" do - svc = OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } + @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } existing_flight = ZDemoFlight.new existing_flight.__metadata = { :uri => "http://test.com/test.svc/flight_dataCollection/1" } - svc.update_object(existing_flight) - svc.save_changes + @service.update_object(existing_flight) + @service.save_changes a_request(:put, "http://test.com/test.svc/flight_dataCollection/1?x=1&y=2").should have_been_made end it "should pass the parameters as part of a delete" do - svc = OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } + @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } existing_flight = ZDemoFlight.new existing_flight.__metadata = { :uri => "http://test.com/test.svc/flight_dataCollection/1" } - svc.delete_object(existing_flight) - svc.save_changes + @service.delete_object(existing_flight) + @service.save_changes a_request(:delete, "http://test.com/test.svc/flight_dataCollection/1?x=1&y=2").should have_been_made end it "should pass the parameters as part of a batch save" do - svc = OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } + @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } new_flight = ZDemoFlight.new - svc.AddToflight_dataCollection(new_flight) + @service.AddToflight_dataCollection(new_flight) new_flight2 = ZDemoFlight.new - svc.AddToflight_dataCollection(new_flight2) - svc.save_changes + @service.AddToflight_dataCollection(new_flight2) + @service.save_changes a_request(:post, "http://test.com/test.svc/$batch?x=1&y=2").should have_been_made end it "should pass the parameters as part of an add link" do - svc = OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } + @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } existing_flight1 = ZDemoFlight.new existing_flight1.__metadata = { :uri => "http://test.com/test.svc/flight_dataCollection/1" } existing_flight2 = ZDemoFlight.new existing_flight2.__metadata = { :uri => "http://test.com/test.svc/flight_dataCollection/2" } - svc.add_link(existing_flight1, "flight_data_r", existing_flight2) - svc.save_changes + @service.add_link(existing_flight1, "flight_data_r", existing_flight2) + @service.save_changes a_request(:post, "http://test.com/test.svc/flight_dataCollection/1/$links/flight_data_r?x=1&y=2").should have_been_made end it "should pass the parameters as part of a function import with a parameter" do - svc = OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } - svc.get_flight(1) + @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } + @service.get_flight(1) a_request(:get, "http://test.com/test.svc/get_flight?id=1&x=1&y=2").should have_been_made end it "should pass the parameters as part of a function import without parameters" do - svc = OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } - svc.get_top_flight + @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } + @service.get_top_flight a_request(:get, "http://test.com/test.svc/get_top_flight?x=1&y=2").should have_been_made end end @@ -133,9 +139,9 @@ module OData end it "includes a generic message if the error is not in the response" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.Categories.select "Price" - expect { svc.execute }.to raise_error(OData::ServiceError) { |error| + @service =OData::Service.new "http://test.com/test.svc/" + @service.Categories.select "Price" + expect { @service.execute }.to raise_error(OData::ServiceError) { |error| error.http_code.should eq 400 error.message.should eq "Server returned error but no message." } @@ -151,13 +157,13 @@ module OData end it "should respond_to a lowercase collection" do - svc = OData::Service.new "http://test.com/test.svc" - expect(svc.respond_to?('acronyms')).to eq true + @service =OData::Service.new "http://test.com/test.svc" + expect(@service.respond_to?('acronyms')).to eq true end it "should allow a lowercase collections to be queried" do - svc = OData::Service.new "http://test.com/test.svc" - lambda { svc.send('acronyms') }.should_not raise_error + @service =OData::Service.new "http://test.com/test.svc" + lambda { @service.send('acronyms') }.should_not raise_error end end @@ -179,31 +185,28 @@ module OData with(:headers => DEFAULT_HEADERS). to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/feed_customization/result_feed_customization_categories_expand.xml", __FILE__)), :headers => {}) end - after(:each) do - Object.send(:remove_const, 'Product') - Object.send(:remove_const, 'Category') - end + describe "handling feed customizations" do describe "property metadata" do it "should fill the class_metadata hash" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.class_metadata.should_not be_empty + @service =OData::Service.new "http://test.com/test.svc/" + @service.class_metadata.should_not be_empty end it "should add a key (based on the name) for each property class_metadata hash" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.class_metadata['Product'].should have_key 'ID' - svc.class_metadata['Product'].should have_key 'Name' - svc.class_metadata['Product'].should have_key 'Description' + @service =OData::Service.new "http://test.com/test.svc/" + @service.class_metadata['Product'].should have_key 'ID' + @service.class_metadata['Product'].should have_key 'Name' + @service.class_metadata['Product'].should have_key 'Description' end it "should have a PropertyMetadata object for each property class_metadata hash" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.class_metadata['Product']['ID'].should be_a OData::PropertyMetadata - svc.class_metadata['Product']['Name'].should be_a OData::PropertyMetadata - svc.class_metadata['Product']['Description'].should be_a OData::PropertyMetadata + @service =OData::Service.new "http://test.com/test.svc/" + @service.class_metadata['Product']['ID'].should be_a OData::PropertyMetadata + @service.class_metadata['Product']['Name'].should be_a OData::PropertyMetadata + @service.class_metadata['Product']['Description'].should be_a OData::PropertyMetadata end it "should have the correct PropertyMetadata object for Id" do - svc = OData::Service.new "http://test.com/test.svc/" - meta = svc.class_metadata['Product']['ID'] + @service =OData::Service.new "http://test.com/test.svc/" + meta = @service.class_metadata['Product']['ID'] meta.name.should eq 'ID' meta.type.should eq 'Edm.Int32' meta.nullable.should eq false @@ -211,8 +214,8 @@ module OData meta.fc_keep_in_content.should be_nil end it "should have the correct PropertyMetadata object for Name" do - svc = OData::Service.new "http://test.com/test.svc/" - meta = svc.class_metadata['Product']['Name'] + @service =OData::Service.new "http://test.com/test.svc/" + meta = @service.class_metadata['Product']['Name'] meta.name.should eq 'Name' meta.type.should eq 'Edm.String' meta.nullable.should eq true @@ -220,8 +223,8 @@ module OData meta.fc_keep_in_content.should eq false end it "should have the correct PropertyMetadata object for Description" do - svc = OData::Service.new "http://test.com/test.svc/" - meta = svc.class_metadata['Product']['Description'] + @service =OData::Service.new "http://test.com/test.svc/" + meta = @service.class_metadata['Product']['Description'] meta.name.should eq 'Description' meta.type.should eq 'Edm.String' meta.nullable.should eq true @@ -232,24 +235,24 @@ module OData describe "single class" do it "should handle properties where a property is represented in the syndication title instead of the properties collection" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.Products - results = svc.execute + @service =OData::Service.new "http://test.com/test.svc/" + @service.Products + results = @service.execute results.first.Name.should eq "Bread" end it "should handle properties where a property is represented in the syndication summary instead of the properties collection" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.Products - results = svc.execute + @service =OData::Service.new "http://test.com/test.svc/" + @service.Products + results = @service.execute results.first.Description.should eq "Whole grain bread" end end describe "expanded inline class" do it "should handle properties where a property is represented in the syndication title instead of the properties collection" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.Categories - results = svc.execute + @service =OData::Service.new "http://test.com/test.svc/" + @service.Categories + results = @service.execute beverages = results[1] @@ -266,18 +269,18 @@ module OData describe "handling inline collections/properties" do it "should make plural named properties arrays and not a single class" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.Categories - results = svc.execute + @service =OData::Service.new "http://test.com/test.svc/" + @service.Categories + results = @service.execute food = results[0] food.Products.should be_an Array end it "should not make an array if the navigation property name is singular" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.Products - results = svc.execute + @service =OData::Service.new "http://test.com/test.svc/" + @service.Products + results = @service.execute product = results.first product.Category.should_not be_an Array end @@ -285,8 +288,8 @@ module OData describe "navigation properties" do it "should fill in PropertyMetadata for navigation properties" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.class_metadata['Product'].should have_key 'Category' + @service =OData::Service.new "http://test.com/test.svc/" + @service.class_metadata['Product'].should have_key 'Category' end end end @@ -305,7 +308,7 @@ module OData end it "should build all inherited attributes" do - OData::Service.new "http://test.com/test.svc/" + @service = OData::Service.new "http://test.com/test.svc/" methods = Course.instance_methods.reject {|m| Object.methods.index(m)} # Ruby 1.9 uses symbols, and 1.8 uses strings, so this normalizes the data @@ -323,14 +326,14 @@ module OData end it "should not build abstract classes" do - OData::Service.new "http://test.com/test.svc/" + @service = OData::Service.new "http://test.com/test.svc/" defined?(ModelItemBase).should eq nil end it "should fill inherited properties" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.Courses - courses = svc.execute + @service =OData::Service.new "http://test.com/test.svc/" + @service.Courses + courses = @service.execute course = courses.first course.Title.should_not be_nil course.Description.should_not be_nil @@ -362,20 +365,20 @@ module OData end it "should return the whole collection by default" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.Partials - results = svc.execute + @service =OData::Service.new "http://test.com/test.svc/" + @service.Partials + results = @service.execute results.count.should eq 3 end it "should return only the partial when specified by options" do - svc = OData::Service.new("http://test.com/test.svc/", :eager_partial => false) - svc.Partials - results = svc.execute + @service =OData::Service.new("http://test.com/test.svc/", :eager_partial => false) + @service.Partials + results = @service.execute results.count.should eq 1 - svc.should be_partial - while svc.partial? - results.concat svc.next + @service.should be_partial + while @service.partial? + results.concat @service.next end results.count.should eq 3 end @@ -396,10 +399,10 @@ module OData end it "should persist the additional parameters for the next call" do - svc = OData::Service.new("http://test.com/test.svc/", :eager_partial => false, :additional_params => { :extra_param => 'value' }) - svc.Partials - svc.execute - svc.next + @service =OData::Service.new("http://test.com/test.svc/", :eager_partial => false, :additional_params => { :extra_param => 'value' }) + @service.Partials + @service.execute + @service.next a_request(:get, "http://test.com/test.svc/Partials?$skiptoken='ERNSH'&extra_param=value").should have_been_made end @@ -418,9 +421,9 @@ module OData to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/links/result_links_query.xml", __FILE__)), :headers => {}) end it "should be able to parse the results of a links query" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.Categories(1).links('Products') - results = svc.execute + @service =OData::Service.new "http://test.com/test.svc/" + @service.Categories(1).links('Products') + results = @service.execute results.count.should eq 3 results.first.should be_a_kind_of(URI) results[0].path.should eq "/SampleService/RubyOData.svc/Products(1)" @@ -442,38 +445,41 @@ module OData with(:headers => DEFAULT_HEADERS). to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/nested_expands/northwind_products_category_expands.xml", __FILE__)), :headers => {}) end + after(:each) do + #Object.send(:remove_const, 'Product') if Object.const_defined? 'Product' + end it "should successfully parse the results" do - svc = OData::Service.new "http://test.com/test.svc", { :namespace => "NW" } - svc.Products.expand('Category').expand('Category/Products').top(2) - lambda { svc.execute }.should_not raise_exception + @service =OData::Service.new "http://test.com/test.svc", { :namespace => "NW" } + @service.Products.expand('Category').expand('Category/Products').top(2) + lambda { @service.execute }.should_not raise_exception end it "should successfully parse a Category as a Category" do - svc = OData::Service.new "http://test.com/test.svc", { :namespace => "NW" } - svc.Products.expand('Category').expand('Category/Products').top(2) - products = svc.execute + @service =OData::Service.new "http://test.com/test.svc", { :namespace => "NW" } + @service.Products.expand('Category').expand('Category/Products').top(2) + products = @service.execute products.first.Category.should be_a_kind_of(NW::Category) end it "should successfully parse the Category properties" do - svc = OData::Service.new "http://test.com/test.svc", { :namespace => "NW" } - svc.Products.expand('Category').expand('Category/Products').top(2) - products = svc.execute + @service =OData::Service.new "http://test.com/test.svc", { :namespace => "NW" } + @service.Products.expand('Category').expand('Category/Products').top(2) + products = @service.execute products.first.Category.CategoryID.should eq 1 end it "should successfully parse the Category children Products" do - svc = OData::Service.new "http://test.com/test.svc", { :namespace => "NW" } - svc.Products.expand('Category').expand('Category/Products').top(2) - products = svc.execute + @service =OData::Service.new "http://test.com/test.svc", { :namespace => "NW" } + @service.Products.expand('Category').expand('Category/Products').top(2) + products = @service.execute products.first.Category.Products.length.should eq 12 end it "should successfully parse the Category's child Product properties" do - svc = OData::Service.new "http://test.com/test.svc", { :namespace => "NW" } - svc.Products.expand('Category').expand('Category/Products').top(2) - products = svc.execute + @service =OData::Service.new "http://test.com/test.svc", { :namespace => "NW" } + @service.Products.expand('Category').expand('Category/Products').top(2) + products = @service.execute products.first.Category.Products.first.ProductName.should eq "Chai" end end @@ -484,70 +490,83 @@ module OData before(:each) do stub_request(:get, "http://test.com/test.svc/$metadata"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/edmx_categories_products.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => File.new( FIXTURES + "/sample_service/edmx_categories_products.xml"), :headers => {}) stub_request(:get, "http://test.com/test.svc/Products?$select=Name,Price"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_select_products_name_price.xml", __FILE__)), :headers => {}) - end + to_return(:status => 200, :body => File.new( FIXTURES + "/sample_service/result_select_products_name_price.xml"), :headers => {}) + end - subject do - svc = OData::Service.new "http://test.com/test.svc/" - svc.Products.select "Name", "Price" - svc.execute + before(:each) do + @service =OData::Service.new "http://test.com/test.svc/" + @service.Products.select "Name", "Price" + @result = @service.execute end - it { should be_an Array } - it { should_not be_empty } - its(:first) { should be_a Product } + it "returns an Array og Products" do + expect(@result).to be_an Array + expect(@result).not_to be_empty + expect(@result.first).to be_a Product + end end context "when there isn't a property by the name specified" do before(:each) do stub_request(:get, "http://test.com/test.svc/$metadata"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/edmx_categories_products.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => File.new( FIXTURES + "/sample_service/edmx_categories_products.xml"), :headers => {}) stub_request(:get, "http://test.com/test.svc/Categories?$select=Price"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 400, :body => File.new(File.expand_path("../fixtures/sample_service/result_select_categories_no_property.xml", __FILE__)), :headers => {}) + to_return(:status => 400, :body => File.new( FIXTURES + "/sample_service/result_select_categories_no_property.xml"), :headers => {}) end it "raises an exception" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.Categories.select "Price" - expect { svc.execute }.to raise_error(OData::ServiceError) { |error| + @service =OData::Service.new "http://test.com/test.svc/" + @service.Categories.select "Price" + expect { @service.execute }.to raise_error(OData::ServiceError) { |error| error.http_code.should eq 400 error.message.should eq "Type 'RubyODataService.Category' does not have a property named 'Price' or there is no type with 'Price' name." } end end - context "when a property requires $expand to traverse" do + context "when a property requires $expand to traverse", focus: true do before(:each) do stub_request(:get, "http://test.com/test.svc/$metadata"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/edmx_categories_products.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => File.new( FIXTURES + "/sample_service/edmx_categories_products.xml"), :headers => {}) stub_request(:get, "http://test.com/test.svc/Categories?$select=Name,Products/Name"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 400, :body => File.new(File.expand_path("../fixtures/sample_service/result_select_categories_travsing_no_expand.xml", __FILE__)), :headers => {}) + to_return(:status => 400, :body => File.new( FIXTURES + "/sample_service/result_select_categories_travsing_no_expand.xml"), :headers => {}) stub_request(:get, "http://test.com/test.svc/Categories?$select=Name,Products/Name&$expand=Products"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_select_categories_expand.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => File.new( FIXTURES + "/sample_service/result_select_categories_expand.xml"), :headers => {}) + + stub_request(:get, "http://test.com/test.svc/Categories"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new( FIXTURES + "/sample_service/result_select_categories_expand.xml"), :headers => {}) + + end + + it "retursn Categoris" do + @service =OData::Service.new "http://test.com/test.svc/" + @service.Categories + c = @service.execute end it "doesn't error" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.Categories.select "Name", "Products/Name" - expect { svc.execute }.to_not raise_error + @service =OData::Service.new "http://test.com/test.svc/" + @service.Categories.select "Name", "Products/Name" + expect { @service.execute }.to_not raise_error end it "returns the classes with the properties filled in" do - svc = OData::Service.new "http://test.com/test.svc/" - svc.Categories.select "Name", "Products/Name" - results = svc.execute + @service =OData::Service.new "http://test.com/test.svc/" + @service.Categories.select "Name", "Products/Name" + results = @service.execute category = results.first category.Name.should eq "Category 0001" product = category.Products.first @@ -563,14 +582,14 @@ module OData # Required for the build_classes method stub_request(:get, "http://test.com/test.svc/$metadata"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/edmx_empty.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => File.new( FIXTURES + "/edmx_empty.xml" ), :headers => {}) end it "should not error on an 'out of range' date" do # This date was returned in the Netflix OData service and failed with an ArgumentError: out of range using 1.8.7 (2010-12-23 patchlevel 330) [i386-mingw32] - svc = OData::Service.new "http://test.com/test.svc/" + @service =OData::Service.new "http://test.com/test.svc/" element_to_parse = Nokogiri::XML.parse('2100-01-01T00:00:00').elements[0] - lambda { svc.parse_value_xml(element_to_parse) }.should_not raise_exception + lambda { @service.parse_value_xml(element_to_parse) }.should_not raise_exception end end end From 0520f49f8339ad89352230ccb15b3c7a1ffd2d11 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 7 Sep 2017 22:55:56 +0200 Subject: [PATCH 12/28] define constant to path for Fixtures --- spec/spec_helper.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 8b54526..8219ff2 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,12 +2,16 @@ require 'webmock/rspec' require 'simplecov' require 'rspec/its' + # require 'coveralls' # Coveralls.wear_merged! Dir[File.expand_path('../support/**/*.rb', __FILE__)].each { |f| require f } +FIXTURES = File.expand_path('../fixtures', __FILE__) + WebMock.disable_net_connect!(allow_localhost: true) + DEFAULT_HEADERS = { 'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip,deflate' From 356b30d12c700ee78b6958c841d488d82f8bb8ac Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 7 Sep 2017 22:57:22 +0200 Subject: [PATCH 13/28] create json_serialization_spec --- spec/internal/json_serialization_spec.rb | 42 ++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 spec/internal/json_serialization_spec.rb diff --git a/spec/internal/json_serialization_spec.rb b/spec/internal/json_serialization_spec.rb new file mode 100644 index 0000000..c8f1760 --- /dev/null +++ b/spec/internal/json_serialization_spec.rb @@ -0,0 +1,42 @@ +require 'spec_helper' + +module OData + describe "JSON serialization of objects" do + let(:username) { "blabla" } + let(:password) { "" } + + before(:each) do + # Required for the build_classes method + stub_request(:get, "http://test.com/test.svc/$metadata"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new( FIXTURES + "/ms_system_center/edmx_ms_system_center.xml"), :headers => {}) + + stub_request(:get, "http://test.com/test.svc/VirtualMachines"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new( FIXTURES + "/ms_system_center/virtual_machines.xml"), :headers => {}) + @service = OData::Service.new "http://test.com/test.svc/", { :username => username, :password => password, :verify_ssl => false, :namespace => "VMM" } + @service.VirtualMachines + results = @service.execute + @json = results.first.as_json + end + + after(:each) do + remove_classes @service + end + + it "Should quote Edm.Int64 properties" do + @json["PerfDiskBytesWrite"].should be_a(String) + end + + it "Should output collections with metadata" do + @json["VMNetworkAssignments"].should be_a(Hash) + @json["VMNetworkAssignments"].should have_key("__metadata") + @json["VMNetworkAssignments"]["__metadata"].should be_a(Hash) + @json["VMNetworkAssignments"]["__metadata"].should have_key("type") + @json["VMNetworkAssignments"]["__metadata"]["type"].should eq("Collection(VMM.VMNetworkAssignment)") + @json["VMNetworkAssignments"].should have_key("results") + @json["VMNetworkAssignments"]["results"].should be_a(Array) + @json["VMNetworkAssignments"]["results"].should eq([]) + end + end +end \ No newline at end of file From b23f5edc12d040b99596e53c84d829941a74ced1 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 7 Sep 2017 22:58:04 +0200 Subject: [PATCH 14/28] remove faraday from Gemfile --- Gemfile | 2 -- 1 file changed, 2 deletions(-) diff --git a/Gemfile b/Gemfile index f1f2238..f005a1f 100644 --- a/Gemfile +++ b/Gemfile @@ -2,5 +2,3 @@ source "http://rubygems.org" # Specify your gem's dependencies in ruby_odata.gemspec gemspec - -gem 'faraday_middleware', github: 'lostisland/faraday_middleware' From 87180b005afe064fae6851eee3266c3d080bb8f8 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 7 Sep 2017 22:58:46 +0200 Subject: [PATCH 15/28] use latest version from faraday in gemspec --- ruby_odata.gemspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ruby_odata.gemspec b/ruby_odata.gemspec index 96ad47a..ab39c63 100644 --- a/ruby_odata.gemspec +++ b/ruby_odata.gemspec @@ -19,10 +19,10 @@ Gem::Specification.new do |s| s.add_dependency("addressable", ">= 2.3.4") s.add_dependency("i18n", ">= 0.7.0") - s.add_dependency("activesupport", ">= 3.0.0") + s.add_dependency("activesupport", "~> 3.0.0") s.add_dependency("excon", "~> 0.45.3") + s.add_dependency("faraday") s.add_dependency("faraday_middleware") - s.add_dependency("faraday", "~> 0.9.1") s.add_dependency("nokogiri", ">= 1.4.2") s.add_development_dependency("rake", ">= 12.0.0") From 0a88a7a95064ec3d7a1c5c30836b216665de5d25 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 7 Sep 2017 22:59:10 +0200 Subject: [PATCH 16/28] use latest version from webmock in gemspec --- ruby_odata.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby_odata.gemspec b/ruby_odata.gemspec index ab39c63..a186355 100644 --- a/ruby_odata.gemspec +++ b/ruby_odata.gemspec @@ -31,7 +31,7 @@ Gem::Specification.new do |s| s.add_development_dependency("cucumber", "~> 2.0.0") s.add_development_dependency("pickle", "~> 0.5.1") s.add_development_dependency("machinist", "~> 2.0") - s.add_development_dependency("webmock", "~> 1.21.0") + s.add_development_dependency("webmock") s.add_development_dependency("guard", "~> 2.12.5") s.add_development_dependency("guard-rspec", "~> 4.5.0") s.add_development_dependency("guard-cucumber", "~> 1.6.0") From e913dae3a5428b327323a0de06df9b1c38aa0bb9 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 7 Sep 2017 22:59:40 +0200 Subject: [PATCH 17/28] rename @svc to @service --- spec/internal/association_spec.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/spec/internal/association_spec.rb b/spec/internal/association_spec.rb index 0ad3c24..a075a0f 100644 --- a/spec/internal/association_spec.rb +++ b/spec/internal/association_spec.rb @@ -2,20 +2,22 @@ module OData describe Association do + before(:all) do stub_request(:get, /http:\/\/test\.com\/test\.svc\/\$metadata(?:\?.+)?/). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/edmx_categories_products.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => File.new( FIXTURES + "/sample_service/edmx_categories_products.xml"), :headers => {}) - @svc = OData::Service.new "http://test.com/test.svc/$metadata" + @service = OData::Service.new "http://test.com/test.svc/$metadata" @product_category = RSpecSupport::ElementHelpers.string_to_element('') end - describe "#initialize singlular navigation property" do - before { @association = Association.new @product_category, @svc.edmx } after(:all) do remove_classes @service end + + describe "#initialize singular navigation property" do + before { @association = Association.new @product_category, @service.edmx } subject { @association } it "sets the association name" do @@ -53,4 +55,4 @@ module OData end end end -end \ No newline at end of file +end From 6564668d4e6bde19849902321ad7a0a5b49928a7 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 7 Sep 2017 23:00:21 +0200 Subject: [PATCH 18/28] simplify url in spec --- spec/service_v4_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/service_v4_spec.rb b/spec/service_v4_spec.rb index dc3159c..ffe2eb7 100644 --- a/spec/service_v4_spec.rb +++ b/spec/service_v4_spec.rb @@ -2,7 +2,7 @@ describe "V4 Service" do before(:all) do - stub_request(:get, /http:\/\/test\.com\/test\.svc\/\$metadata(?:\?.+)?/). + stub_request(:get, "http://test.com/test.svc/$metadata"). with(:headers => DEFAULT_HEADERS). to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/v4/edmx_metadata.xml", __FILE__)), :headers => {}) @@ -104,4 +104,4 @@ end end end -end \ No newline at end of file +end From 3deca9fc2a56fd1bda58501d98d5becc895e6d8c Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 7 Sep 2017 23:44:28 +0200 Subject: [PATCH 19/28] Rename specs --- spec/revised_service_spec.rb | 259 ------------ spec/service_detailed_spec.rb | 596 +++++++++++++++++++++++++++ spec/service_spec.rb | 751 ++++++++++------------------------ spec/service_v4_spec.rb | 185 +++++---- 4 files changed, 897 insertions(+), 894 deletions(-) delete mode 100644 spec/revised_service_spec.rb create mode 100644 spec/service_detailed_spec.rb diff --git a/spec/revised_service_spec.rb b/spec/revised_service_spec.rb deleted file mode 100644 index 470b1c7..0000000 --- a/spec/revised_service_spec.rb +++ /dev/null @@ -1,259 +0,0 @@ -require 'spec_helper' -require 'base64' - -module OData - - describe Service do - before(:each) do - stub_request(:get, /http:\/\/test\.com\/test\.svc\/\$metadata(?:\?.+)?/). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/edmx_categories_products.xml", __FILE__)), :headers => {}) - - @service = OData::Service.new "http://test.com/test.svc" - end - - after(:each) do - remove_classes @service - end - - subject { @service } - - context "methods" do - it { should respond_to :collections } - it { should respond_to :class_metadata } - it { should respond_to :function_imports } - it { should respond_to :classes } - - it { should respond_to :execute } - - it { should respond_to :update_object } - it { should respond_to :delete_object } - it { should respond_to :save_changes } - it { should respond_to :load_property } - it { should respond_to :add_link } - it { should respond_to :partial? } - it { should respond_to :next } - it { should respond_to :options } - - context "after parsing metadata" do - it { should respond_to :Products } - it { should respond_to :Categories } - it { should respond_to :AddToProducts } - it { should respond_to :AddToCategories } - end - end - - context "collections method" do - subject { @service.collections } - it { should include 'Products' } - it { should include 'Categories' } - it "should expose the edmx type of objects" do - subject['Products'][:edmx_type].should eq 'RubyODataService.Product' - subject['Categories'][:edmx_type].should eq 'RubyODataService.Category' - end - it "should expose the local model type" do - subject['Products'][:type].should eq Product - subject['Categories'][:type].should eq Category - end - end - - context "class metadata" do - subject { @service.class_metadata } - it { should_not be_empty} - it { should have_key 'Product' } - it { should have_key 'Category' } - - context "should have keys for each property" do - subject { @service.class_metadata['Category'] } - it { should have_key 'Id' } - it { should have_key 'Name' } - it { should have_key 'Products' } - it "should return a PropertyMetadata object for each property" do - subject['Id'].should be_a PropertyMetadata - subject['Name'].should be_a PropertyMetadata - subject['Products'].should be_a PropertyMetadata - end - it "should have correct PropertyMetadata for Category.Id" do - meta = subject['Id'] - meta.name.should eq 'Id' - meta.type.should eq 'Edm.Int32' - meta.nullable.should eq false - meta.fc_target_path.should be_nil - meta.fc_keep_in_content.should be_nil - meta.nav_prop.should eq false - meta.is_key.should eq true - end - it "should have correct PropertyMetadata for Category.Name" do - meta = subject['Name'] - meta.name.should eq 'Name' - meta.type.should eq 'Edm.String' - meta.nullable.should eq false - meta.fc_target_path.should be_nil - meta.fc_keep_in_content.should be_nil - meta.nav_prop.should eq false - meta.is_key.should eq false - end - it "should have correct PropertyMetadata for Category.Products" do - meta = subject['Products'] - meta.name.should eq 'Products' - meta.type.should be_nil - meta.nullable.should eq true - meta.fc_target_path.should be_nil - meta.fc_keep_in_content.should be_nil - meta.nav_prop.should eq true - meta.association.should_not be_nil - meta.is_key.should eq false - end - end - end - - context "function_imports method" do - subject { @service.function_imports } - it { should_not be_empty} - it { should have_key 'CleanDatabaseForTesting' } - it { should have_key 'EntityCategoryWebGet' } - it { should have_key 'EntitySingleCategoryWebGet' } - it "should expose the http method" do - subject['CleanDatabaseForTesting'][:http_method].should eq 'POST' - subject['EntityCategoryWebGet'][:http_method].should eq 'GET' - subject['EntitySingleCategoryWebGet'][:http_method].should eq 'GET' - end - it "should expose the return type" do - subject['CleanDatabaseForTesting'][:return_typo].should be_nil - subject['EntityCategoryWebGet'][:return_type].should eq Array - subject['EntityCategoryWebGet'][:inner_return_type].should eq Category - subject['EntitySingleCategoryWebGet'][:return_type].should eq Category - subject['EntitySingleCategoryWebGet'][:inner_return_type].should be_nil - subject['CategoryNames'][:return_type].should eq Array - subject['CategoryNames'][:inner_return_type].should eq String - end - it "should provide a hash of parameters" do - subject['EntityCategoryWebGet'][:parameters].should be_nil - subject['EntitySingleCategoryWebGet'][:parameters].should be_a Hash - subject['EntitySingleCategoryWebGet'][:parameters]['id'].should eq 'Edm.Int32' - end - context "after parsing function imports" do - subject { @service } - it { should respond_to :CleanDatabaseForTesting } - it { should respond_to :EntityCategoryWebGet } - it { should respond_to :EntitySingleCategoryWebGet } - it { should respond_to :CategoryNames } - end - context "error checking" do - subject { @service } - it "should throw an exception if a parameter is passed in to a method that doesn't require one" do - lambda { subject.EntityCategoryWebGet(1) }.should raise_error(ArgumentError, "wrong number of arguments (1 for 0)") - end - it "should throw and exception if more parameters are passed in than required by the function" do - lambda { subject.EntitySingleCategoryWebGet(1,2) }.should raise_error(ArgumentError, "wrong number of arguments (2 for 1)") - end - end - context "url and http method checks" do - subject { @service } - before { stub_request(:any, /http:\/\/test\.com\/test\.svc\/(.*)/) } - it "should call the correct url with the correct http method for a post with no parameters" do - subject.CleanDatabaseForTesting - a_request(:post, "http://test.com/test.svc/CleanDatabaseForTesting").should have_been_made - end - it "should call the correct url with the correct http method for a get with no parameters" do - subject.EntityCategoryWebGet - a_request(:get, "http://test.com/test.svc/EntityCategoryWebGet").should have_been_made - end - it "should call the correct url with the correct http method for a get with parameters" do - subject.EntitySingleCategoryWebGet(1) - a_request(:get, "http://test.com/test.svc/EntitySingleCategoryWebGet?id=1").should have_been_made - end - end - context "function import result parsing" do - subject { @service } - before(:each) do - stub_request(:post, "http://test.com/test.svc/CleanDatabaseForTesting").to_return(:status => 204) - - stub_request(:get, "http://test.com/test.svc/EntityCategoryWebGet"). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_entity_category_web_get.xml", __FILE__)), :headers => {}) - - stub_request(:get, "http://test.com/test.svc/EntitySingleCategoryWebGet?id=1"). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_entity_single_category_web_get.xml", __FILE__)), :headers => {}) - - stub_request(:get, "http://test.com/test.svc/CategoryNames"). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_category_names.xml", __FILE__)), :headers => {}) - - stub_request(:get, "http://test.com/test.svc/FirstCategoryId"). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_first_category_id.xml", __FILE__)), :headers => {}) - end - it "should return true if a function import post that returns successfully and doesn't have a return value (HTTP 204)" do - result = subject.CleanDatabaseForTesting - expect(result).to eq true - end - it "should return a collection of entities for a collection" do - result = subject.EntityCategoryWebGet - result.should be_an Enumerable - result.first.should be_a Category - result.first.Name.should eq "Test Category" - end - it "should return a single entity if it isn't a collection" do - result = subject.EntitySingleCategoryWebGet(1) - result.should be_a Category - result.Name.should eq "Test Category" - end - it "should return a collection of primitive types" do - result = subject.CategoryNames - result.should be_an Enumerable - result.first.should be_a String - result.first.should eq "Test Category 1" - end - it "should return a single primitive type" do - result = subject.FirstCategoryId - result.should be_a Integer - result.should eq 1 - end - end - end - end - - - describe "Dual Namespaces" do - before(:each) do - auth_string = "xxxx\\yyyy:zzzz" - authorization_header = { authorization: "Basic #{Base64::encode64(auth_string).strip}" } - stub_request(:get, "http://test.com/test.svc/$metadata"). - with(:headers => DEFAULT_HEADERS.merge(authorization_header)). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_system_center/edmx_ms_system_center_v2.xml", __FILE__)), :headers => {}) - end - - after(:each) do - remove_classes @service - end - - it "should parse the service without errors" do - lambda { @service = OData::Service.new "http://test.com/test.svc/", { :username => "xxxx\\yyyy", :password=> "zzzz", :verify_ssl => false, :namespace => "VMM" } }.should_not raise_error - end - - end - - describe "Dual Services" do - before(:each) do - stub_request(:get, "http://service1.com/test.svc/$metadata"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/edmx_categories_products.xml", __FILE__)), :headers => {}) - - stub_request(:get, "http://service2.com/test.svc/$metadata"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/int64_ids/edmx_car_service.xml", __FILE__)), :headers => {}) - - - @service1 = OData::Service.new "http://service1.com/test.svc" - @service2 = OData::Service.new "http://service2.com/test.svc" - end - - after(:each) do - remove_classes @service1 - remove_classes @service2 - end - - it "should use the correct service uri" do - expect(@service1.class_metadata[:uri]).to eq 'http://service1.com/test.svc' - expect(@service2.class_metadata[:uri]).to eq 'http://service2.com/test.svc' - end - end -end diff --git a/spec/service_detailed_spec.rb b/spec/service_detailed_spec.rb new file mode 100644 index 0000000..50c9b29 --- /dev/null +++ b/spec/service_detailed_spec.rb @@ -0,0 +1,596 @@ +require 'spec_helper' + +module OData + describe Service do + + after(:each) do + remove_classes @service + end + + describe "#initialize" do + it "truncates passed in end slash from uri when making the request" do + # Required for the build_classes method + stub_request(:get, "http://test.com/test.svc/$metadata"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/edmx_empty.xml", __FILE__)), :headers => {}) + + @service =OData::Service.new "http://test.com/test.svc/" + end + it "doesn't error with lowercase entities" do + # Required for the build_classes method + stub_request(:get, "http://test.com/test.svc/$metadata"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/edmx_lowercase.xml", __FILE__)), :headers => {}) + + expect { @service = OData::Service.new "http://test.com/test.svc" }.not_to raise_error + end + + describe "additional query string parameters" do + before(:each) do + # Required for the build_classes method + stub_request(:get, /http:\/\/test\.com\/test\.svc\/\$metadata(?:\?.+)?/). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/edmx_empty.xml", __FILE__)), :headers => {}) + end + it "should accept additional query string parameters" do + @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } + @service.options[:additional_params].should eq Hash[:x=>1, :y=>2] + end + it "should call the correct metadata uri when additional_params are passed in" do + @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x => 1, :y => 2 } } + a_request(:get, "http://test.com/test.svc/$metadata?x=1&y=2").should have_been_made + end + end + + describe "rest-client options" do + before(:each) do + # Required for the build_classes method + stub_request(:get, /http:\/\/test\.com\/test\.svc\/\$metadata(?:\?.+)?/). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/edmx_empty.xml", __FILE__)), :headers => {}) + end + it "should accept in options that will be passed to the rest-client lib" do + @service =OData::Service.new "http://test.com/test.svc/", { :rest_options => { :ssl_ca_file => "ca_certificate.pem" } } + @service.options[:rest_options].should eq Hash[:ssl_ca_file => "ca_certificate.pem"] + end + it "should merge the rest options with the built in options" do + @service =OData::Service.new "http://test.com/test.svc/", { :rest_options => { :ssl_ca_file => "ca_certificate.pem" } } + @service.instance_variable_get(:@rest_options).should eq Hash[:verify_ssl => 1, :user => nil, :password => nil, :ssl_ca_file => "ca_certificate.pem"] + end + end + end + + describe "additional query string parameters" do + before(:each) do + # Required for the build_classes method + stub_request(:any, /http:\/\/test\.com\/test\.svc(?:.*)/). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sap/edmx_sap_demo_flight.xml", __FILE__)), :headers => {}) + end + it "should pass the parameters as part of a query" do + @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } + @service.flight_dataCollection + @service.execute + a_request(:get, "http://test.com/test.svc/flight_dataCollection?x=1&y=2").should have_been_made + end + it "should pass the parameters as part of a save" do + @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } + new_flight = ZDemoFlight.new + @service.AddToflight_dataCollection(new_flight) + @service.save_changes + a_request(:post, "http://test.com/test.svc/flight_dataCollection?x=1&y=2").should have_been_made + end + it "should pass the parameters as part of an update" do + @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } + existing_flight = ZDemoFlight.new + existing_flight.__metadata = { :uri => "http://test.com/test.svc/flight_dataCollection/1" } + @service.update_object(existing_flight) + @service.save_changes + a_request(:put, "http://test.com/test.svc/flight_dataCollection/1?x=1&y=2").should have_been_made + end + it "should pass the parameters as part of a delete" do + @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } + existing_flight = ZDemoFlight.new + existing_flight.__metadata = { :uri => "http://test.com/test.svc/flight_dataCollection/1" } + @service.delete_object(existing_flight) + @service.save_changes + a_request(:delete, "http://test.com/test.svc/flight_dataCollection/1?x=1&y=2").should have_been_made + end + it "should pass the parameters as part of a batch save" do + @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } + new_flight = ZDemoFlight.new + @service.AddToflight_dataCollection(new_flight) + new_flight2 = ZDemoFlight.new + @service.AddToflight_dataCollection(new_flight2) + @service.save_changes + a_request(:post, "http://test.com/test.svc/$batch?x=1&y=2").should have_been_made + end + it "should pass the parameters as part of an add link" do + @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } + existing_flight1 = ZDemoFlight.new + existing_flight1.__metadata = { :uri => "http://test.com/test.svc/flight_dataCollection/1" } + existing_flight2 = ZDemoFlight.new + existing_flight2.__metadata = { :uri => "http://test.com/test.svc/flight_dataCollection/2" } + @service.add_link(existing_flight1, "flight_data_r", existing_flight2) + @service.save_changes + a_request(:post, "http://test.com/test.svc/flight_dataCollection/1/$links/flight_data_r?x=1&y=2").should have_been_made + end + it "should pass the parameters as part of a function import with a parameter" do + @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } + @service.get_flight(1) + a_request(:get, "http://test.com/test.svc/get_flight?id=1&x=1&y=2").should have_been_made + end + it "should pass the parameters as part of a function import without parameters" do + @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } + @service.get_top_flight + a_request(:get, "http://test.com/test.svc/get_top_flight?x=1&y=2").should have_been_made + end + end + + describe "exception handling" do + before(:each) do + stub_request(:get, "http://test.com/test.svc/$metadata"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/edmx_categories_products.xml", __FILE__)), :headers => {}) + + stub_request(:get, "http://test.com/test.svc/Categories?$select=Price"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 400, :body => File.new(File.expand_path("../fixtures/error_without_message.xml", __FILE__)), :headers => {}) + end + + it "includes a generic message if the error is not in the response" do + @service =OData::Service.new "http://test.com/test.svc/" + @service.Categories.select "Price" + expect { @service.execute }.to raise_error(OData::ServiceError) { |error| + error.http_code.should eq 400 + error.message.should eq "Server returned error but no message." + } + end + end + + describe "lowercase collections" do + before(:each) do + # Required for the build_classes method + stub_request(:get, "http://test.com/test.svc/$metadata"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/edmx_lowercase.xml", __FILE__)), :headers => {}) + end + + it "should respond_to a lowercase collection" do + @service =OData::Service.new "http://test.com/test.svc" + expect(@service.respond_to?('acronyms')).to eq true + end + + it "should allow a lowercase collections to be queried" do + @service =OData::Service.new "http://test.com/test.svc" + lambda { @service.send('acronyms') }.should_not raise_error + end + end + + + describe "collections, objects, metadata etc" do + before(:each) do + # Metadata + stub_request(:get, "http://test.com/test.svc/$metadata"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/feed_customization/edmx_feed_customization.xml", __FILE__)), :headers => {}) + + # Content - Products + stub_request(:get, /http:\/\/test\.com\/test\.svc\/Products(?:.*)/). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/feed_customization/result_feed_customization_products_expand.xml", __FILE__)), :headers => {}) + + # Content - Categories expanded Products + stub_request(:get, /http:\/\/test\.com\/test\.svc\/Categories(?:.*)/). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/feed_customization/result_feed_customization_categories_expand.xml", __FILE__)), :headers => {}) + end + + describe "handling feed customizations" do + describe "property metadata" do + it "should fill the class_metadata hash" do + @service =OData::Service.new "http://test.com/test.svc/" + @service.class_metadata.should_not be_empty + end + it "should add a key (based on the name) for each property class_metadata hash" do + @service =OData::Service.new "http://test.com/test.svc/" + @service.class_metadata['Product'].should have_key 'ID' + @service.class_metadata['Product'].should have_key 'Name' + @service.class_metadata['Product'].should have_key 'Description' + end + it "should have a PropertyMetadata object for each property class_metadata hash" do + @service =OData::Service.new "http://test.com/test.svc/" + @service.class_metadata['Product']['ID'].should be_a OData::PropertyMetadata + @service.class_metadata['Product']['Name'].should be_a OData::PropertyMetadata + @service.class_metadata['Product']['Description'].should be_a OData::PropertyMetadata + end + it "should have the correct PropertyMetadata object for Id" do + @service =OData::Service.new "http://test.com/test.svc/" + meta = @service.class_metadata['Product']['ID'] + meta.name.should eq 'ID' + meta.type.should eq 'Edm.Int32' + meta.nullable.should eq false + meta.fc_target_path.should be_nil + meta.fc_keep_in_content.should be_nil + end + it "should have the correct PropertyMetadata object for Name" do + @service =OData::Service.new "http://test.com/test.svc/" + meta = @service.class_metadata['Product']['Name'] + meta.name.should eq 'Name' + meta.type.should eq 'Edm.String' + meta.nullable.should eq true + meta.fc_target_path.should eq "SyndicationTitle" + meta.fc_keep_in_content.should eq false + end + it "should have the correct PropertyMetadata object for Description" do + @service =OData::Service.new "http://test.com/test.svc/" + meta = @service.class_metadata['Product']['Description'] + meta.name.should eq 'Description' + meta.type.should eq 'Edm.String' + meta.nullable.should eq true + meta.fc_target_path.should eq "SyndicationSummary" + meta.fc_keep_in_content.should eq false + end + end + + describe "single class" do + it "should handle properties where a property is represented in the syndication title instead of the properties collection" do + @service =OData::Service.new "http://test.com/test.svc/" + @service.Products + results = @service.execute + results.first.Name.should eq "Bread" + end + it "should handle properties where a property is represented in the syndication summary instead of the properties collection" do + @service =OData::Service.new "http://test.com/test.svc/" + @service.Products + results = @service.execute + results.first.Description.should eq "Whole grain bread" + end + end + + describe "expanded inline class" do + it "should handle properties where a property is represented in the syndication title instead of the properties collection" do + @service =OData::Service.new "http://test.com/test.svc/" + @service.Categories + results = @service.execute + + beverages = results[1] + + milk = beverages.Products.first + milk.Name.should eq "Milk" + milk.Description.should eq "Low fat milk" + + lemonade = beverages.Products.last + lemonade.Name.should eq "Pink Lemonade" + lemonade.Description.should eq "36 Ounce Cans (Pack of 3)" + end + end + end + + describe "handling inline collections/properties" do + it "should make plural named properties arrays and not a single class" do + @service =OData::Service.new "http://test.com/test.svc/" + @service.Categories + results = @service.execute + food = results[0] + + food.Products.should be_an Array + end + + it "should not make an array if the navigation property name is singular" do + @service =OData::Service.new "http://test.com/test.svc/" + @service.Products + results = @service.execute + product = results.first + product.Category.should_not be_an Array + end + end + + describe "navigation properties" do + it "should fill in PropertyMetadata for navigation properties" do + @service =OData::Service.new "http://test.com/test.svc/" + @service.class_metadata['Product'].should have_key 'Category' + end + end + end + + describe "single layer inheritance" do + before(:each) do + # Metadata + stub_request(:get, "http://test.com/test.svc/$metadata"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/inheritance/edmx_pluralsight.xml", __FILE__)), :headers => {}) + + # Content - Courses + stub_request(:get, /http:\/\/test\.com\/test\.svc\/Courses(?:.*)/). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/inheritance/result_pluralsight_courses.xml", __FILE__)), :headers => {}) + end + + it "should build all inherited attributes" do + @service = OData::Service.new "http://test.com/test.svc/" + methods = Course.instance_methods.reject {|m| Object.methods.index(m)} + + # Ruby 1.9 uses symbols, and 1.8 uses strings, so this normalizes the data + methods.map! {|m| m.to_sym} + + methods.should include(:Title) + methods.should include(:Description) + methods.should include(:VideoLength) + methods.should include(:Category) + + methods.should include(:Title=) + methods.should include(:Description=) + methods.should include(:VideoLength=) + methods.should include(:Category=) + end + + it "should not build abstract classes" do + @service = OData::Service.new "http://test.com/test.svc/" + defined?(ModelItemBase).should eq nil + end + + it "should fill inherited properties" do + @service =OData::Service.new "http://test.com/test.svc/" + @service.Courses + courses = @service.execute + course = courses.first + course.Title.should_not be_nil + course.Description.should_not be_nil + course.VideoLength.should_not be_nil + course.Category.should_not be_nil + end + end + + describe "handling partial collections" do + before(:each) do + # Metadata + stub_request(:get, "http://test.com/test.svc/$metadata"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/partial/partial_feed_metadata.xml", __FILE__)), :headers => {}) + + # Content - Partial + stub_request(:get, "http://test.com/test.svc/Partials"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/partial/partial_feed_part_1.xml", __FILE__)), :headers => {}) + + stub_request(:get, "http://test.com/test.svc/Partials?$skiptoken='ERNSH'"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/partial/partial_feed_part_2.xml", __FILE__)), :headers => {}) + + stub_request(:get, "http://test.com/test.svc/Partials?$skiptoken='ERNSH2'"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/partial/partial_feed_part_3.xml", __FILE__)), :headers => {}) + + end + + it "should return the whole collection by default" do + @service =OData::Service.new "http://test.com/test.svc/" + @service.Partials + results = @service.execute + results.count.should eq 3 + end + + it "should return only the partial when specified by options" do + @service =OData::Service.new("http://test.com/test.svc/", :eager_partial => false) + @service.Partials + results = @service.execute + results.count.should eq 1 + @service.should be_partial + while @service.partial? + results.concat @service.next + end + results.count.should eq 3 + end + + context "with additional_params" do + before(:each) do + stub_request(:get, "http://test.com/test.svc/$metadata?extra_param=value"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/partial/partial_feed_metadata.xml", __FILE__)), :headers => {}) + + stub_request(:get, "http://test.com/test.svc/Partials?extra_param=value"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/partial/partial_feed_part_1.xml", __FILE__)), :headers => {}) + + stub_request(:get, "http://test.com/test.svc/Partials?$skiptoken='ERNSH'&extra_param=value"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/partial/partial_feed_part_2.xml", __FILE__)), :headers => {}) + end + + it "should persist the additional parameters for the next call" do + @service =OData::Service.new("http://test.com/test.svc/", :eager_partial => false, :additional_params => { :extra_param => 'value' }) + @service.Partials + @service.execute + @service.next + + a_request(:get, "http://test.com/test.svc/Partials?$skiptoken='ERNSH'&extra_param=value").should have_been_made + end + end + end + + describe "link queries" do + before(:each) do + # Required for the build_classes method + stub_request(:get, /http:\/\/test\.com\/test\.svc\/\$metadata(?:\?.+)?/). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/edmx_categories_products.xml", __FILE__)), :headers => {}) + + stub_request(:get, "http://test.com/test.svc/Categories(1)/$links/Products"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/links/result_links_query.xml", __FILE__)), :headers => {}) + end + it "should be able to parse the results of a links query" do + @service =OData::Service.new "http://test.com/test.svc/" + @service.Categories(1).links('Products') + results = @service.execute + results.count.should eq 3 + results.first.should be_a_kind_of(URI) + results[0].path.should eq "/SampleService/RubyOData.svc/Products(1)" + results[1].path.should eq "/SampleService/RubyOData.svc/Products(2)" + results[2].path.should eq "/SampleService/RubyOData.svc/Products(3)" + end + end + + + + + describe "handling of nested expands" do + before(:each) do + stub_request(:get, "http://test.com/test.svc/$metadata"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/nested_expands/edmx_northwind.xml", __FILE__)), :headers => {}) + + stub_request(:get, "http://test.com/test.svc/Products?$expand=Category,Category/Products&$top=2"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/nested_expands/northwind_products_category_expands.xml", __FILE__)), :headers => {}) + end + after(:each) do + #Object.send(:remove_const, 'Product') if Object.const_defined? 'Product' + end + + it "should successfully parse the results" do + @service =OData::Service.new "http://test.com/test.svc", { :namespace => "NW" } + @service.Products.expand('Category').expand('Category/Products').top(2) + lambda { @service.execute }.should_not raise_exception + end + + it "should successfully parse a Category as a Category" do + @service =OData::Service.new "http://test.com/test.svc", { :namespace => "NW" } + @service.Products.expand('Category').expand('Category/Products').top(2) + products = @service.execute + products.first.Category.should be_a_kind_of(NW::Category) + end + + it "should successfully parse the Category properties" do + @service =OData::Service.new "http://test.com/test.svc", { :namespace => "NW" } + @service.Products.expand('Category').expand('Category/Products').top(2) + products = @service.execute + products.first.Category.CategoryID.should eq 1 + end + + it "should successfully parse the Category children Products" do + @service =OData::Service.new "http://test.com/test.svc", { :namespace => "NW" } + @service.Products.expand('Category').expand('Category/Products').top(2) + products = @service.execute + products.first.Category.Products.length.should eq 12 + end + + it "should successfully parse the Category's child Product properties" do + @service =OData::Service.new "http://test.com/test.svc", { :namespace => "NW" } + @service.Products.expand('Category').expand('Category/Products').top(2) + products = @service.execute + products.first.Category.Products.first.ProductName.should eq "Chai" + end + end + + describe "handling of custom select queries" do + + context "when results are found" do + before(:each) do + stub_request(:get, "http://test.com/test.svc/$metadata"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new( FIXTURES + "/sample_service/edmx_categories_products.xml"), :headers => {}) + + stub_request(:get, "http://test.com/test.svc/Products?$select=Name,Price"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new( FIXTURES + "/sample_service/result_select_products_name_price.xml"), :headers => {}) + end + + before(:each) do + @service =OData::Service.new "http://test.com/test.svc/" + @service.Products.select "Name", "Price" + @result = @service.execute + end + + it "returns an Array og Products" do + expect(@result).to be_an Array + expect(@result).not_to be_empty + expect(@result.first).to be_a Product + end + end + + context "when there isn't a property by the name specified" do + before(:each) do + stub_request(:get, "http://test.com/test.svc/$metadata"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new( FIXTURES + "/sample_service/edmx_categories_products.xml"), :headers => {}) + + stub_request(:get, "http://test.com/test.svc/Categories?$select=Price"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 400, :body => File.new( FIXTURES + "/sample_service/result_select_categories_no_property.xml"), :headers => {}) + end + + it "raises an exception" do + @service =OData::Service.new "http://test.com/test.svc/" + @service.Categories.select "Price" + expect { @service.execute }.to raise_error(OData::ServiceError) { |error| + error.http_code.should eq 400 + error.message.should eq "Type 'RubyODataService.Category' does not have a property named 'Price' or there is no type with 'Price' name." + } + end + end + + context "when a property requires $expand to traverse", focus: true do + before(:each) do + stub_request(:get, "http://test.com/test.svc/$metadata"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new( FIXTURES + "/sample_service/edmx_categories_products.xml"), :headers => {}) + + stub_request(:get, "http://test.com/test.svc/Categories?$select=Name,Products/Name"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 400, :body => File.new( FIXTURES + "/sample_service/result_select_categories_travsing_no_expand.xml"), :headers => {}) + + stub_request(:get, "http://test.com/test.svc/Categories?$select=Name,Products/Name&$expand=Products"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new( FIXTURES + "/sample_service/result_select_categories_expand.xml"), :headers => {}) + + stub_request(:get, "http://test.com/test.svc/Categories"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new( FIXTURES + "/sample_service/result_select_categories_expand.xml"), :headers => {}) + + end + + it "retursn Categoris" do + @service =OData::Service.new "http://test.com/test.svc/" + @service.Categories + c = @service.execute + end + + it "doesn't error" do + @service =OData::Service.new "http://test.com/test.svc/" + @service.Categories.select "Name", "Products/Name" + expect { @service.execute }.to_not raise_error + end + + it "returns the classes with the properties filled in" do + @service =OData::Service.new "http://test.com/test.svc/" + @service.Categories.select "Name", "Products/Name" + results = @service.execute + category = results.first + category.Name.should eq "Category 0001" + product = category.Products.first + product.Name.should eq "Widget 0001" + end + end + end + end + + describe_private OData::Service do + describe "parse value" do + before(:each) do + # Required for the build_classes method + stub_request(:get, "http://test.com/test.svc/$metadata"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new( FIXTURES + "/edmx_empty.xml" ), :headers => {}) + end + + it "should not error on an 'out of range' date" do + # This date was returned in the Netflix OData service and failed with an ArgumentError: out of range using 1.8.7 (2010-12-23 patchlevel 330) [i386-mingw32] + @service =OData::Service.new "http://test.com/test.svc/" + element_to_parse = Nokogiri::XML.parse('2100-01-01T00:00:00').elements[0] + lambda { @service.parse_value_xml(element_to_parse) }.should_not raise_exception + end + end + end +end diff --git a/spec/service_spec.rb b/spec/service_spec.rb index 50c9b29..470b1c7 100644 --- a/spec/service_spec.rb +++ b/spec/service_spec.rb @@ -1,596 +1,259 @@ require 'spec_helper' +require 'base64' module OData + describe Service do + before(:each) do + stub_request(:get, /http:\/\/test\.com\/test\.svc\/\$metadata(?:\?.+)?/). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/edmx_categories_products.xml", __FILE__)), :headers => {}) + + @service = OData::Service.new "http://test.com/test.svc" + end after(:each) do remove_classes @service end - describe "#initialize" do - it "truncates passed in end slash from uri when making the request" do - # Required for the build_classes method - stub_request(:get, "http://test.com/test.svc/$metadata"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/edmx_empty.xml", __FILE__)), :headers => {}) - - @service =OData::Service.new "http://test.com/test.svc/" + subject { @service } + + context "methods" do + it { should respond_to :collections } + it { should respond_to :class_metadata } + it { should respond_to :function_imports } + it { should respond_to :classes } + + it { should respond_to :execute } + + it { should respond_to :update_object } + it { should respond_to :delete_object } + it { should respond_to :save_changes } + it { should respond_to :load_property } + it { should respond_to :add_link } + it { should respond_to :partial? } + it { should respond_to :next } + it { should respond_to :options } + + context "after parsing metadata" do + it { should respond_to :Products } + it { should respond_to :Categories } + it { should respond_to :AddToProducts } + it { should respond_to :AddToCategories } end - it "doesn't error with lowercase entities" do - # Required for the build_classes method - stub_request(:get, "http://test.com/test.svc/$metadata"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/edmx_lowercase.xml", __FILE__)), :headers => {}) + end - expect { @service = OData::Service.new "http://test.com/test.svc" }.not_to raise_error + context "collections method" do + subject { @service.collections } + it { should include 'Products' } + it { should include 'Categories' } + it "should expose the edmx type of objects" do + subject['Products'][:edmx_type].should eq 'RubyODataService.Product' + subject['Categories'][:edmx_type].should eq 'RubyODataService.Category' end - - describe "additional query string parameters" do - before(:each) do - # Required for the build_classes method - stub_request(:get, /http:\/\/test\.com\/test\.svc\/\$metadata(?:\?.+)?/). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/edmx_empty.xml", __FILE__)), :headers => {}) - end - it "should accept additional query string parameters" do - @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } - @service.options[:additional_params].should eq Hash[:x=>1, :y=>2] - end - it "should call the correct metadata uri when additional_params are passed in" do - @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x => 1, :y => 2 } } - a_request(:get, "http://test.com/test.svc/$metadata?x=1&y=2").should have_been_made - end + it "should expose the local model type" do + subject['Products'][:type].should eq Product + subject['Categories'][:type].should eq Category end + end - describe "rest-client options" do - before(:each) do - # Required for the build_classes method - stub_request(:get, /http:\/\/test\.com\/test\.svc\/\$metadata(?:\?.+)?/). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/edmx_empty.xml", __FILE__)), :headers => {}) + context "class metadata" do + subject { @service.class_metadata } + it { should_not be_empty} + it { should have_key 'Product' } + it { should have_key 'Category' } + + context "should have keys for each property" do + subject { @service.class_metadata['Category'] } + it { should have_key 'Id' } + it { should have_key 'Name' } + it { should have_key 'Products' } + it "should return a PropertyMetadata object for each property" do + subject['Id'].should be_a PropertyMetadata + subject['Name'].should be_a PropertyMetadata + subject['Products'].should be_a PropertyMetadata end - it "should accept in options that will be passed to the rest-client lib" do - @service =OData::Service.new "http://test.com/test.svc/", { :rest_options => { :ssl_ca_file => "ca_certificate.pem" } } - @service.options[:rest_options].should eq Hash[:ssl_ca_file => "ca_certificate.pem"] + it "should have correct PropertyMetadata for Category.Id" do + meta = subject['Id'] + meta.name.should eq 'Id' + meta.type.should eq 'Edm.Int32' + meta.nullable.should eq false + meta.fc_target_path.should be_nil + meta.fc_keep_in_content.should be_nil + meta.nav_prop.should eq false + meta.is_key.should eq true end - it "should merge the rest options with the built in options" do - @service =OData::Service.new "http://test.com/test.svc/", { :rest_options => { :ssl_ca_file => "ca_certificate.pem" } } - @service.instance_variable_get(:@rest_options).should eq Hash[:verify_ssl => 1, :user => nil, :password => nil, :ssl_ca_file => "ca_certificate.pem"] + it "should have correct PropertyMetadata for Category.Name" do + meta = subject['Name'] + meta.name.should eq 'Name' + meta.type.should eq 'Edm.String' + meta.nullable.should eq false + meta.fc_target_path.should be_nil + meta.fc_keep_in_content.should be_nil + meta.nav_prop.should eq false + meta.is_key.should eq false + end + it "should have correct PropertyMetadata for Category.Products" do + meta = subject['Products'] + meta.name.should eq 'Products' + meta.type.should be_nil + meta.nullable.should eq true + meta.fc_target_path.should be_nil + meta.fc_keep_in_content.should be_nil + meta.nav_prop.should eq true + meta.association.should_not be_nil + meta.is_key.should eq false end end end - - describe "additional query string parameters" do - before(:each) do - # Required for the build_classes method - stub_request(:any, /http:\/\/test\.com\/test\.svc(?:.*)/). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sap/edmx_sap_demo_flight.xml", __FILE__)), :headers => {}) - end - it "should pass the parameters as part of a query" do - @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } - @service.flight_dataCollection - @service.execute - a_request(:get, "http://test.com/test.svc/flight_dataCollection?x=1&y=2").should have_been_made - end - it "should pass the parameters as part of a save" do - @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } - new_flight = ZDemoFlight.new - @service.AddToflight_dataCollection(new_flight) - @service.save_changes - a_request(:post, "http://test.com/test.svc/flight_dataCollection?x=1&y=2").should have_been_made - end - it "should pass the parameters as part of an update" do - @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } - existing_flight = ZDemoFlight.new - existing_flight.__metadata = { :uri => "http://test.com/test.svc/flight_dataCollection/1" } - @service.update_object(existing_flight) - @service.save_changes - a_request(:put, "http://test.com/test.svc/flight_dataCollection/1?x=1&y=2").should have_been_made - end - it "should pass the parameters as part of a delete" do - @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } - existing_flight = ZDemoFlight.new - existing_flight.__metadata = { :uri => "http://test.com/test.svc/flight_dataCollection/1" } - @service.delete_object(existing_flight) - @service.save_changes - a_request(:delete, "http://test.com/test.svc/flight_dataCollection/1?x=1&y=2").should have_been_made - end - it "should pass the parameters as part of a batch save" do - @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } - new_flight = ZDemoFlight.new - @service.AddToflight_dataCollection(new_flight) - new_flight2 = ZDemoFlight.new - @service.AddToflight_dataCollection(new_flight2) - @service.save_changes - a_request(:post, "http://test.com/test.svc/$batch?x=1&y=2").should have_been_made - end - it "should pass the parameters as part of an add link" do - @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } - existing_flight1 = ZDemoFlight.new - existing_flight1.__metadata = { :uri => "http://test.com/test.svc/flight_dataCollection/1" } - existing_flight2 = ZDemoFlight.new - existing_flight2.__metadata = { :uri => "http://test.com/test.svc/flight_dataCollection/2" } - @service.add_link(existing_flight1, "flight_data_r", existing_flight2) - @service.save_changes - a_request(:post, "http://test.com/test.svc/flight_dataCollection/1/$links/flight_data_r?x=1&y=2").should have_been_made - end - it "should pass the parameters as part of a function import with a parameter" do - @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } - @service.get_flight(1) - a_request(:get, "http://test.com/test.svc/get_flight?id=1&x=1&y=2").should have_been_made - end - it "should pass the parameters as part of a function import without parameters" do - @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } - @service.get_top_flight - a_request(:get, "http://test.com/test.svc/get_top_flight?x=1&y=2").should have_been_made - end - end - - describe "exception handling" do - before(:each) do - stub_request(:get, "http://test.com/test.svc/$metadata"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/edmx_categories_products.xml", __FILE__)), :headers => {}) - - stub_request(:get, "http://test.com/test.svc/Categories?$select=Price"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 400, :body => File.new(File.expand_path("../fixtures/error_without_message.xml", __FILE__)), :headers => {}) - end - - it "includes a generic message if the error is not in the response" do - @service =OData::Service.new "http://test.com/test.svc/" - @service.Categories.select "Price" - expect { @service.execute }.to raise_error(OData::ServiceError) { |error| - error.http_code.should eq 400 - error.message.should eq "Server returned error but no message." - } - end - end - - describe "lowercase collections" do - before(:each) do - # Required for the build_classes method - stub_request(:get, "http://test.com/test.svc/$metadata"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/edmx_lowercase.xml", __FILE__)), :headers => {}) - end - - it "should respond_to a lowercase collection" do - @service =OData::Service.new "http://test.com/test.svc" - expect(@service.respond_to?('acronyms')).to eq true - end - - it "should allow a lowercase collections to be queried" do - @service =OData::Service.new "http://test.com/test.svc" - lambda { @service.send('acronyms') }.should_not raise_error - end - end - - - describe "collections, objects, metadata etc" do - before(:each) do - # Metadata - stub_request(:get, "http://test.com/test.svc/$metadata"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/feed_customization/edmx_feed_customization.xml", __FILE__)), :headers => {}) - - # Content - Products - stub_request(:get, /http:\/\/test\.com\/test\.svc\/Products(?:.*)/). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/feed_customization/result_feed_customization_products_expand.xml", __FILE__)), :headers => {}) - - # Content - Categories expanded Products - stub_request(:get, /http:\/\/test\.com\/test\.svc\/Categories(?:.*)/). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/feed_customization/result_feed_customization_categories_expand.xml", __FILE__)), :headers => {}) - end - - describe "handling feed customizations" do - describe "property metadata" do - it "should fill the class_metadata hash" do - @service =OData::Service.new "http://test.com/test.svc/" - @service.class_metadata.should_not be_empty - end - it "should add a key (based on the name) for each property class_metadata hash" do - @service =OData::Service.new "http://test.com/test.svc/" - @service.class_metadata['Product'].should have_key 'ID' - @service.class_metadata['Product'].should have_key 'Name' - @service.class_metadata['Product'].should have_key 'Description' - end - it "should have a PropertyMetadata object for each property class_metadata hash" do - @service =OData::Service.new "http://test.com/test.svc/" - @service.class_metadata['Product']['ID'].should be_a OData::PropertyMetadata - @service.class_metadata['Product']['Name'].should be_a OData::PropertyMetadata - @service.class_metadata['Product']['Description'].should be_a OData::PropertyMetadata - end - it "should have the correct PropertyMetadata object for Id" do - @service =OData::Service.new "http://test.com/test.svc/" - meta = @service.class_metadata['Product']['ID'] - meta.name.should eq 'ID' - meta.type.should eq 'Edm.Int32' - meta.nullable.should eq false - meta.fc_target_path.should be_nil - meta.fc_keep_in_content.should be_nil - end - it "should have the correct PropertyMetadata object for Name" do - @service =OData::Service.new "http://test.com/test.svc/" - meta = @service.class_metadata['Product']['Name'] - meta.name.should eq 'Name' - meta.type.should eq 'Edm.String' - meta.nullable.should eq true - meta.fc_target_path.should eq "SyndicationTitle" - meta.fc_keep_in_content.should eq false - end - it "should have the correct PropertyMetadata object for Description" do - @service =OData::Service.new "http://test.com/test.svc/" - meta = @service.class_metadata['Product']['Description'] - meta.name.should eq 'Description' - meta.type.should eq 'Edm.String' - meta.nullable.should eq true - meta.fc_target_path.should eq "SyndicationSummary" - meta.fc_keep_in_content.should eq false - end + + context "function_imports method" do + subject { @service.function_imports } + it { should_not be_empty} + it { should have_key 'CleanDatabaseForTesting' } + it { should have_key 'EntityCategoryWebGet' } + it { should have_key 'EntitySingleCategoryWebGet' } + it "should expose the http method" do + subject['CleanDatabaseForTesting'][:http_method].should eq 'POST' + subject['EntityCategoryWebGet'][:http_method].should eq 'GET' + subject['EntitySingleCategoryWebGet'][:http_method].should eq 'GET' + end + it "should expose the return type" do + subject['CleanDatabaseForTesting'][:return_typo].should be_nil + subject['EntityCategoryWebGet'][:return_type].should eq Array + subject['EntityCategoryWebGet'][:inner_return_type].should eq Category + subject['EntitySingleCategoryWebGet'][:return_type].should eq Category + subject['EntitySingleCategoryWebGet'][:inner_return_type].should be_nil + subject['CategoryNames'][:return_type].should eq Array + subject['CategoryNames'][:inner_return_type].should eq String + end + it "should provide a hash of parameters" do + subject['EntityCategoryWebGet'][:parameters].should be_nil + subject['EntitySingleCategoryWebGet'][:parameters].should be_a Hash + subject['EntitySingleCategoryWebGet'][:parameters]['id'].should eq 'Edm.Int32' + end + context "after parsing function imports" do + subject { @service } + it { should respond_to :CleanDatabaseForTesting } + it { should respond_to :EntityCategoryWebGet } + it { should respond_to :EntitySingleCategoryWebGet } + it { should respond_to :CategoryNames } + end + context "error checking" do + subject { @service } + it "should throw an exception if a parameter is passed in to a method that doesn't require one" do + lambda { subject.EntityCategoryWebGet(1) }.should raise_error(ArgumentError, "wrong number of arguments (1 for 0)") end - - describe "single class" do - it "should handle properties where a property is represented in the syndication title instead of the properties collection" do - @service =OData::Service.new "http://test.com/test.svc/" - @service.Products - results = @service.execute - results.first.Name.should eq "Bread" - end - it "should handle properties where a property is represented in the syndication summary instead of the properties collection" do - @service =OData::Service.new "http://test.com/test.svc/" - @service.Products - results = @service.execute - results.first.Description.should eq "Whole grain bread" - end - end - - describe "expanded inline class" do - it "should handle properties where a property is represented in the syndication title instead of the properties collection" do - @service =OData::Service.new "http://test.com/test.svc/" - @service.Categories - results = @service.execute - - beverages = results[1] - - milk = beverages.Products.first - milk.Name.should eq "Milk" - milk.Description.should eq "Low fat milk" - - lemonade = beverages.Products.last - lemonade.Name.should eq "Pink Lemonade" - lemonade.Description.should eq "36 Ounce Cans (Pack of 3)" - end + it "should throw and exception if more parameters are passed in than required by the function" do + lambda { subject.EntitySingleCategoryWebGet(1,2) }.should raise_error(ArgumentError, "wrong number of arguments (2 for 1)") end end - - describe "handling inline collections/properties" do - it "should make plural named properties arrays and not a single class" do - @service =OData::Service.new "http://test.com/test.svc/" - @service.Categories - results = @service.execute - food = results[0] - - food.Products.should be_an Array + context "url and http method checks" do + subject { @service } + before { stub_request(:any, /http:\/\/test\.com\/test\.svc\/(.*)/) } + it "should call the correct url with the correct http method for a post with no parameters" do + subject.CleanDatabaseForTesting + a_request(:post, "http://test.com/test.svc/CleanDatabaseForTesting").should have_been_made end - - it "should not make an array if the navigation property name is singular" do - @service =OData::Service.new "http://test.com/test.svc/" - @service.Products - results = @service.execute - product = results.first - product.Category.should_not be_an Array + it "should call the correct url with the correct http method for a get with no parameters" do + subject.EntityCategoryWebGet + a_request(:get, "http://test.com/test.svc/EntityCategoryWebGet").should have_been_made end - end - - describe "navigation properties" do - it "should fill in PropertyMetadata for navigation properties" do - @service =OData::Service.new "http://test.com/test.svc/" - @service.class_metadata['Product'].should have_key 'Category' + it "should call the correct url with the correct http method for a get with parameters" do + subject.EntitySingleCategoryWebGet(1) + a_request(:get, "http://test.com/test.svc/EntitySingleCategoryWebGet?id=1").should have_been_made end end - end - - describe "single layer inheritance" do - before(:each) do - # Metadata - stub_request(:get, "http://test.com/test.svc/$metadata"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/inheritance/edmx_pluralsight.xml", __FILE__)), :headers => {}) - - # Content - Courses - stub_request(:get, /http:\/\/test\.com\/test\.svc\/Courses(?:.*)/). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/inheritance/result_pluralsight_courses.xml", __FILE__)), :headers => {}) - end - - it "should build all inherited attributes" do - @service = OData::Service.new "http://test.com/test.svc/" - methods = Course.instance_methods.reject {|m| Object.methods.index(m)} - - # Ruby 1.9 uses symbols, and 1.8 uses strings, so this normalizes the data - methods.map! {|m| m.to_sym} - - methods.should include(:Title) - methods.should include(:Description) - methods.should include(:VideoLength) - methods.should include(:Category) - - methods.should include(:Title=) - methods.should include(:Description=) - methods.should include(:VideoLength=) - methods.should include(:Category=) - end - - it "should not build abstract classes" do - @service = OData::Service.new "http://test.com/test.svc/" - defined?(ModelItemBase).should eq nil - end - - it "should fill inherited properties" do - @service =OData::Service.new "http://test.com/test.svc/" - @service.Courses - courses = @service.execute - course = courses.first - course.Title.should_not be_nil - course.Description.should_not be_nil - course.VideoLength.should_not be_nil - course.Category.should_not be_nil - end - end - - describe "handling partial collections" do - before(:each) do - # Metadata - stub_request(:get, "http://test.com/test.svc/$metadata"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/partial/partial_feed_metadata.xml", __FILE__)), :headers => {}) + context "function import result parsing" do + subject { @service } + before(:each) do + stub_request(:post, "http://test.com/test.svc/CleanDatabaseForTesting").to_return(:status => 204) - # Content - Partial - stub_request(:get, "http://test.com/test.svc/Partials"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/partial/partial_feed_part_1.xml", __FILE__)), :headers => {}) + stub_request(:get, "http://test.com/test.svc/EntityCategoryWebGet"). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_entity_category_web_get.xml", __FILE__)), :headers => {}) - stub_request(:get, "http://test.com/test.svc/Partials?$skiptoken='ERNSH'"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/partial/partial_feed_part_2.xml", __FILE__)), :headers => {}) + stub_request(:get, "http://test.com/test.svc/EntitySingleCategoryWebGet?id=1"). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_entity_single_category_web_get.xml", __FILE__)), :headers => {}) - stub_request(:get, "http://test.com/test.svc/Partials?$skiptoken='ERNSH2'"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/partial/partial_feed_part_3.xml", __FILE__)), :headers => {}) + stub_request(:get, "http://test.com/test.svc/CategoryNames"). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_category_names.xml", __FILE__)), :headers => {}) - end - - it "should return the whole collection by default" do - @service =OData::Service.new "http://test.com/test.svc/" - @service.Partials - results = @service.execute - results.count.should eq 3 - end - - it "should return only the partial when specified by options" do - @service =OData::Service.new("http://test.com/test.svc/", :eager_partial => false) - @service.Partials - results = @service.execute - results.count.should eq 1 - @service.should be_partial - while @service.partial? - results.concat @service.next + stub_request(:get, "http://test.com/test.svc/FirstCategoryId"). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_first_category_id.xml", __FILE__)), :headers => {}) end - results.count.should eq 3 - end - - context "with additional_params" do - before(:each) do - stub_request(:get, "http://test.com/test.svc/$metadata?extra_param=value"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/partial/partial_feed_metadata.xml", __FILE__)), :headers => {}) - - stub_request(:get, "http://test.com/test.svc/Partials?extra_param=value"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/partial/partial_feed_part_1.xml", __FILE__)), :headers => {}) - - stub_request(:get, "http://test.com/test.svc/Partials?$skiptoken='ERNSH'&extra_param=value"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/partial/partial_feed_part_2.xml", __FILE__)), :headers => {}) + it "should return true if a function import post that returns successfully and doesn't have a return value (HTTP 204)" do + result = subject.CleanDatabaseForTesting + expect(result).to eq true end - - it "should persist the additional parameters for the next call" do - @service =OData::Service.new("http://test.com/test.svc/", :eager_partial => false, :additional_params => { :extra_param => 'value' }) - @service.Partials - @service.execute - @service.next - - a_request(:get, "http://test.com/test.svc/Partials?$skiptoken='ERNSH'&extra_param=value").should have_been_made + it "should return a collection of entities for a collection" do + result = subject.EntityCategoryWebGet + result.should be_an Enumerable + result.first.should be_a Category + result.first.Name.should eq "Test Category" + end + it "should return a single entity if it isn't a collection" do + result = subject.EntitySingleCategoryWebGet(1) + result.should be_a Category + result.Name.should eq "Test Category" + end + it "should return a collection of primitive types" do + result = subject.CategoryNames + result.should be_an Enumerable + result.first.should be_a String + result.first.should eq "Test Category 1" + end + it "should return a single primitive type" do + result = subject.FirstCategoryId + result.should be_a Integer + result.should eq 1 end end end + end - describe "link queries" do - before(:each) do - # Required for the build_classes method - stub_request(:get, /http:\/\/test\.com\/test\.svc\/\$metadata(?:\?.+)?/). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/edmx_categories_products.xml", __FILE__)), :headers => {}) - stub_request(:get, "http://test.com/test.svc/Categories(1)/$links/Products"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/links/result_links_query.xml", __FILE__)), :headers => {}) - end - it "should be able to parse the results of a links query" do - @service =OData::Service.new "http://test.com/test.svc/" - @service.Categories(1).links('Products') - results = @service.execute - results.count.should eq 3 - results.first.should be_a_kind_of(URI) - results[0].path.should eq "/SampleService/RubyOData.svc/Products(1)" - results[1].path.should eq "/SampleService/RubyOData.svc/Products(2)" - results[2].path.should eq "/SampleService/RubyOData.svc/Products(3)" - end + describe "Dual Namespaces" do + before(:each) do + auth_string = "xxxx\\yyyy:zzzz" + authorization_header = { authorization: "Basic #{Base64::encode64(auth_string).strip}" } + stub_request(:get, "http://test.com/test.svc/$metadata"). + with(:headers => DEFAULT_HEADERS.merge(authorization_header)). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_system_center/edmx_ms_system_center_v2.xml", __FILE__)), :headers => {}) end - - - - describe "handling of nested expands" do - before(:each) do - stub_request(:get, "http://test.com/test.svc/$metadata"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/nested_expands/edmx_northwind.xml", __FILE__)), :headers => {}) - - stub_request(:get, "http://test.com/test.svc/Products?$expand=Category,Category/Products&$top=2"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/nested_expands/northwind_products_category_expands.xml", __FILE__)), :headers => {}) - end - after(:each) do - #Object.send(:remove_const, 'Product') if Object.const_defined? 'Product' - end - - it "should successfully parse the results" do - @service =OData::Service.new "http://test.com/test.svc", { :namespace => "NW" } - @service.Products.expand('Category').expand('Category/Products').top(2) - lambda { @service.execute }.should_not raise_exception - end - - it "should successfully parse a Category as a Category" do - @service =OData::Service.new "http://test.com/test.svc", { :namespace => "NW" } - @service.Products.expand('Category').expand('Category/Products').top(2) - products = @service.execute - products.first.Category.should be_a_kind_of(NW::Category) - end - - it "should successfully parse the Category properties" do - @service =OData::Service.new "http://test.com/test.svc", { :namespace => "NW" } - @service.Products.expand('Category').expand('Category/Products').top(2) - products = @service.execute - products.first.Category.CategoryID.should eq 1 - end - - it "should successfully parse the Category children Products" do - @service =OData::Service.new "http://test.com/test.svc", { :namespace => "NW" } - @service.Products.expand('Category').expand('Category/Products').top(2) - products = @service.execute - products.first.Category.Products.length.should eq 12 - end - - it "should successfully parse the Category's child Product properties" do - @service =OData::Service.new "http://test.com/test.svc", { :namespace => "NW" } - @service.Products.expand('Category').expand('Category/Products').top(2) - products = @service.execute - products.first.Category.Products.first.ProductName.should eq "Chai" - end + after(:each) do + remove_classes @service end - describe "handling of custom select queries" do - - context "when results are found" do - before(:each) do - stub_request(:get, "http://test.com/test.svc/$metadata"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new( FIXTURES + "/sample_service/edmx_categories_products.xml"), :headers => {}) - - stub_request(:get, "http://test.com/test.svc/Products?$select=Name,Price"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new( FIXTURES + "/sample_service/result_select_products_name_price.xml"), :headers => {}) - end - - before(:each) do - @service =OData::Service.new "http://test.com/test.svc/" - @service.Products.select "Name", "Price" - @result = @service.execute - end - - it "returns an Array og Products" do - expect(@result).to be_an Array - expect(@result).not_to be_empty - expect(@result.first).to be_a Product - end - end - - context "when there isn't a property by the name specified" do - before(:each) do - stub_request(:get, "http://test.com/test.svc/$metadata"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new( FIXTURES + "/sample_service/edmx_categories_products.xml"), :headers => {}) - - stub_request(:get, "http://test.com/test.svc/Categories?$select=Price"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 400, :body => File.new( FIXTURES + "/sample_service/result_select_categories_no_property.xml"), :headers => {}) - end - - it "raises an exception" do - @service =OData::Service.new "http://test.com/test.svc/" - @service.Categories.select "Price" - expect { @service.execute }.to raise_error(OData::ServiceError) { |error| - error.http_code.should eq 400 - error.message.should eq "Type 'RubyODataService.Category' does not have a property named 'Price' or there is no type with 'Price' name." - } - end - end - - context "when a property requires $expand to traverse", focus: true do - before(:each) do - stub_request(:get, "http://test.com/test.svc/$metadata"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new( FIXTURES + "/sample_service/edmx_categories_products.xml"), :headers => {}) - - stub_request(:get, "http://test.com/test.svc/Categories?$select=Name,Products/Name"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 400, :body => File.new( FIXTURES + "/sample_service/result_select_categories_travsing_no_expand.xml"), :headers => {}) + it "should parse the service without errors" do + lambda { @service = OData::Service.new "http://test.com/test.svc/", { :username => "xxxx\\yyyy", :password=> "zzzz", :verify_ssl => false, :namespace => "VMM" } }.should_not raise_error + end - stub_request(:get, "http://test.com/test.svc/Categories?$select=Name,Products/Name&$expand=Products"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new( FIXTURES + "/sample_service/result_select_categories_expand.xml"), :headers => {}) + end - stub_request(:get, "http://test.com/test.svc/Categories"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new( FIXTURES + "/sample_service/result_select_categories_expand.xml"), :headers => {}) + describe "Dual Services" do + before(:each) do + stub_request(:get, "http://service1.com/test.svc/$metadata"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/edmx_categories_products.xml", __FILE__)), :headers => {}) - end + stub_request(:get, "http://service2.com/test.svc/$metadata"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/int64_ids/edmx_car_service.xml", __FILE__)), :headers => {}) - it "retursn Categoris" do - @service =OData::Service.new "http://test.com/test.svc/" - @service.Categories - c = @service.execute - end - it "doesn't error" do - @service =OData::Service.new "http://test.com/test.svc/" - @service.Categories.select "Name", "Products/Name" - expect { @service.execute }.to_not raise_error - end - - it "returns the classes with the properties filled in" do - @service =OData::Service.new "http://test.com/test.svc/" - @service.Categories.select "Name", "Products/Name" - results = @service.execute - category = results.first - category.Name.should eq "Category 0001" - product = category.Products.first - product.Name.should eq "Widget 0001" - end - end + @service1 = OData::Service.new "http://service1.com/test.svc" + @service2 = OData::Service.new "http://service2.com/test.svc" end - end - describe_private OData::Service do - describe "parse value" do - before(:each) do - # Required for the build_classes method - stub_request(:get, "http://test.com/test.svc/$metadata"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new( FIXTURES + "/edmx_empty.xml" ), :headers => {}) - end + after(:each) do + remove_classes @service1 + remove_classes @service2 + end - it "should not error on an 'out of range' date" do - # This date was returned in the Netflix OData service and failed with an ArgumentError: out of range using 1.8.7 (2010-12-23 patchlevel 330) [i386-mingw32] - @service =OData::Service.new "http://test.com/test.svc/" - element_to_parse = Nokogiri::XML.parse('2100-01-01T00:00:00').elements[0] - lambda { @service.parse_value_xml(element_to_parse) }.should_not raise_exception - end + it "should use the correct service uri" do + expect(@service1.class_metadata[:uri]).to eq 'http://service1.com/test.svc' + expect(@service2.class_metadata[:uri]).to eq 'http://service2.com/test.svc' end end end diff --git a/spec/service_v4_spec.rb b/spec/service_v4_spec.rb index ffe2eb7..2e7386e 100644 --- a/spec/service_v4_spec.rb +++ b/spec/service_v4_spec.rb @@ -1,107 +1,110 @@ require 'spec_helper' -describe "V4 Service" do - before(:all) do - stub_request(:get, "http://test.com/test.svc/$metadata"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/v4/edmx_metadata.xml", __FILE__)), :headers => {}) +module OData - stub_request(:get, "http://test.com/test.svc/Categories"). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/v4/result_categories.xml", __FILE__)), :headers => {}) + describe "V4 Service" do + before(:all) do + stub_request(:get, "http://test.com/test.svc/$metadata"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/v4/edmx_metadata.xml", __FILE__)), :headers => {}) - @service = OData::Service.new "http://test.com/test.svc" - end - - after(:all) do - remove_classes @service - end - - subject { @service } + stub_request(:get, "http://test.com/test.svc/Categories"). + to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/v4/result_categories.xml", __FILE__)), :headers => {}) - context "methods" do - it { should respond_to :update_object } - it { should respond_to :delete_object } - it { should respond_to :save_changes } - it { should respond_to :load_property } - it { should respond_to :add_link } - it { should respond_to :execute } - it { should respond_to :partial? } - it { should respond_to :next } - it { should respond_to :classes } - it { should respond_to :class_metadata } - it { should respond_to :collections } - it { should respond_to :options } - it { should respond_to :function_imports } - - context "after parsing metadata" do - it { should respond_to :Products } - it { should respond_to :Categories } - it { should respond_to :AddToProducts } - it { should respond_to :AddToCategories } + @service = OData::Service.new "http://test.com/test.svc" end - end - context "collections method" do - subject { @service.collections } - it { should include 'Products' } - it { should include 'Categories' } - it "should expose the edmx type of objects" do - subject['Products'][:edmx_type].should eq 'ODataDemo.Product' - subject['Categories'][:edmx_type].should eq 'ODataDemo.Category' - end - it "should expose the local model type" do - subject['Products'][:type].should eq Product - subject['Categories'][:type].should eq Category + after(:all) do + remove_classes @service end - end - context "class metadata" do - subject { @service.class_metadata } - it { should_not be_empty} - it { should have_key 'Product' } - it { should have_key 'Category' } + subject { @service } + + context "methods" do + it { should respond_to :update_object } + it { should respond_to :delete_object } + it { should respond_to :save_changes } + it { should respond_to :load_property } + it { should respond_to :add_link } + it { should respond_to :execute } + it { should respond_to :partial? } + it { should respond_to :next } + it { should respond_to :classes } + it { should respond_to :class_metadata } + it { should respond_to :collections } + it { should respond_to :options } + it { should respond_to :function_imports } - context "should have keys for each property" do - subject { @service.class_metadata['Category'] } - it { should have_key 'ID' } - it { should have_key 'Name' } - it { should have_key 'Products' } - it "should return a PropertyMetadata object for each property" do - subject['ID'].should be_an OData::PropertyMetadata - subject['Name'].should be_an OData::PropertyMetadata - subject['Products'].should be_an OData::PropertyMetadata + context "after parsing metadata" do + it { should respond_to :Products } + it { should respond_to :Categories } + it { should respond_to :AddToProducts } + it { should respond_to :AddToCategories } end - it "should have correct PropertyMetadata for Category.Id" do - meta = subject['ID'] - meta.name.should eq 'ID' - meta.type.should eq 'Edm.Int32' - meta.nullable.should eq false - meta.fc_target_path.should be_nil - meta.fc_keep_in_content.should be_nil - meta.nav_prop.should eq false - meta.is_key.should eq true + end + + context "collections method" do + subject { @service.collections } + it { should include 'Products' } + it { should include 'Categories' } + it "should expose the edmx type of objects" do + subject['Products'][:edmx_type].should eq 'ODataDemo.Product' + subject['Categories'][:edmx_type].should eq 'ODataDemo.Category' end - it "should have correct PropertyMetadata for Category.Name" do - meta = subject['Name'] - meta.name.should eq 'Name' - meta.type.should eq 'Edm.String' - meta.nullable.should eq false - meta.fc_target_path.should be_nil - meta.fc_keep_in_content.should be_nil - meta.nav_prop.should eq false - meta.is_key.should eq false + it "should expose the local model type" do + subject['Products'][:type].should eq Product + subject['Categories'][:type].should eq Category end - it "should have correct PropertyMetadata for Category.Products" do - meta = subject['Products'] - meta.name.should eq 'Products' - meta.type.should eq 'Collection(ODataDemo.Product)' - meta.nullable.should eq true - meta.fc_target_path.should be_nil - meta.fc_keep_in_content.should be_nil - meta.nav_prop.should eq true - meta.association.should_not be_nil - meta.is_key.should eq false + end + + context "class metadata" do + subject { @service.class_metadata } + it { should_not be_empty} + it { should have_key 'Product' } + it { should have_key 'Category' } + + context "should have keys for each property" do + subject { @service.class_metadata['Category'] } + it { should have_key 'ID' } + it { should have_key 'Name' } + it { should have_key 'Products' } + it "should return a PropertyMetadata object for each property" do + subject['ID'].should be_an OData::PropertyMetadata + subject['Name'].should be_an OData::PropertyMetadata + subject['Products'].should be_an OData::PropertyMetadata + end + it "should have correct PropertyMetadata for Category.Id" do + meta = subject['ID'] + meta.name.should eq 'ID' + meta.type.should eq 'Edm.Int32' + meta.nullable.should eq false + meta.fc_target_path.should be_nil + meta.fc_keep_in_content.should be_nil + meta.nav_prop.should eq false + meta.is_key.should eq true + end + it "should have correct PropertyMetadata for Category.Name" do + meta = subject['Name'] + meta.name.should eq 'Name' + meta.type.should eq 'Edm.String' + meta.nullable.should eq false + meta.fc_target_path.should be_nil + meta.fc_keep_in_content.should be_nil + meta.nav_prop.should eq false + meta.is_key.should eq false + end + it "should have correct PropertyMetadata for Category.Products" do + meta = subject['Products'] + meta.name.should eq 'Products' + meta.type.should eq 'Collection(ODataDemo.Product)' + meta.nullable.should eq true + meta.fc_target_path.should be_nil + meta.fc_keep_in_content.should be_nil + meta.nav_prop.should eq true + meta.association.should_not be_nil + meta.is_key.should eq false + end end end end -end +end \ No newline at end of file From 1dede5af163ea3ea71a87964a6051c7ea071b272 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 7 Sep 2017 23:44:59 +0200 Subject: [PATCH 20/28] Get fixture data from odata.org --- spec/fixtures/odata.org/edmx_odata_v3.xml | 169 ++++++++++ spec/fixtures/odata.org/edmx_odata_v4.xml | 143 +++++++++ spec/fixtures/odata.org/odata_v3_products.xml | 297 ++++++++++++++++++ spec/fixtures/odata.org/odata_v3_service.xml | 27 ++ .../fixtures/odata.org/odata_v4_products.json | 106 +++++++ spec/fixtures/odata.org/odata_v4_service.xml | 27 ++ 6 files changed, 769 insertions(+) create mode 100644 spec/fixtures/odata.org/edmx_odata_v3.xml create mode 100644 spec/fixtures/odata.org/edmx_odata_v4.xml create mode 100644 spec/fixtures/odata.org/odata_v3_products.xml create mode 100644 spec/fixtures/odata.org/odata_v3_service.xml create mode 100644 spec/fixtures/odata.org/odata_v4_products.json create mode 100644 spec/fixtures/odata.org/odata_v4_service.xml diff --git a/spec/fixtures/odata.org/edmx_odata_v3.xml b/spec/fixtures/odata.org/edmx_odata_v3.xml new file mode 100644 index 0000000..97be127 --- /dev/null +++ b/spec/fixtures/odata.org/edmx_odata_v3.xml @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spec/fixtures/odata.org/edmx_odata_v4.xml b/spec/fixtures/odata.org/edmx_odata_v4.xml new file mode 100644 index 0000000..62317cd --- /dev/null +++ b/spec/fixtures/odata.org/edmx_odata_v4.xml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spec/fixtures/odata.org/odata_v3_products.xml b/spec/fixtures/odata.org/odata_v3_products.xml new file mode 100644 index 0000000..6615a29 --- /dev/null +++ b/spec/fixtures/odata.org/odata_v3_products.xml @@ -0,0 +1,297 @@ + + + http://services.odata.org/V3/OData/OData.svc/Products + Products + 2017-09-07T21:23:43Z + + + http://services.odata.org/V3/OData/OData.svc/Products(0) + + + + + + Bread + Whole grain bread + 2017-09-07T21:23:43Z + + + + + + + + + 0 + 1992-01-01T00:00:00 + + 4 + 2.5 + + + + + http://services.odata.org/V3/OData/OData.svc/Products(1) + + + + + + Milk + Low fat milk + 2017-09-07T21:23:43Z + + + + + + + + + 1 + 1995-10-01T00:00:00 + + 3 + 3.5 + + + + + http://services.odata.org/V3/OData/OData.svc/Products(2) + + + + + + Vint soda + Americana Variety - Mix of 6 flavors + 2017-09-07T21:23:43Z + + + + + + + + + 2 + 2000-10-01T00:00:00 + + 3 + 20.9 + + + + + http://services.odata.org/V3/OData/OData.svc/Products(3) + + + + + + Havina Cola + The Original Key Lime Cola + 2017-09-07T21:23:43Z + + + + + + + + + 3 + 2005-10-01T00:00:00 + 2006-10-01T00:00:00 + 3 + 19.9 + + + + + http://services.odata.org/V3/OData/OData.svc/Products(4) + + + + + + Fruit Punch + Mango flavor, 8.3 Ounce Cans (Pack of 24) + 2017-09-07T21:23:43Z + + + + + + + + + 4 + 2003-01-05T00:00:00 + + 3 + 22.99 + + + + + http://services.odata.org/V3/OData/OData.svc/Products(5) + + + + + + Cranberry Juice + 16-Ounce Plastic Bottles (Pack of 12) + 2017-09-07T21:23:43Z + + + + + + + + + 5 + 2006-08-04T00:00:00 + + 3 + 22.8 + + + + + http://services.odata.org/V3/OData/OData.svc/Products(6) + + + + + + Pink Lemonade + 36 Ounce Cans (Pack of 3) + 2017-09-07T21:23:43Z + + + + + + + + + 6 + 2006-11-05T00:00:00 + + 3 + 18.8 + + + + + http://services.odata.org/V3/OData/OData.svc/Products(7) + + + + + + DVD Player + 1080P Upconversion DVD Player + 2017-09-07T21:23:43Z + + + + + + + + + 7 + 2006-11-15T00:00:00 + + 5 + 35.88 + + + + + http://services.odata.org/V3/OData/OData.svc/Products(8) + + + + + + LCD HDTV + 42 inch 1080p LCD with Built-in Blu-ray Disc Player + 2017-09-07T21:23:43Z + + + + + + + + + 8 + 2008-05-08T00:00:00 + + 3 + 1088.8 + + + + + http://services.odata.org/V3/OData/OData.svc/Products(9) + + + + + + + Lemonade + Classic, refreshing lemonade (Single bottle) + 2017-09-07T21:23:43Z + + + + + + + + + + 9 + 1970-01-01T00:00:00 + + 7 + 1.01 + + + + + http://services.odata.org/V3/OData/OData.svc/Products(10) + + + + + + + Coffee + Bulk size can of instant coffee + 2017-09-07T21:23:43Z + + + + + + + + + + 10 + 1982-12-31T00:00:00 + + 1 + 6.99 + + + + \ No newline at end of file diff --git a/spec/fixtures/odata.org/odata_v3_service.xml b/spec/fixtures/odata.org/odata_v3_service.xml new file mode 100644 index 0000000..d2207b0 --- /dev/null +++ b/spec/fixtures/odata.org/odata_v3_service.xml @@ -0,0 +1,27 @@ + + + + Default + + Products + + + ProductDetails + + + Categories + + + Suppliers + + + Persons + + + PersonDetails + + + Advertisements + + + \ No newline at end of file diff --git a/spec/fixtures/odata.org/odata_v4_products.json b/spec/fixtures/odata.org/odata_v4_products.json new file mode 100644 index 0000000..b9c0e9a --- /dev/null +++ b/spec/fixtures/odata.org/odata_v4_products.json @@ -0,0 +1,106 @@ +{ + "@odata.context": "http://services.odata.org/V4/OData/OData.svc/$metadata#Products", + "value": [ + { + "Description": "Whole grain bread", + "DiscontinuedDate": null, + "ID": 0, + "Name": "Bread", + "Price": 2.5, + "Rating": 4, + "ReleaseDate": "1992-01-01T00:00:00Z" + }, + { + "Description": "Low fat milk", + "DiscontinuedDate": null, + "ID": 1, + "Name": "Milk", + "Price": 3.5, + "Rating": 3, + "ReleaseDate": "1995-10-01T00:00:00Z" + }, + { + "Description": "Americana Variety - Mix of 6 flavors", + "DiscontinuedDate": null, + "ID": 2, + "Name": "Vint soda", + "Price": 20.9, + "Rating": 3, + "ReleaseDate": "2000-10-01T00:00:00Z" + }, + { + "Description": "The Original Key Lime Cola", + "DiscontinuedDate": "2006-10-01T00:00:00Z", + "ID": 3, + "Name": "Havina Cola", + "Price": 19.9, + "Rating": 3, + "ReleaseDate": "2005-10-01T00:00:00Z" + }, + { + "Description": "Mango flavor, 8.3 Ounce Cans (Pack of 24)", + "DiscontinuedDate": null, + "ID": 4, + "Name": "Fruit Punch", + "Price": 22.99, + "Rating": 3, + "ReleaseDate": "2003-01-05T00:00:00Z" + }, + { + "Description": "16-Ounce Plastic Bottles (Pack of 12)", + "DiscontinuedDate": null, + "ID": 5, + "Name": "Cranberry Juice", + "Price": 22.8, + "Rating": 3, + "ReleaseDate": "2006-08-04T00:00:00Z" + }, + { + "Description": "36 Ounce Cans (Pack of 3)", + "DiscontinuedDate": null, + "ID": 6, + "Name": "Pink Lemonade", + "Price": 18.8, + "Rating": 3, + "ReleaseDate": "2006-11-05T00:00:00Z" + }, + { + "Description": "1080P Upconversion DVD Player", + "DiscontinuedDate": null, + "ID": 7, + "Name": "DVD Player", + "Price": 35.88, + "Rating": 5, + "ReleaseDate": "2006-11-15T00:00:00Z" + }, + { + "Description": "42 inch 1080p LCD with Built-in Blu-ray Disc Player", + "DiscontinuedDate": null, + "ID": 8, + "Name": "LCD HDTV", + "Price": 1088.8, + "Rating": 3, + "ReleaseDate": "2008-05-08T00:00:00Z" + }, + { + "@odata.type": "#ODataDemo.FeaturedProduct", + "Description": "Classic, refreshing lemonade (Single bottle)", + "DiscontinuedDate": null, + "ID": 9, + "Name": "Lemonade", + "Price": 1.01, + "Rating": 7, + "ReleaseDate": "1970-01-01T00:00:00Z" + }, + { + "@odata.type": "#ODataDemo.FeaturedProduct", + "Description": "Bulk size can of instant coffee", + "DiscontinuedDate": null, + "ID": 10, + "Name": "Coffee", + "Price": 6.99, + "Rating": 1, + "ReleaseDate": "1982-12-31T00:00:00Z" + } + ] +} \ No newline at end of file diff --git a/spec/fixtures/odata.org/odata_v4_service.xml b/spec/fixtures/odata.org/odata_v4_service.xml new file mode 100644 index 0000000..75a0b40 --- /dev/null +++ b/spec/fixtures/odata.org/odata_v4_service.xml @@ -0,0 +1,27 @@ + + + + Default + + Products + + + ProductDetails + + + Categories + + + Suppliers + + + Persons + + + PersonDetails + + + Advertisements + + + \ No newline at end of file From 5d48613ddaca3c06df0650bf00ee89d5ea4e84d9 Mon Sep 17 00:00:00 2001 From: Klaus Date: Tue, 19 Sep 2017 23:14:44 +0200 Subject: [PATCH 21/28] rewrite json_serialization_spec to use subject --- spec/internal/json_serialization_spec.rb | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/spec/internal/json_serialization_spec.rb b/spec/internal/json_serialization_spec.rb index c8f1760..009e801 100644 --- a/spec/internal/json_serialization_spec.rb +++ b/spec/internal/json_serialization_spec.rb @@ -14,10 +14,14 @@ module OData stub_request(:get, "http://test.com/test.svc/VirtualMachines"). with(:headers => DEFAULT_HEADERS). to_return(:status => 200, :body => File.new( FIXTURES + "/ms_system_center/virtual_machines.xml"), :headers => {}) + @service = OData::Service.new "http://test.com/test.svc/", { :username => username, :password => password, :verify_ssl => false, :namespace => "VMM" } + end + + subject do @service.VirtualMachines results = @service.execute - @json = results.first.as_json + results.first.as_json end after(:each) do @@ -25,18 +29,18 @@ module OData end it "Should quote Edm.Int64 properties" do - @json["PerfDiskBytesWrite"].should be_a(String) + subject["PerfDiskBytesWrite"].should be_a(String) end it "Should output collections with metadata" do - @json["VMNetworkAssignments"].should be_a(Hash) - @json["VMNetworkAssignments"].should have_key("__metadata") - @json["VMNetworkAssignments"]["__metadata"].should be_a(Hash) - @json["VMNetworkAssignments"]["__metadata"].should have_key("type") - @json["VMNetworkAssignments"]["__metadata"]["type"].should eq("Collection(VMM.VMNetworkAssignment)") - @json["VMNetworkAssignments"].should have_key("results") - @json["VMNetworkAssignments"]["results"].should be_a(Array) - @json["VMNetworkAssignments"]["results"].should eq([]) + subject["VMNetworkAssignments"].should be_a(Hash) + subject["VMNetworkAssignments"].should have_key("__metadata") + subject["VMNetworkAssignments"]["__metadata"].should be_a(Hash) + subject["VMNetworkAssignments"]["__metadata"].should have_key("type") + subject["VMNetworkAssignments"]["__metadata"]["type"].should eq("Collection(VMM.VMNetworkAssignment)") + subject["VMNetworkAssignments"].should have_key("results") + subject["VMNetworkAssignments"]["results"].should be_a(Array) + subject["VMNetworkAssignments"]["results"].should eq([]) end end end \ No newline at end of file From 9162df657867792ade9b2f9f38448740a8ba1387 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 5 Oct 2017 22:49:07 +0200 Subject: [PATCH 22/28] remove_classes in keys_spec --- spec/internal/keys_spec.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spec/internal/keys_spec.rb b/spec/internal/keys_spec.rb index 86ddb67..5eafcdf 100644 --- a/spec/internal/keys_spec.rb +++ b/spec/internal/keys_spec.rb @@ -12,6 +12,10 @@ module OData @service = OData::Service.new "http://test.com/test.svc/" end + after(:all) do + remove_classes @service + end + context "has the correct metadata" do before(:all) do @id_meta = @service.class_metadata['Car']['id'] @@ -53,6 +57,10 @@ module OData @service = OData::Service.new "http://test.com/test.svc/" end + after(:all) do + remove_classes @service + end + context "has the correct metadata" do before(:all) do @id_meta = @service.class_metadata['Boat']['KeyId'] From 5725862320b3944c5d87c665394326db5c3fbf05 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 5 Oct 2017 23:16:12 +0200 Subject: [PATCH 23/28] create spec/support/fixtures_load --- spec/spec_helper.rb | 2 -- spec/support/fixtures_load.rb | 8 ++++++++ 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 spec/support/fixtures_load.rb diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 8219ff2..5550b87 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -8,8 +8,6 @@ Dir[File.expand_path('../support/**/*.rb', __FILE__)].each { |f| require f } -FIXTURES = File.expand_path('../fixtures', __FILE__) - WebMock.disable_net_connect!(allow_localhost: true) DEFAULT_HEADERS = { diff --git a/spec/support/fixtures_load.rb b/spec/support/fixtures_load.rb new file mode 100644 index 0000000..9f01730 --- /dev/null +++ b/spec/support/fixtures_load.rb @@ -0,0 +1,8 @@ +class Fixtures + + FIXTURES = File.expand_path('../../fixtures', __FILE__) + + def self.load(file) + File.new(FIXTURES + "/" + file) + end +end \ No newline at end of file From a4626eca271668a5c2694dbe6c344ec74728a42c Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 5 Oct 2017 23:17:10 +0200 Subject: [PATCH 24/28] use Fixtures.load() in specs --- spec/internal/association_spec.rb | 2 +- spec/internal/json_serialization_spec.rb | 4 +- spec/internal/keys_spec.rb | 12 ++-- spec/service_detailed_spec.rb | 66 +++++++++---------- spec/service_spec.rb | 15 +++-- spec/service_v4_spec.rb | 4 +- spec/services/dynamics_nav_spec.rb | 10 +-- spec/services/microsoft_system_center_spec.rb | 8 +-- spec/services/sample_service_spec.rb | 12 ++-- spec/services/sap_spec.rb | 4 +- 10 files changed, 70 insertions(+), 67 deletions(-) diff --git a/spec/internal/association_spec.rb b/spec/internal/association_spec.rb index a075a0f..c84f4f7 100644 --- a/spec/internal/association_spec.rb +++ b/spec/internal/association_spec.rb @@ -6,7 +6,7 @@ module OData before(:all) do stub_request(:get, /http:\/\/test\.com\/test\.svc\/\$metadata(?:\?.+)?/). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new( FIXTURES + "/sample_service/edmx_categories_products.xml"), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/sample_service/edmx_categories_products.xml"), :headers => {}) @service = OData::Service.new "http://test.com/test.svc/$metadata" @product_category = RSpecSupport::ElementHelpers.string_to_element('') diff --git a/spec/internal/json_serialization_spec.rb b/spec/internal/json_serialization_spec.rb index 009e801..3c42f8a 100644 --- a/spec/internal/json_serialization_spec.rb +++ b/spec/internal/json_serialization_spec.rb @@ -9,11 +9,11 @@ module OData # Required for the build_classes method stub_request(:get, "http://test.com/test.svc/$metadata"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new( FIXTURES + "/ms_system_center/edmx_ms_system_center.xml"), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/ms_system_center/edmx_ms_system_center.xml"), :headers => {}) stub_request(:get, "http://test.com/test.svc/VirtualMachines"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new( FIXTURES + "/ms_system_center/virtual_machines.xml"), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/ms_system_center/virtual_machines.xml"), :headers => {}) @service = OData::Service.new "http://test.com/test.svc/", { :username => username, :password => password, :verify_ssl => false, :namespace => "VMM" } end diff --git a/spec/internal/keys_spec.rb b/spec/internal/keys_spec.rb index 5eafcdf..d1545c7 100644 --- a/spec/internal/keys_spec.rb +++ b/spec/internal/keys_spec.rb @@ -8,7 +8,7 @@ module OData before(:all) do stub_request(:get, "http://test.com/test.svc/$metadata"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new( FIXTURES + "/int64_ids/edmx_car_service.xml"), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/int64_ids/edmx_car_service.xml"), :headers => {}) @service = OData::Service.new "http://test.com/test.svc/" end @@ -34,7 +34,7 @@ module OData before(:each) do stub_request(:get, "http://test.com/test.svc/Cars(213L)"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new( FIXTURES + "/int64_ids/result_cars.xml"), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/int64_ids/result_cars.xml"), :headers => {}) @service.Cars(213) results = @service.execute @@ -53,7 +53,7 @@ module OData before(:all) do stub_request(:get, "http://test.com/test.svc/$metadata"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new( FIXTURES + "/int64_ids/edmx_boat_service.xml"), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/int64_ids/edmx_boat_service.xml"), :headers => {}) @service = OData::Service.new "http://test.com/test.svc/" end @@ -79,7 +79,7 @@ module OData before(:each) do stub_request(:get, "http://test.com/test.svc/Boats(213L)"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new( FIXTURES + "/int64_ids/result_boats.xml"), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/int64_ids/result_boats.xml"), :headers => {}) @service.Boats(213) results = @service.execute @@ -93,9 +93,9 @@ module OData end end - describe "Collection with a string key named 'xxx" do + describe "Collection with a string key named 'xxx" do end - + end end diff --git a/spec/service_detailed_spec.rb b/spec/service_detailed_spec.rb index 50c9b29..41912d6 100644 --- a/spec/service_detailed_spec.rb +++ b/spec/service_detailed_spec.rb @@ -12,7 +12,7 @@ module OData # Required for the build_classes method stub_request(:get, "http://test.com/test.svc/$metadata"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/edmx_empty.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("edmx_empty.xml"), :headers => {}) @service =OData::Service.new "http://test.com/test.svc/" end @@ -20,7 +20,7 @@ module OData # Required for the build_classes method stub_request(:get, "http://test.com/test.svc/$metadata"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/edmx_lowercase.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("edmx_lowercase.xml"), :headers => {}) expect { @service = OData::Service.new "http://test.com/test.svc" }.not_to raise_error end @@ -30,7 +30,7 @@ module OData # Required for the build_classes method stub_request(:get, /http:\/\/test\.com\/test\.svc\/\$metadata(?:\?.+)?/). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/edmx_empty.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("edmx_empty.xml"), :headers => {}) end it "should accept additional query string parameters" do @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } @@ -47,7 +47,7 @@ module OData # Required for the build_classes method stub_request(:get, /http:\/\/test\.com\/test\.svc\/\$metadata(?:\?.+)?/). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/edmx_empty.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("edmx_empty.xml"), :headers => {}) end it "should accept in options that will be passed to the rest-client lib" do @service =OData::Service.new "http://test.com/test.svc/", { :rest_options => { :ssl_ca_file => "ca_certificate.pem" } } @@ -65,7 +65,7 @@ module OData # Required for the build_classes method stub_request(:any, /http:\/\/test\.com\/test\.svc(?:.*)/). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sap/edmx_sap_demo_flight.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/sap/edmx_sap_demo_flight.xml"), :headers => {}) end it "should pass the parameters as part of a query" do @service =OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } } @@ -131,11 +131,11 @@ module OData before(:each) do stub_request(:get, "http://test.com/test.svc/$metadata"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/edmx_categories_products.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("sample_service/edmx_categories_products.xml"), :headers => {}) stub_request(:get, "http://test.com/test.svc/Categories?$select=Price"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 400, :body => File.new(File.expand_path("../fixtures/error_without_message.xml", __FILE__)), :headers => {}) + to_return(:status => 400, :body => Fixtures.load("error_without_message.xml"), :headers => {}) end it "includes a generic message if the error is not in the response" do @@ -153,7 +153,7 @@ module OData # Required for the build_classes method stub_request(:get, "http://test.com/test.svc/$metadata"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/edmx_lowercase.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("edmx_lowercase.xml"), :headers => {}) end it "should respond_to a lowercase collection" do @@ -173,17 +173,17 @@ module OData # Metadata stub_request(:get, "http://test.com/test.svc/$metadata"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/feed_customization/edmx_feed_customization.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("feed_customization/edmx_feed_customization.xml"), :headers => {}) # Content - Products stub_request(:get, /http:\/\/test\.com\/test\.svc\/Products(?:.*)/). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/feed_customization/result_feed_customization_products_expand.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("feed_customization/result_feed_customization_products_expand.xml"), :headers => {}) # Content - Categories expanded Products stub_request(:get, /http:\/\/test\.com\/test\.svc\/Categories(?:.*)/). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/feed_customization/result_feed_customization_categories_expand.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("feed_customization/result_feed_customization_categories_expand.xml"), :headers => {}) end describe "handling feed customizations" do @@ -299,12 +299,12 @@ module OData # Metadata stub_request(:get, "http://test.com/test.svc/$metadata"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/inheritance/edmx_pluralsight.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("inheritance/edmx_pluralsight.xml"), :headers => {}) # Content - Courses stub_request(:get, /http:\/\/test\.com\/test\.svc\/Courses(?:.*)/). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/inheritance/result_pluralsight_courses.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("inheritance/result_pluralsight_courses.xml"), :headers => {}) end it "should build all inherited attributes" do @@ -347,20 +347,20 @@ module OData # Metadata stub_request(:get, "http://test.com/test.svc/$metadata"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/partial/partial_feed_metadata.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("partial/partial_feed_metadata.xml"), :headers => {}) # Content - Partial stub_request(:get, "http://test.com/test.svc/Partials"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/partial/partial_feed_part_1.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("partial/partial_feed_part_1.xml"), :headers => {}) stub_request(:get, "http://test.com/test.svc/Partials?$skiptoken='ERNSH'"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/partial/partial_feed_part_2.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("partial/partial_feed_part_2.xml"), :headers => {}) stub_request(:get, "http://test.com/test.svc/Partials?$skiptoken='ERNSH2'"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/partial/partial_feed_part_3.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("partial/partial_feed_part_3.xml"), :headers => {}) end @@ -387,15 +387,15 @@ module OData before(:each) do stub_request(:get, "http://test.com/test.svc/$metadata?extra_param=value"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/partial/partial_feed_metadata.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("partial/partial_feed_metadata.xml"), :headers => {}) stub_request(:get, "http://test.com/test.svc/Partials?extra_param=value"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/partial/partial_feed_part_1.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("partial/partial_feed_part_1.xml"), :headers => {}) stub_request(:get, "http://test.com/test.svc/Partials?$skiptoken='ERNSH'&extra_param=value"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/partial/partial_feed_part_2.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("partial/partial_feed_part_2.xml"), :headers => {}) end it "should persist the additional parameters for the next call" do @@ -414,11 +414,11 @@ module OData # Required for the build_classes method stub_request(:get, /http:\/\/test\.com\/test\.svc\/\$metadata(?:\?.+)?/). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/edmx_categories_products.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("sample_service/edmx_categories_products.xml"), :headers => {}) stub_request(:get, "http://test.com/test.svc/Categories(1)/$links/Products"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/links/result_links_query.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("links/result_links_query.xml"), :headers => {}) end it "should be able to parse the results of a links query" do @service =OData::Service.new "http://test.com/test.svc/" @@ -439,11 +439,11 @@ module OData before(:each) do stub_request(:get, "http://test.com/test.svc/$metadata"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/nested_expands/edmx_northwind.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("nested_expands/edmx_northwind.xml"), :headers => {}) stub_request(:get, "http://test.com/test.svc/Products?$expand=Category,Category/Products&$top=2"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/nested_expands/northwind_products_category_expands.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("nested_expands/northwind_products_category_expands.xml"), :headers => {}) end after(:each) do #Object.send(:remove_const, 'Product') if Object.const_defined? 'Product' @@ -490,11 +490,11 @@ module OData before(:each) do stub_request(:get, "http://test.com/test.svc/$metadata"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new( FIXTURES + "/sample_service/edmx_categories_products.xml"), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/sample_service/edmx_categories_products.xml"), :headers => {}) stub_request(:get, "http://test.com/test.svc/Products?$select=Name,Price"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new( FIXTURES + "/sample_service/result_select_products_name_price.xml"), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/sample_service/result_select_products_name_price.xml"), :headers => {}) end before(:each) do @@ -514,11 +514,11 @@ module OData before(:each) do stub_request(:get, "http://test.com/test.svc/$metadata"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new( FIXTURES + "/sample_service/edmx_categories_products.xml"), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("sample_service/edmx_categories_products.xml"), :headers => {}) stub_request(:get, "http://test.com/test.svc/Categories?$select=Price"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 400, :body => File.new( FIXTURES + "/sample_service/result_select_categories_no_property.xml"), :headers => {}) + to_return(:status => 400, :body => Fixtures.load("sample_service/result_select_categories_no_property.xml"), :headers => {}) end it "raises an exception" do @@ -535,19 +535,19 @@ module OData before(:each) do stub_request(:get, "http://test.com/test.svc/$metadata"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new( FIXTURES + "/sample_service/edmx_categories_products.xml"), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("sample_service/edmx_categories_products.xml"), :headers => {}) stub_request(:get, "http://test.com/test.svc/Categories?$select=Name,Products/Name"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 400, :body => File.new( FIXTURES + "/sample_service/result_select_categories_travsing_no_expand.xml"), :headers => {}) + to_return(:status => 400, :body => Fixtures.load("sample_service/result_select_categories_travsing_no_expand.xml"), :headers => {}) stub_request(:get, "http://test.com/test.svc/Categories?$select=Name,Products/Name&$expand=Products"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new( FIXTURES + "/sample_service/result_select_categories_expand.xml"), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("sample_service/result_select_categories_expand.xml"), :headers => {}) stub_request(:get, "http://test.com/test.svc/Categories"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new( FIXTURES + "/sample_service/result_select_categories_expand.xml"), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("sample_service/result_select_categories_expand.xml"), :headers => {}) end @@ -582,7 +582,7 @@ module OData # Required for the build_classes method stub_request(:get, "http://test.com/test.svc/$metadata"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new( FIXTURES + "/edmx_empty.xml" ), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("edmx_empty.xml" ), :headers => {}) end it "should not error on an 'out of range' date" do diff --git a/spec/service_spec.rb b/spec/service_spec.rb index 470b1c7..7a4a2e5 100644 --- a/spec/service_spec.rb +++ b/spec/service_spec.rb @@ -5,9 +5,9 @@ module OData describe Service do before(:each) do - stub_request(:get, /http:\/\/test\.com\/test\.svc\/\$metadata(?:\?.+)?/). + stub_request(:get, "http://test.com/test.svc/$metadata"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/edmx_categories_products.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/sample_service/edmx_categories_products.xml"), :headers => {}) @service = OData::Service.new "http://test.com/test.svc" end @@ -45,6 +45,7 @@ module OData context "collections method" do subject { @service.collections } + it { should include 'Products' } it { should include 'Categories' } it "should expose the edmx type of objects" do @@ -59,6 +60,7 @@ module OData context "class metadata" do subject { @service.class_metadata } + it { should_not be_empty} it { should have_key 'Product' } it { should have_key 'Category' } @@ -109,6 +111,7 @@ module OData context "function_imports method" do subject { @service.function_imports } + it { should_not be_empty} it { should have_key 'CleanDatabaseForTesting' } it { should have_key 'EntityCategoryWebGet' } @@ -119,7 +122,7 @@ module OData subject['EntitySingleCategoryWebGet'][:http_method].should eq 'GET' end it "should expose the return type" do - subject['CleanDatabaseForTesting'][:return_typo].should be_nil + subject['CleanDatabaseForTesting'][:return_type].should be_nil subject['EntityCategoryWebGet'][:return_type].should eq Array subject['EntityCategoryWebGet'][:inner_return_type].should eq Category subject['EntitySingleCategoryWebGet'][:return_type].should eq Category @@ -218,7 +221,7 @@ module OData authorization_header = { authorization: "Basic #{Base64::encode64(auth_string).strip}" } stub_request(:get, "http://test.com/test.svc/$metadata"). with(:headers => DEFAULT_HEADERS.merge(authorization_header)). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/ms_system_center/edmx_ms_system_center_v2.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/ms_system_center/edmx_ms_system_center_v2.xml"), :headers => {}) end after(:each) do @@ -235,11 +238,11 @@ module OData before(:each) do stub_request(:get, "http://service1.com/test.svc/$metadata"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/edmx_categories_products.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/edmx_empty.xml"), :headers => {}) stub_request(:get, "http://service2.com/test.svc/$metadata"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/int64_ids/edmx_car_service.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/edmx_empty.xml"), :headers => {}) @service1 = OData::Service.new "http://service1.com/test.svc" diff --git a/spec/service_v4_spec.rb b/spec/service_v4_spec.rb index 2e7386e..549d1c7 100644 --- a/spec/service_v4_spec.rb +++ b/spec/service_v4_spec.rb @@ -6,10 +6,10 @@ module OData before(:all) do stub_request(:get, "http://test.com/test.svc/$metadata"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/v4/edmx_metadata.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/v4/edmx_metadata.xml"), :headers => {}) stub_request(:get, "http://test.com/test.svc/Categories"). - to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/v4/result_categories.xml", __FILE__)), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/v4/result_categories.xml"), :headers => {}) @service = OData::Service.new "http://test.com/test.svc" end diff --git a/spec/services/dynamics_nav_spec.rb b/spec/services/dynamics_nav_spec.rb index 8fa7fc3..854bf53 100644 --- a/spec/services/dynamics_nav_spec.rb +++ b/spec/services/dynamics_nav_spec.rb @@ -15,23 +15,23 @@ module OData stub_request(:get, "http://test.com/nav.svc/$metadata"). with(:headers => headers). - to_return(:status => 200, :body => File.new( FIXTURES + "/ms_dynamics_nav/edmx_ms_dynamics_nav.xml"), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/ms_dynamics_nav/edmx_ms_dynamics_nav.xml"), :headers => {}) stub_request(:get, "http://test.com/nav.svc/Customer"). with(:headers => headers). - to_return(:status => 200, :body => File.new( FIXTURES + "/ms_dynamics_nav/result_customer.xml"), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/ms_dynamics_nav/result_customer.xml"), :headers => {}) stub_request(:get, "http://test.com/nav.svc/Customer('100013')"). with(:headers => headers). - to_return(:status => 200, :body => File.new( FIXTURES + "/ms_dynamics_nav/result_customer.xml"), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/ms_dynamics_nav/result_customer.xml"), :headers => {}) stub_request(:get, "http://test.com/nav.svc/Customer(100013)"). with(:headers => headers). - to_return(:status => 400, :body => File.new( FIXTURES + "/ms_dynamics_nav/result_customer_error.xml"), :headers => {}) + to_return(:status => 400, :body => Fixtures.load("/ms_dynamics_nav/result_customer_error.xml"), :headers => {}) stub_request(:get, "http://test.com/nav.svc/SalesOrder(Document_Type='Order',No='AB-1600013')"). with(:headers => headers). - to_return(:status => 200, :body => File.new( FIXTURES + "/ms_dynamics_nav/result_sales_order.xml"), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/ms_dynamics_nav/result_sales_order.xml"), :headers => {}) @service = OData::Service.new "http://test.com/nav.svc/", { :username => username, :password => password, :verify_ssl => false } diff --git a/spec/services/microsoft_system_center_spec.rb b/spec/services/microsoft_system_center_spec.rb index 30a0e65..1a1924e 100644 --- a/spec/services/microsoft_system_center_spec.rb +++ b/spec/services/microsoft_system_center_spec.rb @@ -14,19 +14,19 @@ module OData # Required for the build_classes method stub_request(:get, "http://test.com/test.svc/$metadata"). with(:headers => headers). - to_return(:status => 200, :body => File.new( FIXTURES + "/ms_system_center/edmx_ms_system_center.xml"), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/ms_system_center/edmx_ms_system_center.xml"), :headers => {}) stub_request(:get, "http://test.com/test.svc/VirtualMachines"). with(:headers => headers). - to_return(:status => 200, :body => File.new( FIXTURES + "/ms_system_center/virtual_machines.xml"), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/ms_system_center/virtual_machines.xml"), :headers => {}) stub_request(:get, "http://test.com/test.svc/HardwareProfiles?$filter=Memory%20eq%203500"). with(:headers => headers). - to_return(:status => 200, :body => File.new( FIXTURES + "/ms_system_center/hardware_profiles.xml"), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/ms_system_center/hardware_profiles.xml"), :headers => {}) stub_request(:get, "http://test.com/test.svc/VMTemplates"). with(:headers => headers). - to_return(:status => 200, :body => File.new( FIXTURES + "/ms_system_center/vm_templates.xml"), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/ms_system_center/vm_templates.xml"), :headers => {}) @service = OData::Service.new "http://test.com/test.svc/", { :username => username, :password => password, :verify_ssl => false, :namespace => "VMM" } end diff --git a/spec/services/sample_service_spec.rb b/spec/services/sample_service_spec.rb index 42f7d31..39c98ef 100644 --- a/spec/services/sample_service_spec.rb +++ b/spec/services/sample_service_spec.rb @@ -7,27 +7,27 @@ module OData # Required for the build_classes method stub_request(:get, /http:\/\/test\.com\/test\.svc\/\$metadata(?:\?.+)?/). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new( FIXTURES + "/sample_service/edmx_categories_products.xml"), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/sample_service/edmx_categories_products.xml"), :headers => {}) stub_request(:get, /http:\/\/test\.com\/test\.svc\/Products\(\d\)/). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new( FIXTURES + "/sample_service/result_single_product.xml"), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/sample_service/result_single_product.xml"), :headers => {}) stub_request(:get, /http:\/\/test\.com\/test\.svc\/Products\(\d{2,}\)/). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(FIXTURES + "/sample_service/result_single_product_not_found.xml"), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/sample_service/result_single_product_not_found.xml"), :headers => {}) stub_request(:get, "http://test.com/test.svc/Products(1)/Category"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(FIXTURES + "/sample_service/result_single_category.xml"), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/sample_service/result_single_category.xml"), :headers => {}) stub_request(:get, "http://test.com/test.svc/Categories(1)"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(FIXTURES + "/sample_service/result_single_category.xml"), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/sample_service/result_single_category.xml"), :headers => {}) stub_request(:get, "http://test.com/test.svc/Categories(1)/Products"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new(FIXTURES + "/sample_service/result_multiple_category_products.xml"), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/sample_service/result_multiple_category_products.xml"), :headers => {}) stub_request(:post, "http://test.com/test.svc/Categories(1)/$links/Products").to_return(:status => 204) stub_request(:post, "http://test.com/test.svc/$batch").to_return(:status => 202) diff --git a/spec/services/sap_spec.rb b/spec/services/sap_spec.rb index 08e9247..238b2a6 100644 --- a/spec/services/sap_spec.rb +++ b/spec/services/sap_spec.rb @@ -7,11 +7,11 @@ module OData # Required for the build_classes method stub_request(:get, "http://test.com/test.svc/$metadata"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new( FIXTURES + "/sap/edmx_sap_demo_flight.xml"), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/sap/edmx_sap_demo_flight.xml"), :headers => {}) stub_request(:get, "http://test.com/test.svc/z_demo_flightCollection"). with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => File.new( FIXTURES + "/sap/result_sap_demo_flight_missing_category.xml"), :headers => {}) + to_return(:status => 200, :body => Fixtures.load("/sap/result_sap_demo_flight_missing_category.xml"), :headers => {}) end it "should handle entities without a category element" do From 2ebedb768bdb0bfc8d9546f8c0ae52eeb7a77058 Mon Sep 17 00:00:00 2001 From: klaus Date: Mon, 9 Oct 2017 17:27:08 +0200 Subject: [PATCH 25/28] Reformat XML fixtures for better readability --- spec/fixtures/decimal/metadata.xml | 1285 ++++++++++++++++- .../partial/partial_feed_metadata.xml | 48 +- .../edmx_categories_products.xml | 72 +- .../sample_service/result_category_names.xml | 6 +- 4 files changed, 1382 insertions(+), 29 deletions(-) diff --git a/spec/fixtures/decimal/metadata.xml b/spec/fixtures/decimal/metadata.xml index c4855f7..ceb6b86 100644 --- a/spec/fixtures/decimal/metadata.xml +++ b/spec/fixtures/decimal/metadata.xml @@ -1 +1,1284 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spec/fixtures/partial/partial_feed_metadata.xml b/spec/fixtures/partial/partial_feed_metadata.xml index 66e26cd..ce934ac 100644 --- a/spec/fixtures/partial/partial_feed_metadata.xml +++ b/spec/fixtures/partial/partial_feed_metadata.xml @@ -1,25 +1,25 @@ - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spec/fixtures/sample_service/edmx_categories_products.xml b/spec/fixtures/sample_service/edmx_categories_products.xml index 201838a..d80324d 100644 --- a/spec/fixtures/sample_service/edmx_categories_products.xml +++ b/spec/fixtures/sample_service/edmx_categories_products.xml @@ -1 +1,71 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spec/fixtures/sample_service/result_category_names.xml b/spec/fixtures/sample_service/result_category_names.xml index e2988c4..c48895f 100644 --- a/spec/fixtures/sample_service/result_category_names.xml +++ b/spec/fixtures/sample_service/result_category_names.xml @@ -1,5 +1,5 @@ -Test Category 1 -Test Category 2 -Test Category 3 + Test Category 1 + Test Category 2 + Test Category 3 \ No newline at end of file From a345c8b638bbbb21855543eae5291488850be0ac Mon Sep 17 00:00:00 2001 From: klaus Date: Mon, 9 Oct 2017 18:05:51 +0200 Subject: [PATCH 26/28] extract spec to internal/parse_value_spec --- spec/internal/parse_value_spec.rb | 21 +++++++++++++++++++++ spec/service_detailed_spec.rb | 27 +++++---------------------- 2 files changed, 26 insertions(+), 22 deletions(-) create mode 100644 spec/internal/parse_value_spec.rb diff --git a/spec/internal/parse_value_spec.rb b/spec/internal/parse_value_spec.rb new file mode 100644 index 0000000..6819410 --- /dev/null +++ b/spec/internal/parse_value_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +module OData + describe_private OData::Service do + describe "parse value" do + before(:each) do + # Required for the build_classes method + stub_request(:get, "http://test.com/test.svc/$metadata"). + with(:headers => DEFAULT_HEADERS). + to_return(:status => 200, :body => Fixtures.load("edmx_empty.xml" ), :headers => {}) + end + + it "should not error on an 'out of range' date" do + # This date was returned in the Netflix OData service and failed with an ArgumentError: out of range using 1.8.7 (2010-12-23 patchlevel 330) [i386-mingw32] + @service =OData::Service.new "http://test.com/test.svc/" + element_to_parse = Nokogiri::XML.parse('2100-01-01T00:00:00').elements[0] + lambda { @service.parse_value_xml(element_to_parse) }.should_not raise_exception + end + end + end +end \ No newline at end of file diff --git a/spec/service_detailed_spec.rb b/spec/service_detailed_spec.rb index 41912d6..a9a4743 100644 --- a/spec/service_detailed_spec.rb +++ b/spec/service_detailed_spec.rb @@ -495,7 +495,7 @@ module OData stub_request(:get, "http://test.com/test.svc/Products?$select=Name,Price"). with(:headers => DEFAULT_HEADERS). to_return(:status => 200, :body => Fixtures.load("/sample_service/result_select_products_name_price.xml"), :headers => {}) - end + end before(:each) do @service =OData::Service.new "http://test.com/test.svc/" @@ -504,8 +504,8 @@ module OData end it "returns an Array og Products" do - expect(@result).to be_an Array - expect(@result).not_to be_empty + expect(@result).to be_an Array + expect(@result).not_to be_empty expect(@result.first).to be_a Product end end @@ -552,13 +552,13 @@ module OData end it "retursn Categoris" do - @service =OData::Service.new "http://test.com/test.svc/" + @service =OData::Service.new "http://test.com/test.svc/" @service.Categories c = @service.execute end it "doesn't error" do - @service =OData::Service.new "http://test.com/test.svc/" + @service =OData::Service.new "http://test.com/test.svc/" @service.Categories.select "Name", "Products/Name" expect { @service.execute }.to_not raise_error end @@ -576,21 +576,4 @@ module OData end end - describe_private OData::Service do - describe "parse value" do - before(:each) do - # Required for the build_classes method - stub_request(:get, "http://test.com/test.svc/$metadata"). - with(:headers => DEFAULT_HEADERS). - to_return(:status => 200, :body => Fixtures.load("edmx_empty.xml" ), :headers => {}) - end - - it "should not error on an 'out of range' date" do - # This date was returned in the Netflix OData service and failed with an ArgumentError: out of range using 1.8.7 (2010-12-23 patchlevel 330) [i386-mingw32] - @service =OData::Service.new "http://test.com/test.svc/" - element_to_parse = Nokogiri::XML.parse('2100-01-01T00:00:00').elements[0] - lambda { @service.parse_value_xml(element_to_parse) }.should_not raise_exception - end - end - end end From 208a3d8a4ee4bec797ea71b09bc2ffdb2c697872 Mon Sep 17 00:00:00 2001 From: klaus Date: Mon, 9 Oct 2017 18:26:30 +0200 Subject: [PATCH 27/28] describe Service#classes --- spec/service_spec.rb | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/spec/service_spec.rb b/spec/service_spec.rb index 7a4a2e5..5c39e14 100644 --- a/spec/service_spec.rb +++ b/spec/service_spec.rb @@ -23,9 +23,9 @@ module OData it { should respond_to :class_metadata } it { should respond_to :function_imports } it { should respond_to :classes } - + it { should respond_to :execute } - + it { should respond_to :update_object } it { should respond_to :delete_object } it { should respond_to :save_changes } @@ -108,7 +108,7 @@ module OData end end end - + context "function_imports method" do subject { @service.function_imports } @@ -212,6 +212,19 @@ module OData end end end + + context "classes method" do + subject { @service.classes } + + it { should include 'EdmMetadata' } + + it { should include 'AuditFields' } # ComplexType + + it { should include 'Product' } # EntityType + it { should include 'Category' } # EntityType + + end + end @@ -224,7 +237,7 @@ module OData to_return(:status => 200, :body => Fixtures.load("/ms_system_center/edmx_ms_system_center_v2.xml"), :headers => {}) end - after(:each) do + after(:each) do remove_classes @service end From 79eb6bcc555da151ea555baef4b7a88e5bd21061 Mon Sep 17 00:00:00 2001 From: klaus Date: Tue, 10 Oct 2017 10:31:08 +0200 Subject: [PATCH 28/28] Reformat service_v4_spec --- spec/service_v4_spec.rb | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/spec/service_v4_spec.rb b/spec/service_v4_spec.rb index 549d1c7..e7d924c 100644 --- a/spec/service_v4_spec.rb +++ b/spec/service_v4_spec.rb @@ -14,26 +14,28 @@ module OData @service = OData::Service.new "http://test.com/test.svc" end - after(:all) do - remove_classes @service + after(:all) do + remove_classes @service end subject { @service } context "methods" do + it { should respond_to :collections } + it { should respond_to :class_metadata } + it { should respond_to :function_imports } + it { should respond_to :classes } + + it { should respond_to :execute } + it { should respond_to :update_object } it { should respond_to :delete_object } it { should respond_to :save_changes } it { should respond_to :load_property } it { should respond_to :add_link } - it { should respond_to :execute } it { should respond_to :partial? } it { should respond_to :next } - it { should respond_to :classes } - it { should respond_to :class_metadata } - it { should respond_to :collections } it { should respond_to :options } - it { should respond_to :function_imports } context "after parsing metadata" do it { should respond_to :Products }