ruby_odata 0.0.10 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. data/.gitignore +9 -3
  2. data/.travis.yml +4 -0
  3. data/CHANGELOG.rdoc +18 -1
  4. data/README.rdoc +77 -19
  5. data/config/cucumber.yml +4 -3
  6. data/features/basic_auth.feature +5 -7
  7. data/features/batch_request.feature +11 -11
  8. data/features/complex_types.feature +11 -11
  9. data/features/query_builder.feature +27 -18
  10. data/features/service.feature +8 -8
  11. data/features/service_manage.feature +27 -12
  12. data/features/service_methods.feature +37 -0
  13. data/features/ssl.feature +4 -4
  14. data/features/step_definitions/pickle_steps.rb +100 -0
  15. data/features/step_definitions/service_steps.rb +99 -65
  16. data/features/support/constants.rb +3 -0
  17. data/features/support/custom_helpers.rb +54 -0
  18. data/features/support/env.rb +0 -10
  19. data/features/support/hooks.rb +1 -1
  20. data/features/support/pickle.rb +82 -0
  21. data/features/type_conversion.feature +10 -10
  22. data/lib/ruby_odata.rb +4 -1
  23. data/lib/ruby_odata/association.rb +36 -0
  24. data/lib/ruby_odata/class_builder.rb +96 -16
  25. data/lib/ruby_odata/helpers.rb +10 -0
  26. data/lib/ruby_odata/operation.rb +7 -5
  27. data/lib/ruby_odata/property_metadata.rb +11 -6
  28. data/lib/ruby_odata/query_builder.rb +20 -1
  29. data/lib/ruby_odata/service.rb +407 -94
  30. data/lib/ruby_odata/version.rb +1 -1
  31. data/ruby_odata.gemspec +2 -0
  32. data/spec/association_spec.rb +48 -0
  33. data/spec/class_builder_spec.rb +11 -2
  34. data/spec/fixtures/inheritance/edmx_pluralsight.xml +111 -0
  35. data/spec/fixtures/inheritance/result_pluralsight_courses.xml +229 -0
  36. data/spec/fixtures/links/result_links_query.xml +6 -0
  37. data/spec/fixtures/partial/partial_feed_metadata.xml +25 -0
  38. data/spec/fixtures/partial/partial_feed_part_1.xml +42 -0
  39. data/spec/fixtures/partial/partial_feed_part_2.xml +42 -0
  40. data/spec/fixtures/partial/partial_feed_part_3.xml +40 -0
  41. data/spec/fixtures/sample_service/edmx_categories_products.xml +1 -0
  42. data/spec/fixtures/sample_service/result_category_names.xml +5 -0
  43. data/spec/fixtures/sample_service/result_entity_category_web_get.xml +29 -0
  44. data/spec/fixtures/sample_service/result_entity_single_category_web_get.xml +23 -0
  45. data/spec/fixtures/sample_service/result_first_category_id.xml +2 -0
  46. data/spec/fixtures/sample_service/result_multiple_category_products.xml +57 -0
  47. data/spec/fixtures/sample_service/result_single_category.xml +18 -0
  48. data/spec/fixtures/sample_service/result_single_product.xml +26 -0
  49. data/spec/fixtures/sample_service/result_single_product_not_found.xml +4 -0
  50. data/spec/fixtures/sap/edmx_sap_demo_flight.xml +62 -58
  51. data/spec/property_metadata_spec.rb +9 -2
  52. data/spec/query_builder_spec.rb +11 -0
  53. data/spec/revised_service_spec.rb +197 -0
  54. data/spec/service_spec.rb +463 -4
  55. data/test/RubyODataService/RubyODataService.sln +20 -0
  56. data/test/RubyODataService/RubyODataService/App_Start/EntityFramework.SqlServerCompact.cs +12 -0
  57. data/test/RubyODataService/RubyODataService/BasicAuth/RubyOData.svc +3 -0
  58. data/test/RubyODataService/RubyODataService/BasicAuth/RubyOData.svc.cs +107 -0
  59. data/test/RubyODataService/RubyODataService/Global.asax +1 -0
  60. data/test/RubyODataService/RubyODataService/Global.asax.cs +49 -0
  61. data/test/RubyODataService/RubyODataService/Models/AuditFields.cs +16 -0
  62. data/test/RubyODataService/RubyODataService/Models/Category.cs +16 -0
  63. data/test/RubyODataService/RubyODataService/Models/Product.cs +20 -0
  64. data/test/RubyODataService/RubyODataService/Properties/AssemblyInfo.cs +35 -0
  65. data/test/RubyODataService/RubyODataService/RubyOData.svc +3 -0
  66. data/test/RubyODataService/RubyODataService/RubyOData.svc.cs +57 -0
  67. data/test/RubyODataService/RubyODataService/RubyODataContext.cs +16 -0
  68. data/test/RubyODataService/RubyODataService/RubyODataService.csproj +159 -0
  69. data/test/RubyODataService/RubyODataService/RubyODataService.csproj.user +31 -0
  70. data/test/RubyODataService/RubyODataService/Web.Debug.config +30 -0
  71. data/test/RubyODataService/RubyODataService/Web.Release.config +31 -0
  72. data/test/RubyODataService/RubyODataService/Web.config +27 -0
  73. data/test/RubyODataService/RubyODataService/bin/EntityFramework.dll +0 -0
  74. data/test/RubyODataService/RubyODataService/bin/Microsoft.Data.Edm.dll +0 -0
  75. data/test/RubyODataService/RubyODataService/bin/Microsoft.Data.OData.dll +0 -0
  76. data/test/RubyODataService/RubyODataService/bin/Microsoft.Data.Services.Client.dll +0 -0
  77. data/test/RubyODataService/RubyODataService/bin/Microsoft.Data.Services.dll +0 -0
  78. data/test/RubyODataService/RubyODataService/bin/Microsoft.Data.Spatial.dll +0 -0
  79. data/test/RubyODataService/RubyODataService/bin/RubyODataService.dll +0 -0
  80. data/test/RubyODataService/RubyODataService/bin/System.Data.SqlServerCe.Entity.dll +0 -0
  81. data/test/RubyODataService/RubyODataService/bin/System.Data.SqlServerCe.dll +0 -0
  82. data/test/RubyODataService/RubyODataService/bin/System.Spatial.dll +0 -0
  83. data/test/RubyODataService/RubyODataService/bin/WebActivator.dll +0 -0
  84. data/test/RubyODataService/RubyODataService/bin/amd64/Microsoft.VC90.CRT/Microsoft.VC90.CRT.manifest +6 -0
  85. data/test/RubyODataService/RubyODataService/bin/amd64/Microsoft.VC90.CRT/README_ENU.txt +0 -0
  86. data/test/RubyODataService/RubyODataService/bin/amd64/Microsoft.VC90.CRT/msvcr90.dll +0 -0
  87. data/test/RubyODataService/RubyODataService/bin/amd64/sqlcecompact40.dll +0 -0
  88. data/test/RubyODataService/RubyODataService/bin/amd64/sqlceer40EN.dll +0 -0
  89. data/test/RubyODataService/RubyODataService/bin/amd64/sqlceme40.dll +0 -0
  90. data/test/RubyODataService/RubyODataService/bin/amd64/sqlceqp40.dll +0 -0
  91. data/test/RubyODataService/RubyODataService/bin/amd64/sqlcese40.dll +0 -0
  92. data/test/RubyODataService/RubyODataService/bin/x86/Microsoft.VC90.CRT/Microsoft.VC90.CRT.manifest +6 -0
  93. data/test/RubyODataService/RubyODataService/bin/x86/Microsoft.VC90.CRT/README_ENU.txt +0 -0
  94. data/test/RubyODataService/RubyODataService/bin/x86/Microsoft.VC90.CRT/msvcr90.dll +0 -0
  95. data/test/RubyODataService/RubyODataService/bin/x86/sqlcecompact40.dll +0 -0
  96. data/test/RubyODataService/RubyODataService/bin/x86/sqlceer40EN.dll +0 -0
  97. data/test/RubyODataService/RubyODataService/bin/x86/sqlceme40.dll +0 -0
  98. data/test/RubyODataService/RubyODataService/bin/x86/sqlceqp40.dll +0 -0
  99. data/test/RubyODataService/RubyODataService/bin/x86/sqlcese40.dll +0 -0
  100. data/test/RubyODataService/RubyODataService/packages.config +7 -0
  101. data/test/RubyODataService/packages/EntityFramework.4.2.0.0/EntityFramework.4.2.0.0.nupkg +0 -0
  102. data/test/RubyODataService/packages/EntityFramework.4.2.0.0/lib/net40/EntityFramework.dll +0 -0
  103. data/test/RubyODataService/packages/EntityFramework.4.2.0.0/lib/net40/EntityFramework.xml +13488 -0
  104. data/test/RubyODataService/packages/EntityFramework.SqlServerCompact.4.1.8482.2/Content/App_Start/EntityFramework.SqlServerCompact.cs.pp +12 -0
  105. data/test/RubyODataService/packages/EntityFramework.SqlServerCompact.4.1.8482.2/EULA_ENU.rtf +969 -0
  106. data/test/RubyODataService/packages/EntityFramework.SqlServerCompact.4.1.8482.2/EntityFramework.SqlServerCompact.4.1.8482.2.nupkg +0 -0
  107. data/test/RubyODataService/packages/EntityFramework.SqlServerCompact.4.1.8482.2/lib/System.Data.SqlServerCe.Entity.dll +0 -0
  108. data/test/RubyODataService/packages/EntityFramework.SqlServerCompact.4.1.8482.2/tools/install.ps1 +3 -0
  109. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/Content/web.config.transform +8 -0
  110. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/amd64/Microsoft.VC90.CRT/Microsoft.VC90.CRT.manifest +6 -0
  111. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/amd64/Microsoft.VC90.CRT/README_ENU.txt +0 -0
  112. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/amd64/Microsoft.VC90.CRT/msvcr90.dll +0 -0
  113. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/amd64/sqlcecompact40.dll +0 -0
  114. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/amd64/sqlceer40EN.dll +0 -0
  115. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/amd64/sqlceme40.dll +0 -0
  116. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/amd64/sqlceqp40.dll +0 -0
  117. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/amd64/sqlcese40.dll +0 -0
  118. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/x86/Microsoft.VC90.CRT/Microsoft.VC90.CRT.manifest +6 -0
  119. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/x86/Microsoft.VC90.CRT/README_ENU.txt +0 -0
  120. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/x86/Microsoft.VC90.CRT/msvcr90.dll +0 -0
  121. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/x86/sqlcecompact40.dll +0 -0
  122. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/x86/sqlceer40EN.dll +0 -0
  123. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/x86/sqlceme40.dll +0 -0
  124. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/x86/sqlceqp40.dll +0 -0
  125. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/NativeBinaries/x86/sqlcese40.dll +0 -0
  126. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/SQLCE_EULA_ENU.rtf +778 -0
  127. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/SqlServerCompact.4.0.8482.1.nupkg +0 -0
  128. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/Tools/GetSqlCEPostBuildCmd.ps1 +12 -0
  129. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/Tools/install.ps1 +11 -0
  130. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/Tools/uninstall.ps1 +9 -0
  131. data/test/RubyODataService/packages/SqlServerCompact.4.0.8482.1/lib/System.Data.SqlServerCe.dll +0 -0
  132. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/DataSvcUtil.exe +0 -0
  133. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/License.rtf +708 -0
  134. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/Microsoft.Data.Edm.dll +0 -0
  135. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/Microsoft.Data.Edm.xml +4150 -0
  136. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/Microsoft.Data.OData.dll +0 -0
  137. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/Microsoft.Data.OData.xml +1969 -0
  138. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/Microsoft.Data.Services.Client.dll +0 -0
  139. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/Microsoft.Data.Services.Client.xml +1442 -0
  140. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/Microsoft.Data.Services.Design.dll +0 -0
  141. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/Microsoft.Data.Services.Design.xml +191 -0
  142. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/Microsoft.Data.Services.dll +0 -0
  143. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/Microsoft.Data.Services.xml +2559 -0
  144. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/Microsoft.Data.Spatial.dll +0 -0
  145. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/Microsoft.Data.Spatial.xml +15 -0
  146. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/Microsoft.VsDesigner.DataServices.Adapter.dll +0 -0
  147. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/README.txt +6 -0
  148. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/System.Spatial.dll +0 -0
  149. data/test/RubyODataService/packages/WCF DataServices October 2011 CTP/System.Spatial.xml +2276 -0
  150. data/test/RubyODataService/packages/WebActivator.1.0.0.0/WebActivator.1.0.0.0.nupkg +0 -0
  151. data/test/RubyODataService/packages/WebActivator.1.0.0.0/lib/WebActivator.dll +0 -0
  152. data/test/RubyODataService/packages/repositories.config +4 -0
  153. data/test/applicationhost.config.template +2 -2
  154. data/test/blueprints.rb +5 -4
  155. data/test/iisExpress x64.bat b/data/test/iisExpress → x64.bat +0 -0
  156. data/test/iisExpress x86.bat b/data/test/iisExpress → x86.bat +0 -0
  157. data/test/setpath.rb +13 -3
  158. data/test/usage_samples/querying.rb +45 -0
  159. data/test/usage_samples/reflection.rb +16 -0
  160. data/test/usage_samples/sample_data.rb +30 -0
  161. metadata +327 -36
  162. data/test/Cassini x64.bat +0 -1
  163. data/test/Cassini x86.bat +0 -1
  164. data/test/SampleService/App_Code/AuditFields.cs +0 -13
  165. data/test/SampleService/App_Code/Entities.cs +0 -145
  166. data/test/SampleService/App_Code/Model.Designer.cs +0 -578
  167. data/test/SampleService/App_Code/Model.edmx +0 -157
  168. data/test/SampleService/App_Code/ModelContainerExtended.cs +0 -32
  169. data/test/SampleService/App_Data/_TestDB.mdf +0 -0
  170. data/test/SampleService/App_Data/_TestDB_Log.ldf +0 -0
  171. data/test/SampleService/BasicAuth/Entities.svc +0 -1
  172. data/test/SampleService/Entities.svc +0 -1
  173. data/test/SampleService/web.config +0 -37
