require 'spec_helper' describe Morpheus::ResponseParser do let(:owner) { Dog } let(:response) { mock(:body => '{ "content": { "foo": "bar" }, "errors": [{ "foo": "cannot be \"bar\"" }] }') } let(:request) { req = mock(:response => response, :url => '/dog/1', :params => { :id => 1 }); req.stub(:method).and_return(:get); req } let(:metadata) { {} } let(:parsed_response) { mock } let(:parser) { Morpheus::ResponseParser.new(owner, request, metadata) } describe '.parse' do before do parser Morpheus::ResponseParser.stub(:new).and_return(parser) parser.stub(:parse).and_return(parsed_response) end it 'creates a new ResponseParser' do Morpheus::ResponseParser.should_receive(:new).with(owner, request, metadata).and_return(parser) Morpheus::ResponseParser.parse(owner, request, metadata) end it 'delegates parsing to the new ResponseParser' do parser.should_receive(:parse) Morpheus::ResponseParser.parse(owner, request, metadata) end it 'returns the parsed response' do response = Morpheus::ResponseParser.parse(owner, request, metadata) response.should eql(parsed_response) end end describe '#parse' do before do parser.stub(:clear_cache!) end it 'clears the cache' do parser.stub(:with_active_support_notifications) parser.should_receive(:clear_cache!) parser.parse end context 'when the response code is 200..299' do before do response.stub(:code).and_return(200) end it 'delegates to #build_from_response' do parser.should_receive(:build_from_response) parser.parse end end context 'when the repsonse code is 404' do before do response.stub(:code).and_return(404) end it 'delegates to #raise_not_found' do parser.should_receive(:raise_not_found) parser.parse end end context 'when the response code is 422' do before do response.stub(:code).and_return(422) end it 'delegates to #build_from_response' do parser.should_receive(:build_from_response) parser.parse end end context 'when the response code is 500..599' do before do response.stub(:code).and_return(500) end it 'raises a ServerError exception' do lambda { parser.parse }.should raise_error(Morpheus::ServerError) end end context 'when the response code is 0' do before do response.stub(:code).and_return(0) end it 'raises a RemoteHostConnectionFailure exception' do lambda { parser.parse }.should raise_error(Morpheus::RemoteHostConnectionFailure) end end context 'when the response code is not one of the above' do before do response.stub(:code).and_return(701) end it 'raises an InvalidResponseCode exception' do lambda { parser.parse }.should raise_error(Morpheus::InvalidResponseCode, 'Response had error code: 701') end end end describe '#clear_cache!' do context 'when the request method is not GET' do before do request.stub(:method).and_return(:put) end it 'clears the RequestCache cache' do Morpheus::RequestCache.cache.should_receive(:clear) parser.send(:clear_cache!) end end context 'when the request method is GET' do before do request.stub(:method).and_return(:get) end it 'does not clear the RequestCache cache' do Morpheus::RequestCache.cache.should_not_receive(:clear) parser.stub(:clear_cache!) end end end describe '#build_from_response' do context 'when there is content' do context 'when the content is an Array' do let(:content) { [{ 'foo' => 'bar' }, { 'baz' => 'qux' }] } before do parser.stub(:content).and_return(content) end it 'delegates to #build_object_with_attributes for each element, collecting the results' do parser.should_receive(:build_object_with_attributes).exactly(2).times parser.send(:build_from_response) end end context 'when the content is not an Array' do let(:content) { { 'foo' => 'bar' } } it 'delegates to #build_object_with_attributes' do parser.should_receive(:build_object_with_attributes).with(content) parser.send(:build_from_response) end end end context 'when there is no content' do before do parser.stub(:content).and_return(nil) end it 'returns nil' do parser.send(:build_from_response).should be_nil end end end describe '#raise_not_found' do context 'when the metadata hash contains :ids' do let(:metadata) { { :ids => [1,2,3] } } it 'raises a ResourceNotFound exception with the appropriate message' do lambda { parser.send(:raise_not_found) }.should raise_error(Morpheus::ResourceNotFound, "Couldn't find all Dogs with IDs (1, 2, 3)") end end context 'when the metadata hash contains :id' do let(:metadata) { { :id => 1 } } it 'raises a ResourceNotFound exception with the appropriate message' do lambda { parser.send(:raise_not_found) }.should raise_error(Morpheus::ResourceNotFound, "Couldn't find Dog with ID=1") end end context 'when the metadata hash does not contain either :ids or :id' do it 'raises a ResourceNotFound exception with the appropriate message' do lambda { parser.send(:raise_not_found) }.should raise_error(Morpheus::ResourceNotFound, /No resource was found at/) end end end describe '#parsed_body' do it 'parses out the entire response body JSON' do parser.send(:parsed_body).should eql({ 'content' => { 'foo' => 'bar' }, 'errors' => [{ 'foo' => 'cannot be "bar"' }] }) end end describe '#content' do it 'parses out the "content" attribute from the response body JSON' do parser.send(:content).should eql({ 'foo' => 'bar' }) end end describe '#response_errors' do it 'parses out the "errors" attribute from the response body JSON' do parser.send(:response_errors).should eql([{ 'foo' => 'cannot be "bar"' }]) end end describe '#build_object_with_attributes' do context 'when the attributes has a "type" key' do let(:attributes) { { 'type' => 'Author', 'name' => 'Chuck Palahniuk' } } it 'creates a new instance of the "type" with the attributes' do Author.should_receive(:new).with(attributes) parser.send(:build_object_with_attributes, attributes) end end context 'when the attributes does not have a "type" key' do let(:instance) { mock } let(:attributes) { { 'name' => 'Fido' } } it 'merges attributes into a new object of the owner class' do Dog.should_receive(:new).and_return(instance) instance.should_receive(:merge_attributes).with(attributes) parser.send(:build_object_with_attributes, attributes) end end end end