Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swift tests: RSC14c #72

Merged
merged 1 commit into from
Nov 18, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions ably-ios/ARTAuth.m
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#import "ARTAuthTokenParams.h"
#import "ARTAuthTokenRequest.h"
#import "ARTEncoder.h"
#import "ARTStatus.h"

@implementation ARTAuth {
__weak ARTRest *_rest;
Expand Down Expand Up @@ -132,11 +133,16 @@ - (void)requestToken:(ARTAuthTokenParams *)tokenParams withOptions:(ARTAuthOptio
// The values supersede matching client library configured params and options.
ARTAuthOptions *mergedOptions = [self mergeOptions:authOptions];
ARTAuthTokenParams *currentTokenParams = [self mergeParams:tokenParams];


if (!mergedOptions.key) {
callback(nil, [NSError errorWithDomain:ARTAblyErrorDomain code:ARTCodeErrorAPIKeyMissing
userInfo:@{ NSLocalizedDescriptionKey: NSLocalizedString(@"API Key is missing", nil) }]);
}

if (mergedOptions.authUrl) {
NSMutableURLRequest *request = [self buildRequest:mergedOptions withParams:currentTokenParams];

[_rest.logger debug:@"%@ %@", request.HTTPMethod, request.URL];
[self.logger debug:@"ARTAuth: using authUrl (%@ %@)", request.HTTPMethod, request.URL];

[_rest executeRequest:request withAuthOption:ARTAuthenticationUseBasic completion:^(NSHTTPURLResponse *response, NSData *data, NSError *error) {
if (error) {
Expand All @@ -153,6 +159,10 @@ - (void)requestToken:(ARTAuthTokenParams *)tokenParams withOptions:(ARTAuthOptio
[self createTokenRequest:currentTokenParams options:mergedOptions callback:callback];
};

if (tokenRequestFactory == mergedOptions.authCallback) {
[self.logger debug:@"ARTAuth: using authCallback"];
}

tokenRequestFactory(currentTokenParams, ^(ARTAuthTokenRequest *tokenRequest, NSError *error) {
if (error) {
callback(nil, error);
Expand Down
8 changes: 7 additions & 1 deletion ably-ios/ARTAuthOptions.m
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,13 @@ - (ARTAuthOptions *)mergeWith:(ARTAuthOptions *)precedenceOptions {
}

- (BOOL)isBasicAuth {
return self.useTokenAuth == false && self.key != nil && self.clientId == nil;
return self.useTokenAuth == false &&
self.key != nil &&
self.clientId == nil &&
self.token == nil &&
self.tokenDetails == nil &&
self.authUrl == nil &&
self.authCallback == nil;
}

- (BOOL)isMethodPOST {
Expand Down
2 changes: 1 addition & 1 deletion ably-ios/ARTAuthTokenDetails.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ ART_ASSUME_NONNULL_BEGIN

- (instancetype)init UNAVAILABLE_ATTRIBUTE;
- (instancetype)initWithToken:(NSString *)token;
- (instancetype)initWithToken:(NSString *)token expires:(NSDate *)expires issued:(NSDate *)issued capability:(NSString *)capability clientId:(NSString *)clientId;
- (instancetype)initWithToken:(NSString *)token expires:(art_nullable NSDate *)expires issued:(art_nullable NSDate *)issued capability:(art_nullable NSString *)capability clientId:(art_nullable NSString *)clientId;

@end

Expand Down
33 changes: 25 additions & 8 deletions ably-ios/ARTRest.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#import "ARTChannelCollection.h"
#import "ARTDataQuery+Private.h"
#import "ARTPaginatedResult+Private.h"
#import "ARTAuth.h"
#import "ARTAuth+Private.h"
#import "ARTHttp.h"
#import "ARTEncoder.h"
#import "ARTJsonEncoder.h"
Expand Down Expand Up @@ -103,7 +103,10 @@ - (void)executeRequest:(NSMutableURLRequest *)request withAuthOption:(ARTAuthent
[self executeRequest:request completion:callback];
break;
case ARTAuthenticationOn:
[self executeRequestWithAuthentication:request withMethod:self.auth.method completion:callback];
[self executeRequestWithAuthentication:request withMethod:self.auth.method force:NO completion:callback];
break;
case ARTAuthenticationNewToken:
[self executeRequestWithAuthentication:request withMethod:self.auth.method force:YES completion:callback];
break;
case ARTAuthenticationUseBasic:
[self executeRequestWithAuthentication:request withMethod:ARTAuthMethodBasic completion:callback];
Expand All @@ -112,7 +115,11 @@ - (void)executeRequest:(NSMutableURLRequest *)request withAuthOption:(ARTAuthent
}

- (void)executeRequestWithAuthentication:(NSMutableURLRequest *)request withMethod:(ARTAuthMethod)method completion:(ARTHttpRequestCallback)callback {
[self calculateAuthorization:method completion:^(NSString *authorization, NSError *error) {
[self executeRequestWithAuthentication:request withMethod:method force:NO completion:callback];
}

- (void)executeRequestWithAuthentication:(NSMutableURLRequest *)request withMethod:(ARTAuthMethod)method force:(BOOL)force completion:(ARTHttpRequestCallback)callback {
[self calculateAuthorization:method force:force completion:^(NSString *authorization, NSError *error) {
if (error && callback) {
callback(nil, nil, error);
} else {
Expand All @@ -129,8 +136,10 @@ - (void)executeRequest:(NSMutableURLRequest *)request completion:(ARTHttpRequest
if (response.statusCode >= 400) {
NSError *error = [self->_encoders[response.MIMEType] decodeError:data];
if (error.code == 40140) {
// TODO: request token or error if no token information
NSAssert(false, @"Request token or error if no token information");
// Send it again, requesting a new token (forward callback)
[self.logger debug:@"ARTRest: requesting new token"];
[self executeRequest:request withAuthOption:ARTAuthenticationNewToken completion:callback];
return;
} else if (callback) {
callback(nil, nil, error);
}
Expand All @@ -141,20 +150,28 @@ - (void)executeRequest:(NSMutableURLRequest *)request completion:(ARTHttpRequest
}

- (void)calculateAuthorization:(ARTAuthMethod)method completion:(void (^)(NSString *authorization, NSError *error))callback {
[self calculateAuthorization:method force:NO completion:callback];
}

- (void)calculateAuthorization:(ARTAuthMethod)method force:(BOOL)force completion:(void (^)(NSString *authorization, NSError *error))callback {
[self.logger debug:@"ARTRest: calculating authorization %lu", (unsigned long)method];
// FIXME: use encoder and should be managed on ARTAuth
if (method == ARTAuthMethodBasic) {
// Include key Base64 encoded in an Authorization header (RFC7235)
NSData *keyData = [self.options.key dataUsingEncoding:NSUTF8StringEncoding];
NSString *keyBase64 = [keyData base64EncodedStringWithOptions:0];
callback([NSString stringWithFormat:@"Basic %@", keyBase64], nil);
if (callback) callback([NSString stringWithFormat:@"Basic %@", keyBase64], nil);
}
else {
[self.auth authorise:nil options:self.options force:NO callback:^(ARTAuthTokenDetails *tokenDetails, NSError *error) {
[self.auth authorise:nil options:self.options force:force callback:^(ARTAuthTokenDetails *tokenDetails, NSError *error) {
if (error) {
if (callback) callback(nil, error);
return;
}
NSData *tokenData = [tokenDetails.token dataUsingEncoding:NSUTF8StringEncoding];
NSString *tokenBase64 = [tokenData base64EncodedStringWithOptions:0];
[self.logger verbose:@"ARTRest: authorization bearer in Base64 %@", tokenBase64];
callback([NSString stringWithFormat:@"Bearer %@", tokenBase64], nil);
if (callback) callback([NSString stringWithFormat:@"Bearer %@", tokenBase64], nil);
}];
}
}
Expand Down
10 changes: 10 additions & 0 deletions ably-ios/ARTStatus.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ typedef NS_ENUM(NSUInteger, ARTState) {
ARTStateError = 99999
};

/**
ARTCodeErrors

The list of all public error codes returned under the error domain ARTAblyErrorDomain
*/
typedef CF_ENUM(NSUInteger, ARTCodeError) {
// FIXME: check hard coded errors
ARTCodeErrorAPIKeyMissing = 80001
};

ART_ASSUME_NONNULL_BEGIN

FOUNDATION_EXPORT NSString *const ARTAblyErrorDomain;
Expand Down
3 changes: 2 additions & 1 deletion ably-ios/ARTStatus.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@

#import "ARTStatus.h"

NSString *const ARTAblyErrorDomain = @"ARTAblyErrorDomain";
// Reverse-DNS style domain
NSString *const ARTAblyErrorDomain = @"io.ably.cocoa";

@implementation ARTErrorInfo

Expand Down
3 changes: 2 additions & 1 deletion ably-ios/ARTTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
typedef NS_ENUM(NSUInteger, ARTAuthentication) {
ARTAuthenticationOff,
ARTAuthenticationOn,
ARTAuthenticationUseBasic
ARTAuthenticationUseBasic,
ARTAuthenticationNewToken
};

typedef NS_ENUM(NSUInteger, ARTAuthMethod) {
Expand Down
38 changes: 38 additions & 0 deletions ablySpec/RestClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,44 @@ class RestClient: QuickSpec {
}
}

// RSC14c
fit("should error when expired token and no means to renew") {
let client = ARTRest(options: AblyTests.commonAppSetup())
let auth = client.auth

let tokenParams = ARTAuthTokenParams()
tokenParams.ttl = 3.0 //Seconds

waitUntil(timeout: testTimeout) { done in
auth.requestToken(tokenParams, withOptions: nil) { tokenDetails, error in
if let e = error {
XCTFail(e.description)
done()
}
else if let currentTokenDetails = tokenDetails {
let options = AblyTests.clientOptions()
options.key = client.options.key

// Expired token
options.tokenDetails = ARTAuthTokenDetails(token: currentTokenDetails.token, expires: currentTokenDetails.expires?.dateByAddingTimeInterval(testTimeout), issued: currentTokenDetails.issued, capability: currentTokenDetails.capability, clientId: currentTokenDetails.clientId)

options.authUrl = NSURL(string: "http://test-auth.ably.io")

let rest = ARTRest(options: options)

// Delay for token expiration
delay(tokenParams.ttl) {
// 40140 - token expired and will not recover because authUrl is invalid
publishTestMessage(rest) { error in
expect(error).toNot(beNil())
done()
}
}
}
}
}
}

} //RestClient
}
}
49 changes: 37 additions & 12 deletions ablySpec/TestUtilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ class AblyTests {

class var jsonRestOptions: ARTClientOptions {
get {
let options = ARTClientOptions()
options.environment = "sandbox"
let options = AblyTests.clientOptions()
options.binary = false
return options
}
Expand Down Expand Up @@ -89,7 +88,6 @@ class AblyTests {

if debug {
print(response)
options.logLevel = .Verbose
}

let key = response["keys"][0]
Expand All @@ -105,6 +103,15 @@ class AblyTests {
class func commonAppSetup(debug debug: Bool = false) -> ARTClientOptions {
return AblyTests.setupOptions(AblyTests.jsonRestOptions, debug: debug)
}

class func clientOptions(debug debug: Bool = false) -> ARTClientOptions {
let options = ARTClientOptions()
options.environment = "sandbox"
if debug {
options.logLevel = .Verbose
}
return options
}

}

Expand All @@ -130,21 +137,33 @@ func querySyslog(forLogsAfter startingTime: NSDate? = nil) -> AnyGenerator<Strin

/// Publish message class
class PublishTestMessage {
var error: NSError?

init(client: ARTRest, failOnError: Bool) {
self.error = NSError(domain: "", code: -1, userInfo: nil)


var completion: Optional<(NSError?)->()>
var error: NSError? = NSError(domain: "", code: -1, userInfo: nil)

convenience init(client: ARTRest, failOnError: Bool) {
self.init(client: client, completion: nil)
}

init(client: ARTRest, completion: Optional<(NSError?)->()>) {
client.channels.get("test").publish("message") { error in
self.error = error
if failOnError {
XCTFail("Got error '\(error)'")
if let callback = completion {
callback(error)
}
else if let e = error {
XCTFail("Got error '\(e)'")
}
}
}

}

/// Publish message
func publishTestMessage(client: ARTRest, completion: Optional<(NSError?)->()>) -> PublishTestMessage {
return PublishTestMessage(client: client, completion: completion)
}

func publishTestMessage(client: ARTRest, failOnError: Bool = true) -> PublishTestMessage {
return PublishTestMessage(client: client, failOnError: failOnError)
}
Expand Down Expand Up @@ -230,7 +249,7 @@ func extractBodyAsJSON(request: NSMutableURLRequest?) -> Result<NSDictionary> {
}

/*
Records each request for test purpose.
Records each request and response for test purpose.
*/
@objc
class MockHTTPExecutor: NSObject, ARTHTTPExecutor {
Expand All @@ -240,10 +259,16 @@ class MockHTTPExecutor: NSObject, ARTHTTPExecutor {
var logger: ARTLog?

var requests: [NSMutableURLRequest] = []
var responses: [NSHTTPURLResponse] = []

func executeRequest(request: NSMutableURLRequest, completion callback: ARTHttpRequestCallback?) {
self.requests.append(request)
self.executor.executeRequest(request, completion: callback)
self.executor.executeRequest(request, completion: { response, data, error in
if let httpResponse = response {
self.responses.append(httpResponse)
}
callback?(response, data, error)
})
}
}

Expand Down