jsonapi_rspec 0.2.1 → 0.2.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bd3c396ce463d2d0e5cc4eadb1388ed33f3904bf
4
- data.tar.gz: d9c1721e2d66bc797690e2b9b2617516c8a1044c
3
+ metadata.gz: 6f6a700c0770ee5ba9954b77fcc7818dce6274ae
4
+ data.tar.gz: cbd8512415d501c272add052d2b7ae9668ab5ca4
5
5
  SHA512:
6
- metadata.gz: 3efb55b570842c4e432b56ffeb62d3bb63c71cb8fe18d077d1bd6e7981e97058fe57d138a7934740b6afa3c809c5e13cd9947e9da4e0e76f47c673283f4d8b4d
7
- data.tar.gz: 9a36e27ecc06158474515f7aa8d2a170d4433a007e1c94673c0605ff51bce66a370c6594dd0445a4ab40e4831fa5b46a05b19832092b1c0d861ba5ed2ccd68e5
6
+ metadata.gz: 2a59ac4bda91aa5f2ec4c41096779803f1266c9e9d509a22c977b9d2b39145957864da2b8b0a8fb2a3948de1e24654f7e12084da0bbd1803375af8d54a4e783d
7
+ data.tar.gz: 9ecc45dad3f2f86d63c992c8c8aff538125c8d9e1a3f2241b5bb740c78aee641c388f036c3fa466a7212d5d9b6a346f371f4cd6ee7afaad6f2a193ca142e2dbd
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- jsonapi_rspec (0.2.1)
4
+ jsonapi_rspec (0.2.2)
5
5
  activesupport (>= 4.2.8)
6
6
 
7
7
  GEM
@@ -1,4 +1,6 @@
1
1
  require 'jsonapi_rspec/version'
2
+ require 'jsonapi_rspec/failure_messages'
3
+ require 'jsonapi_rspec/be_json_api_response'
2
4
  require 'jsonapi_rspec/be_json_api_response_for'
3
5
 
4
6
  module JsonapiRspec
@@ -15,6 +17,69 @@ module JsonapiRspec
15
17
  end
16
18
  end
17
19
 
20
+ def failure_message
21
+ "#{@failure_message} - parsed response: #{pretty_response}"
22
+ end
23
+
24
+ def failure_message_when_negated
25
+ @failure_message = "handle method 'failure_message_when_negated' in custom_matchers.rb"
26
+ "#{@failure_message}: #{pretty_response}"
27
+ end
28
+
29
+ private
30
+
31
+ def pretty_response
32
+ JSON.pretty_generate(@parsed_response)
33
+ rescue JSON::GeneratorError
34
+ @parsed_response.to_s
35
+ end
36
+
37
+ def valid_response?(response)
38
+ return set_failure_message(FailureMessages::EMPTY) if response.body == ''
39
+ return set_failure_message(FailureMessages::NIL) if response.body.nil?
40
+ true
41
+ end
42
+
43
+ def required_top_level_sections?
44
+ valid = @parsed_response.dig('data') || @parsed_response.dig('errors') || @parsed_response.dig('meta')
45
+ return set_failure_message(FailureMessages::MISSING_REQ_TOP_LVL) unless valid
46
+ true
47
+ end
48
+
49
+ def conflicting_sections?
50
+ conflicting = false
51
+ if @parsed_response.dig('included')
52
+ # must have a data section
53
+ if @parsed_response.dig('data').nil?
54
+ conflicting = true
55
+ set_failure_message(FailureMessages::CONFLICTING_TOP_LVL)
56
+ end
57
+ end
58
+ conflicting
59
+ end
60
+
61
+ def valid_meta_section?
62
+ meta = @parsed_response.dig('meta')
63
+ return set_failure_message(FailureMessages::MISSING_META) unless meta.is_a?(Hash)
64
+ true
65
+ end
66
+
67
+ def response_is_error?
68
+ is_error = !@parsed_response.dig('errors').nil?
69
+ set_failure_message(FailureMessages::ERROR) if is_error
70
+ is_error
71
+ end
72
+
73
+ def valid_data_section?
74
+ data_section = @parsed_response.dig('data')
75
+ valid = data_section.is_a?(Hash) || data_section.is_a?(Array)
76
+ unless valid
77
+ return set_failure_message(FailureMessages::INVALID_DATA_SECTION)
78
+ end
79
+ true
80
+ end
81
+
82
+ # Autoload Section
18
83
  autoload :Configuration, 'jsonapi_rspec/configuration'
19
84
  end
20
85
 
@@ -0,0 +1,82 @@
1
+ require 'rspec/matchers'
2
+
3
+ require_relative 'string'
4
+
5
+ # Class BeJsonApiResponse provides custom RSpec matching for json:api
6
+ # responses in general.
7
+ #
8
+ # It expects a Rack::Response (or similar) response object
9
+ #
10
+ # Usage:
11
+ # expect(response).to BeJsonApiResponse.new
12
+ #
13
+ # @author Chris Blackburn <87a1779b@opayq.com>
14
+ #
15
+ class BeJsonApiResponse
16
+ include JsonapiRspec
17
+
18
+ def matches?(response)
19
+ return false unless valid_response?(response)
20
+
21
+ @parsed_response = JSON.parse(response.body)
22
+
23
+ return false if response_is_error?
24
+ return false unless required_top_level_sections?
25
+ return false if conflicting_sections?
26
+
27
+ if JsonapiRspec.configuration.meta_required
28
+ return false unless valid_meta_section?
29
+ end
30
+
31
+ @parsed_response.each_key do |key|
32
+ case key.to_sym
33
+ when :data
34
+ return false unless valid_data_section?
35
+ when :meta
36
+ return false unless valid_meta_section?
37
+ when :jsonapi
38
+ next # this can legally be anything
39
+ when :included
40
+ next # TODO: handle included objects
41
+ when :links
42
+ next # TODO: handle links objects
43
+ else
44
+ return set_failure_message(FailureMessages::UNEXPECTED_TOP_LVL_KEY % key)
45
+ end
46
+ end
47
+
48
+ true
49
+ end
50
+
51
+ private
52
+
53
+ # Set the failure message
54
+ #
55
+ # @param [String] msg Failure message
56
+ #
57
+ # @return [Boolean] always returns false
58
+ #
59
+ def set_failure_message(msg)
60
+ @failure_message = "#{FailureMessages::GENERAL_PREFIX} #{msg}"
61
+ false
62
+ end
63
+ end
64
+
65
+ # Usage:
66
+ # expect(response).to be_jsonapi_response
67
+ #
68
+ RSpec::Matchers.define :be_jsonapi_response do
69
+ match do |actual_response|
70
+ @instance = BeJsonApiResponse.new
71
+
72
+ def failure_message
73
+ @instance.failure_message
74
+ end
75
+
76
+ def failure_message_when_negated
77
+ @instance.failure_message
78
+ end
79
+
80
+ @instance.matches?(actual_response)
81
+ end
82
+ end
@@ -16,6 +16,8 @@ require_relative 'string'
16
16
  # @author Chris Blackburn <87a1779b@opayq.com>
17
17
  #
18
18
  class BeJsonApiResponseFor
19
+ include JsonapiRspec
20
+
19
21
  def initialize(object_instance, plural_form = nil)
20
22
  @object_instance = object_instance
21
23
  @plural_form = plural_form
@@ -42,32 +44,18 @@ class BeJsonApiResponseFor
42
44
  next # this can legally be anything
43
45
  when :included
44
46
  next # TODO: handle included objects
47
+ when :links
48
+ next # TODO: handle links objects
45
49
  else
46
- return set_failure_message("Unexpected key in response: '#{key}'")
50
+ return set_failure_message(FailureMessages::UNEXPECTED_TOP_LVL_KEY % key)
47
51
  end
48
52
  end
49
53
 
50
54
  true
51
55
  end
52
56
 
53
- def failure_message
54
- @failure_message ||= "Expected object [#{@object_instance}] to match"
55
- "#{@failure_message} - parsed response: #{pretty_response}"
56
- end
57
-
58
- def failure_message_when_negated
59
- @failure_message = "handle method 'failure_message_when_negated' in custom_matchers.rb"
60
- "#{@failure_message}: #{pretty_response}"
61
- end
62
-
63
57
  private
64
58
 
65
- def pretty_response
66
- JSON.pretty_generate(@parsed_response)
67
- rescue JSON::GeneratorError
68
- @parsed_response.to_s
69
- end
70
-
71
59
  # Set the failure message
72
60
  #
73
61
  # @param [String] msg Failure message
@@ -75,49 +63,21 @@ class BeJsonApiResponseFor
75
63
  # @return [Boolean] always returns false
76
64
  #
77
65
  def set_failure_message(msg)
78
- @failure_message = msg
66
+ @failure_message = "#{FailureMessages::OBJECT_PREFIX} #{msg}"
79
67
  false
80
68
  end
81
69
 
82
- def valid_response?(response)
83
- if response.body == ''
84
- return set_failure_message('Expected response to match an object instance but it is an empty string')
85
- end
86
- true
87
- end
88
-
89
- def valid_data_section?
90
- unless @parsed_response.dig('data').is_a?(Hash)
91
- return set_failure_message("The 'data' section is missing or invalid")
92
- end
93
- true
94
- end
95
-
96
70
  def valid_type?(data_type)
