Rework path based response descriptor matching and expand test coverage. Eliminate path normalization. fixes #987

This commit is contained in:
Blake Watters
2012-10-14 13:46:38 -04:00
parent 4a7ed3cbd3
commit f3ece00743
13 changed files with 300 additions and 109 deletions

View File

@@ -36,11 +36,10 @@
Creates and returns a new `RKResponseDescriptor` object.
@param mapping The mapping for the response descriptor.
@param pathPattern A path pattern that matches against URLs for which the mapping should be used. The path pattern will be normalized on input via `RKPathNormalize`.
@param pathPattern A path pattern that matches against URLs for which the mapping should be used.
@param keyPath A key path specifying the subset of the parsed response for which the mapping is to be used.
@param statusCodes A set of HTTP status codes for which the mapping is to be used.
@return A new `RKResponseDescriptor` object.
@see `RKPathNormalize`
*/
+ (RKResponseDescriptor *)responseDescriptorWithMapping:(RKMapping *)mapping
pathPattern:(NSString *)pathPattern

View File

@@ -20,7 +20,7 @@
#import "RKPathMatcher.h"
#import "RKResponseDescriptor.h"
#import "RKPathUtilities.h"
#import "RKHTTPUtilities.h"
// Cloned from AFStringFromIndexSet -- method should be non-static for reuse
static NSString *RKStringFromIndexSet(NSIndexSet *indexSet) {
@@ -53,23 +53,6 @@ static NSString *RKStringFromIndexSet(NSIndexSet *indexSet) {
return string;
}
// Assumes that URL is relative to baseURL
static NSString *RKPathAndQueryStringFromURLRelativeToURL(NSURL *URL, NSURL *baseURL)
{
if (baseURL) {
NSString *pathAndQuery = [[URL absoluteString] substringFromIndex:[[baseURL absoluteString] length]];
return ([pathAndQuery characterAtIndex:0] != '/') ? [NSString stringWithFormat:@"/%@", pathAndQuery] : pathAndQuery;
} else {
NSString *query = [URL query];
return (query && [query length]) ? [NSString stringWithFormat:@"%@?%@", [URL path], query] : [URL path];
}
}
static BOOL RKURLIsRelativeToURL(NSURL *sourceURL, NSURL *baseURL)
{
return [[sourceURL absoluteString] hasPrefix:[baseURL absoluteString]];
}
@interface RKResponseDescriptor ()
@property (nonatomic, strong, readwrite) RKMapping *mapping;
@property (nonatomic, copy, readwrite) NSString *pathPattern;
@@ -87,7 +70,7 @@ static BOOL RKURLIsRelativeToURL(NSURL *sourceURL, NSURL *baseURL)
NSParameterAssert(mapping);
RKResponseDescriptor *mappingDescriptor = [self new];
mappingDescriptor.mapping = mapping;
mappingDescriptor.pathPattern = pathPattern ? RKPathNormalize(pathPattern) : nil;
mappingDescriptor.pathPattern = pathPattern;
mappingDescriptor.keyPath = keyPath;
mappingDescriptor.statusCodes = statusCodes;

View File

@@ -55,7 +55,7 @@
*/
- (id)initWithResponse:(NSHTTPURLResponse *)response
data:(NSData *)data
responseDescriptors:(NSArray *)responseDescriptors;
responseDescriptors:(NSArray *)responseDescriptors;
///------------------------------
/// @name Accessing Response Data

View File

@@ -95,3 +95,25 @@ NSDate *RKDateFromHTTPDateString(NSString *);
@return The expiration date as specified by the cache headers or `nil` if none was found.
*/
NSDate *RKHTTPCacheExpirationDateFromHeadersWithStatusCode(NSDictionary *headers, NSInteger statusCode);
/**
Returns a Boolean value that indicates if a given URL is relative to another URL.
This method does not rely on the `baseURL` method of `NSURL` as it only indicates a relationship between the initialization of two URL objects. The relativity of the given URL is assessed by evaluating a prefix match of the URL's absolute string value with the absolute string value of the potential base URL.
@param URL The URL to assess the relativity of.
@param baseURL The base URL to determine if the given URL is relative to.
@return `YES` is URL is relative to the base URL, else `NO`.
*/
BOOL RKURLIsRelativeToURL(NSURL *URL, NSURL *baseURL);
/**
Returns a string object containing the relative path and query string of a given URL object and a base URL that the given URL is relative to.
If the given URL is found not to be relative to the baseURL, `nil` is returned.
@param URL The URL to retrieve the relative path and query string of.
@param baseURL The base URL to be omitted from the returned path and query string.
@return A string containing the relative path and query parameters.
*/
NSString *RKPathAndQueryStringFromURLRelativeToURL(NSURL *URL, NSURL *baseURL);

View File

@@ -404,3 +404,20 @@ NSDate * RKHTTPCacheExpirationDateFromHeadersWithStatusCode(NSDictionary *header
// If nothing permitted to define the cache expiration delay nor to restrict its cacheability, use a default cache expiration delay
return [[NSDate alloc] initWithTimeInterval:kRKURLCacheDefault sinceDate:now];
}
BOOL RKURLIsRelativeToURL(NSURL *URL, NSURL *baseURL)
{
return [[URL absoluteString] hasPrefix:[baseURL absoluteString]];
}
NSString *RKPathAndQueryStringFromURLRelativeToURL(NSURL *URL, NSURL *baseURL)
{
if (baseURL) {
if (! RKURLIsRelativeToURL(URL, baseURL)) return nil;
return [[URL absoluteString] substringFromIndex:[[baseURL absoluteString] length]];
} else {
// NOTE: [URL relativeString] would return the same value as `absoluteString` if URL is not relative to a baseURL
NSString *query = [URL query];
return (query && [query length]) ? [NSString stringWithFormat:@"%@?%@", [URL path], query] : [URL path];
}
}

View File

@@ -22,7 +22,6 @@
#import "SOCKit.h"
#import "RKLog.h"
#import "RKDictionaryUtilities.h"
#import "RKPathUtilities.h"
static NSString *RKEncodeURLString(NSString *unencodedString);
extern NSDictionary *RKQueryParametersFromStringWithEncoding(NSString *string, NSStringEncoding stringEncoding);
@@ -77,12 +76,6 @@ static NSString *RKEncodeURLString(NSString *unencodedString)
return matcher;
}
// Normalize our root path for matching cleanly with SOCKit
- (void)setRootPath:(NSString *)rootPath
{
_rootPath = RKPathNormalize(rootPath);
}
- (BOOL)matches
{
NSAssert((self.socPattern != NULL && self.rootPath != NULL), @"Matcher is insufficiently configured. Before attempting pattern matching, you must provide a path string and a pattern to match it against.");
@@ -145,10 +138,11 @@ static NSString *RKEncodeURLString(NSString *unencodedString)
NSAssert(self.socPattern != NULL, @"Matcher has no established pattern. Instantiate it using pathMatcherWithPattern: before calling pathFromObject:");
NSAssert(object != NULL, @"Object provided is invalid; cannot create a path from a NULL object");
NSString *(^encoderBlock)(NSString *interpolatedString) = nil;
if (addEscapes)
if (addEscapes) {
encoderBlock = ^NSString *(NSString *interpolatedString) {
return RKEncodeURLString(interpolatedString);
};
}
NSString *path = [self.socPattern stringFromObject:object withBlock:encoderBlock];
return path;
}

View File

@@ -55,11 +55,3 @@ NSString *RKPathFromPatternWithObject(NSString *pathPattern, id object);
@return The expected MIME Type of the resource identified by the path or nil if unknown.
*/
NSString *RKMIMETypeFromPathExtension(NSString *path);
/**
Normalizes a given string into a path by ensuring that it includes a leading slash and does not include a trailing slash.
@param path The path to be normalized.
@return The given path, normalized with a leading slash and without a trailing sla
*/
NSString *RKPathNormalize(NSString *path);

View File

@@ -119,12 +119,3 @@ NSString *RKMIMETypeFromPathExtension(NSString *path)
// Consult our internal dictionary of mappings if not found
return [RKDictionaryOfFileExtensionsToMIMETypes() valueForKey:pathExtension];
}
NSString *RKPathNormalize(NSString *path)
{
path = [path stringByReplacingOccurrencesOfString:@"//" withString:@"/"];
NSUInteger length = [path length];
if ([path characterAtIndex:length - 1] == '/') path = [path substringToIndex:length - 1];
if ([path characterAtIndex:0] != '/') path = [NSString stringWithFormat:@"/%@", path];
return path;
}

View File

@@ -12,16 +12,6 @@
SPEC_BEGIN(RKResponseDescriptorSpec)
describe(@"init", ^{
context(@"when given a relative path pattern", ^{
it(@"normalizes the path pattern", ^{
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[RKTestUser class]];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:@"monkeys" keyPath:nil statusCodes:[NSIndexSet indexSetWithIndex:200]];
[[responseDescriptor.pathPattern should] equal:@"/monkeys"];
});
});
});
describe(@"matchesURL:", ^{
__block NSURL *baseURL;
__block RKResponseDescriptor *responseDescriptor;
@@ -139,10 +129,10 @@ describe(@"matchesURL:", ^{
baseURL = [NSURL URLWithString:@"http://0.0.0.0:5000"];
});
context(@"and the path pattern is '/api/v1/organizations'", ^{
context(@"and the path pattern is '/api/v1/organizations/'", ^{
beforeEach(^{
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[RKTestUser class]];
responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:@"/api/v1/organizations" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:@"/api/v1/organizations/" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
responseDescriptor.baseURL = baseURL;
});
@@ -199,7 +189,7 @@ describe(@"matchesURL:", ^{
context(@"and the path pattern is '/monkeys/:monkeyID\\.json'", ^{
beforeEach(^{
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[RKTestUser class]];
responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:@"/monkeys/:monkeyID\\.json" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:@"monkeys/:monkeyID\\.json" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
responseDescriptor.baseURL = baseURL;
});
@@ -244,11 +234,11 @@ describe(@"matchesResponse:", ^{
__block RKResponseDescriptor *responseDescriptor;
context(@"when the baseURL is 'http://0.0.0.0:5000", ^{
context(@"and the path pattern is '/api/v1/organizations'", ^{
context(@"and the path pattern is '/api/v1/organizations/'", ^{
context(@"and given the URL 'http://0.0.0.0:5000/api/v1/organizations/?client_search=t'", ^{
beforeEach(^{
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[RKTestUser class]];
responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:@"/api/v1/organizations" keyPath:nil statusCodes:[NSIndexSet indexSetWithIndex:200]];
responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:@"/api/v1/organizations/" keyPath:nil statusCodes:[NSIndexSet indexSetWithIndex:200]];
responseDescriptor.baseURL = [NSURL URLWithString:@"http://0.0.0.0:5000"];
});
@@ -270,10 +260,10 @@ describe(@"matchesResponse:", ^{
responseDescriptor.baseURL = [NSURL URLWithString:@"http://domain.com/domain/api/v1/"];
});
it(@"returns YES", ^{
it(@"returns NO", ^{
NSURL *URL = [NSURL URLWithString:@"http://domain.com/domain/api/v1/recommendation/"];
NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:URL statusCode:200 HTTPVersion:@"1.1" headerFields:nil];
[[@([responseDescriptor matchesResponse:response]) should] beYes];
[[@([responseDescriptor matchesResponse:response]) should] beNo];
});
});
@@ -284,10 +274,10 @@ describe(@"matchesResponse:", ^{
responseDescriptor.baseURL = [NSURL URLWithString:@"http://domain.com/domain/api/v1/"];
});
it(@"returns YES", ^{
it(@"returns NO", ^{
NSURL *URL = [NSURL URLWithString:@"http://domain.com/domain/api/v1/recommendation?action=search&type=whatever"];
NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:URL statusCode:200 HTTPVersion:@"1.1" headerFields:nil];
[[@([responseDescriptor matchesResponse:response]) should] beYes];
[[@([responseDescriptor matchesResponse:response]) should] beNo];
});
});
});

View File

@@ -11,6 +11,8 @@
#import "RKErrorMessage.h"
#import "RKMappingErrors.h"
NSString *RKPathAndQueryStringFromURLRelativeToURL(NSURL *URL, NSURL *baseURL);
@interface RKObjectResponseMapperOperationTest : RKTestCase
@end
@@ -106,4 +108,96 @@
expect([mapper.error localizedDescription]).to.equal(@"Loaded an unprocessable client error response (422)");
}
#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);
}
@end

View File

@@ -0,0 +1,108 @@
//
// RKHTTPUtilitiesTest.m
// RestKit
//
// Created by Blake Watters on 10/14/12.
// Copyright (c) 2012 RestKit. All rights reserved.
//
#import "RKTestEnvironment.h"
#import "RKHTTPUtilities.h"
@interface RKHTTPUtilitiesTest : RKTestCase
@end
@implementation RKHTTPUtilitiesTest
#pragma mark - RKPathAndQueryStringFromURLRelativeToURL
- (void)testThatNilIsReturnedWhenTheGivenURLIsNotRelativeToTheBaseURL
{
NSURL *baseURL = [NSURL URLWithString:@"http://google.com/path"];
NSURL *testURL = [NSURL URLWithString:@"http://restkit.org/path"];
NSString *pathAndQueryString = RKPathAndQueryStringFromURLRelativeToURL(testURL, baseURL);
expect(pathAndQueryString).to.beNil();
}
- (void)testReturningThePathAndQueryParametersWithANilBaseURLForURLWithNoPath
{
NSURL *baseURL = nil;
NSURL *testURL = [NSURL URLWithString:@"http://restkit.org"];
NSString *pathAndQueryString = RKPathAndQueryStringFromURLRelativeToURL(testURL, baseURL);
expect(pathAndQueryString).to.equal(@"");
}
- (void)testReturningThePathAndQueryParametersWithANilBaseURLForURLWithASingleSlashAsThePath
{
NSURL *baseURL = nil;
NSURL *testURL = [NSURL URLWithString:@"http://restkit.org/"];
NSString *pathAndQueryString = RKPathAndQueryStringFromURLRelativeToURL(testURL, baseURL);
expect(pathAndQueryString).to.equal(@"/");
}
- (void)testReturningThePathAndQueryParametersWithANilBaseURLForURLWithAPath
{
NSURL *baseURL = nil;
NSURL *testURL = [NSURL URLWithString:@"http://restkit.org/the/path"];
NSString *pathAndQueryString = RKPathAndQueryStringFromURLRelativeToURL(testURL, baseURL);
expect(pathAndQueryString).to.equal(@"/the/path");
}
- (void)testReturningThePathAndQueryParametersWithANilBaseURLForURLWithAPathAndQueryString
{
NSURL *baseURL = nil;
NSURL *testURL = [NSURL URLWithString:@"http://restkit.org/search?this=that&type=search"];
NSString *pathAndQueryString = RKPathAndQueryStringFromURLRelativeToURL(testURL, baseURL);
expect(pathAndQueryString).to.equal(@"/search?this=that&type=search");
}
- (void)testShouldReturnAnEmptyStringGivenAURLEqualToTheBaseURLWithNoPath
{
NSURL *baseURL = [NSURL URLWithString:@"http://restkit.org"];
NSURL *testURL = [NSURL URLWithString:@"http://restkit.org"];
NSString *pathAndQueryString = RKPathAndQueryStringFromURLRelativeToURL(testURL, baseURL);
expect(pathAndQueryString).to.equal(@"");
}
- (void)testShouldReturnAnEmptyStringGivenAURLEqualToTheBaseURLWithASingleSlashAsThePath
{
NSURL *baseURL = [NSURL URLWithString:@"http://restkit.org/"];
NSURL *testURL = [NSURL URLWithString:@"http://restkit.org/"];
NSString *pathAndQueryString = RKPathAndQueryStringFromURLRelativeToURL(testURL, baseURL);
expect(pathAndQueryString).to.equal(@"");
}
- (void)testShouldReturnTheCompletePathAndQueryStringGivenABaseURLWithNoPath
{
NSURL *baseURL = [NSURL URLWithString:@"http://restkit.org"];
NSURL *testURL = [NSURL URLWithString:@"http://restkit.org/search?this=that&type=search"];
NSString *pathAndQueryString = RKPathAndQueryStringFromURLRelativeToURL(testURL, baseURL);
expect(pathAndQueryString).to.equal(@"/search?this=that&type=search");
}
- (void)testShouldReturnJustTheRelativePathGivenABaseURLWithASingleTrailingSlash
{
NSURL *baseURL = [NSURL URLWithString:@"http://restkit.org/"];
NSURL *testURL = [NSURL URLWithString:@"http://restkit.org/search?this=that&type=search"];
NSString *pathAndQueryString = RKPathAndQueryStringFromURLRelativeToURL(testURL, baseURL);
expect(pathAndQueryString).to.equal(@"search?this=that&type=search");
}
- (void)testShouldReturnTheCompletePathAndQueryStringGivenABaseURLWithAComplexPathWithNoTrailingSlash
{
NSURL *baseURL = [NSURL URLWithString:@"http://restkit.org/api/v1"];
NSURL *testURL = [NSURL URLWithString:@"http://restkit.org/api/v1/search?this=that&type=search"];
NSString *pathAndQueryString = RKPathAndQueryStringFromURLRelativeToURL(testURL, baseURL);
expect(pathAndQueryString).to.equal(@"/search?this=that&type=search");
}
- (void)testShouldReturnTheCompletePathAndQueryStringGivenABaseURLWithAComplexPathWithATrailingSlash
{
NSURL *baseURL = [NSURL URLWithString:@"http://restkit.org/api/v1/"];
NSURL *testURL = [NSURL URLWithString:@"http://restkit.org/api/v1/search?this=that&type=search"];
NSString *pathAndQueryString = RKPathAndQueryStringFromURLRelativeToURL(testURL, baseURL);
expect(pathAndQueryString).to.equal(@"search?this=that&type=search");
}
@end

View File

@@ -32,10 +32,10 @@
NSDictionary *arguments = nil;
RKPathMatcher *pathMatcher = [RKPathMatcher pathMatcherWithPath:@"/this/is/my/backend?foo=bar&this=that"];
BOOL isMatchingPattern = [pathMatcher matchesPattern:@"/this/is/:controllerName/:entityName" tokenizeQueryStrings:YES parsedArguments:&arguments];
assertThatBool(isMatchingPattern, is(equalToBool(YES)));
assertThat(arguments, isNot(empty()));
assertThat(arguments, hasEntries(@"controllerName", @"my", @"entityName", @"backend", @"foo", @"bar", @"this", @"that", nil));
expect(isMatchingPattern).to.equal(YES);
expect(arguments).notTo.beEmpty();
NSDictionary *expectedArguments = @{ @"controllerName": @"my", @"entityName": @"backend", @"foo": @"bar", @"this": @"that" };
expect(arguments).to.equal(expectedArguments);
}
- (void)testShouldMatchPathsWithEscapedArguments
@@ -43,9 +43,10 @@
NSDictionary *arguments = nil;
RKPathMatcher *pathMatcher = [RKPathMatcher pathMatcherWithPath:@"/bills/tx/82/SB%2014?apikey=GC12d0c6af"];
BOOL isMatchingPattern = [pathMatcher matchesPattern:@"/bills/:stateID/:session/:billID" tokenizeQueryStrings:YES parsedArguments:&arguments];
assertThatBool(isMatchingPattern, is(equalToBool(YES)));
assertThat(arguments, isNot(empty()));
assertThat(arguments, hasEntries(@"stateID", @"tx", @"session", @"82", @"billID", @"SB 14", @"apikey", @"GC12d0c6af", nil));
expect(isMatchingPattern).to.equal(YES);
expect(arguments).notTo.beEmpty();
NSDictionary *expectedArguments = @{ @"stateID": @"tx", @"session": @"82", @"billID": @"SB 14", @"apikey": @"GC12d0c6af" };
expect(arguments).to.equal(expectedArguments);
}
@@ -54,9 +55,10 @@
NSDictionary *arguments = nil;
RKPathMatcher *patternMatcher = [RKPathMatcher pathMatcherWithPattern:@"github.com/:username"];
BOOL isMatchingPattern = [patternMatcher matchesPath:@"github.com/jverkoey" tokenizeQueryStrings:NO parsedArguments:&arguments];
assertThatBool(isMatchingPattern, is(equalToBool(YES)));
assertThat(arguments, isNot(empty()));
assertThat(arguments, hasEntry(@"username", @"jverkoey"));
expect(isMatchingPattern).to.equal(YES);
expect(arguments).notTo.beEmpty();
NSDictionary *params = @{ @"username": @"jverkoey" };
expect(arguments).to.equal(params);
}
- (void)testShouldMatchPathsWithoutAnyArguments
@@ -64,8 +66,8 @@
NSDictionary *arguments = nil;
RKPathMatcher *patternMatcher = [RKPathMatcher pathMatcherWithPattern:@"/metadata"];
BOOL isMatchingPattern = [patternMatcher matchesPath:@"/metadata" tokenizeQueryStrings:NO parsedArguments:&arguments];
assertThatBool(isMatchingPattern, is(equalToBool(YES)));
assertThat(arguments, is(empty()));
expect(isMatchingPattern).to.equal(YES);
expect(arguments).to.beEmpty();
}
- (void)testShouldPerformTwoMatchesInARow
@@ -73,11 +75,11 @@
NSDictionary *arguments = nil;
RKPathMatcher *pathMatcher = [RKPathMatcher pathMatcherWithPath:@"/metadata?apikey=GC12d0c6af"];
BOOL isMatchingPattern1 = [pathMatcher matchesPattern:@"/metadata/:stateID" tokenizeQueryStrings:YES parsedArguments:&arguments];
assertThatBool(isMatchingPattern1, is(equalToBool(NO)));
expect(isMatchingPattern1).to.equal(NO);
BOOL isMatchingPattern2 = [pathMatcher matchesPattern:@"/metadata" tokenizeQueryStrings:YES parsedArguments:&arguments];
assertThatBool(isMatchingPattern2, is(equalToBool(YES)));
assertThat(arguments, isNot(empty()));
assertThat(arguments, hasEntry(@"apikey", @"GC12d0c6af"));
expect(isMatchingPattern2).to.equal(YES);
expect(arguments).notTo.beNil();
expect(arguments).to.equal(@{ @"apikey": @"GC12d0c6af" });
}
- (void)testShouldCreatePathsFromInterpolatedObjects
@@ -86,9 +88,9 @@
@"CuddleGuts", @"name", [NSNumber numberWithInt:6], @"age", nil];
RKPathMatcher *matcher = [RKPathMatcher pathMatcherWithPattern:@"/people/:name/:age"];
NSString *interpolatedPath = [matcher pathFromObject:person addingEscapes:YES];
assertThat(interpolatedPath, isNot(equalTo(nil)));
expect(interpolatedPath).notTo.beNil();
NSString *expectedPath = @"/people/CuddleGuts/6";
assertThat(interpolatedPath, is(equalTo(expectedPath)));
expect(interpolatedPath).to.equal(expectedPath);
}
- (void)testShouldCreatePathsFromInterpolatedObjectsWithAddedEscapes
@@ -97,9 +99,9 @@
@"JUICE|BOX&121", @"password", @"Joe Bob Briggs", @"name", [NSNumber numberWithInt:15], @"group", nil];
RKPathMatcher *matcher = [RKPathMatcher pathMatcherWithPattern:@"/people/:group/:name?password=:password"];
NSString *interpolatedPath = [matcher pathFromObject:person addingEscapes:YES];
assertThat(interpolatedPath, isNot(equalTo(nil)));
expect(interpolatedPath).notTo.beNil();
NSString *expectedPath = @"/people/15/Joe%20Bob%20Briggs?password=JUICE%7CBOX%26121";
assertThat(interpolatedPath, is(equalTo(expectedPath)));
expect(interpolatedPath).to.equal(expectedPath);
}
- (void)testShouldCreatePathsFromInterpolatedObjectsWithoutAddedEscapes
@@ -108,9 +110,9 @@
@"JUICE|BOX&121", @"password", @"Joe Bob Briggs", @"name", [NSNumber numberWithInt:15], @"group", nil];
RKPathMatcher *matcher = [RKPathMatcher pathMatcherWithPattern:@"/people/:group/:name?password=:password"];
NSString *interpolatedPath = [matcher pathFromObject:person addingEscapes:NO];
assertThat(interpolatedPath, isNot(equalTo(nil)));
expect(interpolatedPath).notTo.beNil();
NSString *expectedPath = @"/people/15/Joe Bob Briggs?password=JUICE|BOX&121";
assertThat(interpolatedPath, is(equalTo(expectedPath)));
expect(interpolatedPath).to.equal(expectedPath);
}
- (void)testShouldCreatePathsThatIncludePatternArgumentsFollowedByEscapedNonPatternDots
@@ -118,14 +120,14 @@
NSDictionary *arguments = [NSDictionary dictionaryWithObjectsAndKeys:@"Resources", @"filename", nil];
RKPathMatcher *matcher = [RKPathMatcher pathMatcherWithPattern:@"/directory/:filename\\.json"];
NSString *interpolatedPath = [matcher pathFromObject:arguments addingEscapes:YES];
assertThat(interpolatedPath, isNot(equalTo(nil)));
expect(interpolatedPath).notTo.beNil();
NSString *expectedPath = @"/directory/Resources.json";
assertThat(interpolatedPath, is(equalTo(expectedPath)));
expect(interpolatedPath).to.equal(expectedPath);
}
- (void)testMatchingPathWithTrailingSlashAndQuery
{
RKPathMatcher *pathMatcher = [RKPathMatcher pathMatcherWithPattern:@"/api/v1/organizations"];
RKPathMatcher *pathMatcher = [RKPathMatcher pathMatcherWithPattern:@"/api/v1/organizations/"];
BOOL matches = [pathMatcher matchesPath:@"/api/v1/organizations/?client_search=t" tokenizeQueryStrings:NO parsedArguments:nil];
expect(matches).to.equal(YES);
}
@@ -141,11 +143,27 @@
expect(matches).to.equal(NO);
matches = [pathMatcher matchesPath:@"/api/v1/organizations/" tokenizeQueryStrings:NO parsedArguments:nil];
expect(matches).to.equal(YES);
expect(matches).to.equal(NO);
pathMatcher = [RKPathMatcher pathMatcherWithPattern:@"/api/:version/organizations/:organizationID"];
matches = [pathMatcher matchesPath:@"/api/v1/organizations/1234/" tokenizeQueryStrings:NO parsedArguments:nil];
expect(matches).to.equal(YES);
}
- (void)testMatchingPathPatternWithTrailingSlash
{
NSDictionary *argsDictionary = nil;
RKPathMatcher *pathMatcher = [RKPathMatcher pathMatcherWithPattern:@"/api/v1/organizations/:identifier/"];
BOOL match = [pathMatcher matchesPath:@"/api/v1/organizations/1/" tokenizeQueryStrings:YES parsedArguments:&argsDictionary];
expect(match).to.beTruthy();
}
- (void)testMatchingPathPatternWithTrailingSlashAndQueryParameters
{
NSDictionary *argsDictionary = nil;
RKPathMatcher *pathMatcher = [RKPathMatcher pathMatcherWithPattern:@"/api/v1/organizations/"];
BOOL match = [pathMatcher matchesPath:@"/api/v1/organizations/?client_search=s" tokenizeQueryStrings:YES parsedArguments:&argsDictionary];
expect(match).to.beTruthy();
}
@end

View File

@@ -15,22 +15,5 @@
@implementation RKPathUtilitiesTest
- (void)testPathNormalizationRemovesTrailingSlash
{
NSString *normalizedPath = RKPathNormalize(@"/api/v1/organizations/");
expect(normalizedPath).to.equal(@"/api/v1/organizations");
}
- (void)testPathNormalizationAddsLeadingSlash
{
NSString *normalizedPath = RKPathNormalize(@"api/v1/organizations/");
expect(normalizedPath).to.equal(@"/api/v1/organizations");
}
- (void)testPathNormalizationRemovesDuplicateSlashes
{
NSString *normalizedPath = RKPathNormalize(@"api//v1/organizations//");
expect(normalizedPath).to.equal(@"/api/v1/organizations");
}
@end