1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
|
# frozen_string_literal: true
module ActiveModelSerializers
module Test
module Schema
# A Minitest Assertion that test the response is valid against a schema.
# @param schema_path [String] a custom schema path
# @param message [String] a custom error message
# @return [Boolean] true when the response is valid
# @return [Minitest::Assertion] when the response is invalid
# @example
# get :index
# assert_response_schema
def assert_response_schema(schema_path = nil, message = nil)
matcher = AssertResponseSchema.new(schema_path, request, response, message)
assert(matcher.call, matcher.message)
end
def assert_request_schema(schema_path = nil, message = nil)
matcher = AssertRequestSchema.new(schema_path, request, response, message)
assert(matcher.call, matcher.message)
end
# May be renamed
def assert_request_response_schema(schema_path = nil, message = nil)
assert_request_schema(schema_path, message)
assert_response_schema(schema_path, message)
end
def assert_schema(payload, schema_path = nil, message = nil)
matcher = AssertSchema.new(schema_path, request, response, message, payload)
assert(matcher.call, matcher.message)
end
MissingSchema = Class.new(Minitest::Assertion)
InvalidSchemaError = Class.new(Minitest::Assertion)
class AssertSchema
attr_reader :schema_path, :request, :response, :message, :payload
# Interface may change.
def initialize(schema_path, request, response, message, payload = nil)
require_json_schema!
@request = request
@response = response
@payload = payload
@schema_path = schema_path || schema_path_default
@message = message
@document_store = JsonSchema::DocumentStore.new
add_schema_to_document_store
end
def call
json_schema.expand_references!(store: document_store)
status, errors = json_schema.validate(response_body)
@message = [message, errors.map(&:to_s).to_sentence].compact.join(': ')
status
end
protected
attr_reader :document_store
def controller_path
request.filtered_parameters.with_indifferent_access[:controller]
end
def action
request.filtered_parameters.with_indifferent_access[:action]
end
def schema_directory
ActiveModelSerializers.config.schema_path
end
def schema_full_path
"#{schema_directory}/#{schema_path}"
end
def schema_path_default
"#{controller_path}/#{action}.json"
end
def schema_data
load_json_file(schema_full_path)
end
def response_body
load_json(response.body)
end
def request_params
request.env['action_dispatch.request.request_parameters']
end
def json_schema
@json_schema ||= JsonSchema.parse!(schema_data)
end
def add_schema_to_document_store
Dir.glob("#{schema_directory}/**/*.json").each do |path|
schema_data = load_json_file(path)
extra_schema = JsonSchema.parse!(schema_data)
document_store.add_schema(extra_schema)
end
end
def load_json(json)
JSON.parse(json)
rescue JSON::ParserError => ex
raise InvalidSchemaError, ex.message
end
def load_json_file(path)
load_json(File.read(path))
rescue Errno::ENOENT
raise MissingSchema, "No Schema file at #{schema_full_path}"
end
def require_json_schema!
require 'json_schema'
rescue LoadError
raise LoadError, "You don't have json_schema installed in your application. Please add it to your Gemfile and run bundle install"
end
end
class AssertResponseSchema < AssertSchema
def initialize(*)
super
@payload = response_body
end
end
class AssertRequestSchema < AssertSchema
def initialize(*)
super
@payload = request_params
end
end
end
end
end
|