@@ -257,6 +257,174 @@ final class AuthTests: XCTestCase {
257257 }
258258 }
259259
260+ func testDontRefreshTokenWithoutCredentials( ) async throws {
261+ let auth = Auth . live (
262+ config: . test,
263+ keychain: {
264+ var keychain = Keychain . unimplemented ( )
265+ keychain. loadCredentials = { nil }
266+ return keychain
267+ } ( ) ,
268+ httpClient: . unimplemented( ) ,
269+ openURL: . unimplemented( ) ,
270+ dateGenerator: . unimplemented( ) ,
271+ pkceUtils: . unimplemented( )
272+ )
273+
274+ try await auth. refreshToken ( )
275+ }
276+
277+ func testDontRefreshTokenIfNotExpired( ) async throws {
278+ let date = Date ( timeIntervalSince1970: 1_000_000 )
279+ let credentials = ActorIsolated < Credentials ? > ( Credentials (
280+ accessToken: " " ,
281+ tokenType: " " ,
282+ expiresAt: date. addingTimeInterval ( 1 ) ,
283+ refreshToken: " " ,
284+ scope: " " ,
285+ uid: " " ,
286+ accountId: " "
287+ ) )
288+ let auth = Auth . live (
289+ config: . test,
290+ keychain: {
291+ var keychain = Keychain . unimplemented ( )
292+ keychain. loadCredentials = { await credentials. value }
293+ return keychain
294+ } ( ) ,
295+ httpClient: . unimplemented( ) ,
296+ openURL: . unimplemented( ) ,
297+ dateGenerator: . init { date } ,
298+ pkceUtils: . unimplemented( )
299+ )
300+
301+ try await auth. refreshToken ( )
302+ }
303+
304+ func testRefreshExpiredToken( ) async throws {
305+ let httpRequests = ActorIsolated < [ URLRequest ] > ( [ ] )
306+ let date = Date ( timeIntervalSince1970: 1_000_000 )
307+ let credentials = ActorIsolated < Credentials ? > ( Credentials (
308+ accessToken: " access-token-1 " ,
309+ tokenType: " token-type-1 " ,
310+ expiresAt: date. addingTimeInterval ( - 1 ) ,
311+ refreshToken: " refresh-token-1 " ,
312+ scope: " scope-1 " ,
313+ uid: " uid-1 " ,
314+ accountId: " accountId-1 "
315+ ) )
316+ let auth = Auth . live (
317+ config: . test,
318+ keychain: {
319+ var keychain = Keychain . unimplemented ( )
320+ keychain. loadCredentials = { await credentials. value }
321+ keychain. saveCredentials = { await credentials. setValue ( $0) }
322+ return keychain
323+ } ( ) ,
324+ httpClient: . init { request in
325+ await httpRequests. withValue { $0. append ( request) }
326+ return (
327+ """
328+ {
329+ " access_token " : " access-token-2 " ,
330+ " expires_in " : 4321,
331+ " token_type " : " token-type-2 "
332+ }
333+ """ . data ( using: . utf8) !,
334+ HTTPURLResponse (
335+ url: URL ( filePath: " / " ) ,
336+ statusCode: 200 ,
337+ httpVersion: nil ,
338+ headerFields: nil
339+ ) !
340+ )
341+ } ,
342+ openURL: . unimplemented( ) ,
343+ dateGenerator: . init { date } ,
344+ pkceUtils: . unimplemented( )
345+ )
346+
347+ try await auth. refreshToken ( )
348+
349+ await httpRequests. withValue {
350+ let url = URL ( string: " https://api.dropboxapi.com/oauth2/token " ) !
351+ var expectedRequest = URLRequest ( url: url)
352+ expectedRequest. httpMethod = " POST "
353+ expectedRequest. allHTTPHeaderFields = [
354+ " Content-Type " : " application/x-www-form-urlencoded "
355+ ]
356+ expectedRequest. httpBody = [
357+ " grant_type=refresh_token " ,
358+ " refresh_token=refresh-token-1 " ,
359+ " client_id= \( Config . test. appKey) " ,
360+ ] . joined ( separator: " & " ) . data ( using: . utf8) !
361+
362+ XCTAssertEqual ( $0, [ expectedRequest] )
363+ XCTAssertEqual ( $0. first? . httpBody, expectedRequest. httpBody!)
364+ }
365+ await credentials. withValue {
366+ XCTAssertEqual ( $0, Credentials (
367+ accessToken: " access-token-2 " ,
368+ tokenType: " token-type-2 " ,
369+ expiresAt: date. addingTimeInterval ( 4321 ) ,
370+ refreshToken: " refresh-token-1 " ,
371+ scope: " scope-1 " ,
372+ uid: " uid-1 " ,
373+ accountId: " accountId-1 "
374+ ) )
375+ }
376+ }
377+
378+ func testRefreshTokenErrorResponse( ) async {
379+ let date = Date ( timeIntervalSince1970: 1_000_000 )
380+ let credentials = ActorIsolated < Credentials ? > ( Credentials (
381+ accessToken: " access-token-1 " ,
382+ tokenType: " token-type-1 " ,
383+ expiresAt: date. addingTimeInterval ( - 1 ) ,
384+ refreshToken: " refresh-token-1 " ,
385+ scope: " scope-1 " ,
386+ uid: " uid-1 " ,
387+ accountId: " accountId-1 "
388+ ) )
389+ let auth = Auth . live (
390+ config: . test,
391+ keychain: {
392+ var keychain = Keychain . unimplemented ( )
393+ keychain. loadCredentials = { await credentials. value }
394+ keychain. saveCredentials = { await credentials. setValue ( $0) }
395+ return keychain
396+ } ( ) ,
397+ httpClient: . init { _ in
398+ (
399+ " Error!!! " . data ( using: . utf8) !,
400+ HTTPURLResponse (
401+ url: URL ( filePath: " / " ) ,
402+ statusCode: 500 ,
403+ httpVersion: nil ,
404+ headerFields: nil
405+ ) !
406+ )
407+ } ,
408+ openURL: . unimplemented( ) ,
409+ dateGenerator: . init { date } ,
410+ pkceUtils: . unimplemented( )
411+ )
412+
413+ do {
414+ try await auth. refreshToken ( )
415+ XCTFail ( " Expected to throw, but didn't " )
416+ } catch {
417+ XCTAssertEqual (
418+ error as? Auth . Error ,
419+ . response(
420+ statusCode: 500 ,
421+ data: " Error!!! " . data ( using: . utf8) !
422+ ) ,
423+ " Expected to throw response error, got \( error) "
424+ )
425+ }
426+ }
427+
260428 func testSignOut( ) async {
261429 let credentials = ActorIsolated < Credentials ? > ( Credentials (
262430 accessToken: " " ,
0 commit comments