hanami 2.1.0.beta1 → 2.1.0.beta2.1

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -4
  3. data/README.md +1 -1
  4. data/lib/hanami/app.rb +5 -0
  5. data/lib/hanami/config/actions.rb +4 -7
  6. data/lib/hanami/config/assets.rb +84 -0
  7. data/lib/hanami/config/null_config.rb +3 -0
  8. data/lib/hanami/config.rb +17 -5
  9. data/lib/hanami/extensions/action.rb +4 -2
  10. data/lib/hanami/extensions/view/standard_helpers.rb +4 -0
  11. data/lib/hanami/helpers/assets_helper.rb +772 -0
  12. data/lib/hanami/middleware/assets.rb +21 -0
  13. data/lib/hanami/middleware/render_errors.rb +4 -7
  14. data/lib/hanami/providers/assets.rb +44 -0
  15. data/lib/hanami/rake_tasks.rb +19 -18
  16. data/lib/hanami/settings.rb +1 -1
  17. data/lib/hanami/slice.rb +25 -4
  18. data/lib/hanami/version.rb +1 -1
  19. data/lib/hanami.rb +2 -2
  20. data/spec/integration/assets/assets_spec.rb +101 -0
  21. data/spec/integration/assets/serve_static_assets_spec.rb +152 -0
  22. data/spec/integration/logging/exception_logging_spec.rb +115 -0
  23. data/spec/integration/logging/notifications_spec.rb +68 -0
  24. data/spec/integration/logging/request_logging_spec.rb +128 -0
  25. data/spec/integration/rack_app/middleware_spec.rb +4 -4
  26. data/spec/integration/rack_app/rack_app_spec.rb +0 -221
  27. data/spec/integration/rake_tasks_spec.rb +107 -0
  28. data/spec/integration/view/context/assets_spec.rb +3 -9
  29. data/spec/integration/web/render_detailed_errors_spec.rb +17 -0
  30. data/spec/integration/web/render_errors_spec.rb +6 -4
  31. data/spec/support/app_integration.rb +46 -2
  32. data/spec/unit/hanami/config/actions/content_security_policy_spec.rb +24 -36
  33. data/spec/unit/hanami/config/actions/csrf_protection_spec.rb +4 -3
  34. data/spec/unit/hanami/config/actions/default_values_spec.rb +3 -2
  35. data/spec/unit/hanami/env_spec.rb +11 -25
  36. data/spec/unit/hanami/helpers/assets_helper/asset_url_spec.rb +109 -0
  37. data/spec/unit/hanami/helpers/assets_helper/audio_spec.rb +136 -0
  38. data/spec/unit/hanami/helpers/assets_helper/favicon_spec.rb +91 -0
  39. data/spec/unit/hanami/helpers/assets_helper/image_spec.rb +96 -0
  40. data/spec/unit/hanami/helpers/assets_helper/javascript_spec.rb +147 -0
  41. data/spec/unit/hanami/helpers/assets_helper/stylesheet_spec.rb +130 -0
  42. data/spec/unit/hanami/helpers/assets_helper/video_spec.rb +136 -0
  43. data/spec/unit/hanami/version_spec.rb +1 -1
  44. metadata +32 -4
  45. data/lib/hanami/assets/app_config.rb +0 -61
  46. data/lib/hanami/assets/config.rb +0 -53
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack/static"
4
+
5
+ module Hanami
6
+ module Middleware
7
+ class Assets < Rack::Static
8
+ def initialize(app, options = {}, config: Hanami.app.config)
9
+ root = config.actions.public_directory
10
+ urls = [config.assets.path_prefix]
11
+
12
+ defaults = {
13
+ root: root,
14
+ urls: urls
15
+ }
16
+
17
+ super(app, defaults.merge(options))
18
+ end
19
+ end
20
+ end
21
+ end
@@ -56,18 +56,15 @@ module Hanami
56
56
  def call(env)
57
57
  @app.call(env)
58
58
  rescue Exception => exception
59
- request = Rack::Request.new(env)
59
+ raise unless @config.render_errors
60
60
 
61
- if @config.render_errors
62
- render_exception(request, exception)
63
- else
64
- raise exception
65
- end
61
+ render_exception(env, exception)
66
62
  end
67
63
 
68
64
  private
69
65
 
