odata4 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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