webhukhs 0.5.0 → 0.7.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.
@@ -0,0 +1,244 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class StringIdReceivedWebhook < Webhukhs::ReceivedWebhook
6
+ self.table_name = "string_id_received_webhooks"
7
+ end
8
+
9
+ unless ActiveRecord::Base.connection.data_source_exists?("string_id_received_webhooks")
10
+ original_migration_verbose = ActiveRecord::Migration.verbose
11
+ ActiveRecord::Migration.verbose = false
12
+
13
+ begin
14
+ ActiveRecord::Schema.define do
15
+ create_table "string_id_received_webhooks", id: :string, force: true do |t|
16
+ t.string "handler_event_id", null: false
17
+ t.string "handler_module_name", null: false
18
+ t.string "status", default: "received", null: false
19
+ t.binary "body", null: false
20
+ t.json "request_headers", null: true
21
+ t.datetime "created_at", null: false
22
+ t.datetime "updated_at", null: false
23
+ end
24
+ end
25
+ ensure
26
+ ActiveRecord::Migration.verbose = original_migration_verbose
27
+ end
28
+ end
29
+
30
+ class ReceivedWebhookTest < ActiveSupport::TestCase
31
+ cover "Webhukhs::ReceivedWebhook*"
32
+
33
+ teardown do
34
+ Webhukhs::ReceivedWebhook.delete_all
35
+ StringIdReceivedWebhook.delete_all if ActiveRecord::Base.connection.data_source_exists?("string_id_received_webhooks")
36
+ end
37
+
38
+ test "request stores uppercase string headers and rewinds the body io" do
39
+ body_io = StringIO.new('{"ok":true}')
40
+ request = ActionDispatch::Request.new(
41
+ "CONTENT_TYPE" => "application/json",
42
+ "HTTP_X_SIGNATURE" => "sig",
43
+ "lowercase" => "skip",
44
+ "Mixed-Case" => "skip",
45
+ "HTTP_X_NUMBER" => 123,
46
+ 123 => "skip",
47
+ "RAW_POST_DATA" => "legacy",
48
+ "rack.input" => body_io,
49
+ "action_dispatch.request.path_parameters" => {"user_id" => "123"}
50
+ )
51
+ webhook = Webhukhs::ReceivedWebhook.new
52
+
53
+ webhook.request = request
54
+
55
+ assert_equal 0, body_io.pos
56
+ assert_equal body_io.string.b, webhook.body
57
+ assert_equal Encoding::ASCII_8BIT, webhook.body.encoding
58
+ assert_equal(
59
+ {
60
+ "CONTENT_TYPE" => "application/json",
61
+ "HTTP_X_SIGNATURE" => "sig",
62
+ "action_dispatch.request.path_parameters" => {"user_id" => "123"}
63
+ },
64
+ webhook.request_headers
65
+ )
66
+ end
67
+
68
+ test "request writer coerces the stored body to binary before type casting" do
69
+ body_io = StringIO.new("payload")
70
+ request = ActionDispatch::Request.new(
71
+ "CONTENT_TYPE" => "application/json",
72
+ "rack.input" => body_io,
73
+ "action_dispatch.request.path_parameters" => {}
74
+ )
75
+ webhook = Webhukhs::ReceivedWebhook.new
76
+ captured_body = nil
77
+
78
+ with_overridden_singleton_method(webhook, :write_attribute, ->(name, value) {
79
+ captured_body = value if name == "body"
80
+ }) do
81
+ webhook.request = request
82
+
83
+ assert_equal "payload".b, captured_body
84
+ assert_equal Encoding::ASCII_8BIT, captured_body.encoding
85
+ end
86
+ end
87
+
88
+ test "request rejects oversized bodies and rewinds the body io" do
89
+ original_limit = Webhukhs.configuration.request_body_size_limit
90
+ Webhukhs.configuration.request_body_size_limit = 3
91
+
92
+ body_io = StringIO.new("four")
93
+ request = ActionDispatch::Request.new(
94
+ "CONTENT_TYPE" => "application/json",
95
+ "rack.input" => body_io,
96
+ "action_dispatch.request.path_parameters" => {}
97
+ )
98
+ webhook = Webhukhs::ReceivedWebhook.new
99
+
100
+ error = assert_raises(RuntimeError) { webhook.request = request }
101
+
102
+ assert_equal "Cannot accept the webhook as the request body is larger than 3 bytes", error.message
103
+ assert_equal 0, body_io.pos
104
+ ensure
105
+ Webhukhs.configuration.request_body_size_limit = original_limit
106
+ end
107
+
108
+ test "request reconstructs the body without mutating stored headers" do
109
+ request_headers = {
110
+ "CONTENT_TYPE" => "application/json",
111
+ "action_dispatch.request.path_parameters" => {"user_id" => "123"}
112
+ }
113
+ webhook = Webhukhs::ReceivedWebhook.new(
114
+ handler_event_id: "request-read",
115
+ handler_module_name: "WebhookTestHandler",
116
+ status: "received",
117
+ body: "payload",
118
+ request_headers: request_headers.deep_dup
119
+ )
120
+
121
+ request = webhook.request
122
+ request_body = request.body.read
123
+
124
+ assert_equal "payload".b, request_body.b
125
+ assert_equal Encoding::ASCII_8BIT, request_body.encoding
126
+ assert_equal request_headers, webhook.request_headers
127
+ end
128
+
129
+ test "request reader coerces the reconstructed body to binary" do
130
+ webhook = Webhukhs::ReceivedWebhook.new
131
+
132
+ request_headers = {
133
+ "CONTENT_TYPE" => "application/json",
134
+ "action_dispatch.request.path_parameters" => {}
135
+ }
136
+
137
+ with_overridden_singleton_methods(webhook, {
138
+ body: -> { "payload" },
139
+ request_headers: -> {
140
+ request_headers
141
+ }
142
+ }) do
143
+ request_body = webhook.request.body.read
144
+
145
+ assert_equal "payload".b, request_body.b
146
+ assert_equal Encoding::ASCII_8BIT, request_body.encoding
147
+ end
148
+ end
149
+
150
+ test "assigns uuid ids for string primary keys" do
151
+ with_overridden_singleton_methods(SecureRandom, {
152
+ respond_to?: ->(method_name, include_all = false) {
153
+ method_name == :uuid_v7 || super(method_name, include_all)
154
+ },
155
+ uuid_v7: -> {
156
+ "00000000-0000-7000-8000-000000000001"
157
+ },
158
+ uuid: -> {
159
+ "00000000-0000-4000-8000-000000000002"
160
+ }
161
+ }) do
162
+ webhook = StringIdReceivedWebhook.create!(
163
+ handler_event_id: "generated-id-event",
164
+ handler_module_name: "WebhookTestHandler",
165
+ status: "received",
166
+ body: {}.to_json,
167
+ request_headers: {}
168
+ )
169
+
170
+ assert_equal "00000000-0000-7000-8000-000000000001", webhook.id
171
+ end
172
+ end
173
+
174
+ test "falls back to uuid when uuid_v7 is unavailable" do
175
+ with_overridden_singleton_methods(SecureRandom, {
176
+ respond_to?: ->(method_name, include_all = false) {
177
+ (method_name == :uuid_v7) ? false : super(method_name, include_all)
178
+ },
179
+ uuid_v7: -> {
180
+ raise "uuid_v7 should not be called when unavailable"
181
+ },
182
+ uuid: -> {
183
+ "00000000-0000-4000-8000-000000000002"
184
+ }
185
+ }) do
186
+ webhook = StringIdReceivedWebhook.create!(
187
+ handler_event_id: "fallback-id-event",
188
+ handler_module_name: "WebhookTestHandler",
189
+ status: "received",
190
+ body: {}.to_json,
191
+ request_headers: {}
192
+ )
193
+
194
+ assert_equal "00000000-0000-4000-8000-000000000002", webhook.id
195
+ end
196
+ end
197
+
198
+ test "does not call uuid generation for integer primary keys" do
199
+ with_overridden_singleton_methods(SecureRandom, {
200
+ uuid_v7: -> {
201
+ raise "uuid_v7 should not be called for integer primary keys"
202
+ },
203
+ uuid: -> {
204
+ raise "uuid should not be called for integer primary keys"
205
+ }
206
+ }) do
207
+ webhook = Webhukhs::ReceivedWebhook.create!(
208
+ handler_event_id: "integer-id-event",
209
+ handler_module_name: "WebhookTestHandler",
210
+ status: "received",
211
+ body: {}.to_json,
212
+ request_headers: {}
213
+ )
214
+
215
+ assert_kind_of Integer, webhook.id
216
+ end
217
+ end
218
+
219
+ test "does not overwrite explicitly assigned string ids" do
220
+ webhook = StringIdReceivedWebhook.create!(
221
+ id: "manual-id",
222
+ handler_event_id: "manual-id-event",
223
+ handler_module_name: "WebhookTestHandler",
224
+ status: "received",
225
+ body: {}.to_json,
226
+ request_headers: {}
227
+ )
228
+
229
+ assert_equal "manual-id", webhook.id
230
+ end
231
+
232
+ test "assigns a uuid when a string id is blank" do
233
+ webhook = StringIdReceivedWebhook.create!(
234
+ id: "",
235
+ handler_event_id: "blank-id-event",
236
+ handler_module_name: "WebhookTestHandler",
237
+ status: "received",
238
+ body: {}.to_json,
239
+ request_headers: {}
240
+ )
241
+
242
+ assert_match(/\A[0-9a-f-]{36}\z/, webhook.id)
243
+ end
244
+ end
data/test/test_app.rb CHANGED
@@ -5,24 +5,46 @@ require "action_pack"
5
5
  require "action_controller"
6
6
  require "rails"
7
7
 
8
- database = "development.sqlite3"
9
- ENV["DATABASE_URL"] = "sqlite3:#{database}"
10
- ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: database)
11
- ActiveRecord::Base.logger = Logger.new(nil)
12
- ActiveRecord::Schema.define do
13
- create_table "received_webhooks", force: :cascade do |t|
14
- t.string "handler_event_id", null: false
15
- t.string "handler_module_name", null: false
16
- t.string "status", default: "received", null: false
17
- t.binary "body", null: false
18
- t.json "request_headers", null: true
19
- t.datetime "created_at", null: false
20
- t.datetime "updated_at", null: false
21
- t.index ["handler_module_name", "handler_event_id"], name: "webhook_dedup_idx", unique: true
22
- t.index ["status"], name: "index_received_webhooks_on_status"
8
+ module Test
9
+ DEFAULT_DATABASE_PATH = File.expand_path("development.sqlite3", __dir__)
10
+ DEFAULT_DATABASE_URL = "sqlite3:#{DEFAULT_DATABASE_PATH}"
11
+
12
+ def self.database_url
13
+ ENV.fetch("DATABASE_URL", DEFAULT_DATABASE_URL)
14
+ end
15
+
16
+ def self.database_path
17
+ File.expand_path(database_url.delete_prefix("sqlite3:"))
18
+ end
19
+
20
+ def self.establish_database_connection
21
+ database = database_path
22
+
23
+ ENV["DATABASE_URL"] = database_url
24
+ ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: database)
25
+ ActiveRecord::Base.logger = Logger.new(nil)
26
+ end
27
+
28
+ def self.define_test_schema
29
+ ActiveRecord::Schema.define do
30
+ create_table "received_webhooks", force: :cascade do |t|
31
+ t.string "handler_event_id", null: false
32
+ t.string "handler_module_name", null: false
33
+ t.string "status", default: "received", null: false
34
+ t.binary "body", null: false
35
+ t.json "request_headers", null: true
36
+ t.datetime "created_at", null: false
37
+ t.datetime "updated_at", null: false
38
+ t.index ["handler_module_name", "handler_event_id"], name: "webhook_dedup_idx", unique: true
39
+ t.index ["status"], name: "index_received_webhooks_on_status"
40
+ end
41
+ end
23
42
  end