@@ -18,6 +18,17 @@ module OData
18
18
  builder.top(10)
19
19
  builder.query.should eq "Products?$top=10&x=1&y=2"
20
20
  end
21
+ it "should properly handle queries for links" do
22
+ builder = QueryBuilder.new 'Categories(1)'
23
+ builder.links('Products')
24
+ builder.query.should eq "Categories(1)/$links/Products"
25
+ end
26
+ it "should properly handle queries for links with additional operations" do
27
+ builder = QueryBuilder.new 'Categories(1)'
28
+ builder.links('Products')
29
+ builder.top(5)
30
+ builder.query.should eq "Categories(1)/$links/Products?$top=5"
31
+ end
21
32
  end
22
33
  end
23
34
  end
@@ -0,0 +1,197 @@
1
+ require 'spec_helper'
2
+
3
+ module OData
4
+ describe Service do
5
+ before(:all) do
6
+ stub_request(:get, /http:\/\/test\.com\/test\.svc\/\$metadata(?:\?.+)?/).
7
+ with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
8
+ to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/edmx_categories_products.xml", __FILE__)), :headers => {})
9
+
10
+ @cat_prod_service = OData::Service.new "http://test.com/test.svc"
11
+ end
12
+ subject { @cat_prod_service }
13
+
14
+ context "methods" do
15
+ it { should respond_to :update_object }
16
+ it { should respond_to :delete_object }
17
+ it { should respond_to :save_changes }
18
+ it { should respond_to :load_property }
19
+ it { should respond_to :add_link }
20
+ it { should respond_to :execute }
21
+ it { should respond_to :partial? }
22
+ it { should respond_to :next }
23
+ it { should respond_to :classes }
24
+ it { should respond_to :class_metadata }
25
+ it { should respond_to :collections }
26
+ it { should respond_to :options }
27
+ it { should respond_to :function_imports }
28
+
29
+ context "after parsing metadata" do
30
+ it { should respond_to :Products }
31
+ it { should respond_to :Categories }
32
+ it { should respond_to :AddToProducts }
33
+ it { should respond_to :AddToCategories }
34
+ end
35
+ end
36
+ context "collections method" do
37
+ subject { @cat_prod_service.collections }
38
+ it { should include 'Products' }
39
+ it { should include 'Categories' }
40
+ it "should expose the edmx type of objects" do
41
+ subject['Products'][:edmx_type].should eq 'RubyODataService.Product'
42
+ subject['Categories'][:edmx_type].should eq 'RubyODataService.Category'
43
+ end
44
+ it "should expose the local model type" do
45
+ subject['Products'][:type].should eq Product
46
+ subject['Categories'][:type].should eq Category
47
+ end
48
+ end
49
+ context "class metadata" do
50
+ subject { @cat_prod_service.class_metadata }
51
+ it { should_not be_empty}
52
+ it { should have_key 'Product' }
53
+ it { should have_key 'Category' }
54
+ context "should have keys for each property" do
55
+ subject { @cat_prod_service.class_metadata['Category'] }
56
+ it { should have_key 'Id' }
57
+ it { should have_key 'Name' }
58
+ it { should have_key 'Products' }
59
+ it "should return a PropertyMetadata object for each property" do
60
+ subject['Id'].should be_a PropertyMetadata
61
+ subject['Name'].should be_a PropertyMetadata
62
+ subject['Products'].should be_a PropertyMetadata
63
+ end
64
+ it "should have correct PropertyMetadata for Category.Id" do
65
+ meta = subject['Id']
66
+ meta.name.should eq 'Id'
67
+ meta.type.should eq 'Edm.Int32'
68
+ meta.nullable.should eq false
69
+ meta.fc_target_path.should be_nil
70
+ meta.fc_keep_in_content.should be_nil
71
+ meta.nav_prop.should eq false
72
+ end
73
+ it "should have correct PropertyMetadata for Category.Name" do
74
+ meta = subject['Name']
75
+ meta.name.should eq 'Name'
76
+ meta.type.should eq 'Edm.String'
77
+ meta.nullable.should eq false
78
+ meta.fc_target_path.should be_nil
79
+ meta.fc_keep_in_content.should be_nil
80
+ meta.nav_prop.should eq false
81
+ end
82
+ it "should have correct PropertyMetadata for Category.Products" do
83
+ meta = subject['Products']
84
+ meta.name.should eq 'Products'
85
+ meta.type.should be_nil
86
+ meta.nullable.should eq true
87
+ meta.fc_target_path.should be_nil
88
+ meta.fc_keep_in_content.should be_nil
89
+ meta.nav_prop.should eq true
90
+ meta.association.should_not be_nil
91
+ end
92
+ end
93
+ end
94
+ context "function_imports method" do
95
+ subject { @cat_prod_service.function_imports }
96
+ it { should_not be_empty}
97
+ it { should have_key 'CleanDatabaseForTesting' }
98
+ it { should have_key 'EntityCategoryWebGet' }
99
+ it { should have_key 'EntitySingleCategoryWebGet' }
100
+ it "should expose the http method" do
101
+ subject['CleanDatabaseForTesting'][:http_method].should eq 'POST'
102
+ subject['EntityCategoryWebGet'][:http_method].should eq 'GET'
103
+ subject['EntitySingleCategoryWebGet'][:http_method].should eq 'GET'
104
+ end
105
+ it "should expose the return type" do
106
+ subject['CleanDatabaseForTesting'][:return_typo].should be_nil
107
+ subject['EntityCategoryWebGet'][:return_type].should eq Array
108
+ subject['EntityCategoryWebGet'][:inner_return_type].should eq Category
109
+ subject['EntitySingleCategoryWebGet'][:return_type].should eq Category
110
+ subject['EntitySingleCategoryWebGet'][:inner_return_type].should be_nil
111
+ subject['CategoryNames'][:return_type].should eq Array
112
+ subject['CategoryNames'][:inner_return_type].should eq String
113
+ end
114
+ it "should provide a hash of parameters" do
115
+ subject['EntityCategoryWebGet'][:parameters].should be_nil
116
+ subject['EntitySingleCategoryWebGet'][:parameters].should be_a Hash
117
+ subject['EntitySingleCategoryWebGet'][:parameters]['id'].should eq 'Edm.Int32'
118
+ end
119
+ context "after parsing function imports" do
120
+ subject { @cat_prod_service }
121
+ it { should respond_to :CleanDatabaseForTesting }
122
+ it { should respond_to :EntityCategoryWebGet }
123
+ it { should respond_to :EntitySingleCategoryWebGet }
124
+ it { should respond_to :CategoryNames }
125
+ end
126
+ context "error checking" do
127
+ subject { @cat_prod_service }
128
+ it "should throw an exception if a parameter is passed in to a method that doesn't require one" do
129
+ lambda { subject.EntityCategoryWebGet(1) }.should raise_error(ArgumentError, "wrong number of arguments (1 for 0)")
130
+ end
131
+ it "should throw and exception if more parameters are passed in than required by the function" do
132
+ lambda { subject.EntitySingleCategoryWebGet(1,2) }.should raise_error(ArgumentError, "wrong number of arguments (2 for 1)")
133
+ end
134
+ end
135
+ context "url and http method checks" do
136
+ subject { @cat_prod_service }
137
+ before { stub_request(:any, /http:\/\/test\.com\/test\.svc\/(.*)/) }
138
+ it "should call the correct url with the correct http method for a post with no parameters" do
139
+ subject.CleanDatabaseForTesting
140
+ a_request(:post, "http://test.com/test.svc/CleanDatabaseForTesting").should have_been_made
141
+ end
142
+ it "should call the correct url with the correct http method for a get with no parameters" do
143
+ subject.EntityCategoryWebGet
144
+ a_request(:get, "http://test.com/test.svc/EntityCategoryWebGet").should have_been_made
145
+ end
146
+ it "should call the correct url with the correct http method for a get with parameters" do
147
+ subject.EntitySingleCategoryWebGet(1)
148
+ a_request(:get, "http://test.com/test.svc/EntitySingleCategoryWebGet?id=1").should have_been_made
149
+ end
150
+ end
151
+ context "function import result parsing" do
152
+ subject { @cat_prod_service }
153
+ before(:each) do
154
+ stub_request(:post, "http://test.com/test.svc/CleanDatabaseForTesting").to_return(:status => 204)
155
+
156
+ stub_request(:get, "http://test.com/test.svc/EntityCategoryWebGet").
157
+ to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_entity_category_web_get.xml", __FILE__)), :headers => {})
158
+
159
+ stub_request(:get, "http://test.com/test.svc/EntitySingleCategoryWebGet?id=1").
160
+ to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_entity_single_category_web_get.xml", __FILE__)), :headers => {})
161
+
162
+ stub_request(:get, "http://test.com/test.svc/CategoryNames").
163
+ to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_category_names.xml", __FILE__)), :headers => {})
164
+
165
+ stub_request(:get, "http://test.com/test.svc/FirstCategoryId").
166
+ to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_first_category_id.xml", __FILE__)), :headers => {})
167
+ end
168
+ it "should return true if a function import post that returns successfully and doesn't have a return value (HTTP 204)" do
169
+ result = subject.CleanDatabaseForTesting
170
+ result.should be_true
171
+ end
172
+ it "should return a collection of entities for a collection" do
173
+ result = subject.EntityCategoryWebGet
174
+ result.should be_an Enumerable
175
+ result.first.should be_a Category
176
+ result.first.Name.should eq "Test Category"
177
+ end
178
+ it "should return a single entity if it isn't a collection" do
179
+ result = subject.EntitySingleCategoryWebGet(1)
180
+ result.should be_a Category
181
+ result.Name.should eq "Test Category"
182
+ end
183
+ it "should return a collection of primitive types" do
184
+ result = subject.CategoryNames
185
+ result.should be_an Enumerable
186
+ result.first.should be_a String
187
+ result.first.should eq "Test Category 1"
188
+ end
189
+ it "should return a single primitive type" do
190
+ result = subject.FirstCategoryId
191
+ result.should be_a Fixnum
192
+ result.should eq 1
193
+ end
194
+ end
195
+ end
196
+ end
197
+ end
@@ -37,7 +37,6 @@ module OData
37
37
  end