70
- def render_exception(request, exception)
66
+ def render_exception(env, exception)
67
+ request = Rack::Request.new(env)
71
68
  renderable = RenderableException.new(exception, responses: @config.render_error_responses)
72
69
 
73
70
  status = renderable.status_code
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ # @api private
5
+ module Providers
6
+ # Provider source to register routes helper component in Hanami slices.
7
+ #
8
+ # @see Hanami::Slice::RoutesHelper
9
+ #
10
+ # @api private
11
+ # @since 2.0.0
12
+ class Assets < Dry::System::Provider::Source
13
+ # @api private
14
+ def self.for_slice(slice)
15
+ Class.new(self) do |klass|
16
+ klass.instance_variable_set(:@slice, slice)
17
+ end
18
+ end
19
+
20
+ # @api private
21
+ def self.slice
22
+ @slice || Hanami.app
23
+ end
24
+
25
+ # @api private
26
+ def prepare
27
+ require "hanami/assets"
28
+ end
29
+
30
+ # @api private
31
+ def start
32
+ assets = Hanami::Assets.new(config: slice.config.assets)
33
+
34
+ register(:assets, assets)
35
+ end
36
+
37
+ private
38
+
39
+ def slice
40
+ self.class.slice
41
+ end
42
+ end
43
+ end
44
+ end
@@ -10,7 +10,7 @@ Hanami::CLI::RakeTasks.register_tasks do
10
10
 
11
11
  # Ruby ecosystem compatibility
12
12
  #
13
- # Most of the SaaS automatic tasks are designed after Ruby on Rails.
13
+ # Most of the hosting SaaS automatic tasks are designed after Ruby on Rails.
14
14
  # They expect the following Rake tasks to be present:
15
15
  #
16
16
  # * db:migrate
@@ -20,31 +20,32 @@ Hanami::CLI::RakeTasks.register_tasks do
20
20
  #
21
21
  # ===
22
22
  #
23
- # These Rake tasks aren't listed when someone runs `rake -T`, because we
24
- # want to encourage developers to use `hanami` commands.
23
+ # These Rake tasks are **NOT** listed when someone runs `rake -T`, because we
24
+ # want to encourage developers to use `hanami` CLI commands.
25
25
  #
26
- # In order to migrate the database or precompile assets a developer should
27
- # use:
26
+ # In order to migrate the database or compile assets a developer should use:
28
27
  #
29
28
  # * hanami db migrate
30
- # * hanami assets precompile
29
+ # * hanami assets compile
31
30
  #
32
31
  # This is the preferred way to run Hanami command line tasks.
33
32
  # Please use them when you're in control of your deployment environment.
34
33
  #
35
34
  # If you're not in control and your deployment requires these "standard"
36
35
  # Rake tasks, they are here to solve this only specific problem.
37
- namespace :db do
38
- task :migrate do
39
- # TODO(@jodosha): Enable when we'll integrate with ROM
40
- # run_hanami_command("db migrate")
41
- end
42
- end
43
-
44
- namespace :assets do
45
- task :precompile do
46
- # TODO(@jodosha): Enable when we'll integrate with hanami-assets
47
- # run_hanami_command("assets precompile")
36
+ #
37
+ # namespace :db do
38
+ # task :migrate do
39
+ # # TODO(@jodosha): Enable when we'll integrate with ROM
40
+ # # run_hanami_command("db migrate")
41
+ # end
42
+ # end
43
+
44
+ if Hanami.bundled?("hanami-assets")
45
+ namespace :assets do
46
+ task :precompile do
47
+ run_hanami_command("assets compile")
48
+ end
48
49
  end
49
50
  end
50
51
 
@@ -53,7 +54,7 @@ Hanami::CLI::RakeTasks.register_tasks do
53
54
  @_hanami_cli_bundler = Hanami::CLI::Bundler.new
54
55
 
55
56
  def run_hanami_command(command)
56
- @_hanami_cli_bundler.exec(command)
57
+ @_hanami_cli_bundler.hanami_exec(command)
57
58
  end
58
59
  end
59
60
 
@@ -45,7 +45,7 @@ module Hanami
45
45
  #
46
46
  # Setting values are loaded from a configurable store, which defaults to
47
47
  # {Hanami::Settings::EnvStore}, which fetches the values from equivalent upper-cased keys in