24
43
  end
25
44
 
45
+ Test.establish_database_connection
46
+ Test.define_test_schema
47
+
26
48
  require_relative "../lib/webhukhs"
27
49
  require_relative "test-webhook-handlers/webhook_test_handler"
28
50
  require_relative "test-webhook-handlers/inactive_handler"
data/test/test_helper.rb CHANGED
@@ -1,5 +1,18 @@
1
1
  require_relative "test_app"
2
2
  require "rails/test_help"
3
+ require "mutant/minitest/coverage"
4
+ require "active_support/logger"
5
+ require "stringio"
6
+
7
+ if ENV["SIMPLECOV"]
8
+ require "simplecov"
9
+
10
+ SimpleCov.start do
11
+ add_filter "/spec/"
12
+ minimum_coverage 100
13
+ minimum_coverage_by_file 100
14
+ end
15
+ end
3
16
 
4
17
  class ActiveSupport::TestCase
5
18
  # Same as "assert_changes" in Rails but for countable entities.
@@ -47,4 +60,57 @@ class ActiveSupport::TestCase
47
60
 
48
61
  retval
49
62
  end
63
+
64
+ def with_overridden_singleton_method(target, method_name, implementation, &block)
65
+ with_overridden_singleton_methods(target, {method_name => implementation}, &block)
66
+ end
67
+
68
+ def with_captured_info_logs(logger_owner)
69
+ messages = []
70
+ original_logger = logger_owner.logger
71
+ test_logger = ActiveSupport::Logger.new(StringIO.new)
72
+ test_logger.level = Logger::INFO
73
+ test_logger.formatter = lambda do |_severity, _time, _progname, message|
74
+ messages << message
75
+ ""
76
+ end
77
+
78
+ logger_owner.logger = test_logger
79
+ yield messages
80
+ ensure
81
+ logger_owner.logger = original_logger
82
+ end
83
+
84
+ def with_overridden_singleton_methods(target, methods)
85
+ singleton_class = target.singleton_class
86
+ original_names = methods.keys.each_with_index.to_h do |name, index|
87
+ sanitized_name = name.to_s.gsub(/[^a-zA-Z0-9_]/, "_")
88
+ [name, :"__original_#{sanitized_name}_for_test_#{object_id}_#{index}"]
89
+ end
90
+
91
+ silence_warnings do
92
+ singleton_class.class_eval do
93
+ original_names.each do |name, original_name|
94
+ alias_method original_name, name
95
+ end
96
+
97
+ methods.each do |name, override|
98
+ define_method(name, &override)
99
+ end
100
+ end
101
+ end
102
+
103
+ yield
104
+ ensure
105
+ silence_warnings do
106
+ singleton_class.class_eval do
107
+ original_names.each do |name, original_name|
108
+ next unless method_defined?(original_name)
109
+
110
+ alias_method name, original_name
111
+ remove_method original_name
112
+ end
113
+ end
114
+ end
115
+ end
50
116
  end
