fortnox-api 0.4.0 → 0.5.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 (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