frodo 0.10.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 (153) hide show
  1. checksums.yaml +7 -0
  2. data/.autotest +2 -0
  3. data/.circleci/config.yml +54 -0
  4. data/.gitignore +24 -0
  5. data/.gitlab-ci.yml +9 -0
  6. data/.rspec +2 -0
  7. data/.ruby-gemset +1 -0
  8. data/.ruby-version +1 -0
  9. data/.travis.yml +75 -0
  10. data/CHANGELOG.md +163 -0
  11. data/Gemfile +4 -0
  12. data/LICENSE.txt +23 -0
  13. data/README.md +479 -0
  14. data/Rakefile +7 -0
  15. data/TODO.md +55 -0
  16. data/frodo.gemspec +39 -0
  17. data/images/frodo.jpg +0 -0
  18. data/lib/frodo/abstract_client.rb +11 -0
  19. data/lib/frodo/client.rb +6 -0
  20. data/lib/frodo/concerns/api.rb +292 -0
  21. data/lib/frodo/concerns/authentication.rb +32 -0
  22. data/lib/frodo/concerns/base.rb +84 -0
  23. data/lib/frodo/concerns/caching.rb +26 -0
  24. data/lib/frodo/concerns/connection.rb +79 -0
  25. data/lib/frodo/concerns/verbs.rb +68 -0
  26. data/lib/frodo/config.rb +143 -0
  27. data/lib/frodo/entity.rb +335 -0
  28. data/lib/frodo/entity_container.rb +75 -0
  29. data/lib/frodo/entity_set.rb +131 -0
  30. data/lib/frodo/errors.rb +70 -0
  31. data/lib/frodo/middleware/authentication/token.rb +13 -0
  32. data/lib/frodo/middleware/authentication.rb +87 -0
  33. data/lib/frodo/middleware/authorization.rb +18 -0
  34. data/lib/frodo/middleware/caching.rb +30 -0
  35. data/lib/frodo/middleware/custom_headers.rb +14 -0
  36. data/lib/frodo/middleware/gzip.rb +33 -0
  37. data/lib/frodo/middleware/instance_url.rb +20 -0
  38. data/lib/frodo/middleware/logger.rb +42 -0
  39. data/lib/frodo/middleware/multipart.rb +64 -0
  40. data/lib/frodo/middleware/odata_headers.rb +13 -0
  41. data/lib/frodo/middleware/raise_error.rb +47 -0
  42. data/lib/frodo/middleware.rb +33 -0
  43. data/lib/frodo/navigation_property/proxy.rb +80 -0
  44. data/lib/frodo/navigation_property.rb +29 -0
  45. data/lib/frodo/properties/binary.rb +50 -0
  46. data/lib/frodo/properties/boolean.rb +37 -0
  47. data/lib/frodo/properties/collection.rb +50 -0
  48. data/lib/frodo/properties/complex.rb +114 -0
  49. data/lib/frodo/properties/date.rb +27 -0
  50. data/lib/frodo/properties/date_time.rb +83 -0
  51. data/lib/frodo/properties/date_time_offset.rb +17 -0
  52. data/lib/frodo/properties/decimal.rb +54 -0
  53. data/lib/frodo/properties/enum.rb +62 -0
  54. data/lib/frodo/properties/float.rb +67 -0
  55. data/lib/frodo/properties/geography/base.rb +162 -0
  56. data/lib/frodo/properties/geography/line_string.rb +33 -0
  57. data/lib/frodo/properties/geography/point.rb +31 -0
  58. data/lib/frodo/properties/geography/polygon.rb +38 -0
  59. data/lib/frodo/properties/geography.rb +13 -0
  60. data/lib/frodo/properties/guid.rb +17 -0
  61. data/lib/frodo/properties/integer.rb +107 -0
  62. data/lib/frodo/properties/number.rb +14 -0
  63. data/lib/frodo/properties/string.rb +72 -0
  64. data/lib/frodo/properties/time.rb +40 -0
  65. data/lib/frodo/properties/time_of_day.rb +27 -0
  66. data/lib/frodo/properties.rb +32 -0
  67. data/lib/frodo/property.rb +139 -0
  68. data/lib/frodo/property_registry.rb +41 -0
  69. data/lib/frodo/query/criteria/comparison_operators.rb +49 -0
  70. data/lib/frodo/query/criteria/date_functions.rb +61 -0
  71. data/lib/frodo/query/criteria/geography_functions.rb +21 -0
  72. data/lib/frodo/query/criteria/lambda_operators.rb +27 -0
  73. data/lib/frodo/query/criteria/string_functions.rb +40 -0
  74. data/lib/frodo/query/criteria.rb +92 -0
  75. data/lib/frodo/query/in_batches.rb +58 -0
  76. data/lib/frodo/query.rb +221 -0
  77. data/lib/frodo/railtie.rb +19 -0
  78. data/lib/frodo/schema/complex_type.rb +79 -0
  79. data/lib/frodo/schema/enum_type.rb +95 -0
  80. data/lib/frodo/schema.rb +164 -0
  81. data/lib/frodo/service.rb +199 -0
  82. data/lib/frodo/service_registry.rb +52 -0
  83. data/lib/frodo/version.rb +3 -0
  84. data/lib/frodo.rb +67 -0
  85. data/spec/fixtures/auth_success_response.json +11 -0
  86. data/spec/fixtures/error.json +11 -0
  87. data/spec/fixtures/files/entity_to_xml.xml +18 -0
  88. data/spec/fixtures/files/error.xml +5 -0
  89. data/spec/fixtures/files/metadata.xml +150 -0
  90. data/spec/fixtures/files/metadata_with_error.xml +157 -0
  91. data/spec/fixtures/files/product_0.json +10 -0
  92. data/spec/fixtures/files/product_0.xml +28 -0
  93. data/spec/fixtures/files/products.json +106 -0
  94. data/spec/fixtures/files/products.xml +308 -0
  95. data/spec/fixtures/files/supplier_0.json +26 -0
  96. data/spec/fixtures/files/supplier_0.xml +32 -0
  97. data/spec/fixtures/leads.json +923 -0
  98. data/spec/fixtures/refresh_error_response.json +8 -0
  99. data/spec/frodo/abstract_client_spec.rb +13 -0
  100. data/spec/frodo/client_spec.rb +57 -0
  101. data/spec/frodo/concerns/authentication_spec.rb +79 -0
  102. data/spec/frodo/concerns/base_spec.rb +68 -0
  103. data/spec/frodo/concerns/caching_spec.rb +40 -0
  104. data/spec/frodo/concerns/connection_spec.rb +65 -0
  105. data/spec/frodo/config_spec.rb +127 -0
  106. data/spec/frodo/entity/shared_examples.rb +83 -0
  107. data/spec/frodo/entity_container_spec.rb +38 -0
  108. data/spec/frodo/entity_set_spec.rb +169 -0
  109. data/spec/frodo/entity_spec.rb +153 -0
  110. data/spec/frodo/errors_spec.rb +48 -0
  111. data/spec/frodo/middleware/authentication/token_spec.rb +87 -0
  112. data/spec/frodo/middleware/authentication_spec.rb +83 -0
  113. data/spec/frodo/middleware/authorization_spec.rb +17 -0
  114. data/spec/frodo/middleware/custom_headers_spec.rb +21 -0
  115. data/spec/frodo/middleware/gzip_spec.rb +68 -0
  116. data/spec/frodo/middleware/instance_url_spec.rb +27 -0
  117. data/spec/frodo/middleware/logger_spec.rb +21 -0
  118. data/spec/frodo/middleware/odata_headers_spec.rb +15 -0
  119. data/spec/frodo/middleware/raise_error_spec.rb +66 -0
  120. data/spec/frodo/navigation_property/proxy_spec.rb +46 -0
  121. data/spec/frodo/navigation_property_spec.rb +55 -0
  122. data/spec/frodo/properties/binary_spec.rb +50 -0
  123. data/spec/frodo/properties/boolean_spec.rb +72 -0
  124. data/spec/frodo/properties/collection_spec.rb +44 -0
  125. data/spec/frodo/properties/date_spec.rb +23 -0
  126. data/spec/frodo/properties/date_time_offset_spec.rb +30 -0
  127. data/spec/frodo/properties/date_time_spec.rb +23 -0
  128. data/spec/frodo/properties/decimal_spec.rb +50 -0
  129. data/spec/frodo/properties/float_spec.rb +45 -0
  130. data/spec/frodo/properties/geography/line_string_spec.rb +33 -0
  131. data/spec/frodo/properties/geography/point_spec.rb +29 -0
  132. data/spec/frodo/properties/geography/polygon_spec.rb +55 -0
  133. data/spec/frodo/properties/geography/shared_examples.rb +72 -0
  134. data/spec/frodo/properties/guid_spec.rb +17 -0
  135. data/spec/frodo/properties/integer_spec.rb +58 -0
  136. data/spec/frodo/properties/string_spec.rb +46 -0
  137. data/spec/frodo/properties/time_of_day_spec.rb +23 -0
  138. data/spec/frodo/properties/time_spec.rb +15 -0
  139. data/spec/frodo/property_registry_spec.rb +16 -0
  140. data/spec/frodo/property_spec.rb +71 -0
  141. data/spec/frodo/query/criteria_spec.rb +229 -0
  142. data/spec/frodo/query_spec.rb +156 -0
  143. data/spec/frodo/schema/complex_type_spec.rb +97 -0
  144. data/spec/frodo/schema/enum_type_spec.rb +112 -0
  145. data/spec/frodo/schema_spec.rb +113 -0
  146. data/spec/frodo/service_registry_spec.rb +19 -0
  147. data/spec/frodo/service_spec.rb +153 -0
  148. data/spec/frodo/usage_example_spec.rb +161 -0
  149. data/spec/spec_helper.rb +35 -0
  150. data/spec/support/coverage.rb +2 -0
  151. data/spec/support/fixture_helpers.rb +14 -0
  152. data/spec/support/middleware.rb +19 -0
  153. metadata +479 -0
@@ -0,0 +1,169 @@
1
+ require 'spec_helper'
2
+
3
+ describe Frodo::EntitySet, vcr: {cassette_name: 'entity_set_specs'} do
4
+ before(:example) do
5
+ Frodo::Service.new('http://services.odata.org/V4/OData/OData.svc', name: 'ODataDemo', metadata_file: metadata_file)
6
+ end
7
+
8
+ let(:metadata_file) { 'spec/fixtures/files/metadata.xml' }
9
+ let(:subject) { Frodo::EntitySet.new(options) }
10
+ let(:options) { {
11
+ container: 'DemoService', namespace: 'ODataDemo', name: 'Products',
12
+ type: 'ODataDemo.Product', service_name: 'ODataDemo'
13
+ } }
14
+
15
+ it { expect(subject).to respond_to(:name) }
16
+ it { expect(subject).to respond_to(:type) }
17
+ it { expect(subject).to respond_to(:container) }
18
+ it { expect(subject).to respond_to(:namespace) }
19
+ it { expect(subject).to respond_to(:service_name) }
20
+ it { expect(subject).to respond_to(:new_entity) }
21
+ # it { expect(subject).to respond_to(:[]) }
22
+ # it { expect(subject).to respond_to(:<<) }
23
+
24
+ it { expect(subject.name).to eq('Products') }
25
+ it { expect(subject.container).to eq('DemoService') }
26
+ it { expect(subject.namespace).to eq('ODataDemo') }
27
+ it { expect(subject.service_name).to eq('ODataDemo') }
28
+ it { expect(subject.type).to eq('ODataDemo.Product') }
29
+
30
+ # describe '#each' do
31
+ # it { expect(subject).to respond_to(:each) }
32
+ # it { expect(lambda {
33
+ # @counter = 0
34
+ # subject.each {|entity| @counter += 1}
35
+ # @counter
36
+ # }.call).to eq(11) }
37
+ # it { expect(lambda {
38
+ # @entities = []
39
+ # subject.each {|entity| @entities << entity}
40
+ # @entities
41
+ # }.call.shuffle.first).to be_a(Frodo::Entity) }
42
+ # end
43
+
44
+ # describe '#first' do
45
+ # it { expect(subject).to respond_to(:first) }
46
+
47
+ # describe 'retrieving a single entity' do
48
+ # it { expect(subject.first).to be_a(Frodo::Entity) }
49
+ # it { expect(subject.first['ID']).to eq(0) }
50
+ # end
51
+
52
+ # describe 'retrieving multiple entities' do
53
+ # it { expect(subject.first(5)).to be_a(Array) }
54
+ # it { expect(subject.first(5).length).to eq(5) }
55
+ # it do
56
+ # subject.first(5).each do |entity|
57
+ # expect(entity).to be_a(Frodo::Entity)
58
+ # end
59
+ # end
60
+ # end
61
+ # end
62
+
63
+ # describe '#count' do
64
+ # it { expect(subject).to respond_to(:count) }
65
+ # it { expect(subject.count).to eq(11) }
66
+ # end
67
+
68
+ describe '#query' do
69
+ it { expect(subject).to respond_to(:query) }
70
+ it { expect(subject.query).to be_a(Frodo::Query) }
71
+ end
72
+
73
+ describe '#new_entity' do
74
+ let(:new_entity) { subject.new_entity(properties) }
75
+ let(:release_date) { DateTime.new(2014,7,5) }
76
+ let(:properties) { {
77
+ Name: 'Widget',
78
+ Description: 'Just a simple widget',
79
+ ReleaseDate: release_date,
80
+ DiscontinuedDate: nil,
81
+ Rating: 4,
82
+ Price: 3.5
83
+ } }
84
+
85
+ it { expect(new_entity.entity_set).to eq(subject) }
86
+ it { expect(new_entity['ID']).to be_nil }
87
+ it { expect(new_entity['Name']).to eq('Widget') }
88
+ it { expect(new_entity['Description']).to eq('Just a simple widget') }
89
+ it { expect(new_entity['ReleaseDate']).to eq(release_date) }
90
+ it { expect(new_entity['DiscontinuedDate']).to be_nil }
91
+ it { expect(new_entity['Rating']).to eq(4) }
92
+ it { expect(new_entity['Price']).to eq(3.5) }
93
+ end
94
+
95
+ # describe '#[]' do
96
+ # let(:existing_entity) { subject[0] }
97
+ # let(:nonexistant_entity) { subject[99] }
98
+
99
+ # it 'finds an entity by its primary key' do
100
+ # expect(existing_entity).to be_a(Frodo::Entity)
101
+ # expect(existing_entity['ID']).to eq(0)
102
+ # end
103
+
104
+ # it 'raises an error when no entity was found' do
105
+ # expect { nonexistant_entity }.to raise_error(Frodo::Errors::NotFound)
106
+ # end
107
+
108
+ # describe 'eager loading' do
109
+ # it 'works with a single property' do
110
+ # product_with_categories = subject[1, expand: 'Categories']
111
+
112
+ # expect(product_with_categories['Categories']).to eq([
113
+ # { "ID" => 0, "Name" => "Food" },
114
+ # { "ID" => 1, "Name" => "Beverages" }
115
+ # ])
116
+ # end
117
+
118
+ # it 'works with multiple properties' do
119
+ # product_with_details = subject[1, expand: %w[Categories Supplier]]
120
+
121
+ # expect(product_with_details['Supplier']).to include('Name' => 'Exotic Liquids')
122
+ # expect(product_with_details['Categories']).to be_a(Array)
123
+ # end
124
+
125
+ # it 'works with special shortcut for all properties' do
126
+ # product_with_all_details = subject[1, expand: :all]
127
+
128
+ # expect(product_with_all_details['Supplier']).to include('Name' => 'Exotic Liquids')
129
+ # expect(product_with_all_details['Categories']).to be_a(Array)
130
+ # expect(product_with_all_details['ProductDetail']).to include('Details' => 'Details of product 1')
131
+ # end
132
+ # end
133
+ # end
134
+
135
+ # describe '#<<' do
136
+ # let(:new_entity) { subject.new_entity(properties) }
137
+ # let(:bad_entity) { subject.new_entity }
138
+ # let(:existing_entity) { subject.first }
139
+ # let(:properties) { {
140
+ # Name: 'Widget',
141
+ # Description: 'Just a simple widget',
142
+ # ReleaseDate: DateTime.now.new_offset(0),
143
+ # DiscontinuedDate: nil,
144
+ # Rating: 4,
145
+ # Price: 3.5
146
+ # } }
147
+
148
+ # xdescribe 'with an existing entity', vcr: {cassette_name: 'entity_set_specs/existing_entry'} do
149
+ # before(:each) do
150
+ # subject << existing_entity
151
+ # end
152
+
153
+ # it { expect(existing_entity.any_errors?).to eq(false) }
154
+ # end
155
+
156
+ # xdescribe 'with a new entity', vcr: {cassette_name: 'entity_set_specs/new_entry'} do
157
+ # it do
158
+ # expect(new_entity['ID']).to be_nil
159
+ # expect {subject << new_entity}.to_not raise_error
160
+ # expect(new_entity['ID']).to_not be_nil
161
+ # expect(new_entity['ID']).to eq(9999)
162
+ # end
163
+ # end
164
+
165
+ # xdescribe 'with a bad entity', vcr: {cassette_name: 'entity_set_specs/bad_entry'} do
166
+ # it { expect{subject << bad_entity}.to raise_error }
167
+ # end
168
+ # end
169
+ end
@@ -0,0 +1,153 @@
1
+ require 'spec_helper'
2
+ require_relative 'entity/shared_examples'
3
+
4
+ describe Frodo::Entity, vcr: {cassette_name: 'entity_specs'} do
5
+ before(:example) do
6
+ Frodo::Service.new('http://services.odata.org/V4/OData/OData.svc', name: 'ODataDemo', metadata_file: metadata_file)
7
+ end
8
+
9
+ let(:metadata_file) { 'spec/fixtures/files/metadata.xml' }
10
+ let(:subject) { Frodo::Entity.new(options) }
11
+ let(:options) { {
12
+ type: 'ODataDemo.Product',
13
+ namespace: 'ODataDemo',
14
+ service_name: 'ODataDemo'
15
+ } }
16
+
17
+ it { expect(subject).to respond_to(:name, :type, :namespace, :service_name) }
18
+
19
+ it { expect(subject.name).to eq('Product') }
20
+ it { expect(subject.type).to eq('ODataDemo.Product') }
21
+ it { expect(subject.namespace).to eq('ODataDemo') }
22
+ it { expect(subject.service_name).to eq('ODataDemo') }
23
+
24
+ describe '.with_properties' do
25
+ let(:subject) { Frodo::Entity.with_properties(properties, options) }
26
+ let(:properties) { {
27
+ "ID" => 0,
28
+ "Name" => "Bread",
29
+ "Description" => "Whole grain bread",
30
+ "ReleaseDate" => "1992-01-01T00:00:00Z",
31
+ "DiscontinuedDate" => nil,
32
+ "Rating" => 4,
33
+ "Price" => 2.5
34
+ } }
35
+ let(:entity_set) {
36
+ Frodo::EntitySet.new(
37
+ container: 'DemoService',
38
+ namespace: 'ODataDemo',
39
+ name: 'Products',
40
+ type: 'Product',
41
+ service_name: 'ODataDemo')
42
+ }
43
+ let(:options) { {
44
+ type: 'ODataDemo.Product',
45
+ namespace: 'ODataDemo',
46
+ service_name: 'ODataDemo',
47
+ entity_set: entity_set
48
+ } }
49
+
50
+ it_behaves_like 'a valid product'
51
+ end
52
+
53
+ describe '.from_xml' do
54
+ let(:subject) { Frodo::Entity.from_xml(product_xml, options) }
55
+ let(:product_xml) {
56
+ document = ::Nokogiri::XML(File.open('spec/fixtures/files/product_0.xml'))
57
+ document.remove_namespaces!
58
+ document.xpath('//entry').first
59
+ }
60
+
61
+ it { expect(Frodo::Entity).to respond_to(:from_xml) }
62
+
63
+ it_behaves_like 'a valid product'
64
+
65
+ context 'with a complex type property' do
66
+ let(:options) { {
67
+ type: 'ODataDemo.Supplier',
68
+ namespace: 'ODataDemo',
69
+ service_name: 'ODataDemo'
70
+ } }
71
+
72
+ let(:subject) { Frodo::Entity.from_xml(supplier_xml, options) }
73
+ let(:supplier_xml) {
74
+ document = ::Nokogiri::XML(File.open('spec/fixtures/files/supplier_0.xml'))
75
+ document.remove_namespaces!
76
+ document.xpath('//entry').first
77
+ }
78
+
79
+ it_behaves_like 'a valid supplier'
80
+ end
81
+ end
82
+
83
+ describe '#to_xml' do
84
+ let(:subject) { Frodo::Entity.with_properties(properties, options) }
85
+ let(:properties) { {
86
+ "ID" => 0,
87
+ "Name" => "Bread",
88
+ "Description" => "Whole grain bread",
89
+ "ReleaseDate" => "1992-01-01T00:00:00Z",
90
+ "DiscontinuedDate" => nil,
91
+ "Rating" => 4,
92
+ "Price" => 2.5
93
+ } }
94
+ let(:options) { {
95
+ type: 'ODataDemo.Product',
96
+ namespace: 'ODataDemo',
97
+ service_name: 'ODataDemo'
98
+ } }
99
+ let(:product_xml) {
100
+ File.read('spec/fixtures/files/entity_to_xml.xml')
101
+ }
102
+
103
+ # TODO: parse the XML and veryify property values instead?
104
+ # TODO: explicitly assert namespace URIs?
105
+ it { expect(subject.to_xml).to eq(product_xml) }
106
+ end
107
+
108
+ describe '.from_json' do
109
+ let(:subject) { Frodo::Entity.from_json(product_json, options) }
110
+ let(:product_json) {
111
+ File.read('spec/fixtures/files/product_0.json')
112
+ }
113
+
114
+ it { expect(Frodo::Entity).to respond_to(:from_json) }
115
+ it_behaves_like 'a valid product'
116
+
117
+ context 'with a complex type property' do
118
+ let(:options) { {
119
+ type: 'ODataDemo.Supplier',
120
+ namespace: 'ODataDemo',
121
+ service_name: 'ODataDemo'
122
+ } }
123
+
124
+ let(:subject) { Frodo::Entity.from_json(supplier_json, options) }
125
+ let(:supplier_json) {
126
+ File.read('spec/fixtures/files/supplier_0.json')
127
+ }
128
+
129
+ it_behaves_like 'a valid supplier'
130
+ end
131
+ end
132
+
133
+ describe '#to_json' do
134
+ let(:subject) { Frodo::Entity.with_properties(properties, options) }
135
+ let(:properties) { {
136
+ "ID" => 0,
137
+ "Name" => "Bread",
138
+ "Description" => "Whole grain bread",
139
+ "ReleaseDate" => "1992-01-01T00:00:00Z",
140
+ "DiscontinuedDate" => nil,
141
+ "Rating" => 4,
142
+ "Price" => 2.5,
143
+ "ProductStatus" => nil
144
+ } }
145
+ let(:options) { {
146
+ type: 'ODataDemo.Product',
147
+ namespace: 'ODataDemo',
148
+ service_name: 'ODataDemo'
149
+ } }
150
+
151
+ it { expect(subject.to_json).to eq(properties.to_json) }
152
+ end
153
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ describe Frodo::RequestError do
4
+ subject { Frodo::RequestError.new(response, 'The server made a boo-boo.') }
5
+ let(:response) { instance_double('Faraday::Response', status: 400) }
6
+
7
+ describe '#http_status' do
8
+ it 'returns the status code' do
9
+ expect(subject.http_status).to eq(response.status)
10
+ end
11
+ end
12
+
13
+ describe '#response' do
14
+ it 'returns the response' do
15
+ expect(subject.response).to eq(response)
16
+ end
17
+ end
18
+
19
+ describe '#message' do
20
+ it 'returns the error message' do
21
+ expect(subject.message).to eq('The server made a boo-boo.')
22
+ end
23
+ end
24
+ end
25
+
26
+ describe Frodo::Errors::InternalServerError do
27
+ let(:response) { instance_double('Faraday::Response', status: 500) }
28
+
29
+ context 'with custom error message' do
30
+ subject { Frodo::Errors::InternalServerError.new(response, 'The server made a boo-boo.')}
31
+
32
+ describe '#message' do
33
+ it 'combines default message with custom message' do
34
+ expect(subject.message).to eq('500 Internal Server Error: The server made a boo-boo.')
35
+ end
36
+ end
37
+ end
38
+
39
+ context 'without custom error message' do
40
+ subject { Frodo::Errors::InternalServerError.new(response) }
41
+
42
+ describe '#message' do
43
+ it 'returns the default message' do
44
+ expect(subject.message).to eq('500 Internal Server Error')
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'webmock/rspec'
5
+
6
+ describe Frodo::Middleware::Authentication::Token do
7
+ include WebMock::API
8
+
9
+ describe 'authentication middleware' do
10
+ let(:options) do
11
+ { refresh_token: 'refresh_token',
12
+ client_id: 'client_id',
13
+ client_secret: 'client_secret',
14
+ adapter: :net_http,
15
+ host: 'login.window.net'
16
+ }
17
+ end
18
+
19
+ let(:success_request) do
20
+ stub_request(:post, "https://login.window.net/common/oauth2/token").with(
21
+ body: "grant_type=refresh_token&refresh_token=refresh_token&" \
22
+ "client_id=client_id&client_secret=client_secret"
23
+ ).to_return(status: 200, body: fixture("auth_success_response"))
24
+ end
25
+
26
+ let(:fail_request) do
27
+ stub_request(:post, "https://login.window.net/common/oauth2/token").with(
28
+ body: "grant_type=refresh_token&refresh_token=refresh_token&" \
29
+ "client_id=client_id&client_secret=client_secret"
30
+ ).to_return(status: 400, body: fixture("refresh_error_response"))
31
+ end
32
+
33
+ describe '.authenticate!' do
34
+ context 'when successful' do
35
+ let!(:request) { success_request }
36
+
37
+ describe '@options' do
38
+ subject { options }
39
+
40
+ before do
41
+ middleware.authenticate!
42
+ end
43
+
44
+ it { expect(subject[:host]).to eq 'login.window.net' }
45
+
46
+ it { expect(subject[:oauth_token]).to eq "gfEzf4azkWZMTjlay7usiSWhc0eOLNkKMw" }
47
+
48
+ it { expect(subject[:refresh_token]).to eq "QswqIkdHSdbyvbDFuLwHNAoU1QgAA" }
49
+ end
50
+
51
+ context 'when an authentication_callback is specified' do
52
+ before(:each) do
53
+ options.merge!(authentication_callback: auth_callback)
54
+ end
55
+
56
+ it 'calls the authentication callback with the response body' do
57
+ expect(auth_callback).to receive(:call)
58
+ middleware.authenticate!
59
+ end
60
+ end
61
+ end
62
+
63
+ context 'when unsuccessful' do
64
+ let!(:request) { fail_request }
65
+
66
+ it 'raises an exception' do
67
+ expect {
68
+ middleware.authenticate!
69
+ }.to raise_error Frodo::AuthenticationError
70
+ end
71
+
72
+ context 'when an authentication_callback is specified' do
73
+ before(:each) do
74
+ options.merge!(authentication_callback: auth_callback)
75
+ end
76
+
77
+ it 'does not call the authentication callback' do
78
+ expect(auth_callback).to_not receive(:call)
79
+ expect do
80
+ middleware.authenticate!
81
+ end.to raise_error
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Frodo::Middleware::Authentication do
6
+ let(:options) do
7
+ { host: 'login.windows.net',
8
+ proxy_uri: 'https://not-a-real-site.com',
9
+ authentication_retries: retries,
10
+ adapter: :net_http,
11
+ ssl: { version: :TLSv1_2 } }
12
+ end
13
+
14
+ describe '.authenticate!' do
15
+ subject { lambda { middleware.authenticate! } }
16
+ it { should raise_error NotImplementedError }
17
+ end
18
+
19
+ describe '.call' do
20
+ subject { lambda { middleware.call(env) } }
21
+
22
+ context 'when successfull' do
23
+ before do
24
+ expect(app).to receive(:call).once
25
+ end
26
+
27
+ it { should_not raise_error }
28
+ end
29
+
30
+ context 'when an exception is thrown' do
31
+ before do
32
+ expect(middleware).to receive(:authenticate!)
33
+ expect(app).to receive(:call).once.
34
+ and_raise(Frodo::UnauthorizedError.new('something bad'))
35
+ end
36
+
37
+ it { should raise_error Frodo::UnauthorizedError }
38
+ end
39
+ end
40
+
41
+ describe '.connection' do
42
+ subject(:connection) { middleware.connection }
43
+
44
+ it { expect(subject.url_prefix).to eq(URI.parse('https://login.windows.net')) }
45
+
46
+ it "should have a proxy URI" do
47
+ expect(connection.proxy[:uri]).to eq(URI.parse('https://not-a-real-site.com'))
48
+ end
49
+
50
+ describe '.builder' do
51
+ subject(:builder) { connection.builder }
52
+
53
+ context 'with logging disabled' do
54
+ before do
55
+ expect(Frodo).to receive(:log?).and_return(false)
56
+ end
57
+
58
+ it { expect(subject.handlers).to include FaradayMiddleware::ParseJson, Faraday::Adapter::NetHttp }
59
+ it { expect(subject.handlers).not_to include Frodo::Middleware::Logger }
60
+ end
61
+
62
+ context 'with logging enabled' do
63
+ before do
64
+ expect(Frodo).to receive(:log?).and_return(true)
65
+ end
66
+
67
+ it { expect(subject.handlers).to include FaradayMiddleware::ParseJson, Faraday::Adapter::NetHttp, Frodo::Middleware::Logger }
68
+ end
69
+
70
+ context 'with specified adapter' do
71
+ before do
72
+ options[:adapter] = :typhoeus
73
+ end
74
+
75
+ it { expect(subject.handlers).to include FaradayMiddleware::ParseJson, Faraday::Adapter::Typhoeus }
76
+ end
77
+ end
78
+
79
+ it "should have SSL config set" do
80
+ expect(connection.ssl[:version]).to eq(:TLSv1_2)
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Frodo::Middleware::Authorization do
6
+ let(:options) { { oauth_token: 'token' } }
7
+
8
+ describe '.call' do
9
+ subject { middleware }
10
+
11
+ before do
12
+ subject.call(env)
13
+ end
14
+
15
+ it { expect(env[:request_headers]['Authorization']).to eq 'Bearer token' }
16
+ end
17
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Frodo::Middleware::CustomHeaders do
6
+ describe '.call' do
7
+ subject { lambda { middleware.call(env) } }
8
+
9
+ context 'when :request_headers are a Hash' do
10
+ let(:options) { { request_headers: { 'x-test-header' => 'Test Value' } } }
11
+
12
+ it { should change { env[:request_headers]['x-test-header'] }.to eq 'Test Value' }
13
+ end
14
+
15
+ context 'when :request_headers are not a Hash' do
16
+ let(:options) { { request_headers: 'bad header' } }
17
+
18
+ it { should_not(change { env[:request_headers] }) }
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Frodo::Middleware::Gzip do
6
+ let(:options) { { oauth_token: 'token' } }
7
+
8
+ # Return a gzipped string.
9
+ def gzip(str)
10
+ StringIO.new.tap do |io|
11
+ gz = Zlib::GzipWriter.new(io)
12
+ gz.write(str)
13
+ gz.close
14
+ end.string
15
+ end
16
+
17
+ describe '.call' do
18
+ subject { lambda { middleware.call(env) } }
19
+
20
+ before do
21
+ expect(app).to receive(:on_complete) { middleware.on_complete(env) }
22
+ expect(app).to receive(:call) do
23
+ env[:body] = gzip fixture('leads')
24
+ env[:response_headers]['Content-Encoding'] = 'gzip'
25
+ app
26
+ end
27
+ end
28
+
29
+ it 'decompresses the body' do
30
+ should change { env[:body] }.to(fixture('leads'))
31
+ end
32
+
33
+ context 'when :compress is false' do
34
+ it { should_not(change { env[:request_headers]['Accept-Encoding'] }) }
35
+ end
36
+
37
+ context 'when :compress is true' do
38
+ before do
39
+ options[:compress] = true
40
+ end
41
+
42
+ it { should(change { env[:request_headers]['Accept-Encoding'] }.to('gzip')) }
43
+ end
44
+ end
45
+
46
+ describe '.decompress' do
47
+ let(:body) { gzip fixture('leads') }
48
+
49
+ subject { middleware.decompress(body) }
50
+ it { should eq fixture('leads') }
51
+ end
52
+
53
+ describe '.gzipped?' do
54
+ subject { middleware.gzipped?(env) }
55
+
56
+ context 'when gzipped' do
57
+ before do
58
+ env[:response_headers]['Content-Encoding'] = 'gzip'
59
+ end
60
+
61
+ it { should be_truthy }
62
+ end
63
+
64
+ context 'when not gzipped' do
65
+ it { should be_falsey }
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Frodo::Middleware::InstanceURL do
6
+ describe '.call' do
7
+ subject { lambda { middleware.call(nil) } }
8
+ let(:connection) { double("connection") }
9
+
10
+ context 'when the instance url is not set' do
11
+ before do
12
+ allow(client).to receive_message_chain :connection, url_prefix: URI.parse('http:/')
13
+ end
14
+
15
+ it { should raise_error Frodo::UnauthorizedError }
16
+ end
17
+
18
+ context 'when the instance url is set' do
19
+ before do
20
+ allow(client).to receive_message_chain :connection, url_prefix: URI.parse('http://foobar.com/')
21
+ expect(app).to receive(:call).once
22
+ end
23
+
24
+ it { should_not raise_error }
25
+ end
26
+ end
27
+ end