jpie 1.1.1 → 1.1.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
  SHA256:
3
- metadata.gz: a9afc70de5e508e561e451595fc67fdad82446fe7b79c4b565d53c8a26719c13
4
- data.tar.gz: fca030ebc361a511587a1a3b152243629ec6fec26f65c929408325cedba2ca95
3
+ metadata.gz: 8c205d5463d3fffd9ccf158d1ef8df04e34e50fd6a7385f9b727a46676181c00
4
+ data.tar.gz: 1c8e628e95a5296baa459c89cbc7d2bd119f4bb5e0456f5d9d3e4c8322b08a92
5
5
  SHA512:
6
- metadata.gz: 6136bb0f0829358a96d5fc1daace4794ad1f3e559701d5ed6b03c63189f8925fa3e6f645e6b447809f3cdbd44596e3defc6ffda2d0f87b62d36f2c3f17ba9cc8
7
- data.tar.gz: 9af6902e625ab5fa282065849bdaa89977389a4c2c86db06805de6c05b3c6325bb2a8dd4253f93bcf8a27016e77ca9b3b3adc9a805058baac1c7b403439a104f
6
+ metadata.gz: 17bb979968aba9d606fdb537c5ebba08e8d29b39e9ffd2dbaefeaaeafc4a41b1ebb686429cd20ffd10db9276320d0e70e7cc94551a418863dbdd633542df67f6
7
+ data.tar.gz: 8fad54116b4eaa6c2e77f9e8034f477a3c9cb4911cc749ff00bf06cc30d801751ff0351771040dd94f6a639776aa0396775637e1d1fa4583a91a965e844ca8e8
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- jpie (1.1.1)
4
+ jpie (1.1.2)
5
5
  actionpack (~> 8.0, >= 8.0.0)
6
6
  rails (~> 8.0, >= 8.0.0)
7
7
 
@@ -9,14 +9,22 @@
9
9
  # This will:
10
10
  # 1. Load all shared example module definitions
11
11
  # 2. Load all shared examples for resources and responses
12
- # 3. Make the modules available at the top level for convenient usage
12
+ # 3. Load STI testing helpers for request specs
13
+ # 4. Make the modules available at the top level for convenient usage
13
14
  #
14
15
  # After requiring, you can use shared examples like:
15
16
  #
16
17
  # it_behaves_like [ Jpie, Resource, :has_attributes ].to_s, :name, :email
17
18
  # it_behaves_like [ Jpie, Response, :data ].to_s, type: "users"
19
+ # it_behaves_like [ Jpie, Response, :sti_types ].to_s, expected_subtypes: ["user_messages"]
20
+ #
21
+ # And STI helpers in request specs:
22
+ #
23
+ # expect_sti_types_in_included(expected_subtypes: ["user_messages"])
24
+ # expect_sti_resources_included(sti_type: "user_messages", expected_ids: [msg.id])
18
25
 
19
26
  require_relative "shared_examples/modules"
27
+ require_relative "sti_helpers"
20
28
 
21
29
  # Load shared examples
22
30
  Dir[File.join(__dir__, "shared_examples", "*.rb")].each do |file|
@@ -30,4 +38,7 @@ RSpec.configure do |config|
30
38
  define_method(const) { Object.const_get(const) }
31
39
  end
32
40
  end)
41
+
42
+ # Include STI helpers for request specs
43
+ config.include JSONAPI::Testing::StiHelpers, type: :request
33
44
  end
@@ -60,7 +60,8 @@ RSpec.shared_examples [Jpie, Response, :relationships].to_s do |*relationships|
60
60
  end
61
61
  end
62
62
 
63
- RSpec.shared_examples [Jpie, Response, :included].to_s do |type: nil, count: nil|
63
+ RSpec.shared_examples [Jpie, Response,
64
+ :included,].to_s do |type: nil, count: nil, sti_subtypes: nil, exclude_base_type: nil|
64
65
  it "includes compound document data" do
65
66
  included = response.parsed_body["included"]
66
67
  expect(included).to be_an(Array)
@@ -71,6 +72,20 @@ RSpec.shared_examples [Jpie, Response, :included].to_s do |type: nil, count: nil
71
72
  expect(matching).not_to be_empty, "Expected included to contain type '#{type}'"
72
73
  expect(matching.size).to eq(count) if count
73
74
  end
75
+
76
+ if sti_subtypes
77
+ included_types = included.pluck("type").uniq
78
+ sti_subtypes.each do |subtype|
79
+ expect(included_types).to include(subtype.to_s),
80
+ "Expected included to contain STI subtype '#{subtype}', got: #{included_types}"
81
+ end
82
+ end
83
+
84
+ if exclude_base_type
85
+ included_types ||= included.pluck("type").uniq
86
+ expect(included_types).not_to include(exclude_base_type.to_s),
87
+ "Expected included NOT to contain base type '#{exclude_base_type}', but it did"
88
+ end
74
89
  end
75
90
  end
76
91
 
@@ -111,3 +126,40 @@ RSpec.shared_examples [Jpie, Response, :errors].to_s do |count: nil|
111
126
  end
112
127
  end
113
128
  end
129
+
130
+ RSpec.shared_examples [Jpie, Response, :sti_types].to_s do |expected_subtypes:, exclude_base_type: nil|
131
+ it "returns STI subtypes correctly" do
132
+ data = response.parsed_body["data"]
133
+ data = [data] unless data.is_a?(Array)
134
+ types = data.pluck("type").uniq
135
+
136
+ expected_subtypes.each do |subtype|
137
+ expect(types).to include(subtype.to_s),
138
+ "Expected data to contain STI subtype '#{subtype}', got: #{types}"
139
+ end
140
+
141
+ if exclude_base_type
142
+ expect(types).not_to include(exclude_base_type.to_s),
143
+ "Expected data NOT to contain base type '#{exclude_base_type}', but it did"
144
+ end
145
+ end
146
+ end
147
+
148
+ RSpec.shared_examples [Jpie, Response, :sti_included].to_s do |expected_subtypes:, exclude_base_type: nil|
149
+ it "includes STI subtypes correctly" do
150
+ included = response.parsed_body["included"] || []
151
+ expect(included).not_to be_empty, "Expected included to be present"
152
+
153
+ included_types = included.pluck("type").uniq
154
+
155
+ expected_subtypes.each do |subtype|
156
+ expect(included_types).to include(subtype.to_s),
157
+ "Expected included to contain STI subtype '#{subtype}', got: #{included_types}"
158
+ end
159
+
160
+ if exclude_base_type
161
+ expect(included_types).not_to include(exclude_base_type.to_s),
162
+ "Expected included NOT to contain base type '#{exclude_base_type}', but it did"
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONAPI
4
+ module Testing
5
+ # Helper methods for testing STI (Single Table Inheritance) resources in JSON:API responses.
6
+ #
7
+ # These helpers simplify testing that STI subtypes are correctly serialized
8
+ # and included in JSON:API responses.
9
+ #
10
+ # Usage in request specs:
11
+ #
12
+ # it "includes user messages", :aggregate_failures do
13
+ # get conversation_path(conversation), params: { include: "messages" }, as: :jsonapi
14
+ # expect_sti_types_in_included(expected_subtypes: ["user_messages"])
15
+ # expect_sti_resources_included(sti_type: "user_messages", expected_ids: [message.id])
16
+ # end
17
+ module StiHelpers
18
+ # Verify that included resources contain expected STI subtypes.
19
+ #
20
+ # @param expected_subtypes [Array<String, Symbol>] Expected STI subtype names
21
+ # @param exclude_base_type [String, Symbol, nil] Base type that should NOT be present (optional)
22
+ # @return [void]
23
+ #
24
+ # @example Verify subtypes are present
25
+ # expect_sti_types_in_included(expected_subtypes: ["user_messages", "ai_messages"])
26
+ #
27
+ # @example Verify subtypes and exclude base type
28
+ # expect_sti_types_in_included(
29
+ # expected_subtypes: ["user_messages"],
30
+ # exclude_base_type: "messages"
31
+ # )
32
+ def expect_sti_types_in_included(expected_subtypes:, exclude_base_type: nil)
33
+ included = response.parsed_body["included"] || []
34
+ included_types = included.pluck("type").uniq
35
+
36
+ expected_subtypes.each do |subtype|
37
+ expect(included_types).to include(subtype.to_s),
38
+ "Expected included to contain STI type '#{subtype}', got: #{included_types}"
39
+ end
40
+
41
+ return if exclude_base_type.blank?
42
+
43
+ expect(included_types).not_to include(exclude_base_type.to_s),
44
+ "Expected included NOT to contain base type '#{exclude_base_type}', but it did"
45
+ end
46
+
47
+ # Find all resources of a specific STI type in the included array.
48
+ #
49
+ # @param sti_type [String, Symbol] The STI subtype to find
50
+ # @return [Array<Hash>] Array of resource objects matching the STI type
51
+ #
52
+ # @example Find all user messages in included
53
+ # messages = find_sti_resources_in_included("user_messages")
54
+ # expect(messages.first["attributes"]["content"]).to eq("Hello")
55
+ def find_sti_resources_in_included(sti_type)
56
+ included = response.parsed_body["included"] || []
57
+ included.select { |item| item["type"] == sti_type.to_s }
58
+ end
59
+
60
+ # Verify that specific STI resources are included by ID.
61
+ #
62
+ # @param sti_type [String, Symbol] The STI subtype
63
+ # @param expected_ids [Array<Integer, String>] Expected resource IDs
64
+ # @return [void]
65
+ #
66
+ # @example Verify specific messages are included
67
+ # expect_sti_resources_included(
68
+ # sti_type: "user_messages",
69
+ # expected_ids: [message1.id, message2.id]
70
+ # )
71
+ def expect_sti_resources_included(sti_type:, expected_ids:)
72
+ resources = find_sti_resources_in_included(sti_type)
73
+ actual_ids = resources.map { |r| r["id"] }
74
+ expected_ids_str = expected_ids.map(&:to_s)
75
+
76
+ expect(actual_ids).to match_array(expected_ids_str),
77
+ "Expected #{sti_type} resources with IDs #{expected_ids_str}, got: #{actual_ids}"
78
+ end
79
+
80
+ # Verify STI types in data array (not included).
81
+ #
82
+ # @param expected_subtypes [Array<String, Symbol>] Expected STI subtype names
83
+ # @param exclude_base_type [String, Symbol, nil] Base type that should NOT be present (optional)
84
+ # @return [void]
85
+ #
86
+ # @example Verify data contains correct STI types
87
+ # expect_sti_types_in_data(
88
+ # expected_subtypes: ["user_messages", "ai_messages"],
89
+ # exclude_base_type: "messages"
90
+ # )
91
+ def expect_sti_types_in_data(expected_subtypes:, exclude_base_type: nil)
92
+ data = response.parsed_body["data"]
93
+ data = [data] unless data.is_a?(Array)
94
+ types = data.pluck("type").uniq
95
+
96
+ expected_subtypes.each do |subtype|
97
+ expect(types).to include(subtype.to_s),
98
+ "Expected data to contain STI subtype '#{subtype}', got: #{types}"
99
+ end
100
+
101
+ return if exclude_base_type.blank?
102
+
103
+ expect(types).not_to include(exclude_base_type.to_s),
104
+ "Expected data NOT to contain base type '#{exclude_base_type}', but it did"
105
+ end
106
+
107
+ # Find all resources of a specific STI type in the data array.
108
+ #
109
+ # @param sti_type [String, Symbol] The STI subtype to find
110
+ # @return [Array<Hash>] Array of resource objects matching the STI type
111
+ #
112
+ # @example Find all user messages in data
113
+ # messages = find_sti_resources_in_data("user_messages")
114
+ def find_sti_resources_in_data(sti_type)
115
+ data = response.parsed_body["data"]
116
+ data = [data] unless data.is_a?(Array)
117
+ data.select { |item| item["type"] == sti_type.to_s }
118
+ end
119
+
120
+ # Verify that specific STI resources are present in data by ID.
121
+ #
122
+ # @param sti_type [String, Symbol] The STI subtype
123
+ # @param expected_ids [Array<Integer, String>] Expected resource IDs
124
+ # @return [void]
125
+ #
126
+ # @example Verify specific messages are in data
127
+ # expect_sti_resources_in_data(
128
+ # sti_type: "user_messages",
129
+ # expected_ids: [message1.id, message2.id]
130
+ # )
131
+ def expect_sti_resources_in_data(sti_type:, expected_ids:)
132
+ resources = find_sti_resources_in_data(sti_type)
133
+ actual_ids = resources.map { |r| r["id"] }
134
+ expected_ids_str = expected_ids.map(&:to_s)
135
+
136
+ expect(actual_ids).to match_array(expected_ids_str),
137
+ "Expected #{sti_type} resources with IDs #{expected_ids_str}, got: #{actual_ids}"
138
+ end
139
+ end
140
+ end
141
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JSONAPI
4
- VERSION = "1.1.1"
4
+ VERSION = "1.1.2"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jpie
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Emil Kampp
@@ -148,6 +148,7 @@ files:
148
148
  - lib/json_api/testing/shared_examples/modules.rb
149
149
  - lib/json_api/testing/shared_examples/resources.rb
150
150
  - lib/json_api/testing/shared_examples/responses.rb
151
+ - lib/json_api/testing/sti_helpers.rb
151
152
  - lib/json_api/testing/test_helper.rb
152
153
  - lib/json_api/version.rb
153
154
  - lib/rubocop/cop/custom/hash_value_omission.rb