Skip to content

Commit

Permalink
Support dynamic paths in response lookup (#19)
Browse files Browse the repository at this point in the history
  • Loading branch information
mkon authored Sep 15, 2022
1 parent 42b4aa6 commit b7cf5f0
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 5 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,19 @@ it 'responds with 200 and matches the doc' do
}
```

### Options

The `match_openapi_doc($doc)` method allows passing options as a 2nd argument.
This allows overriding the default request.path lookup in case this does not find
the correct response definition in your schema. This is especially important with
dynamic paths.

Example:

```ruby
it { is_expected.to match_openapi_doc($api_doc, path: '/messages/{id}').with_http_status(:ok) }
```

### How it works

It uses the `request.path`, `request.method`, `status` and `headers` on the test subject (which must be the response) to find the response schema in the OpenAPI document. Then it does the following checks:
Expand Down
4 changes: 2 additions & 2 deletions lib/openapi_contracts/matchers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ module OpenapiContracts
module Matchers
autoload :MatchOpenapiDoc, 'openapi_contracts/matchers/match_openapi_doc'

def match_openapi_doc(doc)
MatchOpenapiDoc.new(doc)
def match_openapi_doc(doc, options = {})
MatchOpenapiDoc.new(doc, options)
end
end
end
5 changes: 3 additions & 2 deletions lib/openapi_contracts/matchers/match_openapi_doc.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
module OpenapiContracts
module Matchers
class MatchOpenapiDoc
def initialize(doc)
def initialize(doc, options)
@doc = doc
@options = options
@errors = []
end

Expand Down Expand Up @@ -43,7 +44,7 @@ def response_desc

def response_spec
@response_spec ||= @doc.response_for(
@response.request.path,
@options.fetch(:path, @response.request.path),
@response.request.request_method.downcase,
@response.status.to_s
)
Expand Down
20 changes: 20 additions & 0 deletions spec/fixtures/openapi/components/schemas/Message.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
type: object
properties:
id:
type: string
example: acd3751
type:
type: string
attributes:
type: object
properties:
body:
type: string
additionalProperties: false
required:
- body
additionalProperties: false
required:
- id
- type
- attributes
2 changes: 2 additions & 0 deletions spec/fixtures/openapi/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,7 @@ paths:
$ref: components/responses/BadRequest.yaml
'500':
description: Server Error
/messages/{id}:
$ref: 'paths/message.yaml'
/user:
$ref: 'paths/user.yaml'
33 changes: 33 additions & 0 deletions spec/fixtures/openapi/paths/message.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
get:
tags:
- Message
summary: Get Message
description: Get Message
operationId: get_message
parameters:
- name: id
in: path
description: Id of the message.
required: true
schema:
type: string
responses:
'200':
description: OK
headers:
x-request-id:
schema:
type: string
required: true
content:
application/json:
schema:
type: object
properties:
data:
$ref: ../components/schemas/Message.yaml
required:
- data
additionalProperties: false
'400':
$ref: ../components/responses/BadRequest.yaml
23 changes: 23 additions & 0 deletions spec/integration/rspec_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,29 @@
it { is_expected.to match_openapi_doc(doc).with_http_status(:bad_request) }
end

context 'when using dynamic paths' do
let(:request_env) do
{
'PATH_INFO' => '/messages/ef278',
'REQUEST_METHOD' => 'GET'
}
end
let(:response_status) { 200 }
let(:response_body) do
{
data: {
id: '1ef',
type: 'messages',
attributes: {
body: 'foo'
}
}
}
end

it { is_expected.to match_openapi_doc(doc, path: '/messages/{id}').with_http_status(:ok) }
end

context 'when a required header is missing' do
before { response_headers.delete('X-Request-Id') }

Expand Down
2 changes: 1 addition & 1 deletion spec/openapi_contracts/matchers/match_openapi_doc_spec.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
RSpec.describe OpenapiContracts::Matchers::MatchOpenapiDoc do
subject(:matcher) { described_class.new(doc) }
subject(:matcher) { described_class.new(doc, {}) }

let(:doc) { OpenapiContracts::Doc.parse(FIXTURES_PATH.join('openapi')) }

Expand Down

0 comments on commit b7cf5f0

Please sign in to comment.