fortnox-api 0.1.0 → 0.2.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 (212) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +3 -2
  3. data/.env.test +3 -0
  4. data/.rubocop.yml +32 -23
  5. data/.travis.yml +3 -2
  6. data/Guardfile +2 -2
  7. data/LICENSE.txt +165 -22
  8. data/README.md +56 -37
  9. data/fortnox-api.gemspec +9 -6
  10. data/lib/fortnox/api.rb +18 -2
  11. data/lib/fortnox/api/base.rb +5 -3
  12. data/lib/fortnox/api/environment_validation.rb +51 -6
  13. data/lib/fortnox/api/mappers.rb +49 -0
  14. data/lib/fortnox/api/mappers/base.rb +47 -0
  15. data/lib/fortnox/api/mappers/base/from_json.rb +78 -0
  16. data/lib/fortnox/api/mappers/base/to_json.rb +65 -0
  17. data/lib/fortnox/api/mappers/customer.rb +27 -0
  18. data/lib/fortnox/api/mappers/default_delivery_types.rb +13 -0
  19. data/lib/fortnox/api/mappers/default_templates.rb +15 -0
  20. data/lib/fortnox/api/mappers/edi_information.rb +22 -0
  21. data/lib/fortnox/api/mappers/email_information.rb +18 -0
  22. data/lib/fortnox/api/mappers/invoice.rb +27 -0
  23. data/lib/fortnox/api/mappers/invoice_row.rb +20 -0
  24. data/lib/fortnox/api/mappers/order.rb +23 -0
  25. data/lib/fortnox/api/mappers/order_row.rb +16 -0
  26. data/lib/fortnox/api/models.rb +2 -0
  27. data/lib/fortnox/api/models/base.rb +56 -13
  28. data/lib/fortnox/api/models/customer.rb +112 -101
  29. data/lib/fortnox/api/models/document_base.rb +189 -0
  30. data/lib/fortnox/api/models/invoice.rb +29 -195
  31. data/lib/fortnox/api/models/label.rb +17 -0
  32. data/lib/fortnox/api/models/order.rb +27 -0
  33. data/lib/fortnox/api/repositories.rb +2 -0
  34. data/lib/fortnox/api/repositories/base.rb +4 -5
  35. data/lib/fortnox/api/repositories/base/loaders.rb +22 -14
  36. data/lib/fortnox/api/repositories/base/savers.rb +30 -16
  37. data/lib/fortnox/api/repositories/customer.rb +3 -25
  38. data/lib/fortnox/api/repositories/invoice.rb +3 -22
  39. data/lib/fortnox/api/repositories/order.rb +16 -0
  40. data/lib/fortnox/api/request_handling.rb +3 -3
  41. data/lib/fortnox/api/types.rb +44 -0
  42. data/lib/fortnox/api/types/default_delivery_types.rb +20 -0
  43. data/lib/fortnox/api/types/default_templates.rb +23 -0
  44. data/lib/fortnox/api/types/defaulted.rb +11 -0
  45. data/lib/fortnox/api/types/document_row.rb +65 -0
  46. data/lib/fortnox/api/types/edi_information.rb +29 -0
  47. data/lib/fortnox/api/types/email_information.rb +26 -0
  48. data/lib/fortnox/api/types/enums.rb +75 -0
  49. data/lib/fortnox/api/types/invoice_row.rb +19 -0
  50. data/lib/fortnox/api/types/model.rb +40 -0
  51. data/lib/fortnox/api/types/nullable.rb +21 -0
  52. data/lib/fortnox/api/types/order_row.rb +16 -0
  53. data/lib/fortnox/api/types/required.rb +13 -0
  54. data/lib/fortnox/api/types/sized.rb +25 -0
  55. data/lib/fortnox/api/version.rb +1 -1
  56. data/spec/fortnox/api/base_spec.rb +85 -14
  57. data/spec/fortnox/api/mappers/base/from_json_spec.rb +70 -0
  58. data/spec/fortnox/api/mappers/base/to_json_spec.rb +76 -0
  59. data/spec/fortnox/api/mappers/base_spec.rb +156 -0
  60. data/spec/fortnox/api/mappers/contexts/json_conversion.rb +56 -0
  61. data/spec/fortnox/api/mappers/customer_spec.rb +25 -0
  62. data/spec/fortnox/api/mappers/default_delivery_types_spec.rb +12 -0
  63. data/spec/fortnox/api/mappers/edi_information_spec.rb +21 -0
  64. data/spec/fortnox/api/mappers/email_information_spec.rb +17 -0
  65. data/spec/fortnox/api/mappers/examples/mapper.rb +26 -0
  66. data/spec/fortnox/api/mappers/invoice_row_spec.rb +19 -0
  67. data/spec/fortnox/api/mappers/invoice_spec.rb +24 -0
  68. data/spec/fortnox/api/mappers/order_row_spec.rb +14 -0
  69. data/spec/fortnox/api/mappers/order_spec.rb +20 -0
  70. data/spec/fortnox/api/models/base_spec.rb +44 -22
  71. data/spec/fortnox/api/models/customer_spec.rb +9 -0
  72. data/spec/fortnox/api/models/examples/document_base.rb +13 -0
  73. data/spec/fortnox/api/models/examples/model.rb +13 -0
  74. data/spec/fortnox/api/models/invoice_spec.rb +7 -31
  75. data/spec/fortnox/api/models/order_spec.rb +13 -0
  76. data/spec/fortnox/api/repositories/customer_spec.rb +20 -76
  77. data/spec/fortnox/api/repositories/examples/all.rb +17 -0
  78. data/spec/fortnox/api/repositories/examples/find.rb +25 -0
  79. data/spec/fortnox/api/repositories/examples/only.rb +42 -0
  80. data/spec/fortnox/api/repositories/examples/save.rb +69 -0
  81. data/spec/fortnox/api/repositories/examples/save_with_nested_model.rb +32 -0
  82. data/spec/fortnox/api/repositories/examples/save_with_specially_named_attribute.rb +27 -0
  83. data/spec/fortnox/api/repositories/examples/search.rb +31 -0
  84. data/spec/fortnox/api/repositories/invoice_spec.rb +36 -5
  85. data/spec/fortnox/api/repositories/order_spec.rb +35 -0
  86. data/spec/fortnox/api/types/account_number_spec.rb +28 -0
  87. data/spec/fortnox/api/types/default_delivery_types_spec.rb +10 -0
  88. data/spec/fortnox/api/types/edi_information_spec.rb +13 -0
  89. data/spec/fortnox/api/types/email_information_spec.rb +13 -0
  90. data/spec/fortnox/api/types/email_spec.rb +29 -0
  91. data/spec/fortnox/api/types/enums_spec.rb +13 -0
  92. data/spec/fortnox/api/types/examples/document_row.rb +15 -0
  93. data/spec/fortnox/api/types/examples/enum.rb +48 -0
  94. data/spec/fortnox/api/types/examples/types.rb +9 -0
  95. data/spec/fortnox/api/types/house_work_types_spec.rb +60 -0
  96. data/spec/fortnox/api/types/invoice_row_spec.rb +9 -0
  97. data/spec/fortnox/api/types/model_spec.rb +56 -0
  98. data/spec/fortnox/api/types/nullable_spec.rb +57 -0
  99. data/spec/fortnox/api/types/order_row_spec.rb +13 -0
  100. data/spec/fortnox/api/types/required_spec.rb +42 -0
  101. data/spec/fortnox/api/types/sized_spec.rb +74 -0
  102. data/spec/fortnox/api_spec.rb +16 -15
  103. data/spec/spec_helper.rb +19 -9
  104. data/spec/support/helpers/dummy_class_helper.rb +19 -0
  105. data/spec/support/helpers/environment_helper.rb +7 -0
  106. data/spec/support/helpers/repository_helper.rb +8 -0
  107. data/spec/support/helpers/when_performing_helper.rb +5 -0
  108. data/spec/support/matchers.rb +1 -1
  109. data/spec/support/matchers/type.rb +17 -0
  110. data/spec/support/matchers/type/attribute_matcher.rb +39 -0
  111. data/spec/support/matchers/type/enum_matcher.rb +21 -0
  112. data/spec/support/matchers/type/have_account_number_matcher.rb +21 -0
  113. data/spec/support/matchers/type/have_country_code_matcher.rb +13 -0
  114. data/spec/support/matchers/type/have_currency_matcher.rb +7 -0
  115. data/spec/support/matchers/type/have_customer_type_matcher.rb +13 -0
  116. data/spec/support/matchers/type/have_default_delivery_type_matcher.rb +7 -0
  117. data/spec/support/matchers/type/have_discount_type_matcher.rb +7 -0
  118. data/spec/support/matchers/type/have_email_matcher.rb +22 -0
  119. data/spec/support/matchers/type/have_house_work_type_matcher.rb +7 -0
  120. data/spec/support/matchers/type/have_nullable_date_matcher.rb +58 -0
  121. data/spec/support/matchers/type/have_nullable_matcher.rb +52 -0
  122. data/spec/support/matchers/type/have_nullable_string_matcher.rb +49 -0
  123. data/spec/support/matchers/type/have_sized_float_matcher.rb +8 -0
  124. data/spec/support/matchers/type/have_sized_integer_matcher.rb +8 -0
  125. data/spec/support/matchers/type/have_sized_string_matcher.rb +35 -0
  126. data/spec/support/matchers/type/have_vat_type_matcher.rb +7 -0
  127. data/spec/support/matchers/type/numeric_matcher.rb +50 -0
  128. data/spec/support/matchers/type/require_attribute_matcher.rb +69 -0
  129. data/spec/support/matchers/type/type_matcher.rb +38 -0
  130. data/spec/vcr_cassettes/customers/all.yml +119 -9
  131. data/spec/vcr_cassettes/customers/find_id_1.yml +8 -9
  132. data/spec/vcr_cassettes/customers/find_new.yml +46 -0
  133. data/spec/vcr_cassettes/customers/save_new.yml +9 -11
  134. data/spec/vcr_cassettes/customers/save_old.yml +9 -12
  135. data/spec/vcr_cassettes/customers/save_with_specially_named_attribute.yml +45 -0
  136. data/spec/vcr_cassettes/customers/search_by_name.yml +66 -0
  137. data/spec/vcr_cassettes/customers/search_miss.yml +45 -0
  138. data/spec/vcr_cassettes/invoices/all.yml +104 -0
  139. data/spec/vcr_cassettes/invoices/filter_hit.yml +46 -0
  140. data/spec/vcr_cassettes/invoices/filter_invalid.yml +42 -0
  141. data/spec/vcr_cassettes/invoices/find_id_1.yml +47 -0
  142. data/spec/vcr_cassettes/invoices/find_new.yml +49 -0
  143. data/spec/vcr_cassettes/invoices/save_new.yml +48 -0
  144. data/spec/vcr_cassettes/invoices/save_old.yml +49 -0
  145. data/spec/vcr_cassettes/invoices/save_with_nested_model.yml +47 -0
  146. data/spec/vcr_cassettes/invoices/save_with_specially_named_attribute.yml +47 -0
  147. data/spec/vcr_cassettes/invoices/search_by_name.yml +48 -0
  148. data/spec/vcr_cassettes/invoices/search_miss.yml +45 -0
  149. data/spec/vcr_cassettes/orders/all.yml +144 -0
  150. data/spec/vcr_cassettes/orders/filter_hit.yml +48 -0
  151. data/spec/vcr_cassettes/orders/filter_invalid.yml +42 -0
  152. data/spec/vcr_cassettes/orders/find_id_1.yml +48 -0
  153. data/spec/vcr_cassettes/orders/find_new.yml +49 -0
  154. data/spec/vcr_cassettes/orders/house_work_type_babysitting.yml +47 -0
  155. data/spec/vcr_cassettes/orders/house_work_type_cleaning.yml +47 -0
  156. data/spec/vcr_cassettes/orders/house_work_type_construction.yml +47 -0
  157. data/spec/vcr_cassettes/orders/house_work_type_cooking.yml +43 -0
  158. data/spec/vcr_cassettes/orders/house_work_type_electricity.yml +47 -0
  159. data/spec/vcr_cassettes/orders/house_work_type_gardening.yml +47 -0
  160. data/spec/vcr_cassettes/orders/house_work_type_glassmetalwork.yml +47 -0
  161. data/spec/vcr_cassettes/orders/house_work_type_grounddrainagework.yml +47 -0
  162. data/spec/vcr_cassettes/orders/house_work_type_hvac.yml +47 -0
  163. data/spec/vcr_cassettes/orders/house_work_type_masonry.yml +47 -0
  164. data/spec/vcr_cassettes/orders/house_work_type_othercare.yml +47 -0
  165. data/spec/vcr_cassettes/orders/house_work_type_othercosts.yml +47 -0
  166. data/spec/vcr_cassettes/orders/house_work_type_paintingwallpapering.yml +47 -0
  167. data/spec/vcr_cassettes/orders/house_work_type_snowplowing.yml +47 -0
  168. data/spec/vcr_cassettes/orders/house_work_type_textileclothing.yml +47 -0
  169. data/spec/vcr_cassettes/orders/house_work_type_tutoring.yml +43 -0
  170. data/spec/vcr_cassettes/orders/save_new.yml +48 -0
  171. data/spec/vcr_cassettes/orders/save_old.yml +49 -0
  172. data/spec/vcr_cassettes/orders/save_with_nested_model.yml +47 -0
  173. data/spec/vcr_cassettes/orders/search_by_name.yml +47 -0
  174. data/spec/vcr_cassettes/orders/search_miss.yml +45 -0
  175. metadata +301 -71
  176. data/lib/fortnox/api/models/attributes/country_code.rb +0 -17
  177. data/lib/fortnox/api/models/attributes/currency.rb +0 -17
  178. data/lib/fortnox/api/models/edi_information.rb +0 -28
  179. data/lib/fortnox/api/models/email_information.rb +0 -25
  180. data/lib/fortnox/api/models/row.rb +0 -82
  181. data/lib/fortnox/api/repositories/base/json_convertion.rb +0 -68
  182. data/lib/fortnox/api/repositories/base/options.rb +0 -33
  183. data/lib/fortnox/api/validators.rb +0 -1
  184. data/lib/fortnox/api/validators/attributes/country_code.rb +0 -42
  185. data/lib/fortnox/api/validators/attributes/currency.rb +0 -38
  186. data/lib/fortnox/api/validators/base.rb +0 -70
  187. data/lib/fortnox/api/validators/constant.rb +0 -21
  188. data/lib/fortnox/api/validators/customer.rb +0 -29
  189. data/lib/fortnox/api/validators/edi_information.rb +0 -11
  190. data/lib/fortnox/api/validators/email_information.rb +0 -19
  191. data/lib/fortnox/api/validators/invoice.rb +0 -33
  192. data/lib/fortnox/api/validators/row.rb +0 -22
  193. data/spec/fortnox/api/models/attributes/country_code_spec.rb +0 -23
  194. data/spec/fortnox/api/models/attributes/currency_spec.rb +0 -23
  195. data/spec/fortnox/api/models/attributes/dummy_model_context.rb +0 -9
  196. data/spec/fortnox/api/models/row_spec.rb +0 -13
  197. data/spec/fortnox/api/repositories/context.rb +0 -10
  198. data/spec/fortnox/api/repositories/examples.rb +0 -16
  199. data/spec/fortnox/api/validators/attributes/country_code_spec.rb +0 -9
  200. data/spec/fortnox/api/validators/attributes/currency_spec.rb +0 -9
  201. data/spec/fortnox/api/validators/attributes/examples_for_validate.rb +0 -29
  202. data/spec/fortnox/api/validators/base_spec.rb +0 -61
  203. data/spec/fortnox/api/validators/constant_spec.rb +0 -12
  204. data/spec/fortnox/api/validators/context.rb +0 -102
  205. data/spec/fortnox/api/validators/customer_spec.rb +0 -31
  206. data/spec/fortnox/api/validators/edi_information_spec.rb +0 -18
  207. data/spec/fortnox/api/validators/email_information_spec.rb +0 -26
  208. data/spec/fortnox/api/validators/invoice_spec.rb +0 -36
  209. data/spec/fortnox/api/validators/row_spec.rb +0 -27
  210. data/spec/fortnox/api/validators/validator_examples.rb +0 -20
  211. data/spec/support/matchers/models.rb +0 -27
  212. data/spec/support/matchers/validators.rb +0 -36
