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 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
|
# frozen_string_literal: true
RSpec.describe Faraday::Response::Json, type: :response do
let(:options) { {} }
let(:headers) { {} }
let(:middleware) do
described_class.new(lambda { |env|
Faraday::Response.new(env)
}, **options)
end
def process(body, content_type = 'application/json', options = {})
env = {
body: body, request: options,
request_headers: Faraday::Utils::Headers.new,
response_headers: Faraday::Utils::Headers.new(headers)
}
env[:response_headers]['content-type'] = content_type if content_type
yield(env) if block_given?
middleware.call(Faraday::Env.from(env))
end
context 'no type matching' do
it "doesn't change nil body" do
expect(process(nil).body).to be_nil
end
it 'nullifies empty body' do
expect(process('').body).to be_nil
end
it 'parses json body' do
response = process('{"a":1}')
expect(response.body).to eq('a' => 1)
expect(response.env[:raw_body]).to be_nil
end
end
context 'with preserving raw' do
let(:options) { { preserve_raw: true } }
it 'parses json body' do
response = process('{"a":1}')
expect(response.body).to eq('a' => 1)
expect(response.env[:raw_body]).to eq('{"a":1}')
end
end
context 'with default regexp type matching' do
it 'parses json body of correct type' do
response = process('{"a":1}', 'application/x-json')
expect(response.body).to eq('a' => 1)
end
it 'ignores json body of incorrect type' do
response = process('{"a":1}', 'text/json-xml')
expect(response.body).to eq('{"a":1}')
end
end
context 'with array type matching' do
let(:options) { { content_type: %w[a/b c/d] } }
it 'parses json body of correct type' do
expect(process('{"a":1}', 'a/b').body).to be_a(Hash)
expect(process('{"a":1}', 'c/d').body).to be_a(Hash)
end
it 'ignores json body of incorrect type' do
expect(process('{"a":1}', 'a/d').body).not_to be_a(Hash)
end
end
it 'chokes on invalid json' do
expect { process('{!') }.to raise_error(Faraday::ParsingError)
end
it 'includes the response on the ParsingError instance' do
process('{') { |env| env[:response] = Faraday::Response.new }
raise 'Parsing should have failed.'
rescue Faraday::ParsingError => e
expect(e.response).to be_a(Faraday::Response)
end
context 'HEAD responses' do
it "nullifies the body if it's only one space" do
response = process(' ')
expect(response.body).to be_nil
end
it "nullifies the body if it's two spaces" do
response = process(' ')
expect(response.body).to be_nil
end
end
context 'JSON options' do
let(:body) { '{"a": 1}' }
let(:result) { { a: 1 } }
let(:options) do
{
parser_options: {
symbolize_names: true
}
}
end
it 'passes relevant options to JSON parse' do
expect(::JSON).to receive(:parse)
.with(body, options[:parser_options])
.and_return(result)
response = process(body)
expect(response.body).to eq(result)
end
end
context 'with decoder' do
let(:decoder) do
double('Decoder').tap do |e|
allow(e).to receive(:load) { |s, opts| JSON.parse(s, opts) }
end
end
let(:body) { '{"a": 1}' }
let(:result) { { a: 1 } }
context 'when decoder is passed as object' do
let(:options) do
{
parser_options: {
decoder: decoder,
option: :option_value,
symbolize_names: true
}
}
end
it 'passes relevant options to specified decoder\'s load method' do
expect(decoder).to receive(:load)
.with(body, { option: :option_value, symbolize_names: true })
.and_return(result)
response = process(body)
expect(response.body).to eq(result)
end
end
context 'when decoder is passed as an object-method pair' do
let(:options) do
{
parser_options: {
decoder: [decoder, :load],
option: :option_value,
symbolize_names: true
}
}
end
it 'passes relevant options to specified decoder\'s method' do
expect(decoder).to receive(:load)
.with(body, { option: :option_value, symbolize_names: true })
.and_return(result)
response = process(body)
expect(response.body).to eq(result)
end
end
context 'when decoder is not passed' do
let(:options) do
{
parser_options: {
symbolize_names: true
}
}
end
it 'passes relevant options to JSON parse' do
expect(JSON).to receive(:parse)
.with(body, { symbolize_names: true })
.and_return(result)
response = process(body)
expect(response.body).to eq(result)
end
it 'passes relevant options to JSON parse even when nil responds to :load' do
original_allow_message_expectations_on_nil = RSpec::Mocks.configuration.allow_message_expectations_on_nil
RSpec::Mocks.configuration.allow_message_expectations_on_nil = true
allow(nil).to receive(:respond_to?)
.with(:load)
.and_return(true)
expect(JSON).to receive(:parse)
.with(body, { symbolize_names: true })
.and_return(result)
response = process(body)
expect(response.body).to eq(result)
ensure
RSpec::Mocks.configuration.allow_message_expectations_on_nil = original_allow_message_expectations_on_nil
end
end
end
end
|