webhukhs 0.6.0 → 0.8.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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/.standard.yml +2 -0
  3. data/.yard-lint.yml +296 -0
  4. data/AGENTS.md +56 -0
  5. data/Appraisals +6 -2
  6. data/CHANGELOG.md +20 -0
  7. data/README.md +101 -11
  8. data/Rakefile +7 -0
  9. data/config/mutant.rb +37 -0
  10. data/config/mutant.yml +25 -0
  11. data/example/Gemfile +5 -6
  12. data/example/Gemfile.lock +260 -141
  13. data/example/app/webhooks/acme_handler.rb +16 -0
  14. data/example/app/webhooks/flaky_handler.rb +8 -0
  15. data/example/app/webhooks/signed_handler.rb +10 -0
  16. data/example/config/initializers/webhukhs.rb +4 -3
  17. data/example/config/routes.rb +3 -0
  18. data/example/db/migrate/20240601000000_add_headers_to_webhukhs_webhooks.rb +7 -0
  19. data/example/db/schema.rb +2 -1
  20. data/example/db/seeds.rb +71 -7
  21. data/gemfiles/{rails_7.1.gemfile → rails_7.2.gemfile} +1 -4
  22. data/gemfiles/rails_7.2.gemfile.lock +477 -0
  23. data/gemfiles/rails_8.0.gemfile +0 -3
  24. data/gemfiles/rails_8.0.gemfile.lock +74 -4
  25. data/gemfiles/rails_8.1.gemfile +0 -3
  26. data/gemfiles/rails_8.1.gemfile.lock +74 -4
  27. data/gemfiles/rails_master.gemfile +7 -0
  28. data/lib/webhukhs/admin/application_controller.rb +32 -0
  29. data/lib/webhukhs/admin/assets/admin.css +476 -0
  30. data/lib/webhukhs/admin/assets/admin.js +111 -0
  31. data/lib/webhukhs/admin/config/routes.rb +6 -0
  32. data/lib/webhukhs/admin/engine.rb +20 -0
  33. data/lib/webhukhs/admin/received_webhook_presenter.rb +63 -0
  34. data/lib/webhukhs/admin/received_webhooks_controller.rb +60 -0
  35. data/lib/webhukhs/admin/view_helper.rb +50 -0
  36. data/lib/webhukhs/admin/views/layouts/webhukhs/admin/application.html.erb +22 -0
  37. data/lib/webhukhs/admin/views/webhukhs/admin/received_webhooks/index.html.erb +50 -0
  38. data/lib/webhukhs/admin/views/webhukhs/admin/received_webhooks/show.html.erb +55 -0
  39. data/lib/webhukhs/admin.rb +17 -0
  40. data/lib/webhukhs/base_handler.rb +9 -6
  41. data/lib/webhukhs/controllers/receive_webhooks_controller.rb +26 -7
  42. data/lib/webhukhs/engine.rb +1 -4
  43. data/lib/webhukhs/install_generator.rb +9 -2
  44. data/lib/webhukhs/jobs/processing_job.rb +13 -12
  45. data/lib/webhukhs/models/received_webhook.rb +21 -9
  46. data/lib/webhukhs/version.rb +1 -1
  47. data/lib/webhukhs.rb +9 -0
  48. data/test/admin_test.rb +334 -0
  49. data/test/base_handler_test.rb +125 -0
  50. data/test/browser/admin_click_through.rb +208 -0
  51. data/test/processing_job_test.rb +152 -12
  52. data/test/received_webhook_test.rb +260 -0
  53. data/test/test_app.rb +39 -15
  54. data/test/test_helper.rb +69 -0
  55. data/test/webhukhs_test.rb +111 -49
  56. metadata +173 -11
  57. data/example/.ruby-version +0 -1
  58. data/gemfiles/rails_7.1.gemfile.lock +0 -412
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ac61cd0cbe0da35c7a6ad5f2be48cfff0dae74f712ce215eee4e3577fb3ce864
4
- data.tar.gz: a27c3a7d108b70ddf72a073fba8333641e14530d9afea09e720c54b2ffd41867
3
+ metadata.gz: cae5927b7426085a8e140f6bdb4565423d43d1b72c65b65c9602b72bd81a1338
4
+ data.tar.gz: e77b5321fbd8b814b3d6265da104ec9b4e77ffc36a2d0b0c62e67ae1c1f3ff05
5
5
  SHA512:
6
- metadata.gz: b09a61434f3b2b92edd9d5774363f29556f790963bd31e6e93684284cd03395685f1d61f5cb2477bf268168d31ce1cb6ab95da5b6c234e28d98a010fa14cebb9
7
- data.tar.gz: ebb82e8c71c7e449a76bbf87e0db2c3c8350ae7ad85f2ba13708af3cef6554805d1e759310c46da9f78e08db49441e991740d9eb301155eee5b9a33c12768e5d
6
+ metadata.gz: 40519e7b2ca3b8c8e4bbf924f78d86269f6f4b68b1a72f3a7f963ec87fde656f49db69c6268de76f19b22ec762e3e14078c9839f44e080b41bc1e99131ecb449
7
+ data.tar.gz: 2a8e469254e9f76b46068a9b06001157f570f33a6cab931c2ee23cada3c13eadbff765c368cba58e6a62a1d657db8d142a2f89d2b8fe94b4034eb76f45cb4e6c
data/.standard.yml CHANGED
@@ -1,3 +1,5 @@
1
1
  # For available configuration options, see:
2
2
  # https://github.com/standardrb/standard
3
3
  ruby_version: 3.0
4
+ ignore:
5
+ - 'gemfiles/**/*'
data/.yard-lint.yml ADDED
@@ -0,0 +1,296 @@
1
+ # YARD-Lint Configuration
2
+ # See https://github.com/mensfeld/yard-lint for documentation
3
+
4
+ # Global settings for all validators
5
+ AllValidators:
6
+ # YARD command-line options (applied to all validators by default)
7
+ YardOptions:
8
+ - --private
9
+ - --protected
10
+
11
+ # Global file exclusion patterns
12
+ Exclude:
13
+ - '\.git'
14
+ - 'lib/webhukhs/engine.rb'
15
+ - 'vendor/**/*'
16
+ - 'node_modules/**/*'
17
+ - 'spec/**/*'
18
+ - 'test/**/*'
19
+ - 'handler-examples/**/*'
20
+ - 'example/**/*'
21
+
22
+ # Exit code behavior (error, warning, convention, never)
23
+ FailOnSeverity: warning
24
+
25
+ # Minimum documentation coverage percentage (0-100)
26
+ # Fails if coverage is below this threshold
27
+ # MinCoverage: 80.0
28
+
29
+ # Diff mode settings
30
+ DiffMode:
31
+ # Default base ref for --diff (auto-detects main/master if not specified)
32
+ DefaultBaseRef: ~
33
+
34
+ # Documentation validators
35
+ Documentation/UndocumentedObjects:
36
+ Description: 'Checks for classes, modules, and methods without documentation.'
37
+ Enabled: true
38
+ Severity: warning
39
+ ExcludedMethods:
40
+ - 'initialize/0' # Exclude parameter-less initialize
41
+ - '/^_/' # Exclude private methods (by convention)
42
+
43
+ Documentation/UndocumentedMethodArguments:
44
+ Description: 'Checks for method parameters without @param tags.'
45
+ Enabled: true
46
+ Severity: warning
47
+
48
+ Documentation/UndocumentedBooleanMethods:
49
+ Description: 'Checks that question mark methods document their boolean return.'
50
+ Enabled: true
51
+ Severity: warning
52
+
53
+ Documentation/UndocumentedOptions:
54
+ Description: 'Detects methods with options hash parameters but no @option tags.'
55
+ Enabled: true
56
+ Severity: warning
57
+
58
+ Documentation/MarkdownSyntax:
59
+ Description: 'Detects common markdown syntax errors in documentation.'
60
+ Enabled: true
61
+ Severity: warning
62
+
63
+ Documentation/EmptyCommentLine:
64
+ Description: 'Detects empty comment lines at the start or end of documentation blocks.'
65
+ Enabled: true
66
+ Severity: convention
67
+ EnabledPatterns:
68
+ Leading: true
69
+ Trailing: true
70
+
71
+ Documentation/BlankLineBeforeDefinition:
72
+ Description: 'Detects blank lines between YARD documentation and method definition.'
73
+ Enabled: true
74
+ Severity: convention
75
+ OrphanedSeverity: convention
76
+ EnabledPatterns:
77
+ SingleBlankLine: true
78
+ OrphanedDocs: true
79
+
80
+ # Tags validators
81
+ Tags/Order:
82
+ Description: 'Enforces consistent ordering of YARD tags.'
83
+ Enabled: true
84
+ Severity: convention
85
+ EnforcedOrder:
86
+ - param
87
+ - option
88
+ - yield
89
+ - yieldparam
90
+ - yieldreturn
91
+ - return
92
+ - raise
93
+ - see
94
+ - example
95
+ - note
96
+ - todo
97
+
98
+ Tags/InvalidTypes:
99
+ Description: 'Validates type definitions in @param, @return, @option tags.'
100
+ Enabled: true
101
+ Severity: warning
102
+ ValidatedTags:
103
+ - param
104
+ - option
105
+ - return
106
+
107
+ Tags/TypeSyntax:
108
+ Description: 'Validates YARD type syntax using YARD parser.'
109
+ Enabled: true
110
+ Severity: warning
111
+ ValidatedTags:
112
+ - param
113
+ - option
114
+ - return
115
+ - yieldreturn
116
+
117
+ Tags/MeaninglessTag:
118
+ Description: 'Detects @param/@option tags on classes, modules, or constants.'
119
+ Enabled: true
120
+ Severity: warning
121
+ CheckedTags:
122
+ - param
123
+ - option
124
+ InvalidObjectTypes:
125
+ - class
126
+ - module
127
+ - constant
128
+
129
+ Tags/CollectionType:
130
+ Description: 'Validates Hash collection syntax consistency.'
131
+ Enabled: true
132
+ Severity: convention
133
+ EnforcedStyle: long # 'long' for Hash{K => V} (YARD standard), 'short' for {K => V}
134
+ ValidatedTags:
135
+ - param
136
+ - option
137
+ - return
138
+ - yieldreturn
139
+
140
+ Tags/TagTypePosition:
141
+ Description: 'Validates type annotation position in tags.'
142
+ Enabled: true
143
+ Severity: convention
144
+ CheckedTags:
145
+ - param
146
+ - option
147
+ # EnforcedStyle: 'type_after_name' (YARD standard: @param name [Type])
148
+ # or 'type_first' (@param [Type] name)
149
+ EnforcedStyle: type_after_name
150
+
151
+ Tags/ApiTags:
152
+ Description: 'Enforces @api tags on public objects.'
153
+ Enabled: false # Opt-in validator
154
+ Severity: warning
155
+ AllowedApis:
156
+ - public
157
+ - private
158
+ - internal
159
+
160
+ Tags/OptionTags:
161
+ Description: 'Requires @option tags for methods with options parameters.'
162
+ Enabled: true
163
+ Severity: warning
164
+
165
+ Tags/ExampleSyntax:
166
+ Description: 'Validates Ruby syntax in @example tags.'
167
+ Enabled: true
168
+ Severity: warning
169
+
170
+ Tags/RedundantParamDescription:
171
+ Description: 'Detects meaningless parameter descriptions that add no value.'
172
+ Enabled: true
173
+ Severity: convention
174
+ CheckedTags:
175
+ - param
176
+ - option
177
+ Articles:
178
+ - The
179
+ - the
180
+ - A
181
+ - a
182
+ - An
183
+ - an
184
+ MaxRedundantWords: 6
185
+ GenericTerms:
186
+ - object
187
+ - instance
188
+ - value
189
+ - data
190
+ - item
191
+ - element
192
+ EnabledPatterns:
193
+ ArticleParam: true
194
+ PossessiveParam: true
195
+ TypeRestatement: true
196
+ ParamToVerb: true
197
+ IdPattern: true
198
+ DirectionalDate: true
199
+ TypeGeneric: true
200
+
201
+ Tags/InformalNotation:
202
+ Description: 'Detects informal tag notation patterns like "Note:" instead of @note.'
203
+ Enabled: true
204
+ Severity: warning
205
+ CaseSensitive: false
206
+ RequireStartOfLine: true
207
+ Patterns:
208
+ Note: '@note'
209
+ Todo: '@todo'
210
+ TODO: '@todo'
211
+ FIXME: '@todo'
212
+ See: '@see'
213
+ See also: '@see'
214
+ Warning: '@deprecated'
215
+ Deprecated: '@deprecated'
216
+ Author: '@author'
217
+ Version: '@version'
218
+ Since: '@since'
219
+ Returns: '@return'
220
+ Raises: '@raise'
221
+ Example: '@example'
222
+
223
+ Tags/NonAsciiType:
224
+ Description: 'Detects non-ASCII characters in type annotations.'
225
+ Enabled: true
226
+ Severity: warning
227
+ ValidatedTags:
228
+ - param
229
+ - option
230
+ - return
231
+ - yieldreturn
232
+ - yieldparam
233
+
234
+ Tags/TagGroupSeparator:
235
+ Description: 'Enforces blank line separators between different YARD tag groups.'
236
+ Enabled: false # Opt-in validator
237
+ Severity: convention
238
+ TagGroups:
239
+ param: [param, option]
240
+ return: [return]
241
+ error: [raise, throws]
242
+ example: [example]
243
+ meta: [see, note, todo, deprecated, since, version, api]
244
+ yield: [yield, yieldparam, yieldreturn]
245
+ RequireAfterDescription: false
246
+
247
+ Tags/ForbiddenTags:
248
+ Description: 'Detects forbidden tag and type combinations.'
249
+ Enabled: false # Opt-in validator
250
+ Severity: convention
251
+ ForbiddenPatterns: []
252
+ # Example patterns:
253
+ # - Tag: return
254
+ # Types:
255
+ # - void
256
+ # - Tag: param
257
+ # Types:
258
+ # - Object
259
+ # - Tag: api # Forbids @api tag entirely (no Types = any occurrence)
260
+
261
+ # Warnings validators - catches YARD parser errors
262
+ Warnings/UnknownTag:
263
+ Description: 'Detects unknown YARD tags.'
264
+ Enabled: true
265
+ Severity: error
266
+
267
+ Warnings/UnknownDirective:
268
+ Description: 'Detects unknown YARD directives.'
269
+ Enabled: true
270
+ Severity: error
271
+
272
+ Warnings/InvalidTagFormat:
273
+ Description: 'Detects malformed tag syntax.'
274
+ Enabled: true
275
+ Severity: error
276
+
277
+ Warnings/InvalidDirectiveFormat:
278
+ Description: 'Detects malformed directive syntax.'
279
+ Enabled: true
280
+ Severity: error
281
+
282
+ Warnings/DuplicatedParameterName:
283
+ Description: 'Detects duplicate @param tags.'
284
+ Enabled: true
285
+ Severity: error
286
+
287
+ Warnings/UnknownParameterName:
288
+ Description: 'Detects @param tags for non-existent parameters.'
289
+ Enabled: true
290
+ Severity: error
291
+
292
+ # Semantic validators
293
+ Semantic/AbstractMethods:
294
+ Description: 'Ensures @abstract methods do not have real implementations.'
295
+ Enabled: true
296
+ Severity: warning
data/AGENTS.md ADDED
@@ -0,0 +1,56 @@
1
+ You are working in a Ruby project that uses mutation testing, but also tracks line and branch coverage.
2
+
3
+ ## Goal
4
+
5
+ Achieve 100% line, branch and mutation coverage. Verify with:
6
+
7
+ ```
8
+ SIMPLECOV=1 bundle exec rspec
9
+ ```
10
+
11
+ ```
12
+ bundle exec mutant run
13
+ ```
14
+
15
+ When iterating mutant coverage, prefer `--fail-fast` so you address one surviving
16
+ mutant at a time:
17
+
18
+ ```
19
+ bundle exec mutant run --fail-fast
20
+ ```
21
+
22
+ ## When you find coverage issue
23
+
24
+ Decide which bucket it falls into:
25
+
26
+ - **A) The code does too much** for what the tests ask for. The
27
+ surviving mutation reveals behavior that no test requires. The
28
+ fix is to simplify the implementation.
29
+ - **B) A test is missing.** The behavior is intentional but no test
30
+ observes it. The fix is to add a test.
31
+
32
+ Decide between A) and B) before changing anything. If unsure, ask
33
+ the user.
34
+
35
+ ## What you may change
36
+
37
+ - `lib/age.rb` -- the implementation. Multiple designs are valid.
38
+ - `spec/age_spec.rb` -- the tests. Some existing tests are bad and
39
+ may be rewritten or replaced.
40
+
41
+ ## Constraints
42
+
43
+ - You may not skip mutants by configuring mutant to ignore them.
44
+ No `expressions:` filters, no `coverage_criteria:` tweaks.
45
+ - You may not use `send` or `__send__` to invoke private methods
46
+ in tests just to satisfy coverage requirement.
47
+ - You may not stub or mock the system under test (`Age`).
48
+
49
+ ## Done
50
+
51
+ You are done when both of these are green:
52
+
53
+ ```
54
+ SIMPLECOV=1 bundle exec rspec
55
+ bundle exec mutant run
56
+ ```
data/Appraisals CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- appraise "rails-7.1" do
4
- gem "rails", "~> 7.1.0"
3
+ appraise "rails-7.2" do
4
+ gem "rails", "~> 7.2.0"
5
5
  end
6
6
 
7
7
  appraise "rails-8.1" do
@@ -11,3 +11,7 @@ end
11
11
  appraise "rails-8.0" do
12
12
  gem "rails", "~> 8.0"
13
13
  end
14
+
15
+ appraise "rails-master" do
16
+ gem "rails", github: "rails/rails", branch: "main"
17
+ end
data/CHANGELOG.md CHANGED
@@ -5,6 +5,26 @@ This format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
5
5
 
6
6
  ## Unreleased
7
7
 
8
+ ## 0.8.0
9
+ ## Added
10
+ - An optional, read-only admin engine (`require "webhukhs/admin"`, mount `Webhukhs::Admin::Engine`) to list,
11
+ sort, filter and inspect received webhooks
12
+ - `bin/dev`, which runs the `example` app - now with the admin mounted, more handlers and seed data
13
+
14
+ ## Fixed
15
+ - Form-encoded webhooks were stored with an empty body: Rails had already read the body stream to parse
16
+ the params by the time Webhukhs got to it
17
+
18
+ ## Changed
19
+ - Rails 7.2 or newer is now required (and with it Ruby 3.1), and the test matrix runs Rails 7.2 instead of 7.1
20
+
21
+ ## 0.7.0
22
+ ## Added
23
+ - Codebase is fully mutant tested now and test coverage increased
24
+
25
+ ## Changed
26
+ - Codebase was simplified according to mutants
27
+
8
28
  ## 0.6.0
