fortnox-api 0.1.0 → 0.2.0

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