odata4 0.7.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 (114) hide show
  1. checksums.yaml +7 -0
  2. data/.autotest +2 -0
  3. data/.gitignore +24 -0
  4. data/.rspec +2 -0
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +75 -0
  8. data/CHANGELOG.md +120 -0
  9. data/Gemfile +4 -0
  10. data/LICENSE.txt +23 -0
  11. data/README.md +287 -0
  12. data/Rakefile +7 -0
  13. data/TODO.md +55 -0
  14. data/lib/odata4.rb +37 -0
  15. data/lib/odata4/complex_type.rb +76 -0
  16. data/lib/odata4/complex_type/property.rb +114 -0
  17. data/lib/odata4/entity.rb +319 -0
  18. data/lib/odata4/entity_set.rb +162 -0
  19. data/lib/odata4/enum_type.rb +95 -0
  20. data/lib/odata4/enum_type/property.rb +62 -0
  21. data/lib/odata4/navigation_property.rb +29 -0
  22. data/lib/odata4/navigation_property/proxy.rb +76 -0
  23. data/lib/odata4/properties.rb +25 -0
  24. data/lib/odata4/properties/binary.rb +50 -0
  25. data/lib/odata4/properties/boolean.rb +37 -0
  26. data/lib/odata4/properties/date.rb +27 -0
  27. data/lib/odata4/properties/date_time.rb +83 -0
  28. data/lib/odata4/properties/date_time_offset.rb +17 -0
  29. data/lib/odata4/properties/decimal.rb +50 -0
  30. data/lib/odata4/properties/float.rb +67 -0
  31. data/lib/odata4/properties/geography.rb +13 -0
  32. data/lib/odata4/properties/geography/base.rb +162 -0
  33. data/lib/odata4/properties/geography/line_string.rb +33 -0
  34. data/lib/odata4/properties/geography/point.rb +31 -0
  35. data/lib/odata4/properties/geography/polygon.rb +38 -0
  36. data/lib/odata4/properties/guid.rb +17 -0
  37. data/lib/odata4/properties/integer.rb +107 -0
  38. data/lib/odata4/properties/number.rb +14 -0
  39. data/lib/odata4/properties/string.rb +72 -0
  40. data/lib/odata4/properties/time.rb +40 -0
  41. data/lib/odata4/properties/time_of_day.rb +27 -0
  42. data/lib/odata4/property.rb +118 -0
  43. data/lib/odata4/property_registry.rb +41 -0
  44. data/lib/odata4/query.rb +231 -0
  45. data/lib/odata4/query/criteria.rb +92 -0
  46. data/lib/odata4/query/criteria/comparison_operators.rb +49 -0
  47. data/lib/odata4/query/criteria/date_functions.rb +61 -0
  48. data/lib/odata4/query/criteria/geography_functions.rb +21 -0
  49. data/lib/odata4/query/criteria/lambda_operators.rb +27 -0
  50. data/lib/odata4/query/criteria/string_functions.rb +40 -0
  51. data/lib/odata4/query/in_batches.rb +58 -0
  52. data/lib/odata4/query/result.rb +84 -0
  53. data/lib/odata4/query/result/atom.rb +41 -0
  54. data/lib/odata4/query/result/json.rb +42 -0
  55. data/lib/odata4/railtie.rb +19 -0
  56. data/lib/odata4/service.rb +344 -0
  57. data/lib/odata4/service_registry.rb +52 -0
  58. data/lib/odata4/version.rb +3 -0
  59. data/odata4.gemspec +34 -0
  60. data/spec/fixtures/files/entity_to_xml.xml +17 -0
  61. data/spec/fixtures/files/metadata.xml +150 -0
  62. data/spec/fixtures/files/product_0.json +10 -0
  63. data/spec/fixtures/files/product_0.xml +28 -0
  64. data/spec/fixtures/files/products.json +106 -0
  65. data/spec/fixtures/files/products.xml +308 -0
  66. data/spec/fixtures/files/supplier_0.json +26 -0
  67. data/spec/fixtures/files/supplier_0.xml +32 -0
  68. data/spec/fixtures/vcr_cassettes/complex_type_specs.yml +127 -0
  69. data/spec/fixtures/vcr_cassettes/entity_set_specs.yml +1348 -0
  70. data/spec/fixtures/vcr_cassettes/entity_set_specs/bad_entry.yml +183 -0
  71. data/spec/fixtures/vcr_cassettes/entity_set_specs/existing_entry.yml +256 -0
  72. data/spec/fixtures/vcr_cassettes/entity_set_specs/new_entry.yml +185 -0
  73. data/spec/fixtures/vcr_cassettes/entity_specs.yml +285 -0
  74. data/spec/fixtures/vcr_cassettes/navigation_property_proxy_specs.yml +346 -0
  75. data/spec/fixtures/vcr_cassettes/query/result_specs.yml +189 -0
  76. data/spec/fixtures/vcr_cassettes/query_specs.yml +663 -0
  77. data/spec/fixtures/vcr_cassettes/service_registry_specs.yml +129 -0
  78. data/spec/fixtures/vcr_cassettes/service_specs.yml +127 -0
  79. data/spec/fixtures/vcr_cassettes/usage_example_specs.yml +749 -0
  80. data/spec/odata4/complex_type_spec.rb +116 -0
  81. data/spec/odata4/entity/shared_examples.rb +82 -0
  82. data/spec/odata4/entity_set_spec.rb +168 -0
  83. data/spec/odata4/entity_spec.rb +151 -0
  84. data/spec/odata4/enum_type_spec.rb +134 -0
  85. data/spec/odata4/navigation_property/proxy_spec.rb +44 -0
  86. data/spec/odata4/navigation_property_spec.rb +55 -0
  87. data/spec/odata4/properties/binary_spec.rb +50 -0
  88. data/spec/odata4/properties/boolean_spec.rb +72 -0
  89. data/spec/odata4/properties/date_spec.rb +23 -0
  90. data/spec/odata4/properties/date_time_offset_spec.rb +30 -0
  91. data/spec/odata4/properties/date_time_spec.rb +23 -0
  92. data/spec/odata4/properties/decimal_spec.rb +24 -0
  93. data/spec/odata4/properties/float_spec.rb +45 -0
  94. data/spec/odata4/properties/geography/line_string_spec.rb +33 -0
  95. data/spec/odata4/properties/geography/point_spec.rb +29 -0
  96. data/spec/odata4/properties/geography/polygon_spec.rb +55 -0
  97. data/spec/odata4/properties/geography/shared_examples.rb +72 -0
  98. data/spec/odata4/properties/guid_spec.rb +17 -0
  99. data/spec/odata4/properties/integer_spec.rb +58 -0
  100. data/spec/odata4/properties/string_spec.rb +46 -0
  101. data/spec/odata4/properties/time_of_day_spec.rb +23 -0
  102. data/spec/odata4/properties/time_spec.rb +15 -0
  103. data/spec/odata4/property_registry_spec.rb +16 -0
  104. data/spec/odata4/property_spec.rb +32 -0
  105. data/spec/odata4/query/criteria_spec.rb +229 -0
  106. data/spec/odata4/query/result_spec.rb +53 -0
  107. data/spec/odata4/query_spec.rb +196 -0
  108. data/spec/odata4/service_registry_spec.rb +18 -0
  109. data/spec/odata4/service_spec.rb +80 -0
  110. data/spec/odata4/usage_example_spec.rb +176 -0
  111. data/spec/spec_helper.rb +32 -0
  112. data/spec/support/coverage.rb +2 -0
  113. data/spec/support/vcr.rb +9 -0
  114. metadata +380 -0