38
38
  end
39
39
  end
40
-
41
40
  describe "additional query string parameters" do
42
41
  before(:each) do
43
42
  # Required for the build_classes method
@@ -83,7 +82,27 @@ module OData
83
82
  svc.save_changes
84
83
  a_request(:post, "http://test.com/test.svc/$batch?x=1&y=2").should have_been_made
85
84
  end
86
- end
85
+ it "should pass the parameters as part of an add link" do
86
+ svc = OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } }
87
+ existing_flight1 = ZDemoFlight.new
88
+ existing_flight1.__metadata = { :uri => "http://test.com/test.svc/flight_dataCollection/1" }
89
+ existing_flight2 = ZDemoFlight.new
90
+ existing_flight2.__metadata = { :uri => "http://test.com/test.svc/flight_dataCollection/2" }
91
+ svc.add_link(existing_flight1, "flight_data_r", existing_flight2)
92
+ svc.save_changes
93
+ a_request(:post, "http://test.com/test.svc/flight_dataCollection/1/$links/flight_data_r?x=1&y=2").should have_been_made
94
+ end
95
+ it "should pass the parameters as part of a function import with a parameter" do
96
+ svc = OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } }
97
+ svc.get_flight(1)
98
+ a_request(:get, "http://test.com/test.svc/get_flight?id=1&x=1&y=2").should have_been_made
99
+ end
100
+ it "should pass the parameters as part of a function import without parameters" do
101
+ svc = OData::Service.new "http://test.com/test.svc/", { :additional_params => { :x=>1, :y=>2 } }
102
+ svc.get_top_flight
103
+ a_request(:get, "http://test.com/test.svc/get_top_flight?x=1&y=2").should have_been_made
104
+ end
105
+ end
87
106
 
