-
Notifications
You must be signed in to change notification settings - Fork 22
Add Support for Rails Engines in Tests #412
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Hi @ahx - I'm glad you caught that but this did not fix the issue.
You need to run |
|
@ahx once you have run So mounting an engine has broken the test helper because
And you will see this error when you run But if you comment out all of the
And then run And if you drop a debugger in the the test and run |
|
Thanks for the explaination @orionstudt . I'll have another look. |
|
@ahx Alright I found the problem and have a couple options for solutions. I will describe the problem here and then amend this PR as a fix, and start discussions in the places where we can choose which solution is best. The issue is that the def create_session(app)
klass = APP_SESSIONS[app] ||= Class.new(Integration::Session) {
# If the app is a Rails app, make url_helpers available on the session. This
# makes app.url_for and app.foo_path available in the console.
if app.respond_to?(:routes) && app.routes.is_a?(ActionDispatch::Routing::RouteSet)
include app.routes.url_helpers
include app.routes.mounted_helpers
end
}
klass.new(app)
endOur wrapper of the |
| require_relative '../config/environment' | ||
|
|
||
| require 'openapi_first' | ||
| OpenapiFirst::Test.setup do |test| | ||
| test.register Rails.root.join('../../spec/data/train-travel-api/openapi.yaml') | ||
| test.register Rails.root.join('../../spec/data/attachments_openapi.yaml'), as: :attachments | ||
| test.register Rails.root.join('../../spec/data/weather_openapi.yaml'), as: :weather | ||
| test.coverage_formatter_options = { verbose: true } | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ahx This is probably a breaking change. Because of the Rails.root undefined issue the require_relative '../config/environment call now needs to be before any openapi_first test setup.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But anybody that mounted an engine at any point would have encountered that bug and needed to change their order.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, to be clear, nothing will be broken for anyone until they mount an engine. They just will hit the error as soon as they do, and need to change the order here.
| # Option 2 - delegate all missing methods to inner app | ||
| def respond_to_missing?(name, include_private = false) | ||
| app.respond_to?(name, include_private) | ||
| end | ||
|
|
||
| def method_missing(method, *, &) | ||
| app.send(method, *, &) | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ahx This is the potentially more future-proof option - we are forwarding all missing methods to the inner app, in case there is any other missing things that rails relies on that our outer app wrapper is obscuring.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to very rails specific. I am wondering if overwriting "app" is even that useful for rails' integration tests. It just works for basic rack-test tests, but when using rails there seems to be more that is needed to make it work.
I don't think this issue should be solved in the middlewares, but probably inside openapi_first/test. Or maybe in a dedicated openapi_first/rails. What do you think about that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that makes sense. We can go with Option #1 for now since it is in openapi_first/test?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Let's do that.
lib/openapi_first/test.rb
Outdated
| # Option 1 - only delegate routes | ||
| # wrapper.define_singleton_method(:routes) { app.routes } if app.respond_to?(:routes) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ahx And this would be a more targeted option, where we delegate only the routes method that we need for this specific case on our wrapper in order to trigger rails to define those helper methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had a quick look at the rails parts, but did not figure out why the integration tests (ActionDispatch::IntegrationTest) need to call app.routes. But that is not important. What I have learned from this is that just overwriting app with a new proc (or plain rack app) is not the way to go here.
Maybe there is some kind of hook? Something like a before, after request, where we can keep app just what it is and focus on the requests/responses in these tests?
I guess this needs a dedicated rails (railtie?) integration, but I am no longer familiar with rails, as we are all in on plain rack and sinatra. So I would need some opinions about this and probably some more help.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rails adds methods to test classes for url_for helpers and mounted rack apps (engines). Since app.routes was not returning a RouteSet due to this lib, it didn't have the information it needs to define those helpers.
I'm open to contributing more as I work on integrating this library with our Rails app. I am leaning toward your library for configurability instead of committee because of recent activity and because it supports things like polymorphic discriminators and the latest version of OpenAPI. But I'd like to get a quick fix in for this (like this Option 1 snippet here) so that I can move forward.
I don't think a rails-specific integration is a bad idea - I think there are other issues here. For instance, now that the errors are fixed try running rails test engines/weather again. You will get a Open API spec failure that says that the weather controller is not returning an object at root, even though it is. I think this is because rails is returning a serialized JSON String instead of a Hash.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did explain why Rails is calling app.routes and link the exact code where it does so here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the work on this. The lacking OAS / JSON Schema support of committee was one motivation to start this project. Option 1 sounds like a good start. A PR for that would be appreciated.
692a518 to
92d09c1
Compare
|
Hi. This seems to work now. I have forced-pushed a few things. This looks okay from my point of view. What do you think? |
I like the SimpleDelegator. Don't forget to remove the Option 2 methods I added to the middlewares. I started messing with a custom railtie last night. I'll open a separate PR if I get something that works nicely and is configurable. I'm thinking that if the rails app is configured with the middlewares as an initializer than we probably won't need to use the app wrapper for rails at all, and might want a separate test class for rails. But I think this is good for now and will unblock me! Any thoughts about the String vs Hash spec error I was hitting? |
92d09c1 to
fee9399
Compare
I will create a follow-up about this here: ahx#418
Yes. I am preparing an issue about right now and would love to get feedback: #418 Tldr; Currently you have to define a response content-type to fix that. |
ahx
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @orionstudt for the great collaboration. 🚀
Thank you! I'll report back soon about a railtie. |
|
@ahx Hello again! I wanted to come back and give an update. Underdog is now successfully using I did not end up needing to setup a Railtie - we are not actually using the middleware. We decided, for now, to not run this validation at runtime and so did not really have a need for the middleware. We are currently only using it during testing. If we have a need for it I will return to it and give it a look. I did however have to extend I'm optimistic that these features aren't too esoteric and that they generally make sense for your API - and we can of course iterate on them together. I'll link them here: |


Add Support for Rails Engines in Test
See full rundown of issue described here.
This PR will:
weatherto therails_appexample for the purpose of testingappwrapper delegates appropriate methods to rails such that rails knows to setup routing helpers for mounted engines