@@ -1,9 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "test_helper"
4
- require_relative "test_app"
4
+
5
+ class FailingLookupReceiveWebhooksController < Webhukhs::ReceiveWebhooksController
6
+ def initialize(error)
7
+ super()
8
+ @error = error
9
+ end
10
+
11
+ def service_id = "broken"
12
+
13
+ def lookup_handler(_service_id)
14
+ raise @error
15
+ end
16
+ end
5
17
 
6
18
  class TestWebhukhs < ActionDispatch::IntegrationTest
19
+ cover "Webhukhs*"
20
+ cover "Webhukhs::BaseHandler*"
21
+ cover "Webhukhs::Configuration*"
22
+ cover "Webhukhs::ProcessingJob*"
23
+ cover "Webhukhs::ReceiveWebhooksController*"
24
+ cover "Webhukhs::ReceivedWebhook*"
25
+
7
26
  teardown { Webhukhs::ReceivedWebhook.delete_all }
8
27
 
9
28
  def test_that_it_has_a_version_number
@@ -28,6 +47,39 @@ class TestWebhukhs < ActionDispatch::IntegrationTest
28
47
  test(msg) { skip }
29
48
  end
30
49
 
50
+ test "returns a memoized configuration object and yields it to configure" do
51
+ original_configuration = Webhukhs.instance_variable_get(:@configuration)
52
+ Webhukhs.remove_instance_variable(:@configuration) if Webhukhs.instance_variable_defined?(:@configuration)
53
+
54
+ configuration = Webhukhs.configuration
55
+
56
+ assert_instance_of Webhukhs::Configuration, configuration
57
+ assert_same configuration, Webhukhs.configuration
58
+
59
+ yielded = nil
60
+ Webhukhs.configure { |config| yielded = config }
61
+
62
+ assert_same configuration, yielded
63
+ ensure
64
+ Webhukhs.instance_variable_set(:@configuration, original_configuration)
65
+ end
66
+
67
+ test "applies the configured error context before handling requests" do
68
+ original_error_context = Webhukhs.configuration.error_context
69
+
70
+ Webhukhs.configuration.error_context = {component: "webhooks"}
71
+
72
+ report = assert_error_reported(RuntimeError) do
73
+ post "/webhukhs/failing-with-concealed-errors", params: webhook_body, headers: {"CONTENT_TYPE" => "application/json"}
74
+
75
+ assert_response 200
76
+ end
77
+
78
+ assert_equal "webhooks", report.context[:component]
79
+ ensure
80
+ Webhukhs.configuration.error_context = original_error_context
81
+ end
82
+
31
83
  test "ensure webhook is processed only once during creation" do
32
84
  tf = Tempfile.new
33
85
  body = {isValid: true, outputToFilename: tf.path}
@@ -36,6 +88,7 @@ class TestWebhukhs < ActionDispatch::IntegrationTest
36
88
  assert_enqueued_jobs 1, only: Webhukhs::ProcessingJob do
37
89
  post "/webhukhs/test", params: body_json, headers: {"CONTENT_TYPE" => "application/json"}
38
90
  assert_response 200
91
+ assert_equal({"ok" => true, "error" => nil}, response.parsed_body)
39
92
  end
40
93
  end
41
94
 
@@ -133,23 +186,40 @@ class TestWebhukhs < ActionDispatch::IntegrationTest
133
186
  end
134
187
 
135
188
  test "raises an error if the service_id is not known" do
136
- post "/webhukhs/missing_service", params: webhook_body, headers: {"CONTENT_TYPE" => "application/json"}
137
- assert_response 404
189
+ report = assert_error_reported(Webhukhs::ReceiveWebhooksController::UnknownHandler) do
190
+ post "/webhukhs/missing_service", params: webhook_body, headers: {"CONTENT_TYPE" => "application/json"}
191
+
192
+ assert_response 404
193
+ assert_equal "No handler found for \"missing_service\"", response.parsed_body["error"]
194
+ end
195
+
196
+ assert_predicate report, :handled?
197
+ assert_equal :error, report.severity
138
198
  end
139
199
 
140
200
  test "returns a 503 when a handler is inactive" do
141
- post "/webhukhs/inactive", params: webhook_body, headers: {"CONTENT_TYPE" => "application/json"}
201
+ report = assert_error_reported(Webhukhs::ReceiveWebhooksController::HandlerInactive) do
202
+ post "/webhukhs/inactive", params: webhook_body, headers: {"CONTENT_TYPE" => "application/json"}
203
+
204
+ assert_response 503
205
+ assert_equal 'Webhook handler "inactive" is inactive', response.parsed_body["error"]
206
+ end
142
207
 
143
- assert_response 503
144
- assert_equal 'Webhook handler "inactive" is inactive', response.parsed_body["error"]
208
+ assert_predicate report, :handled?
209
+ assert_equal :error, report.severity
145
210
  end
146
211
 
147
212
  test "returns a 200 status and error message if the handler does not expose errors" do
148
- post "/webhukhs/failing-with-concealed-errors", params: webhook_body, headers: {"CONTENT_TYPE" => "application/json"}
213
+ report = assert_error_reported(RuntimeError) do
214
+ post "/webhukhs/failing-with-concealed-errors", params: webhook_body, headers: {"CONTENT_TYPE" => "application/json"}
149
215
 
150
- assert_response 200
151
- assert_equal false, response.parsed_body["ok"]
152
- assert response.parsed_body["error"]
216
+ assert_response 200
217
+ assert_equal false, response.parsed_body["ok"]
218
+ assert_equal "Internal error (oops)", response.parsed_body["error"]
219
+ end
220
+
221
+ assert_predicate report, :handled?
222
+ assert_equal :error, report.severity
153
223
  end
154
224
 
155
225
  test "returns a 500 status and error message if the handler does not expose errors" do
@@ -160,6 +230,40 @@ class TestWebhukhs < ActionDispatch::IntegrationTest
160
230
  # common Rails error page
161
231
  end
162
232
 
233
+ test "re-raises lookup errors before a handler has been assigned" do
234
+ original_active_handlers = Webhukhs.configuration.active_handlers
235
+ Webhukhs.configuration.active_handlers = original_active_handlers.merge(broken: "MissingHandler")
236
+
237
+ post "/webhukhs/broken", params: webhook_body, headers: {"CONTENT_TYPE" => "application/json"}
238
+
239
+ assert_response 500
240
+ assert_includes response.body, "NameError"
241
+ assert_includes response.body, "MissingHandler"
242
+ ensure
243
+ Webhukhs.configuration.active_handlers = original_active_handlers
244
+ end
245
+
246
+ test "re-raises the original error when lookup fails before assigning a handler" do
247
+ original_error = NameError.new("uninitialized constant MissingHandler")
248
+ controller = FailingLookupReceiveWebhooksController.new(original_error)
249
+
250
+ raised_error = assert_raises(NameError) { controller.create }
251
+
252
+ assert_same original_error, raised_error
253
+ end
254
+
255
+ test "accepts handlers configured as class constants" do
256
+ original_active_handlers = Webhukhs.configuration.active_handlers
257
+ Webhukhs.configuration.active_handlers = original_active_handlers.merge(class_handler: WebhookTestHandler)
258
+
259
+ post "/webhukhs/class_handler", params: {isValid: false, outputToFilename: "/tmp/unused"}.to_json, headers: {"CONTENT_TYPE" => "application/json"}
260
+
261
+ assert_response 200
262
+ assert_equal({"ok" => true, "error" => nil}, response.parsed_body)
263
+ ensure
264
+ Webhukhs.configuration.active_handlers = original_active_handlers
265
+ end
266
+
163
267
  test "deduplicates received webhooks based on the event ID" do
164
268
  body = {event_id: SecureRandom.uuid, body: "test"}.to_json
165
269
 
@@ -200,4 +304,16 @@ class TestWebhukhs < ActionDispatch::IntegrationTest
200
304
  assert_equal "received", webhook.status
201
305
  end
202
306
  end
307
+
308
+ test "ensure_id does not override id for integer primary key columns" do
309
+ webhook = Webhukhs::ReceivedWebhook.create!(
310
+ handler_event_id: SecureRandom.uuid,
311
+ handler_module_name: "WebhookTestHandler",
312
+ status: "received",
313
+ body: {isValid: true}.to_json
314
+ )
315
+
316
+ assert_kind_of Integer, webhook.id
317
+ assert webhook.id > 0
318
+ end
203
319
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: webhukhs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stanislav Katkov
@@ -108,9 +108,7 @@ dependencies:
108
108
  - !ruby/object:Gem::Version
109
109
  version: '5.0'
110
110
  description: Webhukhs is a Rails engine for processing webhooks from various services.
111
- Engine saves webhook in database first and later processes webhook in async process.
112
- Supports custom handlers with Webhukhs::BaseHandler and integrates with Rails common
113
- error reporter.
111
+ Engine saves webhook in database first and later processes in async job.
114
112
  email:
115
113
  - github@skatkov.com
116
114
  executables: []
@@ -120,11 +118,15 @@ files:
120
118
  - ".editorconfig"
121
119
  - ".rubocop.yml"
122
120
  - ".standard.yml"
121
+ - ".yard-lint.yml"
122
+ - AGENTS.md
123
123
  - Appraisals
124
124
  - CHANGELOG.md
125
125
  - LICENSE
126
126
  - README.md
127
127
  - Rakefile
128
+ - config/mutant.rb
129
+ - config/mutant.yml
128
130
  - config/routes.rb
129
131
  - example/.gitattributes
130
132
  - example/.gitignore
@@ -193,6 +195,7 @@ files:
193
195
  - gemfiles/rails_8.0.gemfile.lock
194
196
  - gemfiles/rails_8.1.gemfile
195
197
  - gemfiles/rails_8.1.gemfile.lock
198
+ - gemfiles/rails_master.gemfile
196
199
  - handler-examples/customer_io_handler.rb
197
200
  - handler-examples/revolut_business_v1_handler.rb
198
201
  - handler-examples/revolut_business_v2_handler.rb
@@ -209,6 +212,9 @@ files:
209
212
  - lib/webhukhs/templates/create_webhukhs_tables.rb.erb
210
213
  - lib/webhukhs/templates/webhukhs.rb
211
214
  - lib/webhukhs/version.rb
215
+ - test/base_handler_test.rb
216
+ - test/processing_job_test.rb
217
+ - test/received_webhook_test.rb
212
218
  - test/test-webhook-handlers/.DS_Store
213
219
  - test/test-webhook-handlers/extract_id_handler.rb
214
220
  - test/test-webhook-handlers/failing_with_concealed_errors.rb
@@ -241,7 +247,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
241
247
  - !ruby/object:Gem::Version
242
248
  version: '0'
243
249
  requirements: []
244
- rubygems_version: 4.0.1
250
+ rubygems_version: 4.0.10
245
251
  specification_version: 4
246
252
  summary: Webhooks processing engine for Rails applications
247
253
  test_files: []