9
29
 
10
30
  ### Changed
data/README.md CHANGED
@@ -19,30 +19,116 @@ Generate migrations and initializer file.
19
19
 
20
20
  `bin/rails g webhukhs:install`
21
21
 
22
- Mount webhukhs engine in your routes.
22
+ Mount webhukhs engine in `config/routes.rb` file (simple approach, with only one dynamic route showing up in `rails routes`)
23
23
 
24
24
  ```ruby
25
25
  mount Webhukhs::Engine, at: "/webhooks"
26
26
  ```
27
27
 
28
- Define a class for your first handler (let's call it `ExampleHandler`) and inherit it from `Webhukhs::BaseHandler`. Place it somewhere where Rails autoloading can find it, and add it to your `webhukhs.rb` config file:
28
+ Advanced mounting approach (all webhooks paths will be shown in `rails routes`)
29
29
 
30
30
  ```ruby
31
- config.active_handlers = {
32
- "example" => "ExampleHandler"
33
- }
31
+ Webhukhs.configuration.active_handlers.each_key do |service_id|
32
+ post "/webhooks/#{service_id}",
33
+ to: "webhukhs/receive_webhooks#create",
34
+ defaults: { service_id: service_id },
35
+ as: :"#{service_id.tr('-', '_')}_webhook"
36
+ end
34
37
  ```
35
38
 
36
- ## Example handlers
39
+ Define a class for your first handler (let's call it `ExampleHandler`) and inherit it from `Webhukhs::BaseHandler`. We recommend `app/webhooks`, but any place known to autoloading will do.
37
40
 
38
- We provide a number of webhook handlers which demonstrate certain features of Webhukhs. You will find them in `handler-examples`.
41
+ ```ruby
42
+ # app/webhooks/example_handler.rb
43
+ class ExampleHandler < Webhukhs::BaseHandler
44
+ # Called asynchronously to process a stored webhook.
45
+ def process(received_webhook)
46
+ payload = JSON.parse(received_webhook.body, symbolize_names: true)
47
+ # handle payload...
48
+ end
49
+
50
+ # Optional: verify the request signature before processing.
51
+ # Return false to mark the webhook as failed_validation.
52
+ def valid?(action_dispatch_request)
53
+ action_dispatch_request.headers["X-Secret-Token"] == ENV["EXAMPLE_WEBHOOK_SECRET"]
54
+ end
55
+
56
+ # Optional: use a sender-supplied ID for deduplication.
57
+ def extract_event_id_from_request(action_dispatch_request)
58
+ action_dispatch_request.headers["X-Event-Id"] || SecureRandom.uuid
59
+ end
60
+ end
61
+ ```
39
62
 
40
- ## Requirements
63
+ Add the handler to your `webhukhs.rb` config file:
64
+
65
+ ```ruby
66
+ Webhukhs.configure do |config|
67
+ config.active_handlers = {
68
+ "example" => "ExampleHandler"
69
+ }
70
+ end
71
+ ```
72
+
73
+ Now you will be able to accept webhooks on `/webhooks/example` path.
74
+
75
+ ## Admin
76
+
77
+ Webhukhs comes with a read-only admin for looking at the webhooks you have received: a sortable,
78
+ filterable list, and a page per webhook with its body (pretty-printed when it is JSON) and the
79
+ request headers it arrived with. It is not loaded by default - require it in your Gemfile (it has to be
80
+ loaded before the app boots, so `config/routes.rb` is too late):
81
+
82
+ ```ruby
83
+ gem "webhukhs", require: ["webhukhs", "webhukhs/admin"]
84
+ ```
85
+
86
+ and mount it:
87
+
88
+ ```ruby
89
+ # config/routes.rb
90
+ Rails.application.routes.draw do
91
+ mount Webhukhs::Admin::Engine, at: "/admin/webhooks"
92
+ end
93
+ ```
94
+
95
+ The admin does no authentication of its own, and webhook bodies tend to carry personal data and
96
+ secrets - so mount it behind whatever guards the rest of your admin. For example, with Devise:
97
+
98
+ ```ruby
99
+ authenticate :user, ->(user) { user.admin? } do
100
+ mount Webhukhs::Admin::Engine, at: "/admin/webhooks"
101
+ end
102
+ ```
103
+
104
+ or with HTTP basic auth:
41
105
 
42
- This project depends on two dependencies:
106
+ ```ruby
107
+ webhukhs_admin = Rack::Builder.new do
108
+ use Rack::Auth::Basic do |username, password|
109
+ ActiveSupport::SecurityUtils.secure_compare(username, ENV.fetch("ADMIN_USER")) &
110
+ ActiveSupport::SecurityUtils.secure_compare(password, ENV.fetch("ADMIN_PASSWORD"))
111
+ end
112
+ run Webhukhs::Admin::Engine
113
+ end
114
+ mount webhukhs_admin, at: "/admin/webhooks"
115
+ ```
116
+
117
+ The admin brings its own CSS and JS, served by [Assiette](https://github.com/julik/assiette) from
118
+ inside the engine - it does not touch your asset pipeline (Propshaft, Sprockets or anything else) and needs
119
+ no build step.
120
+
121
+ To try it out, run `bin/dev` from a checkout of this repository. It starts the app in `example`
122
+ with some webhooks seeded, and the admin at http://localhost:3000/admin/webhukhs.
43
123
 
44
- - Ruby >= 3.0
45
- - Rails >= 7.0
124
+ ## More Examples
125
+ - `example` folder contains a demo app with the engine and the admin fully configured - run it with `bin/dev`.
126
+ - We provide a number of webhook handlers which demonstrate certain features of Webhukhs. You will find them in `handler-examples`.
127
+
128
+ ## Requirements
129
+
130
+ - Ruby >= 3.1
131
+ - Rails >= 7.2
46
132
 
47
133
  ## Error reporter
48
134
 
@@ -62,6 +148,10 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
62
148
 
63
149
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
64
150
 
151
+ The admin also has a click-through test in a real (headless) Chrome, which `rake test` leaves out.
152
+ Run it with `bundle exec rake test:browser` - it needs Chrome or Chromium installed, and finds it
153
+ by itself or through `BROWSER_PATH`.
154
+
65
155
  ## Contributing
66
156
 
67
157
  Bug reports and pull requests are welcome on GitHub at https://github.com/skatkov/webhukhs.
data/Rakefile CHANGED
@@ -23,4 +23,11 @@ Rake::TestTask.new(:test) do |t|
23
23
  end
24
24
  end
25
25
 
26
+ # Needs Chrome, so it stays out of the default task
27
+ Rake::TestTask.new("test:browser") do |t|
28
+ t.libs << "test"
29
+ t.libs << "lib"
30
+ t.test_files = FileList["test/browser/**/*.rb"]
31
+ end
32
+
26
33
  task default: [:test, :standard]
data/config/mutant.rb ADDED
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+
5
+ project_root = File.expand_path("..", __dir__)
6
+ template_database = File.expand_path("test/development.sqlite3", project_root)
7
+ worker_database_dir = File.expand_path("tmp/mutant", project_root)
8
+
9
+ disconnect_database = lambda do
10
+ next unless defined?(ActiveRecord::Base)
11
+ next unless ActiveRecord::Base.connected?
12
+
13
+ ActiveRecord::Base.connection_pool.disconnect!
14
+ end
15
+
16
+ isolate_database = lambda do |index:|
17
+ disconnect_database.call
18
+ FileUtils.mkdir_p(worker_database_dir)
19
+
20
+ database = File.join(worker_database_dir, "worker-#{index}-#{Process.pid}.sqlite3")
21
+ FileUtils.cp(template_database, database)
22
+
23
+ ENV["DATABASE_URL"] = "sqlite3:#{database}"
24
+ ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: database)
25
+ end
26
+
27
+ hooks.register(:setup_integration_post) do
28
+ disconnect_database.call
29
+ end
30
+
31
+ hooks.register(:test_worker_process_start) do |index:|
32
+ isolate_database.call(index: index)
33
+ end
34
+
35
+ hooks.register(:mutation_worker_process_start) do |index:|
36
+ isolate_database.call(index: index)
37
+ end
data/config/mutant.yml ADDED
@@ -0,0 +1,25 @@
1
+ usage: opensource
2
+
3
+ integration:
4
+ name: minitest
5
+
6
+ includes:
7
+ - lib
8
+ - test
9
+
10
+ environment_variables:
11
+ RAILS_ENV: test
12
+
13
+ requires:
14
+ - ./test/test_app.rb
15
+
16
+ matcher:
17
+ subjects:
18
+ - Webhukhs*
19
+
20
+ coverage_criteria:
21
+ timeout: true
22
+ process_abort: true
23
+
24
+ hooks:
25
+ - ./config/mutant.rb
data/example/Gemfile CHANGED
@@ -3,23 +3,22 @@
3
3
  source "https://rubygems.org"
4
4
  git_source(:github) { |repo| "https://github.com/#{repo}.git" }
5
5
 
6
- ruby "3.2.2"
7
-
8
6
  # Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main"
9
- gem "rails", ">= 7.0.8.1"
7
+ gem "rails", ">= 7.2"
10
8
 
11
9
  # The modern asset pipeline for Rails [https://github.com/rails/propshaft]
12
10
  gem "propshaft"
13
11
 
14
12
  # Use sqlite3 as the database for Active Record
15
- gem "sqlite3", "~> 1.4"
13
+ gem "sqlite3", ">= 2.1"
16
14
 
17
15
  # Use the Puma web server [https://github.com/puma/puma]
18
- gem "puma", "~> 5.0"
16
+ gem "puma", ">= 6.0"
19
17
 
20
18
  # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
21
19
  gem "tzinfo-data", platforms: %i[mingw mswin x64_mingw jruby]
22
- gem "webhukhs", path: "../"
20
+ # The admin is optional, and has to be required before the app boots
21
+ gem "webhukhs", path: "../", require: ["webhukhs", "webhukhs/admin"]
23
22
 
24
23
  group :development, :test do
25
24
  # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem