func (suite *sessionRecoverySuite) TestExpiredCacheRecovery() { scope := New().(*realScope) scope.userCache = newTestCache() mock := multiclient.NewMock() stub := &multiclient.Stub{ Service: loginService, Endpoint: readSessionEndpoint, Response: &sessreadproto.Response{ SessId: proto.String(testSessId), Token: proto.String(testToken), }, } mock.Stub(stub) multiclient.SetCaller(mock.Caller()) u, err := FromSessionToken(testSessId, testToken) suite.Assertions.Equal(testSessId, u.SessId) suite.Assertions.NoError(err) suite.Assertions.NotNil(u) u.RenewTs = time.Now().Add(-2 * time.Minute) u.ExpiryTs = u.RenewTs.Add(1 * time.Minute) suite.Assertions.NoError(scope.userCache.Store(u)) suite.Assertions.Equal(0, stub.CountCalls()) // As this session has expired, the login service should be called to recover (and renew) it suite.Assertions.False(scope.IsAuth()) suite.Assertions.NoError(scope.RecoverSession(testSessId)) suite.Assertions.True(scope.IsAuth()) u = scope.AuthUser() suite.Assertions.NotNil(u) suite.Assertions.Equal(1, stub.CountCalls()) }
// TestRecoverSessionSad tests sad case (login service has some fatal error) func (suite *sessionRecoverySuite) TestRecoverSessionSad() { t := suite.T() scope := New().(*realScope) scope.userCache = newTestCache() mock := multiclient.NewMock() stub := &multiclient.Stub{ Service: loginService, Endpoint: readSessionEndpoint, Error: errors.InternalServerError("com.hailocab.service.login.foo", "Things are foo barred"), } mock.Stub(stub) multiclient.SetCaller(mock.Caller()) err := scope.RecoverSession(testSessId) if err == nil { t.Error("Expecting recovery error, because login call should fail.") } if scope.IsAuth() { t.Error("Expecting scope to be IsAuth==false after failed recovery") } if scope.HasTriedAuth() { t.Error("Expecting scope to have HasTriedAuth()==false after _failed_ recovery") } // verify we made correct request(s) if stub.CountCalls() != 1 { t.Fatalf("Expecting 1 call to readsession; got %v", stub.CountCalls()) } }
// TestRecoverSessionHappyNotFound tests happy case (no service call failures) when // we do not find a user with the session ID func (suite *sessionRecoverySuite) TestRecoverSessionHappyNotFound() { t := suite.T() scope := New().(*realScope) scope.userCache = newTestCache() mock := multiclient.NewMock() stub := &multiclient.Stub{ Service: loginService, Endpoint: readSessionEndpoint, Error: errors.NotFound("com.hailocab.service.login.readsession", "Session not found"), } mock.Stub(stub) multiclient.SetCaller(mock.Caller()) err := scope.RecoverSession(testSessId) if err != nil { t.Errorf("Unexpected recover error (not found should NOT be classed as a recovery error): %v", err) } if scope.IsAuth() { t.Error("Expecting scope to be IsAuth==false after recovery where NOT FOUND") } if !scope.HasTriedAuth() { t.Error("Expecting scope to have HasTriedAuth()==true after recovery attempt") } if u := scope.AuthUser(); u != nil { t.Error("Expecting AuthUser()==nil after recover attempt") } // verify we made correct request(s) if stub.CountCalls() != 1 { t.Fatalf("Expecting 1 call to readsession; got %v", stub.CountCalls()) } // recover AGAIN -- we should NOT cache NOT FOUNDs, because of C* replication/eventual consistency err = scope.RecoverSession(testSessId) if err != nil { t.Errorf("Unexpected recover error (not found should NOT be classed as a recovery error): %v", err) } if scope.IsAuth() { t.Error("Expecting scope to be IsAuth==false after recovery where NOT FOUND") } // verify we called again if stub.CountCalls() != 2 { t.Fatalf("Expecting 2 call to readsession (because it should NOT be cached); got %v", stub.CountCalls()) } }
// TestAuthUnhappyCase tests when things don't work func (suite *sessionRecoverySuite) TestAuthUnhappyCaseInvalid() { t := suite.T() scope := New().(*realScope) scope.userCache = newTestCache() mock := multiclient.NewMock() stub := &multiclient.Stub{ Service: loginService, Endpoint: authEndpoint, Error: errors.InternalServerError("com.hailocab.service.login.auth.foobarred", "It's FOOBARRED"), } mock.Stub(stub) multiclient.SetCaller(mock.Caller()) testMech, testDeviceType := "h2", "cli" testUsername, testPassword := "******", "Securez1" testCreds := map[string]string{ "username": testUsername, "password": testPassword, } err := scope.Auth(testMech, testDeviceType, testCreds) if err == nil { t.Fatal("Expecting auth error") } if err == BadCredentialsError { t.Error("Error should not bubble up as a BAD CREDENTIALS error") } if scope.IsAuth() { t.Error("Expecting scope to be IsAuth==false after failed auth") } if scope.HasTriedAuth() { t.Error("Expecting scope to have HasTriedAuth()==false after failed auth") } if u := scope.AuthUser(); u != nil { t.Error("Expecting AuthUser()==nil after failed auth") } // verify we made correct request(s) if stub.CountCalls() != 1 { t.Fatalf("Expecting 1 call to auth; got %v", stub.CountCalls()) } }
// TestRecoverSessionHappyFound tests happy case (no service call failures) when we do find a user func (suite *sessionRecoverySuite) TestRecoverSessionHappyFound() { scope := New().(*realScope) scope.userCache = newTestCache() mock := multiclient.NewMock() stub := &multiclient.Stub{ Service: loginService, Endpoint: readSessionEndpoint, Response: &sessreadproto.Response{ SessId: proto.String(testSessId), Token: proto.String(testToken), }, } mock.Stub(stub) multiclient.SetCaller(mock.Caller()) err := scope.RecoverSession(testSessId) suite.Assertions.NoError(err, "Unexpected recovery error") suite.Assertions.True(scope.IsAuth()) suite.Assertions.True(scope.HasTriedAuth()) suite.Assertions.Equal(1, stub.CountCalls(), "Expecting 1 call to readsession") req := &sessreadproto.Request{} err = stub.Request(0).Unmarshal(req) suite.Assertions.NoError(err) suite.Assertions.Equal(testSessId, req.GetSessId()) suite.Assertions.False(req.GetNoRenew()) u := scope.authUser suite.Assertions.NotNil(u) suite.Assertions.Equal("dave", u.Id) // Clean out scope scope.Clean() suite.Assertions.False(scope.IsAuth(), "Expecting scope to be IsAuth==false after Clean()") suite.Assertions.False(scope.HasTriedAuth(), "Expecting scope to have HasTriedAuth()==false after Clean()") suite.Assertions.Nil(scope.AuthUser(), "Expecting AuthUser()==nil after Clean()") // recover AGAIN -- this time it shoud be cached suite.Assertions.NoError(scope.RecoverSession(testSessId), "Unexpected recovery error") suite.Assertions.True(scope.IsAuth()) suite.Assertions.Equal(1, stub.CountCalls(), "Expecting 1 call to readsession (should be cached now)") }
// TestSignOutHappy tests signout when it works func (suite *sessionRecoverySuite) TestSignOutHappy() { t := suite.T() scope := New().(*realScope) scope.userCache = newTestCache() mock := multiclient.NewMock() stub := &multiclient.Stub{ Service: loginService, Endpoint: deleteSessionEndpoint, Response: &sessdelproto.Response{}, } readSessStub := &multiclient.Stub{ Service: loginService, Endpoint: readSessionEndpoint, Response: &sessreadproto.Response{ SessId: proto.String(testSessId), Token: proto.String(testToken), }, } mock.Stub(stub).Stub(readSessStub) multiclient.SetCaller(mock.Caller()) // need to recover first err := scope.RecoverSession(testSessId) if err != nil || !scope.IsAuth() { t.Errorf("Unexpected recover error, or not Authed as expected: %v", err) } // now signout err = scope.SignOut(scope.AuthUser()) if err != nil { t.Errorf("Unexpected signout error: %v", err) } // verify we made correct request(s) if stub.CountCalls() != 1 { t.Fatalf("Expecting 1 call to deletesession; got %v", stub.CountCalls()) } req := &sessdelproto.Request{} err = stub.Request(0).Unmarshal(req) if err != nil { t.Fatalf("Unexpected error unmarshaling our request: %v", err) } if req.GetSessId() != testSessId { t.Errorf("Request did not contain our expected sessId '%s', got '%s'", testSessId, req.GetSessId()) } if scope.IsAuth() { t.Error("Expecting scope to be IsAuth==false after SignOut()") } if scope.HasTriedAuth() { t.Error("Expecting scope to have HasTriedAuth()==false after SignOut()") } if u := scope.AuthUser(); u != nil { t.Error("Expecting AuthUser()==nil after SignOut()") } // make sure we have purged cache err = scope.RecoverSession(testSessId) if err != nil { t.Errorf("Expecting error when recovering session again") } if readSessStub.CountCalls() != 2 { t.Error("Expecting 2 calls to readsession - sincee we should have purged the cache after SignOut()") } }
// TestAuthHappyCaseValid tests when things work, and when the credentials are valid func (suite *sessionRecoverySuite) TestAuthHappyCaseValid() { t := suite.T() scope := New().(*realScope) scope.userCache = newTestCache() mock := multiclient.NewMock() stub := &multiclient.Stub{ Service: loginService, Endpoint: authEndpoint, Response: &authproto.Response{ SessId: proto.String(testSessId), Token: proto.String(testToken), }, } sessLookupStub := &multiclient.Stub{ Service: loginService, Endpoint: readSessionEndpoint, Response: &sessreadproto.Response{ SessId: proto.String(testSessId), Token: proto.String(testToken), }, } mock.Stub(stub).Stub(sessLookupStub) multiclient.SetCaller(mock.Caller()) testMech, testDeviceType := "h2", "cli" testUsername, testPassword := "******", "Securez1" testCreds := map[string]string{ "username": testUsername, "password": testPassword, } err := scope.Auth(testMech, testDeviceType, testCreds) if err != nil { t.Errorf("Unexpected auth error: %v", err) } if !scope.IsAuth() { t.Error("Expecting scope to be IsAuth==true after auth") } if !scope.HasTriedAuth() { t.Error("Expecting scope to have HasTriedAuth()==true after auth") } // verify we made correct request(s) if stub.CountCalls() != 1 { t.Fatalf("Expecting 1 call to auth; got %v", stub.CountCalls()) } req := &authproto.Request{} err = stub.Request(0).Unmarshal(req) if err != nil { t.Fatalf("Unexpected error unmarshaling our request: %v", err) } if req.GetMech() != testMech { t.Errorf("Request did not contain our expected mech '%s', got '%s'", testMech, req.GetMech()) } if req.GetDeviceType() != testDeviceType { t.Errorf("Request did not contain our expected device type '%s', got '%s'", testDeviceType, req.GetDeviceType()) } if req.GetUsername() != testUsername { t.Errorf("Request did not contain our expected username '%s', got '%s'", testUsername, req.GetUsername()) } if req.GetPassword() != testPassword { t.Errorf("Request did not contain our expected password '%s', got '%s'", testPassword, req.GetPassword()) } // verify user returned u := scope.AuthUser() if u == nil { t.Fatal("IsAuthed scope returned nil user") } if u.Id != "dave" { t.Errorf("Expecting user ID 'dave'; got '%s'", u.Id) } // clean out scope scope.Clean() if scope.IsAuth() { t.Error("Expecting scope to be IsAuth==false after Clean()") } if scope.HasTriedAuth() { t.Error("Expecting scope to have HasTriedAuth()==false after Clean()") } if u := scope.AuthUser(); u != nil { t.Error("Expecting AuthUser()==nil after Clean()") } // auth AGAIN -- we should be calling login service _again_ err = scope.Auth(testMech, testDeviceType, testCreds) if err != nil { t.Errorf("Unexpected recover error: %v", err) } if !scope.IsAuth() { t.Error("Expecting scope to be IsAuth=true after recovery") } // verify we haven't made _any more_ requests if stub.CountCalls() != 2 { t.Fatalf("Expecting 2 call to auth (because it should be called each time); got %v", stub.CountCalls()) } // session should be pushed into cache err = scope.RecoverSession(testSessId) if err != nil { t.Errorf("Unexpected recover error: %v", err) } if sessLookupStub.CountCalls() != 0 { t.Fatalf("Expecting 0 call to readsession (because it should be cached); got %v", stub.CountCalls()) } }