Files
RestKit/Tests/Logic/Network/RKResponseMapperOperationTest.m
Blake Watters 0a033596da Relax the use of use an the informal procotol for the errorMessage property in favor of the description method. closes #1104, closes #1087, closes #1095
* Change contract to the use the `description` method instead of `errorMessage`. This makes it work with any class out of the box
* Add import for RKErrorMessage to the Support.h header so it is immediately available
* Fix incorrect keyPath in the README.md
* Add additional notes about how the errors are constructed to the README
2012-12-22 19:03:30 -05:00

297 lines
18 KiB
Objective-C

//
// RKResponseMapperOperationTest.m
// RestKit
//
// Created by Blake Watters on 10/5/12.
// Copyright (c) 2012 RestKit. All rights reserved.
//
#import "RKTestEnvironment.h"
#import "RKResponseMapperOperation.h"
#import "RKErrorMessage.h"
#import "RKMappingErrors.h"
#import "RKTestUser.h"
NSString *RKPathAndQueryStringFromURLRelativeToURL(NSURL *URL, NSURL *baseURL);
@interface RKServerError : NSObject
@property (nonatomic, copy) NSString *message;
@property (nonatomic, assign) NSInteger code;
@end
@implementation RKServerError
- (NSString *)description
{
return [NSString stringWithFormat:@"%@ (%ld)", self.message, (long) self.code];
}
@end
@interface RKObjectResponseMapperOperationTest : RKTestCase
@end
@implementation RKObjectResponseMapperOperationTest
#pragma mark - Successful Empty Responses
- (void)testMappingResponseDataThatIsASingleSpace
{
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[NSMutableDictionary class]];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:nil keyPath:nil statusCodes:[NSIndexSet indexSetWithIndex:200]];
NSURL *URL = [NSURL URLWithString:@"http://restkit.org"];
NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:URL statusCode:200 HTTPVersion:@"1.1" headerFields:nil];
NSData *data = [@" " dataUsingEncoding:NSUTF8StringEncoding];
RKObjectResponseMapperOperation *mapper = [[RKObjectResponseMapperOperation alloc] initWithResponse:response data:data responseDescriptors:@[responseDescriptor]];
mapper.treatsEmptyResponseAsSuccess = YES;
[mapper start];
expect(mapper.error).to.beNil();
}
- (void)testMappingAZeroLengthDataIsSucessful
{
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[NSMutableDictionary class]];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:nil keyPath:nil statusCodes:[NSIndexSet indexSetWithIndex:200]];
NSURL *URL = [NSURL URLWithString:@"http://restkit.org"];
NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:URL statusCode:200 HTTPVersion:@"1.1" headerFields:nil];
NSData *data = [@"" dataUsingEncoding:NSUTF8StringEncoding];
RKObjectResponseMapperOperation *mapper = [[RKObjectResponseMapperOperation alloc] initWithResponse:response data:data responseDescriptors:@[responseDescriptor]];
mapper.treatsEmptyResponseAsSuccess = YES;
[mapper start];
expect(mapper.error).to.beNil();
}
- (void)testMappingANilDataIsSucessful
{
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[NSMutableDictionary class]];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:nil keyPath:nil statusCodes:[NSIndexSet indexSetWithIndex:200]];
NSURL *URL = [NSURL URLWithString:@"http://restkit.org"];
NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:URL statusCode:200 HTTPVersion:@"1.1" headerFields:nil];
RKObjectResponseMapperOperation *mapper = [[RKObjectResponseMapperOperation alloc] initWithResponse:response data:nil responseDescriptors:@[responseDescriptor]];
mapper.treatsEmptyResponseAsSuccess = YES;
[mapper start];
expect(mapper.error).to.beNil();
}
#pragma mark - Error Status Codes
// 422, no content
- (void)testThatMappingZeroLengthClientErrorResponseReturnsError
{
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[NSMutableDictionary class]];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:nil keyPath:nil statusCodes:[NSIndexSet indexSetWithIndex:200]];
NSURL *URL = [NSURL URLWithString:@"http://restkit.org"];
NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:URL statusCode:422 HTTPVersion:@"1.1" headerFields:@{@"Content-Type": @"application/json"}];
NSData *data = [@"" dataUsingEncoding:NSUTF8StringEncoding];
RKObjectResponseMapperOperation *mapper = [[RKObjectResponseMapperOperation alloc] initWithResponse:response data:data responseDescriptors:@[responseDescriptor]];
[mapper start];
expect(mapper.error).notTo.beNil();
expect(mapper.error.code).to.equal(NSURLErrorBadServerResponse);
expect([mapper.error localizedDescription]).to.equal(@"Loaded an unprocessable client error response (422)");
}
// 422, with mappable error payload
- (void)testThatMappingMappableErrorPayloadClientErrorResponseReturnsError
{
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[RKErrorMessage class]];
[mapping addAttributeMappingsFromDictionary:@{@"message": @"errorMessage"}];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:nil keyPath:nil statusCodes:[NSIndexSet indexSetWithIndex:422]];
NSURL *URL = [NSURL URLWithString:@"http://restkit.org"];
NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:URL statusCode:422 HTTPVersion:@"1.1" headerFields:@{@"Content-Type": @"application/json"}];
NSData *data = [@"{\"message\": \"Failure\"}" dataUsingEncoding:NSUTF8StringEncoding];
RKObjectResponseMapperOperation *mapper = [[RKObjectResponseMapperOperation alloc] initWithResponse:response data:data responseDescriptors:@[responseDescriptor]];
[mapper start];
expect(mapper.error).notTo.beNil();
expect(mapper.error.code).to.equal(RKMappingErrorFromMappingResult);
expect([mapper.error localizedDescription]).to.equal(@"Failure");
}
// 422, empty JSON dictionary
- (void)testThatMappingEmptyJSONDictionaryClientErrorResponseReturnsError
{
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[NSMutableDictionary class]];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:nil keyPath:nil statusCodes:[NSIndexSet indexSetWithIndex:200]];
NSURL *URL = [NSURL URLWithString:@"http://restkit.org"];
NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:URL statusCode:422 HTTPVersion:@"1.1" headerFields:@{@"Content-Type": @"application/json"}];
NSData *data = [@"{}" dataUsingEncoding:NSUTF8StringEncoding];
RKObjectResponseMapperOperation *mapper = [[RKObjectResponseMapperOperation alloc] initWithResponse:response data:data responseDescriptors:@[responseDescriptor]];
[mapper start];
expect(mapper.error).notTo.beNil();
expect(mapper.error.code).to.equal(NSURLErrorBadServerResponse);
expect([mapper.error localizedDescription]).to.equal(@"Loaded an unprocessable client error response (422)");
}
// 422, empty JSON dictionary, no response descriptors
- (void)testThatMappingEmptyJSONDictionaryClientErrorResponseReturnsErrorNoDescriptors
{
NSURL *URL = [NSURL URLWithString:@"http://restkit.org"];
NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:URL statusCode:422 HTTPVersion:@"1.1" headerFields:@{@"Content-Type": @"application/json"}];
NSData *data = [@"{}" dataUsingEncoding:NSUTF8StringEncoding];
RKObjectResponseMapperOperation *mapper = [[RKObjectResponseMapperOperation alloc] initWithResponse:response data:data responseDescriptors:@[]];
[mapper start];
expect(mapper.error).notTo.beNil();
expect(mapper.error.code).to.equal(NSURLErrorBadServerResponse);
expect([mapper.error localizedDescription]).to.equal(@"Loaded an unprocessable client error response (422)");
}
- (void)testMappingServerErrorToCustomErrorClass
{
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[RKServerError class]];
[mapping addAttributeMappingsFromArray:@[ @"code", @"message" ]];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:nil keyPath:nil statusCodes:[NSIndexSet indexSetWithIndex:422]];
NSURL *URL = [NSURL URLWithString:@"http://restkit.org"];
NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:URL statusCode:422 HTTPVersion:@"1.1" headerFields:@{@"Content-Type": @"application/json"}];
NSData *data = [@"{\"code\": 12345, \"message\": \"This is the error message\"}" dataUsingEncoding:NSUTF8StringEncoding];
RKObjectResponseMapperOperation *mapper = [[RKObjectResponseMapperOperation alloc] initWithResponse:response data:data responseDescriptors:@[responseDescriptor]];
[mapper start];
expect(mapper.error).notTo.beNil();
expect(mapper.error.code).to.equal(RKMappingErrorFromMappingResult);
expect([mapper.error localizedDescription]).to.equal(@"This is the error message (12345)");
}
#pragma mark - Response Descriptor Matching
- (void)testThatResponseMapperMatchesBaseURLWithoutPathAppropriately
{
NSURL *baseURL = [NSURL URLWithString:@"http://restkit.org"];
NSURL *responseURL = [NSURL URLWithString:@"http://restkit.org/api/v1/organizations/"];
NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:responseURL statusCode:200 HTTPVersion:@"1.1" headerFields:@{@"Content-Type": @"application/json"}];
NSData *data = [@"{}" dataUsingEncoding:NSUTF8StringEncoding];
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[NSMutableDictionary class]];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:@"/api/v1/organizations/" keyPath:nil statusCodes:[NSIndexSet indexSetWithIndex:200]];
responseDescriptor.baseURL = baseURL;
RKObjectResponseMapperOperation *mapper = [[RKObjectResponseMapperOperation alloc] initWithResponse:response data:data responseDescriptors:@[responseDescriptor]];
[mapper start];
expect(mapper.error).to.beNil();
NSDictionary *expectedMappingsDictionary = @{ [NSNull null] : mapping };
expect(mapper.responseMappingsDictionary).to.equal(expectedMappingsDictionary);
}
- (void)testThatResponseMapperMatchesBaseURLWithJustASingleSlashAsThePathAppropriately
{
NSURL *baseURL = [NSURL URLWithString:@"http://restkit.org/"];
NSURL *responseURL = [NSURL URLWithString:@"http://restkit.org/api/v1/organizations/"];
NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:responseURL statusCode:200 HTTPVersion:@"1.1" headerFields:@{@"Content-Type": @"application/json"}];
NSData *data = [@"{}" dataUsingEncoding:NSUTF8StringEncoding];
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[NSMutableDictionary class]];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:@"api/v1/organizations/" keyPath:nil statusCodes:[NSIndexSet indexSetWithIndex:200]];
responseDescriptor.baseURL = baseURL;
RKObjectResponseMapperOperation *mapper = [[RKObjectResponseMapperOperation alloc] initWithResponse:response data:data responseDescriptors:@[responseDescriptor]];
[mapper start];
expect(mapper.error).to.beNil();
NSDictionary *expectedMappingsDictionary = @{ [NSNull null] : mapping };
expect(mapper.responseMappingsDictionary).to.equal(expectedMappingsDictionary);
}
- (void)testThatResponseMapperMatchesBaseURLWithPathWithoutATrailingSlashAppropriately
{
NSURL *baseURL = [NSURL URLWithString:@"http://restkit.org/api/v1"];
NSURL *responseURL = [NSURL URLWithString:@"http://restkit.org/api/v1/organizations/"];
NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:responseURL statusCode:200 HTTPVersion:@"1.1" headerFields:@{@"Content-Type": @"application/json"}];
NSData *data = [@"{}" dataUsingEncoding:NSUTF8StringEncoding];
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[NSMutableDictionary class]];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:@"/organizations/" keyPath:nil statusCodes:[NSIndexSet indexSetWithIndex:200]];
responseDescriptor.baseURL = baseURL;
RKObjectResponseMapperOperation *mapper = [[RKObjectResponseMapperOperation alloc] initWithResponse:response data:data responseDescriptors:@[responseDescriptor]];
[mapper start];
expect(mapper.error).to.beNil();
NSDictionary *expectedMappingsDictionary = @{ [NSNull null] : mapping };
expect(mapper.responseMappingsDictionary).to.equal(expectedMappingsDictionary);
}
- (void)testThatResponseMapperMatchesBaseURLWithPathWithATrailingSlashAppropriately
{
NSURL *baseURL = [NSURL URLWithString:@"http://restkit.org/api/v1/"];
NSURL *responseURL = [NSURL URLWithString:@"http://restkit.org/api/v1/organizations/"];
NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:responseURL statusCode:200 HTTPVersion:@"1.1" headerFields:@{@"Content-Type": @"application/json"}];
NSData *data = [@"{}" dataUsingEncoding:NSUTF8StringEncoding];
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[NSMutableDictionary class]];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:@"organizations/" keyPath:nil statusCodes:[NSIndexSet indexSetWithIndex:200]];
responseDescriptor.baseURL = baseURL;
RKObjectResponseMapperOperation *mapper = [[RKObjectResponseMapperOperation alloc] initWithResponse:response data:data responseDescriptors:@[responseDescriptor]];
[mapper start];
expect(mapper.error).to.beNil();
NSDictionary *expectedMappingsDictionary = @{ [NSNull null] : mapping };
expect(mapper.responseMappingsDictionary).to.equal(expectedMappingsDictionary);
}
- (void)testThatResponseMapperMatchesBaseURLWithPathAndQueryParametersAppropriately
{
NSURL *baseURL = [NSURL URLWithString:@"http://restkit.org/api/v1/"];
NSURL *responseURL = [NSURL URLWithString:@"http://restkit.org/api/v1/organizations/?client_search=s"];
NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:responseURL statusCode:200 HTTPVersion:@"1.1" headerFields:@{@"Content-Type": @"application/json"}];
NSData *data = [@"{}" dataUsingEncoding:NSUTF8StringEncoding];
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[NSMutableDictionary class]];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:@"organizations/" keyPath:nil statusCodes:[NSIndexSet indexSetWithIndex:200]];
responseDescriptor.baseURL = baseURL;
RKObjectResponseMapperOperation *mapper = [[RKObjectResponseMapperOperation alloc] initWithResponse:response data:data responseDescriptors:@[responseDescriptor]];
[mapper start];
expect(mapper.error).to.beNil();
NSDictionary *expectedMappingsDictionary = @{ [NSNull null] : mapping };
expect(mapper.responseMappingsDictionary).to.equal(expectedMappingsDictionary);
}
- (void)testThatResponseDescriptorMismatchesIncludeHelpfulError
{
NSURL *responseURL = [NSURL URLWithString:@"http://restkit.org/api/v1/users"];
NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:responseURL statusCode:200 HTTPVersion:@"1.1" headerFields:@{@"Content-Type": @"application/json"}];
NSData *data = [@"{\"some\": \"Data\"}" dataUsingEncoding:NSUTF8StringEncoding];
NSURL *baseURL = [NSURL URLWithString:@"http://restkit.org/api/v1/"];
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[NSMutableDictionary class]];
RKResponseDescriptor *responseDescriptor1 = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:@"/users" keyPath:@"this" statusCodes:[NSIndexSet indexSetWithIndex:200]];
responseDescriptor1.baseURL = baseURL;
RKResponseDescriptor *responseDescriptor2 = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:@"/users" keyPath:@"that" statusCodes:[NSIndexSet indexSetWithIndex:200]];
responseDescriptor2.baseURL = [NSURL URLWithString:@"http://google.com"];
RKResponseDescriptor *responseDescriptor3 = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:@"users" keyPath:@"that" statusCodes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(202, 5)]];
responseDescriptor3.baseURL = baseURL;
RKObjectResponseMapperOperation *mapper = [[RKObjectResponseMapperOperation alloc] initWithResponse:response data:data responseDescriptors:@[ responseDescriptor1, responseDescriptor2, responseDescriptor3 ]];
[mapper start];
expect(mapper.error).notTo.beNil();
expect([mapper.error code]).to.equal(RKMappingErrorNotFound);
NSString *failureReason = [[mapper.error userInfo] valueForKey:NSLocalizedFailureReasonErrorKey];
assertThat(failureReason, containsString(@"A 200 response was loaded from the URL 'http://restkit.org/api/v1/users', which failed to match all (3) response descriptors"));
assertThat(failureReason, containsString(@"failed to match: response path 'users' did not match the path pattern '/users'."));
assertThat(failureReason, containsString(@"failed to match: response URL 'http://restkit.org/api/v1/users' is not relative to the baseURL 'http://google.com'."));
assertThat(failureReason, containsString(@"failed to match: response status code 200 is not within the range 202-206"));
NSDictionary *expectedMappingsDictionary = @{};
expect(mapper.responseMappingsDictionary).to.equal(expectedMappingsDictionary);
}
#pragma mark -
- (void)testThatObjectResponseMapperOperationDoesNotMapWithTargetObjectForUnsuccessfulResponseStatusCode
{
NSURL *responseURL = [NSURL URLWithString:@"http://restkit.org/api/v1/users"];
NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:responseURL statusCode:422 HTTPVersion:@"1.1" headerFields:@{@"Content-Type": @"application/json"}];
NSData *data = [@"{\"name\": \"Blake\"}" dataUsingEncoding:NSUTF8StringEncoding];
RKTestUser *testUser = [RKTestUser new];
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[RKTestUser class]];
[mapping addAttributeMappingsFromArray:@[ @"name" ]];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:nil keyPath:nil statusCodes:[NSIndexSet indexSetWithIndex:200]];
RKObjectMapping *errorMapping = [RKObjectMapping mappingForClass:[RKErrorMessage class]];
RKResponseDescriptor *errorDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:errorMapping pathPattern:nil keyPath:nil statusCodes:[NSIndexSet indexSetWithIndex:422]];
RKObjectResponseMapperOperation *mapper = [[RKObjectResponseMapperOperation alloc] initWithResponse:response data:data responseDescriptors:@[ responseDescriptor, errorDescriptor ]];
mapper.targetObject = testUser;
[mapper start];
expect(mapper.error).notTo.beNil();
expect([mapper.error code]).notTo.equal(RKMappingErrorTypeMismatch);
}
@end