97
71
  object_type = @plural_form ||
98
72
  @object_instance.class.name.pluralize.underscore.dasherize
99
73
  unless data_type == object_type
100
- return set_failure_message("Expected data:type '#{data_type}' to match: '#{object_type}'")
101
- end
102
- true
103
- end
104
-
105
- def valid_meta_section?
106
- meta = @parsed_response.dig('meta')
107
- return set_failure_message("The 'meta' section is missing or invalid") unless meta.is_a?(Hash)
108
- return set_failure_message("The 'meta:version' is missing") if meta.dig('version').nil?
109
- unless meta.dig('copyright') =~ /^Copyright.+\d{4}/
110
- return set_failure_message("The 'meta:copyright' is missing or invalid - regex: '/^Copyright.+\\d{4}/'")
74
+ return set_failure_message(
75
+ format(FailureMessages::DATA_TYPE_MISMATCH, data_type, object_type)
76
+ )
111
77
  end
112
78
  true
113
79
  end
114
80
 
115
- def response_is_error?
116
- is_error = !@parsed_response.dig('errors').nil?
117
- set_failure_message('Response is an error') if is_error
118
- is_error
119
- end
120
-
121
81
  def match_attribute?(attr_name, json_val)
122
82
  obj_val = @object_instance.send(attr_name.to_sym)
123
83
  obj_val_class_name = obj_val.class.name
@@ -129,10 +89,8 @@ class BeJsonApiResponseFor
129
89
  matched = obj_val.to_i == DateTime.parse(json_val).to_i
130
90
  when 'Time'
131
91
  matched = obj_val.to_i == Time.parse(json_val).to_i
132
- when 'String', 'NilClass', 'TrueClass', 'FalseClass', 'Fixnum', 'Integer', 'Bignum'
133
- matched = obj_val == json_val
134
92
  else
135
- return set_failure_message("Fix 'match_attribute?' method to handle: '#{obj_val_class_name}'")
93
+ matched = obj_val == json_val
136
94
  end
137
95
 
138
96
  unless matched
@@ -153,7 +111,9 @@ class BeJsonApiResponseFor
153
111
  when :id
154
112
  object_id = @object_instance.send(key)
155
113
  unless object_id == value.to_i
156
- return set_failure_message("Expected '#{value}' to match object id: '#{object_id}'")
114
+ return set_failure_message(
115
+ format(FailureMessages::OBJECT_ID_MISMATCH, value, object_id)
116
+ )
157
117
  end
158
118
  when :type
159
119
  return false unless valid_type?(value)
@@ -0,0 +1,16 @@
1
+ module FailureMessages
2
+ GENERAL_PREFIX = 'Expected a json:api compliant success response but'.freeze
3
+ OBJECT_PREFIX = 'Expected a json:api response for an object instance but'.freeze
4
+
5
+ ERROR = 'it is an error'.freeze
6
+ EMPTY = 'it is empty'.freeze
7
+ NIL = 'it is nil'.freeze
8
+
9
+ MISSING_REQ_TOP_LVL = 'it is missing a required top-level section'.freeze
10
+ CONFLICTING_TOP_LVL = "it cannot contain 'included' without a 'data' section".freeze
11
+ UNEXPECTED_TOP_LVL_KEY = "it has an unexpected key: '%s'".freeze
12
+ INVALID_DATA_SECTION = "the 'data' section must be a Hash or an Array".freeze
13
+ DATA_TYPE_MISMATCH = "data:type '%s' doesn't match: '%s'".freeze
14
+ OBJECT_ID_MISMATCH = "data:id '%s' doesn't match object id: '%s'".freeze
15
+ MISSING_META = "the 'meta' section is missing or invalid".freeze
16
+ end
@@ -1,3 +1,3 @@
1
1
  module JsonapiRspec
2
- VERSION = '0.2.1'.freeze
2
+ VERSION = '0.2.2'.freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jsonapi_rspec
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Blackburn
@@ -141,8 +141,10 @@ files:
141
141
  - bin/setup
142
142
  - jsonapi_rspec.gemspec
143
143
  - lib/jsonapi_rspec.rb
144
+ - lib/jsonapi_rspec/be_json_api_response.rb
144
145
  - lib/jsonapi_rspec/be_json_api_response_for.rb
145
146
  - lib/jsonapi_rspec/configuration.rb
147
+ - lib/jsonapi_rspec/failure_messages.rb
146
148
  - lib/jsonapi_rspec/string.rb
147
149
  - lib/jsonapi_rspec/version.rb
148
150
  homepage: https://github.com/midwire/jsonapi_rspec