48
- # `ENV`. You can configue an alternative store via {Hanami::Config#settings_store}. Setting stores
48
+ # `ENV`. You can configure an alternative store via {Hanami::Config#settings_store}. Setting stores
49
49
  # must implement a `#fetch` method with the same signature as `Hash#fetch`.
50
50
  #
51
51
  # [dry-c]: https://dry-rb.org/gems/dry-configurable/
data/lib/hanami/slice.rb CHANGED
@@ -952,12 +952,21 @@ module Hanami
952
952
  config = self.config
953
953
  rack_monitor = self["rack.monitor"]
954
954
 
955
+ render_errors = render_errors?
956
+ render_detailed_errors = render_detailed_errors?
957
+
958
+ error_handlers = {}.tap do |hsh|
959
+ if render_errors || render_detailed_errors
960
+ hsh[:not_allowed] = ROUTER_NOT_ALLOWED_HANDLER
961
+ hsh[:not_found] = ROUTER_NOT_FOUND_HANDLER
962
+ end
963
+ end
964
+
955
965
  Slice::Router.new(
956
966
  inspector: inspector,
957
967
  routes: routes,
958
968
  resolver: config.router.resolver.new(slice: self),
959
- not_allowed: ROUTER_NOT_ALLOWED_HANDLER,
960
- not_found: ROUTER_NOT_FOUND_HANDLER,
969
+ **error_handlers,
961
970
  **config.router.options
962
971
  ) do
963
972
  use(rack_monitor)
@@ -968,19 +977,31 @@ module Hanami
968
977
  Hanami::Middleware::PublicErrorsApp.new(slice.root.join("public"))
969
978
  )
970
979
 
971
- if config.render_detailed_errors && Hanami.bundled?("hanami-webconsole")
980
+ if render_detailed_errors
972
981
  require "hanami/webconsole"
973
- use(Hanami::Webconsole::Middleware)
982
+ use(Hanami::Webconsole::Middleware, config)
974
983
  end
975
984
 
976
985
  if Hanami.bundled?("hanami-controller") && config.actions.sessions.enabled?
977
986
  use(*config.actions.sessions.middleware)
978
987
  end
979
988
 
989
+ if Hanami.bundled?("hanami-assets") && config.assets.serve
990
+ use(Hanami::Middleware::Assets)
991
+ end
992
+
980
993
  middleware_stack.update(config.middleware_stack)
981
994
  end
982
995
  end
983
996
 
997
+ def render_errors?
998
+ config.render_errors
999
+ end
1000
+
1001
+ def render_detailed_errors?
1002
+ config.render_detailed_errors && Hanami.bundled?("hanami-webconsole")
1003
+ end
1004
+
984
1005
  ROUTER_NOT_ALLOWED_HANDLER = -> env, allowed_http_methods {
985
1006
  raise Hanami::Router::NotAllowedError.new(env, allowed_http_methods)
986
1007
  }.freeze
@@ -7,7 +7,7 @@ module Hanami
7
7
  # @api private
8
8
  module Version
9
9
  # @api public
10
- VERSION = "2.1.0.beta1"
10
+ VERSION = "2.1.0.beta2.1"
11
11
 
12
12
  # @since 0.9.0
13
13
  # @api private
data/lib/hanami.rb CHANGED
@@ -144,8 +144,8 @@ module Hanami
144
144
  #
145
145
  # @api public
146
146
  # @since 2.0.0
147
- def self.env
148
- ENV.fetch("HANAMI_ENV") { ENV.fetch("RACK_ENV", "development") }.to_sym
147
+ def self.env(e: ENV)
148
+ e.fetch("HANAMI_ENV") { e.fetch("RACK_ENV", "development") }.to_sym
149
149
  end
150
150
 
