Commit 34b49aec authored by Sean McGivern's avatar Sean McGivern

Remove duplicate JSON schema matcher

We had three JSON schema matchers:

1. `match_schema` - takes a data structure and a path to a schema file.
2. `match_response_schema` - takes a response object and a path to a
   schema file.
3. `according_to_schema` - chained matcher on `be_valid_json`, takes a
   schema literal.

The reason for this is that we wanted to operate on various axes:

1. Are we validating a parsed data structure, or a potentially-JSON
   string?
2. Are we providing a path to a schema file, or a literal data structure
   representing a schema?

Happily, the library we're using already solves problem 1 for us.
https://github.com/ruby-json-schema/json-schema#validation says:

> All methods take two arguments, which can be either a JSON string, a
> file containing JSON, or a Ruby object representing JSON data.

So we just needed to solve concern 2, and we handle that by assuming
that if the schema argument is a string, it's not a JSON string
representing the schema, but a path to a schema file.

To handle the chaining that `according_to_schema` had, we can just use
RSpec's `and` method:

    be_valid_json.and match_schema(build_info_payload_schema)
parent 71c1db7a
...@@ -842,6 +842,41 @@ Example: ...@@ -842,6 +842,41 @@ Example:
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
``` ```
#### `match_schema` and `match_response_schema`
The `match_schema` matcher allows validating that the subject matches a
[JSON schema](https://json-schema.org/). The item inside `expect` can be
a JSON string or a JSON-compatible data structure.
`match_response_schema` is a convenience matcher for using with a
response object. from a [request
spec](testing_levels.md#integration-tests).
Examples:
```ruby
# Matches against spec/fixtures/api/schemas/prometheus/additional_metrics_query_result.json
expect(data).to match_schema('prometheus/additional_metrics_query_result')
# Matches against ee/spec/fixtures/api/schemas/board.json
expect(data).to match_schema('board', dir: 'ee')
# Matches against a schema made up of Ruby data structures
expect(data).to match_schema(Atlassian::Schemata.build_info)
```
#### `be_valid_json`
`be_valid_json` allows validating that a string parses as JSON and gives
a non-empty result. To combine it with the schema matching above, use
`and`:
```ruby
expect(json_string).to be_valid_json
expect(json_string).to be_valid_json.and match_schema(schema)
```
### Testing query performance ### Testing query performance
Testing query performance allows us to: Testing query performance allows us to:
......
...@@ -114,7 +114,7 @@ RSpec.describe Atlassian::JiraConnect::Client do ...@@ -114,7 +114,7 @@ RSpec.describe Atlassian::JiraConnect::Client do
end end
let(:body) do let(:body) do
matcher = be_valid_json.according_to_schema(schema) matcher = be_valid_json.and match_schema(schema)
->(text) { matcher.matches?(text) } ->(text) { matcher.matches?(text) }
end end
...@@ -164,7 +164,7 @@ RSpec.describe Atlassian::JiraConnect::Client do ...@@ -164,7 +164,7 @@ RSpec.describe Atlassian::JiraConnect::Client do
end end
let(:body) do let(:body) do
matcher = be_valid_json.according_to_schema(build_info_payload_schema) matcher = be_valid_json.and match_schema(build_info_payload_schema)
->(text) { matcher.matches?(text) } ->(text) { matcher.matches?(text) }
end end
......
...@@ -23,7 +23,7 @@ RSpec.describe Atlassian::JiraConnect::Serializers::BuildEntity do ...@@ -23,7 +23,7 @@ RSpec.describe Atlassian::JiraConnect::Serializers::BuildEntity do
end end
it 'is invalid, since it has no issue keys' do it 'is invalid, since it has no issue keys' do
expect(subject.to_json).not_to be_valid_json.according_to_schema(Atlassian::Schemata.build_info) expect(subject.to_json).not_to match_schema(Atlassian::Schemata.build_info)
end end
end end
end end
...@@ -43,7 +43,7 @@ RSpec.describe Atlassian::JiraConnect::Serializers::BuildEntity do ...@@ -43,7 +43,7 @@ RSpec.describe Atlassian::JiraConnect::Serializers::BuildEntity do
describe '#to_json' do describe '#to_json' do
it 'is valid according to the build info schema' do it 'is valid according to the build info schema' do
expect(subject.to_json).to be_valid_json.according_to_schema(Atlassian::Schemata.build_info) expect(subject.to_json).to be_valid_json.and match_schema(Atlassian::Schemata.build_info)
end end
end end
end end
......
...@@ -23,7 +23,7 @@ RSpec.describe Atlassian::JiraConnect::Serializers::DeploymentEntity do ...@@ -23,7 +23,7 @@ RSpec.describe Atlassian::JiraConnect::Serializers::DeploymentEntity do
end end
it 'is invalid, since it has no issue keys' do it 'is invalid, since it has no issue keys' do
expect(subject.to_json).not_to be_valid_json.according_to_schema(Atlassian::Schemata.deployment_info) expect(subject.to_json).not_to match_schema(Atlassian::Schemata.deployment_info)
end end
end end
end end
...@@ -86,7 +86,7 @@ RSpec.describe Atlassian::JiraConnect::Serializers::DeploymentEntity do ...@@ -86,7 +86,7 @@ RSpec.describe Atlassian::JiraConnect::Serializers::DeploymentEntity do
describe '#to_json' do describe '#to_json' do
it 'is valid according to the deployment info schema' do it 'is valid according to the deployment info schema' do
expect(subject.to_json).to be_valid_json.according_to_schema(Atlassian::Schemata.deployment_info) expect(subject.to_json).to be_valid_json.and match_schema(Atlassian::Schemata.deployment_info)
end end
end end
end end
......
# frozen_string_literal: true # frozen_string_literal: true
RSpec::Matchers.define :be_valid_json do RSpec::Matchers.define :be_valid_json do
def according_to_schema(schema)
@schema = schema
self
end
match do |actual| match do |actual|
data = Gitlab::Json.parse(actual) Gitlab::Json.parse(actual).present?
if @schema.present?
@validation_errors = JSON::Validator.fully_validate(@schema, data)
@validation_errors.empty?
else
data.present?
end
rescue JSON::ParserError => e rescue JSON::ParserError => e
@error = e @error = e
false false
...@@ -23,8 +11,6 @@ RSpec::Matchers.define :be_valid_json do ...@@ -23,8 +11,6 @@ RSpec::Matchers.define :be_valid_json do
def failure_message def failure_message
if @error if @error
"Parse failed with error: #{@error}" "Parse failed with error: #{@error}"
elsif @validation_errors.present?
"Validation failed because #{@validation_errors.join(', and ')}"
else else
"Parsing did not return any data" "Parsing did not return any data"
end end
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
module SchemaPath module SchemaPath
def self.expand(schema, dir = nil) def self.expand(schema, dir = nil)
return schema unless schema.is_a?(String)
if Gitlab.ee? && dir.nil? if Gitlab.ee? && dir.nil?
ee_path = expand(schema, 'ee') ee_path = expand(schema, 'ee')
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment