fortnox-api 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +31 -29
  3. data/bin/console +8 -0
  4. data/fortnox-api.gemspec +1 -3
  5. data/lib/fortnox/api.rb +49 -14
  6. data/lib/fortnox/api/circular_queue.rb +33 -0
  7. data/lib/fortnox/api/mappers.rb +1 -1
  8. data/lib/fortnox/api/mappers/base.rb +3 -0
  9. data/lib/fortnox/api/models/article.rb +25 -25
  10. data/lib/fortnox/api/models/customer.rb +47 -47
  11. data/lib/fortnox/api/models/document_base.rb +4 -4
  12. data/lib/fortnox/api/repositories/article.rb +3 -4
  13. data/lib/fortnox/api/repositories/base.rb +77 -5
  14. data/lib/fortnox/api/repositories/base/savers.rb +2 -2
  15. data/lib/fortnox/api/repositories/customer.rb +3 -4
  16. data/lib/fortnox/api/repositories/invoice.rb +3 -4
  17. data/lib/fortnox/api/repositories/order.rb +3 -4
  18. data/lib/fortnox/api/repositories/project.rb +3 -3
  19. data/lib/fortnox/api/types/document_row.rb +3 -3
  20. data/lib/fortnox/api/types/model.rb +2 -6
  21. data/lib/fortnox/api/version.rb +1 -1
  22. data/spec/fortnox/api/mappers/base_spec.rb +6 -0
  23. data/spec/fortnox/api/repositories/article_spec.rb +4 -0
  24. data/spec/fortnox/api/repositories/base_spec.rb +348 -0
  25. data/spec/fortnox/api/repositories/customer_spec.rb +4 -0
  26. data/spec/fortnox/api/repositories/invoice_spec.rb +4 -0
  27. data/spec/fortnox/api/repositories/order_spec.rb +4 -0
  28. data/spec/fortnox/api/repositories/project_spec.rb +4 -0
  29. data/spec/fortnox/api/types/examples/document_row.rb +2 -2
  30. data/spec/fortnox/api/types/house_work_types_spec.rb +4 -0
  31. data/spec/fortnox/api_spec.rb +161 -31
  32. data/spec/spec_helper.rb +7 -2
  33. data/spec/support/helpers/configuration_helper.rb +10 -0
  34. metadata +10 -27
  35. data/lib/fortnox/api/base.rb +0 -47
  36. data/lib/fortnox/api/class_methods.rb +0 -30
  37. data/lib/fortnox/api/environment_validation.rb +0 -78
  38. data/spec/fortnox/api/base_spec.rb +0 -167
  39. data/spec/support/helpers/environment_helper.rb +0 -7
@@ -44,13 +44,13 @@ module Fortnox
44
44
  base.attribute :contribution_value, Types::Nullable::Float.with( read_only: true )
45
45
 
46
46
  # Country Country for the document address.
47
- base.attribute :country, Fortnox::API::Types::CountryCode
47
+ base.attribute :country, Types::CountryCode
48
48
 
49
49
  # CostCenter Code of the cost center.
50
50
  base.attribute :cost_center, Types::Nullable::String
51
51
 
52
52
  # Currency Code of the currency.
53
- base.attribute :currency, Fortnox::API::Types::Currency
53
+ base.attribute :currency, Types::Currency
54
54
 
55
55
  # CurrencyRate Currency rate used for the document
56
56
  base.attribute :currency_rate, Types::Nullable::Float
@@ -74,7 +74,7 @@ module Fortnox
74
74
  base.attribute :delivery_city, Types::Sized::String[ 1024 ]
75
75
 
76
76
  # DeliveryCountry Country for the document delivery address.
77
- base.attribute :delivery_country, Fortnox::API::Types::CountryCode
77
+ base.attribute :delivery_country, Types::CountryCode
78
78
 
79
79
  # DeliveryDate Date of delivery.
80
80
  base.attribute :delivery_date, Types::Nullable::Date
@@ -98,7 +98,7 @@ module Fortnox
98
98
  base.attribute :external_invoice_reference2, Types::Sized::String[ 80 ]
99
99
 
100
100
  # Freight Freight cost of the document. 12 digits (incl. decimals)
101
- base.attribute :freight, Types::Sized::Float[ 0.0, 99_999_999_999.0 ]
101
+ base.attribute :freight, Types::Sized::Float[ 0.0, 99_999_999_999.9 ]
102
102
 
103
103
  # FreightVAT VAT of the freight cost.
104
104
  base.attribute :freight_vat, Types::Nullable::Float.with( read_only: true )
@@ -5,10 +5,9 @@ require "fortnox/api/mappers/article"
5
5
  module Fortnox
6
6
  module API
7
7
  module Repository
8
- class Article < Fortnox::API::Repository::Base
9
-
10
- MODEL = Fortnox::API::Model::Article
11
- MAPPER = Fortnox::API::Mapper::Article
8
+ class Article < Base
9
+ MODEL = Model::Article
10
+ MAPPER = Mapper::Article
12
11
  URI = '/articles/'.freeze
13
12
  end
14
13
  end
@@ -1,24 +1,76 @@
1
- require "fortnox/api/base"
1
+ require 'httparty'
2
+ require 'fortnox/api/request_handling'
2
3
  require "fortnox/api/repositories/base/loaders"
3
4
  require "fortnox/api/repositories/base/savers"
4
5
 
5
6
  module Fortnox
6
7
  module API
7
8
  module Repository
8
- class Base < Fortnox::API::Base
9
-
9
+ class Base
10
+ include HTTParty
11
+ include Fortnox::API::RequestHandling
10
12
  include Loaders
11
13
  include Savers
12
14
 
15
+ HTTParty::Parser::SupportedFormats[ "text/html" ] = :json
16
+
17
+ DEFAULT_HEADERS = {
18
+ 'Content-Type' => 'application/json',
19
+ 'Accept' => 'application/json',
20
+ }.freeze
21
+
22
+ HTTP_METHODS = [ :get, :put, :post, :delete ].freeze
23
+
24
+ attr_accessor :headers
13
25
  attr_reader :mapper, :keys_filtered_on_save
14
26
 
15
- def initialize( keys_filtered_on_save: [ :url ] )
16
- super()
27
+ def self.set_headers( headers = {} )
28
+ self.headers.merge!( headers )
29
+ end
30
+
31
+ HTTP_METHODS.each do |method|
32
+ define_method method do |*args|
33
+ self.headers['Access-Token'] = next_access_token
34
+ execute do |remote|
35
+ remote.send( method, *args )
36
+ end
37
+ end
38
+ end
39
+
40
+ def initialize( keys_filtered_on_save: [ :url ], token_store: :default )
41
+ self.class.base_uri( get_base_url )
42
+
43
+ self.headers = DEFAULT_HEADERS.merge({
44
+ 'Client-Secret' => get_client_secret,
45
+ })
17
46
 
18
47
  @keys_filtered_on_save = keys_filtered_on_save
48
+ @token_store = token_store
19
49
  @mapper = Registry[ Mapper::Base.canonical_name_sym( self.class::MODEL )].new
20
50
  end
21
51
 
52
+ def next_access_token
53
+ @access_tokens ||= CircularQueue.new( *get_access_tokens )
54
+ @access_tokens.next
55
+ end
56
+
57
+ def check_access_tokens!( tokens )
58
+ if tokens == nil or tokens.empty?
59
+ fail MissingConfiguration, "You have not provided any access tokens in token store #{ @token_store.inspect }."
60
+ end
61
+ end
62
+
63
+ def get_access_tokens
64
+ begin
65
+ tokens = config.token_store.fetch( @token_store )
66
+ rescue KeyError
67
+ token_store_not_found!( @token_store.inspect )
68
+ end
69
+
70
+ check_access_tokens!( tokens )
71
+ tokens
72
+ end
73
+
22
74
  private
23
75
 
24
76
  def instantiate( hash )
@@ -27,6 +79,26 @@ module Fortnox
27
79
  self.class::MODEL.new( hash )
28
80
  end
29
81
 
82
+ def get_base_url
83
+ base_url = config.base_url
84
+ fail MissingConfiguration, 'You have to provide a base url.' unless base_url
85
+ base_url
86
+ end
87
+
88
+ def get_client_secret
89
+ client_secret = config.client_secret
90
+ fail MissingConfiguration, 'You have to provide your client secret.' unless client_secret
91
+ client_secret
92
+ end
93
+
94
+ def config
95
+ Fortnox::API.config
96
+ end
97
+
98
+ def token_store_not_found!( store_name )
99
+ fail MissingConfiguration,
100
+ "There is no token store named #{ store_name }."
101
+ end
30
102
  end
31
103
  end
32
104
  end
@@ -19,13 +19,13 @@ module Fortnox
19
19
 
20
20
  def save_new( entity )
21
21
  execute_save( entity ) do |body|
22
- post( self.class::URI, { body: body })
22
+ post( self.class::URI, { body: body } )
23
23
  end
24
24
  end
25
25
 
26
26
  def update_existing( entity )
27
27
  execute_save( entity ) do |body|
28
- put( get_update_url_for( entity ), { body: body })
28
+ put( get_update_url_for( entity ), { body: body } )
29
29
  end
30
30
  end
31
31
 
@@ -5,10 +5,9 @@ require "fortnox/api/mappers/customer"
5
5
  module Fortnox
6
6
  module API
7
7
  module Repository
8
- class Customer < Fortnox::API::Repository::Base
9
-
10
- MODEL = Fortnox::API::Model::Customer
11
- MAPPER = Fortnox::API::Mapper::Customer
8
+ class Customer < Base
9
+ MODEL = Model::Customer
10
+ MAPPER = Mapper::Customer
12
11
  URI = '/customers/'.freeze
13
12
  end
14
13
  end
@@ -5,10 +5,9 @@ require "fortnox/api/mappers/invoice"
5
5
  module Fortnox
6
6
  module API
7
7
  module Repository
8
- class Invoice < Fortnox::API::Repository::Base
9
-
10
- MODEL = Fortnox::API::Model::Invoice
11
- MAPPER = Fortnox::API::Mapper::Invoice
8
+ class Invoice < Base
9
+ MODEL = Model::Invoice
10
+ MAPPER = Mapper::Invoice
12
11
  URI = '/invoices/'.freeze
13
12
  end
14
13
  end
@@ -5,10 +5,9 @@ require "fortnox/api/mappers/order"
5
5
  module Fortnox
6
6
  module API
7
7
  module Repository
8
- class Order < Fortnox::API::Repository::Base
9
-
10
- MODEL = Fortnox::API::Model::Order
11
- MAPPER = Fortnox::API::Mapper::Order
8
+ class Order < Base
9
+ MODEL = Model::Order
10
+ MAPPER = Mapper::Order
12
11
  URI = '/orders/'.freeze
13
12
  end
14
13
  end
@@ -5,9 +5,9 @@ require "fortnox/api/mappers/project"
5
5
  module Fortnox
6
6
  module API
7
7
  module Repository
8
- class Project < Fortnox::API::Repository::Base
9
- MODEL = Fortnox::API::Model::Project
10
- MAPPER = Fortnox::API::Mapper::Project
8
+ class Project < Base
9
+ MODEL = Model::Project
10
+ MAPPER = Mapper::Project
11
11
  URI = '/projects/'.freeze
12
12
  end
13
13
  end
@@ -20,7 +20,7 @@ module Fortnox
20
20
  attribute :cost_center, Types::Nullable::String
21
21
 
22
22
  #DeliveredQuantity Delivered quantity. 14 digits
23
- attribute :delivered_quantity, Types::Sized::Float[ 0.0, 9_999_999_999_999.0 ]
23
+ attribute :delivered_quantity, Types::Sized::Float[ 0.0, 9_999_999_999_999.9 ]
24
24
 
25
25
  #Description Description Row description. 50 characters
26
26
  attribute :description, Types::Sized::String[ 50 ]
@@ -29,7 +29,7 @@ module Fortnox
29
29
  # TODO(hannes): Verify that we can send in more than 5 digits through
30
30
  # the actual API for DiscountType PERCENT. This cannot be done until
31
31
  # we fix issue #62...
32
- attribute :discount, Types::Sized::Float[ 0.0, 99_999_999_999.0 ]
32
+ attribute :discount, Types::Sized::Float[ 0.0, 99_999_999_999.9 ]
33
33
 
34
34
  #DiscountType The type of discount used for the row.
35
35
  attribute :discount_type, Types::DiscountType
@@ -44,7 +44,7 @@ module Fortnox
44
44
  attribute :house_work_type, Types::HouseWorkType
45
45
 
46
46
  #Price Price per unit. 12 digits
47
- attribute :price, Types::Sized::Float[ 0.0, 99_999_999_999.0 ]
47
+ attribute :price, Types::Sized::Float[ 0.0, 99_999_999_999.9 ]
48
48
 
49
49
  #Project Code of the project for the row.
50
50
  attribute :project, Types::Nullable::String
@@ -24,14 +24,10 @@ module Fortnox
24
24
  end
25
25
 
26
26
  def first_missing_required_key( attributes )
27
- all_missing_keys = missing_keys( attributes )
28
- missing_required = all_missing_keys.select do |name|
27
+ missing_keys( attributes ).find do |name|
29
28
  attribute = self.class.schema[ name ]
30
- next unless attribute.respond_to? :options
31
- attribute.options[:required]
29
+ attribute.respond_to?(:options) && attribute.options[:required]
32
30
  end
33
-
34
- missing_required.first
35
31
  end
36
32
  end
37
33
 
@@ -1,5 +1,5 @@
1
1
  module Fortnox
2
2
  module API
3
- VERSION = "0.4.0".freeze
3
+ VERSION = "0.5.0".freeze
4
4
  end
5
5
  end
@@ -49,6 +49,12 @@ describe Fortnox::API::Mapper::Base do
49
49
  end
50
50
  end
51
51
 
52
+ describe 'array with very large int (Bigint if Ruby <2.4)' do
53
+ include_examples 'identity mapper', :array do
54
+ let( :value ){ [(100**10)] }
55
+ end
56
+ end
57
+
52
58
  describe 'advanced array' do
53
59
  include_examples 'simple mapper', :array, [ "2016-01-01", "2016-01-02" ] do
54
60
  let( :value ){ [ Date.new(2016, 1, 1), Date.new(2016, 1, 2) ] }
@@ -9,6 +9,10 @@ require 'fortnox/api/repositories/examples/save_with_specially_named_attribute'
9
9
  require 'fortnox/api/repositories/examples/search'
10
10
 
11
11
  describe Fortnox::API::Repository::Article, order: :defined, integration: true do
12
+ include Helpers::Configuration
13
+
14
+ before{ set_api_test_configuration }
15
+
12
16
  subject(:repository){ described_class.new }
13
17
 
14
18
  include_examples '.save',