@@ -0,0 +1,229 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples 'an operator criterium' do |operator, value, to_s|
4
+ it { expect(criteria).to eq(subject) }
5
+ it { expect(criteria.operator).to eq(operator) }
6
+ it { expect(criteria.value).to eq(value) }
7
+ it { expect(criteria.to_s).to eq(to_s) }
8
+ end
9
+
10
+ shared_examples 'a function criterium' do |function, argument, to_s|
11
+ it { expect(criteria).to eq(subject) }
12
+ it { expect(criteria.function).to eq(function) }
13
+ it { expect(criteria.argument).to eq(argument) }
14
+ it { expect(criteria.to_s).to eq(to_s) }
15
+ end
16
+
17
+ shared_examples 'an operator-function criterium' do |function, operator, value, to_s|
18
+ it { expect(criteria).to eq(subject) }
19
+ it { expect(criteria.function).to eq(function) }
20
+ it { expect(criteria.operator).to eq(operator) }
21
+ it { expect(criteria.value).to eq(value) }
22
+ it { expect(criteria.to_s).to eq(to_s) }
23
+ end
24
+
25
+ describe OData4::Query::Criteria do
26
+ let(:string_property) { OData4::Properties::String.new(:Name, nil) }
27
+ let(:integer_property) { OData4::Properties::Integer.new(:Age, nil) }
28
+ let(:datetime_property) { OData4::Properties::DateTime.new(:BirthDate, nil) }
29
+ let(:geo_property) { OData4::Properties::Geography::Point.new(:Position, nil) }
30
+ # TODO: use actual `NavigationProperty` class here
31
+ let(:navigation_property) { OData4::Property.new(:Items, nil) }
32
+
33
+ let(:subject) { OData4::Query::Criteria.new(property: string_property) }
34
+
35
+ it { expect(subject).to respond_to(:property) }
36
+ it { expect(subject.property).to eq(string_property)}
37
+
38
+ it { expect(subject).to respond_to(:operator) }
39
+ it { expect(subject).to respond_to(:value) }
40
+ it { expect(subject).to respond_to(:to_s) }
41
+
42
+ describe 'comparison operators' do
43
+ describe '#eq' do
44
+ let(:criteria) { subject.eq('Bread') }
45
+
46
+ it { expect(subject).to respond_to(:eq) }
47
+ it_behaves_like 'an operator criterium', :eq, 'Bread', "Name eq 'Bread'"
48
+ end
49
+
50
+ describe '#ne' do
51
+ let(:criteria) { subject.ne('Bread') }
52
+
53
+ it { expect(subject).to respond_to(:ne) }
54
+ it_behaves_like 'an operator criterium', :ne, 'Bread', "Name ne 'Bread'"
55
+ end
56
+
57
+ describe '#gt' do
58
+ let(:subject) { OData4::Query::Criteria.new(property: integer_property) }
59
+ let(:criteria) { subject.gt(5) }
60
+
61
+ it { expect(subject).to respond_to(:gt) }
62
+ it_behaves_like 'an operator criterium', :gt, 5, "Age gt 5"
63
+ end
64
+
65
+ describe '#ge' do
66
+ let(:subject) { OData4::Query::Criteria.new(property: integer_property) }
67
+ let(:criteria) { subject.ge(5) }
68
+
69
+ it { expect(subject).to respond_to(:ge) }
70
+ it_behaves_like 'an operator criterium', :ge, 5, 'Age ge 5'
71
+ end
72
+
73
+ describe '#lt' do
74
+ let(:subject) { OData4::Query::Criteria.new(property: integer_property) }
75
+ let(:criteria) { subject.lt(5) }
76
+
77
+ it { expect(subject).to respond_to(:lt) }
78
+ it_behaves_like 'an operator criterium', :lt, 5, 'Age lt 5'
79
+ end
80
+
81
+ describe '#le' do
82
+ let(:subject) { OData4::Query::Criteria.new(property: integer_property) }
83
+ let(:criteria) { subject.le(5) }
84
+
85
+ it { expect(subject).to respond_to(:le) }
86
+ it_behaves_like 'an operator criterium', :le, 5, 'Age le 5'
87
+ end
88
+ end
89
+
90
+ describe 'string functions' do
91
+ describe '#contains' do
92
+ let(:criteria) { subject.contains('freds') }
93
+
94
+ it { expect(subject).to respond_to(:contains) }
95
+ it_behaves_like 'a function criterium', :contains, 'freds', "contains(Name,'freds')"
96
+ end
97
+
98
+ describe '#startswith' do
99
+ let(:criteria) { subject.startswith('Alfreds') }
100
+
101
+ it { expect(subject).to respond_to(:startswith) }
102
+ it_behaves_like 'a function criterium', :startswith, 'Alfreds', "startswith(Name,'Alfreds')"
103
+ end
104
+
105
+ describe '#endswith' do
106
+ let(:criteria) { subject.endswith('Futterkiste') }
107
+
108
+ it { expect(subject).to respond_to(:endswith) }
109
+ it_behaves_like 'a function criterium', :endswith, 'Futterkiste', "endswith(Name,'Futterkiste')"
110
+ end
111
+
112
+ describe '#tolower' do
113
+ let(:criteria) { subject.tolower.eq('alfreds futterkiste') }
114
+
115
+ it { expect(subject).to respond_to(:tolower) }
116
+ it_behaves_like 'an operator-function criterium', :tolower, :eq, 'alfreds futterkiste', "tolower(Name) eq 'alfreds futterkiste'"
117
+ end
118
+
119
+ describe '#toupper' do
120
+ let(:criteria) { subject.toupper.eq('ALFREDS FUTTERKISTE') }
121
+
122
+ it { expect(subject).to respond_to(:toupper) }
123
+ it_behaves_like 'an operator-function criterium', :toupper, :eq, 'ALFREDS FUTTERKISTE', "toupper(Name) eq 'ALFREDS FUTTERKISTE'"
124
+ end
125
+ end
126
+
127
+ describe 'date functions' do
128
+ let(:subject) { OData4::Query::Criteria.new(property: datetime_property) }
129
+
130
+ describe '#year' do
131
+ let(:criteria) { subject.year.eq(1981) }
132
+
133
+ it { expect(subject).to respond_to(:year) }
134
+ it_behaves_like 'an operator-function criterium', :year, :eq, 1981, 'year(BirthDate) eq 1981'
135
+ end
136
+
137
+ describe '#month' do
138
+ let(:criteria) { subject.month.eq(8) }
139
+
140
+ it { expect(subject).to respond_to(:month) }
141
+ it_behaves_like 'an operator-function criterium', :month, :eq, 8, 'month(BirthDate) eq 8'
142
+ end
143
+
144
+ describe '#day' do
145
+ let(:criteria) { subject.day.eq(27) }
146
+
147
+ it { expect(subject).to respond_to(:day) }
148
+ it_behaves_like 'an operator-function criterium', :day, :eq, 27, 'day(BirthDate) eq 27'
149
+ end
150
+
151
+ describe '#hour' do
152
+ let(:criteria) { subject.hour.eq(9) }
153
+
154
+ it { expect(subject).to respond_to(:hour) }
155
+ it_behaves_like 'an operator-function criterium', :hour, :eq, 9, 'hour(BirthDate) eq 9'
156
+ end
157
+
158
+ describe '#minute' do
159
+ let(:criteria) { subject.minute.eq(35) }
160
+
161
+ it { expect(subject).to respond_to(:minute) }
162
+ it_behaves_like 'an operator-function criterium', :minute, :eq, 35, 'minute(BirthDate) eq 35'
163
+ end
164
+
165
+ describe '#second' do
166
+ let(:criteria) { subject.second.eq(11) }
167
+
168
+ it { expect(subject).to respond_to(:second) }
169
+ it_behaves_like 'an operator-function criterium', :second, :eq, 11, 'second(BirthDate) eq 11'
170
+ end
171
+
172
+ describe '#fractionalseconds' do
173
+ let(:criteria) { subject.fractionalseconds.eq(0) }
174
+
175
+ it { expect(subject).to respond_to(:fractionalseconds) }
176
+ it_behaves_like 'an operator-function criterium', :fractionalseconds, :eq, 0, 'fractionalseconds(BirthDate) eq 0'
177
+ end
178
+
179
+ describe '#date' do
180
+ let(:criteria) { subject.date.ne('date(EndTime)') }
181
+
182
+ it { expect(subject).to respond_to(:date) }
183
+ it_behaves_like 'an operator-function criterium', :date, :ne, 'date(EndTime)', 'date(BirthDate) ne date(EndTime)'
184
+ end
185
+
186
+ describe '#time' do
187
+ let(:criteria) { subject.time.le('StartOfDay') }
188
+
189
+ it { expect(subject).to respond_to(:time) }
190
+ it_behaves_like 'an operator-function criterium', :time, :le, 'StartOfDay', 'time(BirthDate) le StartOfDay'
191
+ end
192
+ end
193
+
194
+ describe 'geospatial functions' do
195
+ let(:subject) { OData4::Query::Criteria.new(property: geo_property) }
196
+
197
+ describe '#distance' do
198
+ let(:criteria) { subject.distance('TargetPosition').le(42) }
199
+
200
+ it { expect(subject).to respond_to(:distance) }
201
+ it_behaves_like 'an operator-function criterium', :'geo.distance', :le, 42, 'geo.distance(Position,TargetPosition) le 42'
202
+ end
203
+
204
+ describe '#intersects' do
205
+ let(:criteria) { subject.intersects('TargetArea') }
206
+
207
+ it { expect(subject).to respond_to(:intersects) }
208
+ it_behaves_like 'a function criterium', :'geo.intersects', 'TargetArea', 'geo.intersects(Position,TargetArea)'
209
+ end
210
+ end
211
+
212
+ describe 'lambda operators' do
213
+ let(:subject) { OData4::Query::Criteria.new(property: navigation_property) }
214
+
215
+ describe '#any' do
216
+ let(:criteria) { subject.any('Quantity').gt(100) }
217
+
218
+ it { expect(subject).to respond_to(:any) }
219
+ it_behaves_like 'an operator-function criterium', :any, :gt, 100, 'Items/any(d:d/Quantity gt 100)'
220
+ end
221
+
222
+ describe '#all' do
223
+ let(:criteria) { subject.all('Quantity').gt(100) }
224
+
225
+ it { expect(subject).to respond_to(:all) }
226
+ it_behaves_like 'an operator-function criterium', :all, :gt, 100, 'Items/all(d:d/Quantity gt 100)'
227
+ end
228
+ end
229
+ end
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples 'valid result' do
4
+ it { expect(result.count).to eq(11) }
5
+
6
+ describe '#empty?' do
7
+ it { expect(result).to respond_to(:empty?) }
8
+ it { expect(result.empty?).to eq(false) }
9
+ end
10
+
11
+ describe '#each' do
12
+ it { expect(result).to respond_to(:each) }
13
+ it 'returns just OData4::Entities of the right type' do
14
+ result.each do |entity|
15
+ expect(entity).to be_a(OData4::Entity)
16
+ expect(entity.type).to eq('Product')
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ describe OData4::Query::Result, vcr: {cassette_name: 'query/result_specs'} do
23
+ before(:example) do
24
+ OData4::Service.open('http://services.odata.org/V4/OData/OData.svc', name: 'ODataDemo')
25
+ end
26
+
27
+ let(:entity_set) { OData4::ServiceRegistry['ODataDemo']['Products'] }
28
+ # let(:subject) { entity_set.query.execute }
29
+
30
+ let(:response) do
31
+ response = double('response')
32
+ allow(response).to receive_messages(
33
+ headers: { 'Content-Type' => content_type },
34
+ body: response_body
35
+ )
36
+ response
37
+ end
38
+ let(:result) { OData4::Query::Result.new(entity_set.query, response) }
39
+
40
+ context 'with Atom Result' do
41
+ let(:content_type) { 'application/atom+xml' }
42
+ let(:response_body) { File.read('spec/fixtures/files/products.xml') }
43
+
44
+ it_behaves_like 'valid result'
45
+ end
46
+
47
+ context 'with JSON Result' do
48
+ let(:content_type) { 'application/json' }
49
+ let(:response_body) { File.read('spec/fixtures/files/products.json') }
50
+
51
+ it_behaves_like 'valid result'
52
+ end
53
+ end
@@ -0,0 +1,196 @@
1
+ require 'spec_helper'
2
+
3
+ describe OData4::Query, vcr: {cassette_name: 'query_specs'} do
4
+ before(:example) do
5
+ OData4::Service.open('http://services.odata.org/V4/OData/OData.svc', name: 'ODataDemo')
6
+ end
7
+
8
+ let(:subject) { OData4::Query.new(entity_set) }
9
+ let(:entity_set) { OData4::EntitySet.new(options) }
10
+ let(:options) { {
11
+ container: 'DemoService', namespace: 'ODataDemo', name: 'Products',
12
+ service_name: 'ODataDemo', type: 'Product'
13
+ } }
14
+
15
+ it { expect(subject).to respond_to(:to_s) }
16
+ it { expect(subject.to_s).to eq('Products')}
17
+
18
+ it { expect(subject).to respond_to(:[]) }
19
+ describe '#[]' do
20
+ it { expect(subject[:Name]).to be_a(OData4::Query::Criteria) }
21
+ it { expect(subject[:Name].property).to be_a(OData4::Property) }
22
+ end
23
+
24
+ it { expect(subject).to respond_to(:find) }
25
+ describe '#find' do
26
+ let(:product) { subject.find(0) }
27
+
28
+ it 'finds an entity by its ID' do
29
+ expect(product).to be_a(OData4::Entity)
30
+ expect(product['ID']).to eq(0)
31
+ end
32
+
33
+ it 'allows expanding navigational properties' do
34
+ product_with_categories = subject.expand('Categories').find(0)
35
+ expect(product_with_categories['Categories']).to eq([
36
+ { "ID" => 0, "Name" => "Food" }
37
+ ])
38
+ end
39
+ end
40
+
41
+ it { expect(subject).to respond_to(:where) }
42
+ describe '#where' do
43
+ let(:criteria) { subject[:Name].eq('Bread') }
44
+ let(:query_string) { "Products?$filter=Name eq 'Bread'" }
45
+
46
+ it { expect(subject.where(criteria)).to eq(subject) }
47
+ it { expect(subject.where(criteria).to_s).to eq(query_string) }
48
+ end
49
+
50
+ it { expect(subject).to respond_to(:search) }
51
+ describe '#search' do
52
+ let(:term) { '"mountain bike"' }
53
+ let(:query_string) { 'Products?$search="mountain bike"' }
54
+
55
+ it { expect(subject.search(term)).to eq(subject) }
56
+ it { expect(subject.search(term).to_s).to eq(query_string) }
57
+
58
+ describe 'with multiple terms' do
59
+ let(:query_string) { 'Products?$search="mountain bike" AND NOT clothing' }
60
+
61
+ it { expect(subject.search(term).search('NOT clothing').to_s).to eq(query_string) }
62
+ end
63
+ end
64
+
65
+ #it { expect(subject).to respond_to(:and) }
66
+ describe '#and' do
67
+ it { pending; fail }
68
+ end
69
+
70
+ #it { expect(subject).to respond_to(:or) }
71
+ describe '#or' do
72
+ it { pending; fail }
73
+ end
74
+
75
+ it { expect(subject).to respond_to(:skip) }
76
+ describe '#skip' do
77
+ it { expect(subject.skip(5)).to eq(subject) }
78
+ it 'properly formats query with skip specified' do
79
+ subject.skip(5)
80
+ expect(subject.to_s).to eq('Products?$skip=5')
81
+ end
82
+ end
83
+
84
+ it { expect(subject).to respond_to(:limit) }
85
+ describe '#limit' do
86
+
87
+ it { expect(subject.limit(5)).to eq(subject) }
88
+ it 'properly formats query with limit specified' do
89
+ subject.limit(5)
90
+ expect(subject.to_s).to eq('Products?$top=5')
91
+ end
92
+ end
93
+
94
+ it { expect(subject).to respond_to(:include_count) }
95
+ describe '#include_count' do
96
+ it { expect(subject.include_count).to eq(subject) }
97
+ it 'properly formats query with include_count specified' do
98
+ subject.include_count
99
+ expect(subject.to_s).to eq('Products?$count=true')
100
+ end
101
+ end
102
+
103
+ it { expect(subject).to respond_to(:select) }
104
+ describe '#select' do
105
+ it { expect(subject.select(:Name, :Price)).to eq(subject) }
106
+ it 'properly formats query with select operation specified' do
107
+ subject.select(:Name, :Price)
108
+ expect(subject.to_s).to eq('Products?$select=Name,Price')
109
+ end
110
+ end
111
+
112
+ it { expect(subject).to respond_to(:expand) }
113
+ describe '#expand' do
114
+ it { expect(subject.expand(:Supplier)).to eq(subject) }
115
+ it 'properly formats query with expand operation specified' do
116
+ subject.expand(:Supplier)
117
+ expect(subject.to_s).to eq('Products?$expand=Supplier')
118
+ end
119
+ end
120
+
121
+ it { expect(subject).to respond_to(:order_by) }
122
+ describe '#order_by' do
123
+ it { expect(subject.order_by(:Name, :Price)).to eq(subject) }
124
+ it 'properly formats query with orderby operation specified' do
125
+ subject.order_by(:Name, :Price)
126
+ expect(subject.to_s).to eq('Products?$orderby=Name,Price')
127
+ end
128
+ end
129
+
130
+ describe '#execute' do
131
+ it { expect(subject).to respond_to(:execute) }
132
+ it { expect(subject.execute).to be_a(OData4::Query::Result) }
133
+ end
134
+
135
+ describe '#count' do
136
+ it { expect(subject).to respond_to(:count) }
137
+ it { expect(subject.count).to be_a(Integer) }
138
+ it { expect(subject.count).to eq(11) }
139
+
140
+ context 'with filters' do
141
+ let(:criteria) { subject[:Name].eq('Bread') }
142
+
143
+ it { expect(subject.where(criteria).count).to eq(1) }
144
+ end
145
+ end
146
+
147
+ describe '#empty?' do
148
+ it { expect(subject).to respond_to(:empty?) }
149
+ it { expect(subject.empty?).to eq(false) }
150
+
151
+ context 'with filters' do
152
+ let(:non_empty_criteria) { subject[:Name].eq('Bread') }
153
+ let(:empty_criteria) { subject[:Name].eq('NonExistent') }
154
+
155
+ it { expect(subject.where(non_empty_criteria).empty?).to eq(false) }
156
+ it { expect(subject.where(empty_criteria).empty?).to eq(true) }
157
+ end
158
+ end
159
+
160
+ describe '#in_batches' do
161
+ it { expect(subject).to respond_to(:in_batches) }
162
+ it 'returns OData4::Entities in batches of specified size' do
163
+ batch_count = entity_count = 0
164
+
165
+ subject.in_batches(of: 5) do |batch|
166
+ expect(batch).to be_a(OData4::Query::Result)
167
+ expect(batch.count).to eq(5) unless batch_count == 2
168
+
169
+ batch.each do |entity|
170
+ expect(entity).to be_a(OData4::Entity)
171
+ expect(entity.type).to eq('Product')
172
+ entity_count += 1
173
+ end
174
+
175
+ batch_count += 1
176
+ end
177
+
178
+ expect(batch_count).to eq(3)
179
+ expect(entity_count).to eq(11)
180
+ end
181
+
182
+ describe '#each' do
183
+ it 'returns OData4::Entities' do
184
+ entity_count = 0
185
+
186
+ subject.in_batches(of: 5).each do |entity|
187
+ expect(entity).to be_a(OData4::Entity)
188
+ expect(entity.type).to eq('Product')
189
+ entity_count += 1
190
+ end
191
+
192
+ expect(entity_count).to eq(11)
193
+ end
194
+ end
195
+ end
196
+ end