151
151
  # Returns true if {.env} matches any of the given names
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack/test"
4
+ require "stringio"
5
+
6
+ RSpec.describe "Assets", :app_integration do
7
+ include Rack::Test::Methods
8
+ let(:app) { Hanami.app }
9
+ let(:root) { make_tmp_directory }
10
+
11
+ before do
12
+ with_directory(root) do
13
+ write "config/app.rb", <<~RUBY
14
+ module TestApp
15
+ class App < Hanami::App
16
+ config.logger.stream = StringIO.new
17
+ end
18
+ end
19
+ RUBY
20
+
21
+ write "config/routes.rb", <<~RUBY
22
+ module TestApp
23
+ class Routes < Hanami::Routes
24
+ get "posts/:id/edit", to: "posts.edit"
25
+ put "posts/:id", to: "posts.update"
26
+ end
27
+ end
28
+ RUBY
29
+
30
+ write "app/action.rb", <<~RUBY
31
+ # auto_register: false
32
+
33
+ require "hanami/action"
34
+
35
+ module TestApp
36
+ class Action < Hanami::Action
37
+ end
38
+ end
39
+ RUBY
40
+
41
+ write "app/view.rb", <<~RUBY
42
+ # auto_register: false
43
+
44
+ require "hanami/view"
45
+
46
+ module TestApp
47
+ class View < Hanami::View
48
+ config.layout = nil
49
+ end
50
+ end
51
+ RUBY
52
+
53
+ write "app/views/posts/show.rb", <<~RUBY
54
+ module TestApp
55
+ module Views
56
+ module Posts
57
+ class Show < TestApp::View
58
+ end
59
+ end
60
+ end
61
+ end
62
+ RUBY
63
+
64
+ write "app/templates/posts/show.html.erb", <<~ERB
65
+ <%= stylesheet_link_tag("app") %>
66
+ <%= css("app") %>
67
+ <%= javascript_tag("app") %>
68
+ <%= js("app") %>
69
+ ERB
70
+
71
+ write "app/assets/js/app.ts", <<~TS
72
+ import "../css/app.css";
73
+
74
+ console.log("Hello from index.ts");
75
+ TS
76
+
77
+ write "app/assets/css/app.css", <<~CSS
78
+ .btn {
79
+ background: #f00;
80
+ }
81
+ CSS
82
+
83
+ before_prepare if respond_to?(:before_prepare)
84
+ require "hanami/prepare"
85
+ end
86
+ end
87
+
88
+ specify "assets are available in helpers and in `assets` component" do
89
+ compile_assets!
90
+
91
+ output = Hanami.app["views.posts.show"].call.to_s
92
+
93
+ expect(output).to match(%r{<link href="/assets/app-[A-Z0-9]{8}.css" type="text/css" rel="stylesheet">})
94
+ expect(output).to match(%r{<script src="/assets/app-[A-Z0-9]{8}.js" type="text/javascript"></script>})
95
+
96
+ assets = Hanami.app["assets"]
97
+
98
+ expect(assets["app.css"].to_s).to match(%r{/assets/app-[A-Z0-9]{8}.css})
99
+ expect(assets["app.js"].to_s).to match(%r{/assets/app-[A-Z0-9]{8}.js})
100
+ end
101
+ end
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack/test"
4
+ require "stringio"
5
+
6
+ RSpec.describe "Serve Static Assets", :app_integration do
7
+ include Rack::Test::Methods
8
+ let(:app) { Hanami.app }
9
+ let(:root) { make_tmp_directory }
10
+ let!(:env) { ENV.to_h }
11
+
12
+ before do
13
+ with_directory(root) do
14
+ write "config/app.rb", <<~RUBY
15
+ module TestApp
16
+ class App < Hanami::App
17
+ config.logger.stream = StringIO.new
18
+ end
19
+ end
20
+ RUBY
21
+
22
+ write "config/routes.rb", <<~RUBY
23
+ module TestApp
24
+ class Routes < Hanami::Routes
25
+ root to: ->(env) { [200, {}, ["Hello from root"]] }
26
+ end
27
+ end
28
+ RUBY
29
+
30
+ write "public/assets/app.js", <<~JS
31
+ console.log("Hello from app.js");
32
+ JS
33
+ end
34
+ end
35
+
36
+ after do
37
+ ENV.replace(env)
38
+ end
39
+
40
+ context "with default configuration" do
41
+ before do
42
+ with_directory(root) do
43
+ require "hanami/prepare"
44
+ end
45
+ end
46
+
47
+ it "serves static assets" do
48
+ get "/assets/app.js"
49
+
50
+ expect(last_response.status).to eq(200)
51
+ expect(last_response.body).to match(/Hello/)
52
+ end
53
+
54
+ it "returns 404 for missing asset" do
55
+ get "/assets/missing.js"
56
+
57
+ expect(last_response.status).to eq(404)
58
+ expect(last_response.body).to match(/Not Found/i)
59
+ end
60
+
61
+ it "doesn't escape from root directory" do
62
+ get "/assets/../../config/app.rb"
63
+
64
+ expect(last_response.status).to eq(404)
65
+ expect(last_response.body).to match(/Not Found/i)
66
+ end
67
+ end
68
+
69
+ context "when configuration is set to false" do
70
+ before do
71
+ with_directory(root) do
72
+ write "config/app.rb", <<~RUBY
73
+ module TestApp
74
+ class App < Hanami::App
75
+ config.logger.stream = StringIO.new
76
+ config.assets.serve = false
77
+ end
78
+ end
79
+ RUBY
80
+
81
+ require "hanami/boot"
82
+ end
83
+ end
84
+
85
+ it "doesn't serve static assets" do
86
+ get "/assets/app.js"
87
+
88
+ expect(last_response.status).to eq(404)
89
+ end
90
+ end
91
+
92
+ context "when env var is set to true" do
93
+ before do
94
+ with_directory(root) do
95
+ ENV["HANAMI_SERVE_ASSETS"] = "true"
96
+ require "hanami/boot"
97
+ end
98
+ end
99
+
100
+ it "serves static assets" do
101
+ get "/assets/app.js"
102
+
103
+ expect(last_response.status).to eq(200)
104
+ end
105
+ end
106
+
107
+ context "when env var is set to false" do
108
+ before do
109
+ with_directory(root) do
110
+ ENV["HANAMI_SERVE_ASSETS"] = "false"
111
+ require "hanami/boot"
112
+ end
113
+ end
114
+
115
+ it "doesn't serve static assets" do
116
+ get "/assets/app.js"
117
+
118
+ expect(last_response.status).to eq(404)
119
+ end
120
+ end
121
+
122
+ context "when Hanami.env is not :development or :test" do
123
+ before do
124
+ with_directory(root) do
125
+ ENV["HANAMI_ENV"] = "production"
126
+ require "hanami/boot"
127
+ end
128
+ end
129
+
130
+ it "doesn't serve static assets" do
131
+ get "/assets/app.js"
132
+
133
+ expect(last_response.status).to eq(404)
134
+ end
135
+ end
136
+
137
+ context "when Hanami.env is not :development or :test, but env var is set to true" do
138
+ before do
139
+ with_directory(root) do
140
+ ENV["HANAMI_ENV"] = "production"
141
+ ENV["HANAMI_SERVE_ASSETS"] = "true"
142
+ require "hanami/boot"
143
+ end
144
+ end
145
+
146
+ it "serves static assets" do
147
+ get "/assets/app.js"
148
+
149
+ expect(last_response.status).to eq(200)
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack/test"
4
+ require "stringio"
5
+
6
+ RSpec.describe "Logging / Exception logging", :app_integration do
7
+ include Rack::Test::Methods
8
+
9
+ let(:app) { Hanami.app }
10
+
11
+ let(:logger_stream) { StringIO.new }
12
+
13
+ def configure_logger
14
+ Hanami.app.config.logger.stream = logger_stream
15
+ end
16
+
17
+ def logs
18
+ @logs ||= (logger_stream.rewind and logger_stream.read)
19
+ end
20
+
21
+ before do
22
+ with_directory(make_tmp_directory) do
23
+ write "config/app.rb", <<~RUBY
24
+ module TestApp
25
+ class App < Hanami::App
26
+ # Disable framework-level error rendering so we can test the raw action behavior
27
+ config.render_errors = false
28
+ config.render_detailed_errors = false
29
+ end
30
+ end
31
+ RUBY
32
+
33
+ write "config/routes.rb", <<~RUBY
34
+ module TestApp
35
+ class Routes < Hanami::Routes
36
+ root to: "test"
37
+ end
38
+ end
39
+ RUBY
40
+
41
+ require "hanami/setup"
42
+ configure_logger
43
+
44
+ before_prepare if respond_to?(:before_prepare)
45
+ require "hanami/prepare"
46
+ end
47
+ end
48
+
49
+ describe "unhandled exceptions" do
50
+ def before_prepare
51
+ write "app/actions/test.rb", <<~RUBY
52
+ module TestApp
53
+ module Actions
54
+ class Test < Hanami::Action
55
+ UnhandledError = Class.new(StandardError)
56
+
57
+ def handle(request, response)
58
+ raise UnhandledError, "unhandled"
59
+ end
60
+ end
61
+ end
62
+ end
63
+ RUBY
64
+ end
65
+
66
+ it "logs a 500 error and full exception details when an exception is raised" do
67
+ # Make the request with a rescue so the raised exception doesn't crash the tests
68
+ begin
69
+ get "/"
70
+ rescue TestApp::Actions::Test::UnhandledError # rubocop:disable Lint/SuppressedException
71
+ end
72
+
73
+ expect(logs.lines.length).to be > 10
74
+ expect(logs).to match %r{GET 500 \d+(µs|ms) 127.0.0.1 /}
75
+ expect(logs).to include("unhandled (TestApp::Actions::Test::UnhandledError)")
76
+ expect(logs).to include("app/actions/test.rb:7:in `handle'")
77
+ end
78
+
79
+ it "re-raises the exception" do
80
+ expect { get "/" }.to raise_error(TestApp::Actions::Test::UnhandledError)
81
+ end
82
+ end
83
+
84
+ describe "errors handled by handle_exception" do
85
+ def before_prepare
86
+ write "app/actions/test.rb", <<~RUBY
87
+ module TestApp
88
+ module Actions
89
+ class Test < Hanami::Action
90
+ NotFoundError = Class.new(StandardError)
91
+
92
+ handle_exception NotFoundError => :handle_not_found_error
93
+
94
+ def handle(request, response)
95
+ raise NotFoundError
96
+ end
97
+
98
+ private
99
+
100
+ def handle_not_found_error(request, response, exception)
101
+ halt 404
102
+ end
103
+ end
104
+ end
105
+ end
106
+ RUBY
107
+ end
108
+
109
+ it "does not log an error" do
110
+ get "/"
111
+
112
+ expect(logs).to match %r{GET 404 \d+(µs|ms) 127.0.0.1 /}
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack/test"
4
+
5
+ RSpec.describe "Logging / Notifications", :app_integration do
6
+ include Rack::Test::Methods
7
+
8
+ let(:app) { Hanami.app }
9
+
10
+ specify "Request logging continues even when notifications bus has already been used" do
11
+ dir = Dir.mktmpdir
12
+
13
+ with_tmp_directory(dir) do
14
+ write "config/app.rb", <<~RUBY
15
+ require "hanami"
16
+
17
+ module TestApp
18
+ class App < Hanami::App
19
+ config.actions.format :json
20
+ config.logger.options = {colorize: true}
21
+ config.logger.stream = config.root.join("test.log")
22
+ end
23
+ end
24
+ RUBY
25
+
26
+ write "config/routes.rb", <<~RUBY
27
+ module TestApp
28
+ class Routes < Hanami::Routes
29
+ post "/users", to: "users.create"
30
+ end
31
+ end
32
+ RUBY
33
+
34
+ write "app/actions/users/create.rb", <<~RUBY
35
+ module TestApp
36
+ module Actions
37
+ module Users
38
+ class Create < Hanami::Action
39
+ def handle(req, resp)
40
+ resp.body = req.params.to_h.keys
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ RUBY
47
+
48
+ require "hanami/prepare"
49
+
50
+ # Simulate any component interacting with the notifications bus such that it creates its
51
+ # internal bus with a duplicate copy of all currently registered events. This means that the
52
+ # class-level Dry::Monitor::Notification events implicitly registered by the
53
+ # Dry::Monitor::Rack::Middleware activated via the rack provider are ignored, unless our
54
+ # provider explicitly re-registers them on _instance_ of the notifications bus.
55
+ #
56
+ # See Hanami::Providers::Rack for more detail.
57
+ Hanami.app["notifications"].instrument(:sql)
58
+
59
+ logs = -> { Pathname(dir).join("test.log").realpath.read }
60
+
61
+ post "/users", JSON.generate(name: "jane", password: "secret"), {"CONTENT_TYPE" => "application/json"}
62
+ expect(logs.()).to match %r{POST 200 \d+(µs|ms) 127.0.0.1 /}
63
+ end
64
+ end
65
+ end
66
+
67
+
68
+