@@ -0,0 +1,348 @@
1
+ require 'spec_helper'
2
+ require 'fortnox/api'
3
+
4
+ describe Fortnox::API::Repository::Base do
5
+ using_test_class do
6
+ module Model
7
+ class Test
8
+ end
9
+ end
10
+ module Repository
11
+ class Test < Fortnox::API::Repository::Base
12
+ MODEL = Model::Test
13
+ end
14
+ end
15
+
16
+ require 'dry/container/stub'
17
+ Fortnox::API::Registry.enable_stubs!
18
+ Fortnox::API::Registry.stub( :test, Model::Test )
19
+ end
20
+
21
+ let(:access_token){ '3f08d038-f380-4893-94a0-a08f6e60e67a' }
22
+ let(:access_token2){ '89feajou-sif8-8f8u-29ja-xdfniokeniod' }
23
+ let(:client_secret){ 'P5K5vE3Kun' }
24
+ let(:repository){ Repository::Test.new }
25
+ let(:application_json){}
26
+ let(:headers) do
27
+ {
28
+ 'Access-Token' => access_token,
29
+ 'Client-Secret' => client_secret,
30
+ 'Content-Type' => 'application/json',
31
+ 'Accept' => 'application/json',
32
+ }
33
+ end
34
+
35
+ describe 'creation' do
36
+ shared_examples_for 'missing configuration' do
37
+ subject{ ->{ repository } }
38
+
39
+ let(:error){ Fortnox::API::MissingConfiguration }
40
+
41
+ it{ is_expected.to raise_error( error, /#{message}/ ) }
42
+ end
43
+
44
+ context 'without base url' do
45
+ include_examples 'missing configuration' do
46
+ before{ Fortnox::API.configure{ |conf| conf.base_url = nil } }
47
+ let(:message){ 'have to provide a base url' }
48
+ end
49
+ end
50
+
51
+ context 'without client secret' do
52
+ include_examples 'missing configuration' do
53
+ before{ Fortnox::API.configure{ |conf| conf.client_secret = nil } }
54
+ let(:message){ 'have to provide your client secret' }
55
+ end
56
+ end
57
+ end
58
+
59
+ describe '#next_access_token' do
60
+ before{ Fortnox::API.configure{ |conf| conf.client_secret = client_secret } }
61
+
62
+ context 'with default token store' do
63
+ context 'with one access token' do
64
+ subject{ repository.next_access_token }
65
+
66
+ before{ Fortnox::API.configure{ |conf| conf.access_token = access_token } }
67
+ it{ is_expected.to eql( access_token ) }
68
+
69
+ describe 'next request' do
70
+ before{ repository.next_access_token }
71
+
72
+ it 'still uses the same token' do
73
+ is_expected.to eql( access_token )
74
+ end
75
+ end
76
+ end
77
+
78
+ context 'with multiple access tokens' do
79
+ subject{ access_tokens }
80
+
81
+ before{ Fortnox::API.configure{ |conf| conf.access_tokens = access_tokens } }
82
+ let( :access_tokens ){ [access_token, access_token2] }
83
+
84
+ it{ is_expected.to include( repository.next_access_token ) }
85
+
86
+ it 'changes token on next request' do
87
+ token1 = repository.next_access_token
88
+ token2 = repository.next_access_token
89
+
90
+ expect( token1 ).not_to eql( token2 )
91
+ end
92
+
93
+ it 'circulates tokens' do
94
+ token1 = repository.next_access_token
95
+ repository.next_access_token
96
+ token3 = repository.next_access_token
97
+
98
+ expect( token1 ).to eql( token3 )
99
+ end
100
+ end
101
+ end
102
+
103
+ context 'with multiple stores' do
104
+ subject{ repository.next_access_token }
105
+
106
+ before do
107
+ Fortnox::API.configure do |config|
108
+ config.access_tokens = { store1: access_token, store2: access_token2 }
109
+ end
110
+ end
111
+
112
+ describe 'first token store' do
113
+ let( :repository ){ Repository::Test.new( token_store: :store1) }
114
+
115
+ it{ is_expected.to eql access_token }
116
+ end
117
+
118
+ describe 'second token store' do
119
+ let( :repository ){ Repository::Test.new( token_store: :store2 ) }
120
+
121
+ it{ is_expected.to eql access_token2 }
122
+ end
123
+ end
124
+ end
125
+
126
+ describe '#get_access_tokens' do
127
+ subject( :get_access_tokens ){ repository.get_access_tokens }
128
+
129
+ before{ Fortnox::API.configure{ |conf| conf.client_secret = client_secret } }
130
+
131
+ let( :token_store_not_present ){ "no token store named #{ token_store.inspect }." }
132
+ let( :error ){ Fortnox::API::MissingConfiguration }
133
+
134
+ context 'with non existing token store' do
135
+ subject{ ->{ get_access_tokens } }
136
+
137
+ before do
138
+ Fortnox::API.configure{ |conf| conf.access_tokens = { some_store: [access_token] } }
139
+ end
140
+
141
+ let( :repository ){ Repository::Test.new( token_store: token_store ) }
142
+ let( :token_store ){ :non_existing_store }
143
+
144
+ it{ is_expected.to raise_error( error, /#{token_store_not_present}/ ) }
145
+ end
146
+
147
+ context 'with no tokens set' do
148
+ subject{ ->{ get_access_tokens } }
149
+
150
+ before{ Fortnox::API.configure{ |conf| conf.access_tokens = {} } }
151
+ let( :token_store ){ :default }
152
+
153
+ it{ is_expected.to raise_error( error, /#{token_store_not_present}/) }
154
+ end
155
+
156
+ context 'with one access token in token store' do
157
+ before{ Fortnox::API.configure{ |conf| conf.access_token = access_token } }
158
+ let( :token_store ){ :default }
159
+
160
+ it{ is_expected.to eql( access_token ) }
161
+ end
162
+
163
+ context 'with multiple access tokens' do
164
+ before do
165
+ Fortnox::API.configure{ |conf| conf.access_tokens = [access_token, access_token2] }
166
+ end
167
+ let( :token_store ){ :default }
168
+
169
+ it{ is_expected.to eql( [access_token, access_token2] ) }
170
+ end
171
+
172
+ context 'with multiple token stores' do
173
+ before do
174
+ Fortnox::API.configure do |conf|
175
+ conf.access_tokens = { store_a: store_a_tokens, store_b: store_b_token }
176
+ end
177
+ end
178
+ let( :store_a_tokens ){ ['token_a1', 'token_a2'] }
179
+ let( :store_b_token ){ 'token_b1' }
180
+
181
+ context 'with valid store name' do
182
+ let( :repository ){ Repository::Test.new( token_store: :store_a ) }
183
+
184
+ it{ is_expected.to eql( store_a_tokens ) }
185
+ end
186
+
187
+ context 'with non collection' do
188
+ let( :repository ){ Repository::Test.new( token_store: :store_b ) }
189
+
190
+ it{ is_expected.to eql( store_b_token ) }
191
+ end
192
+
193
+ context 'with invalid store name' do
194
+ subject{ ->{ get_access_tokens } }
195
+
196
+ let( :repository ){ Repository::Test.new( token_store: :nonsence_store ) }
197
+
198
+ it{ is_expected.to raise_error( error ) }
199
+ end
200
+ end
201
+ end
202
+
203
+ describe '#check_access_tokens!' do
204
+ subject{ ->{ repository.check_access_tokens!(tokens) } }
205
+
206
+ before{ Fortnox::API.configure{ |conf| conf.client_secret = client_secret } }
207
+ let( :error ){ Fortnox::API::MissingConfiguration }
208
+ let( :message ){ "not provided any access tokens in token store #{ token_store.inspect }" }
209
+ let( :token_store ){ :default }
210
+
211
+ context 'with nil' do
212
+ let( :tokens ){ nil }
213
+
214
+ it{ is_expected.to raise_error( error, /#{message}/ ) }
215
+ end
216
+
217
+ context 'with empty array' do
218
+ let( :tokens ){ [] }
219
+
220
+ it{ is_expected.to raise_error( error, /#{message}/ ) }
221
+ end
222
+
223
+ context 'with an empty, non default, token store' do
224
+ before{ Fortnox::API.configure{ |conf| conf.access_tokens = { token_store => tokens } } }
225
+ let( :repository ){ Repository::Test.new( token_store: token_store ) }
226
+ let( :tokens ){ [] }
227
+ let( :token_store ){ :store1 }
228
+
229
+ it{ is_expected.to raise_error( error, /#{message}/ ) }
230
+ end
231
+
232
+ context 'with valid tokens' do
233
+ let( :tokens ){ ['12345', 'abcde'] }
234
+
235
+ it{ is_expected.not_to raise_error }
236
+ end
237
+ end
238
+
239
+ context 'when making a request including the proper headers' do
240
+ before do
241
+ Fortnox::API.configure do |conf|
242
+ conf.client_secret = client_secret
243
+ conf.access_token = access_token
244
+ end
245
+
246
+ stub_request(
247
+ :get,
248
+ 'https://api.fortnox.se/3/test',
249
+ ).with(
250
+ headers: headers
251
+ ).to_return(
252
+ status: 200
253
+ )
254
+ end
255
+
256
+ subject{ repository.get( '/test', { body: '' } ) }
257
+
258
+ it{ is_expected.to be_nil }
259
+ end
260
+
261
+ describe 'making requests with multiple access tokens' do
262
+ before do
263
+ Fortnox::API.configure do |conf|
264
+ conf.client_secret = client_secret
265
+ conf.access_tokens = [access_token, access_token2]
266
+ end
267
+
268
+ stub_request(
269
+ :get,
270
+ 'https://api.fortnox.se/3/test',
271
+ ).with(
272
+ headers: {
273
+ 'Access-Token' => access_token,
274
+ 'Client-Secret' => client_secret,
275
+ 'Content-Type' => 'application/json',
276
+ 'Accept' => 'application/json',
277
+ }
278
+ ).to_return(
279
+ status: 200,
280
+ body: '1'
281
+ )
282
+
283
+ stub_request(
284
+ :get,
285
+ 'https://api.fortnox.se/3/test',
286
+ ).with(
287
+ headers: {
288
+ 'Access-Token' => access_token2,
289
+ 'Client-Secret' => client_secret,
290
+ 'Content-Type' => 'application/json',
291
+ 'Accept' => 'application/json',
292
+ }
293
+ ).to_return(
294
+ status: 200,
295
+ body: '2'
296
+ )
297
+ end
298
+
299
+ context 'with subsequent requests on same object' do
300
+ let!(:response1){ repository.get( '/test', { body: '' } ) }
301
+ let!(:response2){ repository.get( '/test', { body: '' } ) }
302
+ let!(:response3){ repository.get( '/test', { body: '' } ) }
303
+
304
+ # rubocop:disable RSpec/MultipleExpectations
305
+ # All these checks must be run in same it-statement because
306
+ # of the random starting index.
307
+ it 'works' do
308
+ expect(response1).not_to eq( response2 )
309
+ expect(response1).to eq( response3 )
310
+ expect(response3).not_to eq( response2 )
311
+ end
312
+ # rubocop:enable RSpec/MultipleExpectations
313
+ end
314
+ end
315
+
316
+ context 'when raising error from remote server' do
317
+ before do
318
+ Fortnox::API.configure do |conf|
319
+ conf.client_secret = client_secret
320
+ conf.access_tokens = [access_token, access_token2]
321
+ end
322
+
323
+ stub_request(
324
+ :post,
325
+ 'https://api.fortnox.se/3/test',
326
+ ).to_return(
327
+ status: 500,
328
+ body: { 'ErrorInformation' => { 'error' => 1, 'message' => 'Räkenskapsår finns inte upplagt. För att kunna skapa en faktura krävs det att det finns ett räkenskapsår' } }.to_json,
329
+ headers: { 'Content-Type' => 'application/json' },
330
+ )
331
+ end
332
+
333
+ subject{ ->{ repository.post( '/test', { body: '' } ) } }
334
+
335
+ it{ is_expected.to raise_error( Fortnox::API::RemoteServerError ) }
336
+ it{ is_expected.to raise_error( 'Räkenskapsår finns inte upplagt. För att kunna skapa en faktura krävs det att det finns ett räkenskapsår' ) }
337
+
338
+ context 'with debugging enabled' do
339
+ around do |example|
340
+ Fortnox::API.config.debugging = true
341
+ example.run
342
+ Fortnox::API.config.debugging = false
343
+ end
344
+
345
+ it{ is_expected.to raise_error( /\<HTTParty\:\:Request\:0x/ ) }
346
+ end
347
+ end
348
+ end