88
107
  describe "lowercase collections" do
89
108
  before(:each) do
@@ -124,7 +143,7 @@ module OData
124
143
  end
125
144
  end
126
145
 
127
- describe "collections, objects, etc" do
146
+ describe "collections, objects, metadata etc" do
128
147
  before(:each) do
129
148
  # Metadata
130
149
  stub_request(:get, "http://test.com/test.svc/$metadata").
@@ -244,9 +263,449 @@ module OData
244
263
  product.Category.should_not be_an Array
245
264
  end
246
265
  end
266
+
267
+ describe "navigation properties" do
268
+ it "should fill in PropertyMetadata for navigation properties" do
269
+ svc = OData::Service.new "http://test.com/test.svc/"
270
+ svc.class_metadata['Product'].should have_key 'Category'
271
+ end
272
+ end
247
273
  end
248
- end
249
274
 
275
+ describe "single layer inheritance" do
276
+ before(:each) do
277
+ # Metadata
278
+ stub_request(:get, "http://test.com/test.svc/$metadata").
279
+ with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
280
+ to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/inheritance/edmx_pluralsight.xml", __FILE__)), :headers => {})
281
+
282
+ # Content - Courses
283
+ stub_request(:get, /http:\/\/test\.com\/test\.svc\/Courses(?:.*)/).
284
+ with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
285
+ to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/inheritance/result_pluralsight_courses.xml", __FILE__)), :headers => {})
286
+ end
287
+
288
+ it "should build all inherited attributes" do
289
+ OData::Service.new "http://test.com/test.svc/"
290
+ methods = Course.instance_methods.reject {|m| Object.methods.index(m)}
291
+
292
+ # Ruby 1.9 uses symbols, and 1.8 uses strings, so this normalizes the data
293
+ methods.map! {|m| m.to_sym}
294
+
295
+ methods.should include(:Title)
296
+ methods.should include(:Description)
297
+ methods.should include(:VideoLength)
298
+ methods.should include(:Category)
299
+
300
+ methods.should include(:Title=)
301
+ methods.should include(:Description=)
302
+ methods.should include(:VideoLength=)
303
+ methods.should include(:Category=)
304
+ end
305
+
306
+ it "should not build abstract classes" do
307
+ OData::Service.new "http://test.com/test.svc/"
308
+ defined?(ModelItemBase).should eq nil
309
+ end
310
+
311
+ it "should fill inherited properties" do
312
+ svc = OData::Service.new "http://test.com/test.svc/"
313
+ svc.Courses
314
+ courses = svc.execute
315
+ course = courses.first
316
+ course.Title.should_not be_nil
317
+ course.Description.should_not be_nil
318
+ course.VideoLength.should_not be_nil
319
+ course.Category.should_not be_nil
320
+ end
321
+ end
322
+
323
+ describe "handling partial collections" do
324
+ before(:each) do
325
+ # Metadata
326
+ stub_request(:get, "http://test.com/test.svc/$metadata").
327
+ with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
328
+ to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/partial/partial_feed_metadata.xml", __FILE__)), :headers => {})
329
+
330
+ # Content - Partial
331
+ stub_request(:get, "http://test.com/test.svc/Partials").
332
+ with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
333
+ to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/partial/partial_feed_part_1.xml", __FILE__)), :headers => {})
334
+
335
+ stub_request(:get, "http://test.com/test.svc/Partials?$skiptoken='ERNSH'").
336
+ with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
337
+ to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/partial/partial_feed_part_2.xml", __FILE__)), :headers => {})
338
+
339
+ stub_request(:get, "http://test.com/test.svc/Partials?$skiptoken='ERNSH2'").
340
+ with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
341
+ to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/partial/partial_feed_part_3.xml", __FILE__)), :headers => {})
342
+
343
+ end
344
+
345
+ it "should return the whole collection by default" do
346
+ svc = OData::Service.new "http://test.com/test.svc/"
347
+ svc.Partials
348
+ results = svc.execute
349
+ results.count.should eq 3
350
+ end
351
+
352
+ it "should return only the partial when specified by options" do
353
+ svc = OData::Service.new("http://test.com/test.svc/", :eager_partial => false)
354
+ svc.Partials
355
+ results = svc.execute
356
+ results.count.should eq 1
357
+ svc.should be_partial
358
+ while svc.partial?
359
+ results.concat svc.next
360
+ end
361
+ results.count.should eq 3
362
+ end
363
+ end
364
+
365
+ describe "link queries" do
366
+ before(:each) do
367
+ # Required for the build_classes method
368
+ stub_request(:get, /http:\/\/test\.com\/test\.svc\/\$metadata(?:\?.+)?/).
369
+ with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
370
+ to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/edmx_categories_products.xml", __FILE__)), :headers => {})
371
+
372
+ stub_request(:get, "http://test.com/test.svc/Categories(1)/$links/Products").
373
+ with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
374
+ to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/links/result_links_query.xml", __FILE__)), :headers => {})
375
+ end
376
+ it "should be able to parse the results of a links query" do
377
+ svc = OData::Service.new "http://test.com/test.svc/"
378
+ svc.Categories(1).links('Products')
379
+ results = svc.execute
380
+ results.count.should eq 3
381
+ results.first.should be_a_kind_of(URI)
382
+ results[0].path.should eq "/SampleService/RubyOData.svc/Products(1)"
383
+ results[1].path.should eq "/SampleService/RubyOData.svc/Products(2)"
384
+ results[2].path.should eq "/SampleService/RubyOData.svc/Products(3)"
385
+ end
386
+ end
387
+
388
+ describe "sample service" do
389
+ before(:each) do
390
+ # Required for the build_classes method
391
+ stub_request(:get, /http:\/\/test\.com\/test\.svc\/\$metadata(?:\?.+)?/).
392
+ with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
393
+ to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/edmx_categories_products.xml", __FILE__)), :headers => {})
394
+
395
+ stub_request(:get, /http:\/\/test\.com\/test\.svc\/Products\(\d\)/).
396
+ with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
397
+ to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_single_product.xml", __FILE__)), :headers => {})
398
+
399
+ stub_request(:get, /http:\/\/test\.com\/test\.svc\/Products\(\d{2,}\)/).
400
+ with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
401
+ to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_single_product_not_found.xml", __FILE__)), :headers => {})
402
+
403
+ stub_request(:get, "http://test.com/test.svc/Products(1)/Category").
404
+ with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
405
+ to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_single_category.xml", __FILE__)), :headers => {})
406
+
407
+ stub_request(:get, "http://test.com/test.svc/Categories(1)").
408
+ with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
409
+ to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_single_category.xml", __FILE__)), :headers => {})
410
+
411
+ stub_request(:get, "http://test.com/test.svc/Categories(1)/Products").
412
+ with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate'}).
413
+ to_return(:status => 200, :body => File.new(File.expand_path("../fixtures/sample_service/result_multiple_category_products.xml", __FILE__)), :headers => {})
414
+
415
+ stub_request(:post, "http://test.com/test.svc/Categories(1)/$links/Products").to_return(:status => 204)
416
+ stub_request(:post, "http://test.com/test.svc/$batch").to_return(:status => 202)
417
+ end
418
+
419
+ describe "lazy loading" do
420
+ after(:each) do
421
+ Object.send(:remove_const, 'Product') if Object.const_defined? 'Product'
422
+ Object.send(:remove_const, 'Category') if Object.const_defined? 'Category'
423
+ end
424
+
425
+ it "should have a load property method" do
426
+ svc = OData::Service.new "http://test.com/test.svc/"
427
+ svc.should respond_to(:load_property)
428
+ end
429
+
430
+ it "should throw an exception if the object isn't tracked" do
431
+ svc = OData::Service.new "http://test.com/test.svc/"
432
+ new_object = Product.new
433
+ lambda { svc.load_property(new_object, "Category") }.should raise_error(ArgumentError, "You cannot load a property on an entity that isn't tracked")
434
+ end
435
+
436
+ it "should throw an exception if there isn't a method matching the navigation property passed in" do
437
+ svc = OData::Service.new "http://test.com/test.svc/"
438
+ svc.Products(1)
439
+ product = svc.execute.first
440
+ lambda { svc.load_property(product, "NoMatchingMethod") }.should raise_error(ArgumentError, "'NoMatchingMethod' is not a valid navigation property")
441
+ end
442
+
443
+ it "should throw an exception if the method passed in is a standard property (non-navigation)" do
444
+ svc = OData::Service.new "http://test.com/test.svc/"
445
+ svc.Products(1)
446
+ product = svc.execute.first
447
+ lambda { svc.load_property(product, "Name") }.should raise_error(ArgumentError, "'Name' is not a valid navigation property")
448
+ end
449
+
450
+ it "should fill a single navigation property" do
451
+ svc = OData::Service.new "http://test.com/test.svc/"
452
+ svc.Products(1)
453
+ product = svc.execute.first
454
+ svc.load_property(product, "Category")
455
+ product.Category.should_not be_nil
456
+ product.Category.Id.should eq 1
457
+ product.Category.Name.should eq 'Category 1'
458
+ end
459
+
460
+ it "should fill a collection navigation property" do
461
+ svc = OData::Service.new "http://test.com/test.svc/"
462
+ svc.Categories(1)
463
+ category = svc.execute.first
464
+ svc.load_property(category, "Products")
465
+ category.Products.first.should be_a Product
466
+ category.Products[0].Id.should eq 1
467
+ category.Products[1].Id.should eq 2
468
+ end
469
+ end
470
+
471
+ describe "find, create, add, update, and delete" do
472
+ after(:each) do
473
+ Object.send(:remove_const, 'Product') if Object.const_defined? 'Product'
474
+ Object.send(:remove_const, 'Category') if Object.const_defined? 'Category'
475
+ end
476
+
477
+ it "should implement an AddTo method for collection" do
478
+ svc = OData::Service.new "http://test.com/test.svc/"
479
+ svc.should respond_to :AddToCategories
480
+ svc.should respond_to :AddToProducts
481
+ end
482
+
483
+ it "should create objects with an initialize method that can build the object from a hash" do
484
+ svc = OData::Service.new "http://test.com/test.svc/"
485
+ product = Product.new 'Id' => 1000, 'Name' => 'New Product'
486
+ product.Id.should eq 1000
487
+ product.Name.should eq 'New Product'
488
+ end
489
+
490
+ it "should create objects that rejects keys that don't have corresponding methods" do
491
+ svc = OData::Service.new "http://test.com/test.svc/"
492
+ lambda { Product.new 'NotAProperty' => true }.should raise_error NoMethodError
493
+ end
494
+
495
+ it "should create objects that expose a properties class method that lists the properties for the object" do
496
+ svc = OData::Service.new "http://test.com/test.svc/"
497
+ Product.properties.should include 'Id'
498
+ Product.properties.should include 'Name'
499
+ Product.properties.should include 'Category'
500
+ end
501
+
502
+ it "should have full metadata for a property returned from the properties method" do
503
+ svc = OData::Service.new "http://test.com/test.svc/"
504
+ Product.properties['Category'].should be_a PropertyMetadata
505
+ Product.properties['Category'].nav_prop.should be_true
506
+ end
507
+
508
+ it "should create objects that expose an id property" do
509
+ svc = OData::Service.new "http://test.com/test.svc/"
510
+ svc.Products(1)
511
+ product = svc.execute.first
512
+ product.should respond_to :id
513
+ end
514
+
515
+ it "should extract the id from the metadata" do
516
+ svc = OData::Service.new "http://test.com/test.svc/"
517
+ svc.Products(1)
518
+ product = svc.execute.first
519
+ product.id.should eq 1
520
+ end
521
+
522
+ describe "Class.first method" do
523
+ it "should exist on the create server objects" do
524
+ svc = OData::Service.new "http://test.com/test.svc/"
525
+ Product.should respond_to :first
526
+ end
527
+ it "should return nil if an id isn't passed in" do
528
+ svc = OData::Service.new "http://test.com/test.svc/"
529
+ product = Product.first(nil)
530
+ product.should be_nil
531
+ end
532
+ it "should return nil if an id isn't found" do
533
+ svc = OData::Service.new "http://test.com/test.svc/"
534
+ product = Product.first(1234567890)
535
+ product.should be_nil
536
+ end
537
+ it "should return a product if an id is found" do
538
+ svc = OData::Service.new "http://test.com/test.svc/"
539
+ product = Product.first(1)
540
+ product.should_not be_nil
541
+ end
542
+ end
543
+ end
544
+
545
+ describe "namespaces" do
546
+ after(:each) do
547
+ VisoftInc::Sample::Models.send(:remove_const, 'Product') if VisoftInc::Sample::Models.const_defined? 'Product'
548
+ VisoftInc::Sample::Models.send(:remove_const, 'Category') if VisoftInc::Sample::Models.const_defined? 'Category'
549
+
550
+ VisoftInc::Sample.send(:remove_const, 'Models') if VisoftInc::Sample.const_defined? 'Models'
551
+ VisoftInc.send(:remove_const, 'Sample') if VisoftInc.const_defined? 'Sample'
552
+ Object.send(:remove_const, 'VisoftInc') if Object.const_defined? 'VisoftInc'
553
+ end
554
+
555
+ it "should create models in the specified namespace if the option is set (using a .NET style namespace with dots)" do
556
+ svc = OData::Service.new "http://test.com/test.svc/", { :namespace => 'VisoftInc.Sample.Models' }
557
+ defined?(VisoftInc::Sample::Models::Product).nil?.should be_false, 'VisoftInc::Sample::Models::Product was expected to be defined, but was not'
558
+ defined?(VisoftInc::Sample::Models::Category).nil?.should be_false, 'VisoftInc::Sample::Models::Category was expected to be defined, but was not'
559
+ end
560
+
561
+ it "should create models in the specified namespace if the option is set (using Ruby style namespaces with double colons)" do
562
+ svc = OData::Service.new "http://test.com/test.svc/", { :namespace => 'VisoftInc::Sample::Models' }
563
+ defined?(VisoftInc::Sample::Models::Product).nil?.should be_false, 'VisoftInc::Sample::Models::Product was expected to be defined, but was not'
564
+ defined?(VisoftInc::Sample::Models::Category).nil?.should be_false, 'VisoftInc::Sample::Models::Category was expected to be defined, but was not'
565
+ end
566
+
567
+ it "should fill object defined in a namespace" do
568
+ svc = OData::Service.new "http://test.com/test.svc/", { :namespace => 'VisoftInc::Sample::Models' }
569
+ svc.Categories(1)
570
+ categories = svc.execute
571
+ categories.should_not be_nil
572
+ category = categories.first
573
+ category.Id.should eq 1
574
+ category.Name.should eq 'Category 1'
575
+ end
576
+
577
+ it "should fill the class_metadata hash" do
578
+ svc = OData::Service.new "http://test.com/test.svc/", { :namespace => 'VisoftInc::Sample::Models' }
579
+ svc.class_metadata.should_not be_empty
580
+ end
581
+
582
+ it "should add a key (based on the name) for each property class_metadata hash" do
583
+ svc = OData::Service.new "http://test.com/test.svc/", { :namespace => 'VisoftInc::Sample::Models' }
584
+ svc.class_metadata['VisoftInc::Sample::Models::Product'].should have_key 'Id'
585
+ svc.class_metadata['VisoftInc::Sample::Models::Product'].should have_key 'Name'
586
+ svc.class_metadata['VisoftInc::Sample::Models::Product'].should have_key 'Description'
587
+ end
588
+
589
+ it "should lazy load objects defined in a namespace" do
590
+ svc = OData::Service.new "http://test.com/test.svc/", { :namespace => 'VisoftInc::Sample::Models' }
591
+ svc.Categories(1)
592
+ category = svc.execute.first
593
+ svc.load_property category, 'Products'
594
+ category.Products.should_not be_nil
595
+ category.Products.first.Id.should eq 1
596
+ category.Products.first.Name.should eq 'Widget 1'
597
+ end
598
+ end
599
+
600
+ describe "add_link method" do
601
+ it "should exist as a method on the service" do
602
+ svc = OData::Service.new "http://test.com/test.svc/"
603
+ svc.should respond_to(:add_link)
604
+ end
605
+
606
+ it "shouldn't be allowed if a parent isn't tracked" do
607
+ svc = OData::Service.new "http://test.com/test.svc/"
608
+ category = Category.new :Name => 'New Category'
609
+ property = nil # Not needed for this test
610
+ product = nil # Not needed for this test
611
+ lambda { svc.add_link(category, property, product) }.should raise_error(ArgumentError, "You cannot add a link on an entity that isn't tracked (Category)")
612
+ end
613
+
614
+ it "shouldn't be allowed if a property isn't found on the parent" do
615
+ svc = OData::Service.new "http://test.com/test.svc/"
616
+ svc.Categories(1)
617
+ category = svc.execute.first
618
+ property = 'NotAProperty'
619
+ product = nil # Not needed for this test
620
+ lambda { svc.add_link(category, property, product) }.should raise_error(ArgumentError, "'NotAProperty' is not a valid navigation property for Category")
621
+ end
622
+
623
+ it "shouldn't be allowed if a property isn't a navigation property on the parent" do
624
+ svc = OData::Service.new "http://test.com/test.svc/"
625
+ svc.Categories(1)
626
+ category = svc.execute.first
627
+ property = 'Name'
628
+ product = nil # Not needed for this test
629
+ lambda { svc.add_link(category, property, product) }.should raise_error(ArgumentError, "'Name' is not a valid navigation property for Category")
630
+ end
631
+
632
+ it "shouldn't be allowed if a child isn't tracked" do
633
+ svc = OData::Service.new "http://test.com/test.svc/"
634
+ svc.Categories(1)
635
+ category = svc.execute.first
636
+ property = 'Products'
637
+ product = Product.new :Name => 'Widget 1'
638
+ lambda { svc.add_link(category, property, product) }.should raise_error(ArgumentError, "You cannot add a link on a child entity that isn't tracked (Product)")
639
+ end
640
+
641
+ it "should perform a post against the correct URL with the correct body on a single_save" do
642
+ svc = OData::Service.new "http://test.com/test.svc/"
643
+ svc.Categories(1)
644
+ category = svc.execute.first
645
+ svc.Products(1)
646
+ product = svc.execute.first
647
+ property = 'Products'
648
+ svc.add_link(category, property, product)
649
+ svc.save_changes
650
+ a_request(:post, "http://test.com/test.svc/Categories(1)/$links/Products").
651
+ with(:body => { "uri" => "http://test.com/test.svc/Products(1)" },
652
+ :headers => {'Content-Type' => 'application/json'}).should have_been_made
653
+ end
654
+
655
+ it "should add the child to the parent's navigation property on a single_save" do
656
+ svc = OData::Service.new "http://test.com/test.svc/"
657
+ svc.Categories(1)
658
+ category = svc.execute.first
659
+ svc.Products(1)
660
+ product = svc.execute.first
661
+ property = 'Products'
662
+ svc.add_link(category, property, product)
663
+ svc.save_changes
664
+ category.Products.should include product
665
+ end
666
+
667
+ it "should add the parent to the child's navigation property on a single_save" do
668
+ svc = OData::Service.new "http://test.com/test.svc/"
669
+ svc.Categories(1)
670
+ category = svc.execute.first
671
+ svc.Products(1)
672
+ product = svc.execute.first
673
+ property = 'Products'
674
+ svc.add_link(category, property, product)
675
+ svc.save_changes
676
+ product.Category.should eq category
677
+ end
678
+
679
+ describe "batch_save" do
680
+ before(:each) do
681
+ @svc = OData::Service.new "http://test.com/test.svc/"
682
+ @category = Category.first(1)
683
+ @product = Product.first(1)
684
+ new_category = Category.new
685
+ @svc.AddToCategories(new_category)
686
+ @svc.add_link(@category, 'Products', @product)
687
+ @svc.save_changes
688
+ end
689
+
690
+ it "should perform a post with the correct URL and body on a batch_save" do
691
+ WebMock.should have_requested(:post, "http://test.com/test.svc/$batch").with { |request|
692
+ request.body.include? "POST http://test.com/test.svc/Categories(1)/$links/Products HTTP/1.1"
693
+ request.body.include? '{"uri":"http://test.com/test.svc/Products(1)"}'
694
+ }
695
+ end
696
+ context "child is a part of the parent's collection" do
697
+ subject { @category.Products }
698
+ it { should include @product }
699
+ end
700
+ context "parent object should be filled in on the child" do
701
+ subject { @product.Category }
702
+ it { should eq @category }
703
+ end
704
+ end
705
+ end
706
+ end
707
+ end
708
+
250
709
  describe_private OData::Service do
251
710
  describe "parse value" do
252
711
  before(:each) do