@@ -0,0 +1,13 @@
1
+ module Matchers
2
+ module Type
3
+ def have_country_code( attribute, valid_hash = {} )
4
+ HaveCountryCodeMatcher.new( attribute, valid_hash )
5
+ end
6
+
7
+ class HaveCountryCodeMatcher < EnumMatcher
8
+ def initialize( attribute, valid_hash )
9
+ super( attribute, valid_hash, 'CountryCode', 'CountryCodes', nonsense_value: 'XX'.freeze )
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,7 @@
1
+ module Matchers
2
+ module Type
3
+ def have_currency( attribute, valid_hash = {} )
4
+ EnumMatcher.new( attribute, valid_hash, 'Currency', 'Currencies' )
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,13 @@
1
+ module Matchers
2
+ module Type
3
+ def have_customer_type( attribute, valid_hash = {} )
4
+ HaveCustomerTypeMatcher.new( attribute, valid_hash )
5
+ end
6
+
7
+ class HaveCustomerTypeMatcher < EnumMatcher
8
+ def initialize( attribute, valid_hash )
9
+ super( attribute, valid_hash, 'CustomerType', 'CustomerTypes' )
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,7 @@
1
+ module Matchers
2
+ module Type
3
+ def have_default_delivery_type( attribute, valid_hash = {} )
4
+ EnumMatcher.new( attribute, valid_hash, 'DefaultDeliveryType', 'DefaultDeliveryTypeValues' )
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Matchers
2
+ module Type
3
+ def have_discount_type( attribute, valid_hash = {} )
4
+ EnumMatcher.new( attribute, valid_hash, 'DiscountType', 'DiscountTypes' )
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,22 @@
1
+ module Matchers
2
+ module Type
3
+ def have_email( attribute, valid_hash = {} )
4
+ HaveEmailMatcher.new( attribute, valid_hash )
5
+ end
6
+
7
+ class HaveEmailMatcher < TypeMatcher
8
+ def initialize( attribute, valid_hash )
9
+ super( attribute, valid_hash, 'email', 'valid@email.com', 'invalid@email_without_top_domain' )
10
+ @expected_error = "Exception missing for invalid value #{ @invalid_value.inspect }"
11
+ @expected_type = Fortnox::API::Types::Email
12
+ end
13
+
14
+ private
15
+
16
+ def expected_type?
17
+ @actual_type = @klass.schema[@attribute]
18
+ @actual_type == Fortnox::API::Types::Email
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,7 @@
1
+ module Matchers
2
+ module Type
3
+ def have_house_work_type( attribute, valid_hash = {} )
4
+ EnumMatcher.new( attribute, valid_hash, 'HouseWorkType', 'HouseWorkTypes' )
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,58 @@
1
+ module Matchers
2
+ module Type
3
+ def have_nullable_date( attribute, valid_value, invalid_value )
4
+ HaveNullableDateMatcher.new( attribute, valid_value, invalid_value )
5
+ end
6
+
7
+ class HaveNullableDateMatcher < HaveNullableMatcher
8
+ def initialize( attribute, valid_value, invalid_value )
9
+ @attribute = attribute
10
+ @valid_value = valid_value
11
+ @invalid_value = invalid_value
12
+ @expected_error = ArgumentError
13
+ @expected_error_message = 'invalid date'
14
+ @failure_description = ''
15
+ end
16
+
17
+ def matches?( klass )
18
+ @klass = klass
19
+
20
+ accepts_nil? && accepts_valid_value? && rejects_invalid_value?
21
+ end
22
+
23
+ def description
24
+ "have nullable attribute #{ @attribute.inspect }"
25
+ end
26
+
27
+ def failure_message
28
+ "Expected class to have nullable attribute #{ @attribute.inspect }" << @failure_description
29
+ end
30
+
31
+ private
32
+
33
+ def accepts_nil?
34
+ @klass.new(@attribute => nil)
35
+ end
36
+
37
+ def accepts_valid_value?
38
+ model = @klass.new(@attribute => @valid_value)
39
+ model.send(@attribute) == @valid_value
40
+ end
41
+
42
+ def rejects_invalid_value?
43
+ @klass.new(@attribute => @invalid_value)
44
+
45
+ @failure_description << " (Expected #{ @expected_error }, but got none)"
46
+ return false
47
+ rescue @expected_error => error
48
+ if error.message == @expected_error_message
49
+ return true
50
+ else
51
+ fail_message = "Expected error message to include #{ expected_message.inspect }, "\
52
+ "but was #{ error.message.inspect }"
53
+ fail(fail_message)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,52 @@
1
+ module Matchers
2
+ module Type
3
+ def have_nullable( attribute, valid_value, invalid_value, default_value )
4
+ HaveNullableMatcher.new( attribute, valid_value, invalid_value, default_value )
5
+ end
6
+
7
+ class HaveNullableMatcher
8
+ def initialize( attribute, valid_value, invalid_value, default_value )
9
+ @attribute = attribute
10
+ @valid_value = valid_value
11
+ @invalid_value = invalid_value
12
+ @default_value = default_value
13
+ @failure_description = ''
14
+ end
15
+
16
+ def matches?( klass )
17
+ @klass = klass
18
+
19
+ accepts_nil? && accepts_valid_value? && defaults_invalid_value?
20
+ end
21
+
22
+ def description
23
+ "have nullable attribute #{ @attribute.inspect }"
24
+ end
25
+
26
+ def failure_message
27
+ "Expected class to have nullable attribute #{ @attribute.inspect }" << @failure_description
28
+ end
29
+
30
+ private
31
+
32
+ def accepts_nil?
33
+ @klass.new(@attribute => nil)
34
+ end
35
+
36
+ def accepts_valid_value?
37
+ model = @klass.new(@attribute => @valid_value)
38
+ model.send(@attribute) == @valid_value
39
+ end
40
+
41
+ def defaults_invalid_value?
42
+ model = @klass.new(@attribute => @invalid_value)
43
+ value = model.send(@attribute)
44
+ return true if value == @default_value
45
+
46
+ @failure_description << " (Expected #{ @invalid_value.inspect } to default to "\
47
+ "#{ @default_value.inspect }), but got #{ value.inspect }"
48
+ false
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,49 @@
1
+ module Matchers
2
+ module Type
3
+ def have_nullable_string( attribute, valid_hash = {} )
4
+ HaveNullableStringMatcher.new( attribute, valid_hash )
5
+ end
6
+
7
+ class HaveNullableStringMatcher < AttributeMatcher
8
+ def initialize( attribute, valid_hash )
9
+ super( attribute, valid_hash, 'nullable string' )
10
+ end
11
+
12
+ def matches?( klass )
13
+ super
14
+
15
+ accepts_nil? && accepts_string? && rejects_non_string?
16
+ end
17
+
18
+ private
19
+
20
+ def accepts_nil?
21
+ model = @klass.new( @valid_hash.merge( @attribute => nil ) )
22
+ model.send(@attribute).nil?
23
+ end
24
+
25
+ def accepts_string?
26
+ valid_string = 'A string'
27
+ model = @klass.new( @valid_hash.merge( @attribute => valid_string ) )
28
+ model.send(@attribute) == 'A string'
29
+ end
30
+
31
+ def rejects_non_string?
32
+ non_string = 10.0
33
+ @klass.new( @valid_hash.merge( @attribute => non_string ) )
34
+ rescue Fortnox::API::InvalidAttributeValueError => error
35
+ expected_message = "#{ non_string.inspect } (#{ non_string.class }) "\
36
+ "has invalid type for #{ @attribute.inspect }"
37
+ if error.message.include?(expected_message)
38
+ return true
39
+ else
40
+ fail_message = "Expected error message to include #{ expected_message.inspect }, "\
41
+ "but was #{ error.message.inspect }"
42
+ fail(fail_message)
43
+ end
44
+
45
+ end
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,8 @@
1
+ module Matchers
2
+ module Type
3
+ def have_sized_float( attribute, min_value, max_value, valid_hash = {} )
4
+ options = { min_value: min_value, max_value: max_value, step: 0.1 }
5
+ NumericMatcher.new( attribute, valid_hash, 'sized float', options )
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module Matchers
2
+ module Type
3
+ def have_sized_integer( attribute, min_value, max_value, valid_hash = {} )
4
+ options = { min_value: min_value, max_value: max_value, step: 1 }
5
+ NumericMatcher.new( attribute, valid_hash, 'sized integer', options )
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,35 @@
1
+ module Matchers
2
+ module Type
3
+ def have_sized_string( attribute, max_size, valid_hash = {} )
4
+ HaveSizedStringMatcher.new( attribute, max_size, valid_hash )
5
+ end
6
+
7
+ class HaveSizedStringMatcher < AttributeMatcher
8
+ def initialize( attribute, max_size, valid_hash )
9
+ super( attribute, valid_hash, 'sized string' )
10
+ @max_size = max_size
11
+ end
12
+
13
+ def matches?( klass )
14
+ @klass = klass
15
+
16
+ accepts_max_size? && rejects_too_long_string?
17
+ end
18
+
19
+ private
20
+
21
+ def accepts_max_size?
22
+ @klass.new( @valid_hash.merge( @attribute => 'a' * @max_size ) )
23
+ end
24
+
25
+ def rejects_too_long_string?
26
+ too_long = @max_size + 1
27
+ too_long_string = 'a' * too_long
28
+ expect_error("Exception missing for too long string (#{ too_long } characters)") do
29
+ @klass.new( @valid_hash.merge( @attribute => too_long_string ) )
30
+ end
31
+ end
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,7 @@
1
+ module Matchers
2
+ module Type
3
+ def have_vat_type( attribute, valid_hash = {} )
4
+ EnumMatcher.new( attribute, valid_hash, 'VATType', 'VATTypes' )
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,50 @@
1
+ module Matchers
2
+ module Type
3
+ class NumericMatcher < AttributeMatcher
4
+ def initialize( attribute, valid_hash, attr_type, options )
5
+ super( attribute, valid_hash, attr_type )
6
+
7
+ @min_value = options[:min_value]
8
+ @max_value = options[:max_value]
9
+ @step = options[:step]
10
+ end
11
+
12
+ def matches?( klass )
13
+ super
14
+
15
+ rejects_too_small_value? &&
16
+ accepts_min_value? &&
17
+ accepts_max_value? &&
18
+ rejects_too_big_value?
19
+ end
20
+
21
+ private
22
+
23
+ def rejects_too_small_value?
24
+ too_small_value = @min_value - @step
25
+ expected_error_message = "Exception missing for too small value (#{ too_small_value })"
26
+ rejects_value?(too_small_value, expected_error_message)
27
+ end
28
+
29
+ def accepts_min_value?
30
+ @klass.new( @valid_hash.merge( @attribute => @min_value ) )
31
+ end
32
+
33
+ def accepts_max_value?
34
+ @klass.new( @valid_hash.merge( @attribute => @max_value ) )
35
+ end
36
+
37
+ def rejects_too_big_value?
38
+ too_big_value = @max_value + @step
39
+ expected_error_message = "Exception missing for too big value (#{ too_big_value })"
40
+ rejects_value?(too_big_value, expected_error_message)
41
+ end
42
+
43
+ def rejects_value?(value, expected_error_message)
44
+ expect_error(expected_error_message) do
45
+ @klass.new( @valid_hash.merge( @attribute => value ) )
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,69 @@
1
+ require 'fortnox/api'
2
+
3
+ module Matchers
4
+ module Type
5
+ def require_attribute(attribute, valid_hash = {})
6
+ RequireAttributeMatcher.new(attribute, valid_hash)
7
+ end
8
+
9
+ class RequireAttributeMatcher
10
+ EXCEPTION = Fortnox::API::MissingAttributeError
11
+
12
+ def initialize( attribute, valid_hash )
13
+ @attribute = attribute
14
+ @invalid_hash = valid_hash.dup.tap{ |hash| hash.delete( attribute ) }
15
+ end
16
+
17
+ def matches?( klass )
18
+ @klass = klass
19
+
20
+ raise_error && includes_error_message
21
+ end
22
+
23
+ def failure_message
24
+ return no_exception_failure_message unless raise_error
25
+ return wrong_error_message unless includes_error_message
26
+ end
27
+
28
+ def description
29
+ "require attribute #{ @attribute.inspect }"
30
+ end
31
+
32
+ private
33
+
34
+ def raise_error
35
+ @klass.new(@invalid_hash)
36
+
37
+ false # Fail test since expected error is not thrown
38
+
39
+ rescue EXCEPTION
40
+ true
41
+ end
42
+
43
+ def includes_error_message
44
+ @klass.new(@invalid_hash)
45
+ rescue EXCEPTION => error
46
+ if error.message.include? expected_error_message
47
+ return true
48
+ else
49
+ @wrong_error_message = error.message
50
+ return false
51
+ end
52
+ end
53
+
54
+ def expected_error_message
55
+ @attribute.to_s
56
+ end
57
+
58
+ def no_exception_failure_message
59
+ "Expected class to raise #{ EXCEPTION } "\
60
+ "when attribute #{ @attribute.inspect } is missing."
61
+ end
62
+
63
+ def wrong_error_message
64
+ "Expected exception to equal #{ expected_error_message.inspect }. "\
65
+ "Message was #{ @wrong_error_message.inspect }."
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,38 @@
1
+ module Matchers
2
+ module Type
3
+ class TypeMatcher < AttributeMatcher
4
+ def initialize( attr, valid_hash, attr_name, valid_value, invalid_value )
5
+ super( attr, valid_hash, attr_name )
6
+
7
+ @valid_value = valid_value
8
+ @invalid_value = invalid_value
9
+ end
10
+
11
+ def matches?( klass )
12
+ super
13
+
14
+ correct_type? && rejects_invalid_value? && accepts_valid_value?
15
+ end
16
+
17
+ private
18
+
19
+ def correct_type?
20
+ return true if expected_type?
21
+
22
+ @errors << "Attribute #{ @attribute.inspect } was expected to be of type #{ @attribute_type }, but was #{ @actual_type }"
23
+ return false
24
+ end
25
+
26
+ def accepts_valid_value?
27
+ @klass.new( @valid_hash.merge( @attribute => @valid_value ) )
28
+ end
29
+
30
+ def rejects_invalid_value?
31
+ expect_error(@expected_error) do
32
+ @klass.new( @valid_hash.merge( @attribute => @invalid_value ) )
33
+ end
34
+ end
35
+
36
+ end
37
+ end
38
+ end