Преглед изворни кода

replace instance with screenName in BroadcastBuddyDeparted

This allows us to call BroadcastBuddyDeparted in OnSessionClose
without needing hacks to get an instance that's already been removed.
Mike пре 3 месеци
родитељ
комит
263e87ccc9

+ 6 - 6
foodgroup/buddy.go

@@ -128,8 +128,8 @@ func (s BuddyService) BroadcastBuddyArrived(ctx context.Context, screenName stat
 	return s.buddyBroadcaster.BroadcastBuddyArrived(ctx, screenName, userInfo)
 	return s.buddyBroadcaster.BroadcastBuddyArrived(ctx, screenName, userInfo)
 }
 }
 
 
-func (s BuddyService) BroadcastBuddyDeparted(ctx context.Context, instance *state.SessionInstance) error {
-	return s.buddyBroadcaster.BroadcastBuddyDeparted(ctx, instance)
+func (s BuddyService) BroadcastBuddyDeparted(ctx context.Context, screenName state.IdentScreenName) error {
+	return s.buddyBroadcaster.BroadcastBuddyDeparted(ctx, screenName)
 }
 }
 
 
 func (s BuddyService) BroadcastVisibility(ctx context.Context, you *state.SessionInstance, filter []state.IdentScreenName, doSendDepartures bool) error {
 func (s BuddyService) BroadcastVisibility(ctx context.Context, you *state.SessionInstance, filter []state.IdentScreenName, doSendDepartures bool) error {
@@ -195,8 +195,8 @@ func (s buddyNotifier) BroadcastBuddyArrived(ctx context.Context, screenName sta
 	return nil
 	return nil
 }
 }
 
 
-func (s buddyNotifier) BroadcastBuddyDeparted(ctx context.Context, instance *state.SessionInstance) error {
-	users, err := s.relationshipFetcher.AllRelationships(ctx, instance.IdentScreenName(), nil)
+func (s buddyNotifier) BroadcastBuddyDeparted(ctx context.Context, screenName state.IdentScreenName) error {
+	users, err := s.relationshipFetcher.AllRelationships(ctx, screenName, nil)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -219,8 +219,8 @@ func (s buddyNotifier) BroadcastBuddyDeparted(ctx context.Context, instance *sta
 			TLVUserInfo: wire.TLVUserInfo{
 			TLVUserInfo: wire.TLVUserInfo{
 				// don't include the TLV block, otherwise the AIM client fails
 				// don't include the TLV block, otherwise the AIM client fails
 				// to process the block event
 				// to process the block event
-				ScreenName:   instance.IdentScreenName().String(),
-				WarningLevel: instance.Warning(),
+				ScreenName:   screenName.String(),
+				WarningLevel: 0,
 				TLVBlock: wire.TLVBlock{
 				TLVBlock: wire.TLVBlock{
 					TLVList: wire.TLVList{
 					TLVList: wire.TLVList{
 						// this TLV needs to be set in order for departure
 						// this TLV needs to be set in order for departure

+ 5 - 5
foodgroup/buddy_test.go

@@ -557,15 +557,15 @@ func TestBuddyService_BroadcastDeparture(t *testing.T) {
 	cases := []struct {
 	cases := []struct {
 		// name is the unit test name
 		// name is the unit test name
 		name string
 		name string
-		// instance is the session of the user
-		instance *state.SessionInstance
+		// screenName is the user that departed
+		screenName state.IdentScreenName
 		// mockParams is the list of params sent to mocks that satisfy this
 		// mockParams is the list of params sent to mocks that satisfy this
 		// method's dependencies
 		// method's dependencies
 		mockParams mockParams
 		mockParams mockParams
 	}{
 	}{
 		{
 		{
-			name:     "happy path",
-			instance: newTestInstance("me"),
+			name:       "happy path",
+			screenName: state.NewIdentScreenName("me"),
 			mockParams: mockParams{
 			mockParams: mockParams{
 				relationshipFetcherParams: relationshipFetcherParams{
 				relationshipFetcherParams: relationshipFetcherParams{
 					allRelationshipsParams: allRelationshipsParams{
 					allRelationshipsParams: allRelationshipsParams{
@@ -662,7 +662,7 @@ func TestBuddyService_BroadcastDeparture(t *testing.T) {
 				messageRelayer:      messageRelayer,
 				messageRelayer:      messageRelayer,
 			}
 			}
 
 
-			err := svc.BroadcastBuddyDeparted(context.Background(), tc.instance)
+			err := svc.BroadcastBuddyDeparted(context.Background(), tc.screenName)
 			assert.NoError(t, err)
 			assert.NoError(t, err)
 		})
 		})
 	}
 	}

+ 11 - 11
foodgroup/mock_buddy_broadcaster_test.go

@@ -103,16 +103,16 @@ func (_c *mockbuddyBroadcaster_BroadcastBuddyArrived_Call) RunAndReturn(run func
 }
 }
 
 
 // BroadcastBuddyDeparted provides a mock function for the type mockbuddyBroadcaster
 // BroadcastBuddyDeparted provides a mock function for the type mockbuddyBroadcaster
-func (_mock *mockbuddyBroadcaster) BroadcastBuddyDeparted(ctx context.Context, instance *state.SessionInstance) error {
-	ret := _mock.Called(ctx, instance)
+func (_mock *mockbuddyBroadcaster) BroadcastBuddyDeparted(ctx context.Context, screenName state.IdentScreenName) error {
+	ret := _mock.Called(ctx, screenName)
 
 
 	if len(ret) == 0 {
 	if len(ret) == 0 {
 		panic("no return value specified for BroadcastBuddyDeparted")
 		panic("no return value specified for BroadcastBuddyDeparted")
 	}
 	}
 
 
 	var r0 error
 	var r0 error
-	if returnFunc, ok := ret.Get(0).(func(context.Context, *state.SessionInstance) error); ok {
-		r0 = returnFunc(ctx, instance)
+	if returnFunc, ok := ret.Get(0).(func(context.Context, state.IdentScreenName) error); ok {
+		r0 = returnFunc(ctx, screenName)
 	} else {
 	} else {
 		r0 = ret.Error(0)
 		r0 = ret.Error(0)
 	}
 	}
@@ -126,20 +126,20 @@ type mockbuddyBroadcaster_BroadcastBuddyDeparted_Call struct {
 
 
 // BroadcastBuddyDeparted is a helper method to define mock.On call
 // BroadcastBuddyDeparted is a helper method to define mock.On call
 //   - ctx context.Context
 //   - ctx context.Context
-//   - instance *state.SessionInstance
-func (_e *mockbuddyBroadcaster_Expecter) BroadcastBuddyDeparted(ctx interface{}, instance interface{}) *mockbuddyBroadcaster_BroadcastBuddyDeparted_Call {
-	return &mockbuddyBroadcaster_BroadcastBuddyDeparted_Call{Call: _e.mock.On("BroadcastBuddyDeparted", ctx, instance)}
+//   - screenName state.IdentScreenName
+func (_e *mockbuddyBroadcaster_Expecter) BroadcastBuddyDeparted(ctx interface{}, screenName interface{}) *mockbuddyBroadcaster_BroadcastBuddyDeparted_Call {
+	return &mockbuddyBroadcaster_BroadcastBuddyDeparted_Call{Call: _e.mock.On("BroadcastBuddyDeparted", ctx, screenName)}
 }
 }
 
 
-func (_c *mockbuddyBroadcaster_BroadcastBuddyDeparted_Call) Run(run func(ctx context.Context, instance *state.SessionInstance)) *mockbuddyBroadcaster_BroadcastBuddyDeparted_Call {
+func (_c *mockbuddyBroadcaster_BroadcastBuddyDeparted_Call) Run(run func(ctx context.Context, screenName state.IdentScreenName)) *mockbuddyBroadcaster_BroadcastBuddyDeparted_Call {
 	_c.Call.Run(func(args mock.Arguments) {
 	_c.Call.Run(func(args mock.Arguments) {
 		var arg0 context.Context
 		var arg0 context.Context
 		if args[0] != nil {
 		if args[0] != nil {
 			arg0 = args[0].(context.Context)
 			arg0 = args[0].(context.Context)
 		}
 		}
-		var arg1 *state.SessionInstance
+		var arg1 state.IdentScreenName
 		if args[1] != nil {
 		if args[1] != nil {
-			arg1 = args[1].(*state.SessionInstance)
+			arg1 = args[1].(state.IdentScreenName)
 		}
 		}
 		run(
 		run(
 			arg0,
 			arg0,
@@ -154,7 +154,7 @@ func (_c *mockbuddyBroadcaster_BroadcastBuddyDeparted_Call) Return(err error) *m
 	return _c
 	return _c
 }
 }
 
 
-func (_c *mockbuddyBroadcaster_BroadcastBuddyDeparted_Call) RunAndReturn(run func(ctx context.Context, instance *state.SessionInstance) error) *mockbuddyBroadcaster_BroadcastBuddyDeparted_Call {
+func (_c *mockbuddyBroadcaster_BroadcastBuddyDeparted_Call) RunAndReturn(run func(ctx context.Context, screenName state.IdentScreenName) error) *mockbuddyBroadcaster_BroadcastBuddyDeparted_Call {
 	_c.Call.Return(run)
 	_c.Call.Return(run)
 	return _c
 	return _c
 }
 }

+ 1 - 1
foodgroup/oservice.go

@@ -250,7 +250,7 @@ func (s OServiceService) SetUserInfoFields(ctx context.Context, instance *state.
 		instance.SetUserStatusBitmask(status)
 		instance.SetUserStatusBitmask(status)
 
 
 		if instance.Session().Invisible() {
 		if instance.Session().Invisible() {
-			if err := s.buddyBroadcaster.BroadcastBuddyDeparted(ctx, instance); err != nil {
+			if err := s.buddyBroadcaster.BroadcastBuddyDeparted(ctx, instance.IdentScreenName()); err != nil {
 				return wire.SNACMessage{}, err
 				return wire.SNACMessage{}, err
 			}
 			}
 		} else {
 		} else {

+ 1 - 1
foodgroup/oservice_test.go

@@ -1067,7 +1067,7 @@ func TestOServiceService_SetUserInfoFields(t *testing.T) {
 			}
 			}
 			for _, params := range tc.mockParams.broadcastBuddyDepartedParams {
 			for _, params := range tc.mockParams.broadcastBuddyDepartedParams {
 				buddyUpdateBroadcaster.EXPECT().
 				buddyUpdateBroadcaster.EXPECT().
-					BroadcastBuddyDeparted(mock.Anything, matchSession(params.screenName)).
+					BroadcastBuddyDeparted(mock.Anything, params.screenName).
 					Return(params.err)
 					Return(params.err)
 			}
 			}
 			svc := OServiceService{
 			svc := OServiceService{

+ 1 - 1
foodgroup/types.go

@@ -90,7 +90,7 @@ type buddyBroadcaster interface {
 	BroadcastBuddyArrived(ctx context.Context, screenName state.IdentScreenName, userInfo wire.TLVUserInfo) error
 	BroadcastBuddyArrived(ctx context.Context, screenName state.IdentScreenName, userInfo wire.TLVUserInfo) error
 
 
 	// BroadcastBuddyDeparted notifies all relevant users that the given user has gone offline.
 	// BroadcastBuddyDeparted notifies all relevant users that the given user has gone offline.
-	BroadcastBuddyDeparted(ctx context.Context, instance *state.SessionInstance) error
+	BroadcastBuddyDeparted(ctx context.Context, screenName state.IdentScreenName) error
 
 
 	// BroadcastVisibility sends presence updates to the specified filter list.
 	// BroadcastVisibility sends presence updates to the specified filter list.
 	// If sendDepartures is true, departure events are sent as well.
 	// If sendDepartures is true, departure events are sent as well.

+ 10 - 10
server/oscar/mock_auth_test.go

@@ -424,12 +424,12 @@ type mockAuthService_RegisterBOSSession_Call struct {
 // RegisterBOSSession is a helper method to define mock.On call
 // RegisterBOSSession is a helper method to define mock.On call
 //   - ctx context.Context
 //   - ctx context.Context
 //   - authCookie state.ServerCookie
 //   - authCookie state.ServerCookie
-//   - conf func(sess *state.Session)
-func (_e *mockAuthService_Expecter) RegisterBOSSession(ctx interface{}, authCookie interface{}, conf interface{}) *mockAuthService_RegisterBOSSession_Call {
-	return &mockAuthService_RegisterBOSSession_Call{Call: _e.mock.On("RegisterBOSSession", ctx, authCookie, conf)}
+//   - sessCfg func(sess *state.Session)
+func (_e *mockAuthService_Expecter) RegisterBOSSession(ctx interface{}, authCookie interface{}, sessCfg interface{}) *mockAuthService_RegisterBOSSession_Call {
+	return &mockAuthService_RegisterBOSSession_Call{Call: _e.mock.On("RegisterBOSSession", ctx, authCookie, sessCfg)}
 }
 }
 
 
-func (_c *mockAuthService_RegisterBOSSession_Call) Run(run func(ctx context.Context, authCookie state.ServerCookie, conf func(sess *state.Session))) *mockAuthService_RegisterBOSSession_Call {
+func (_c *mockAuthService_RegisterBOSSession_Call) Run(run func(ctx context.Context, authCookie state.ServerCookie, sessCfg func(sess *state.Session))) *mockAuthService_RegisterBOSSession_Call {
 	_c.Call.Run(func(args mock.Arguments) {
 	_c.Call.Run(func(args mock.Arguments) {
 		var arg0 context.Context
 		var arg0 context.Context
 		if args[0] != nil {
 		if args[0] != nil {
@@ -457,7 +457,7 @@ func (_c *mockAuthService_RegisterBOSSession_Call) Return(sessionInstance *state
 	return _c
 	return _c
 }
 }
 
 
-func (_c *mockAuthService_RegisterBOSSession_Call) RunAndReturn(run func(ctx context.Context, authCookie state.ServerCookie, conf func(sess *state.Session)) (*state.SessionInstance, error)) *mockAuthService_RegisterBOSSession_Call {
+func (_c *mockAuthService_RegisterBOSSession_Call) RunAndReturn(run func(ctx context.Context, authCookie state.ServerCookie, sessCfg func(sess *state.Session)) (*state.SessionInstance, error)) *mockAuthService_RegisterBOSSession_Call {
 	_c.Call.Return(run)
 	_c.Call.Return(run)
 	return _c
 	return _c
 }
 }
@@ -498,12 +498,12 @@ type mockAuthService_RegisterChatSession_Call struct {
 // RegisterChatSession is a helper method to define mock.On call
 // RegisterChatSession is a helper method to define mock.On call
 //   - ctx context.Context
 //   - ctx context.Context
 //   - authCookie state.ServerCookie
 //   - authCookie state.ServerCookie
-//   - cfg func(sess *state.Session)
-func (_e *mockAuthService_Expecter) RegisterChatSession(ctx interface{}, authCookie interface{}, cfg interface{}) *mockAuthService_RegisterChatSession_Call {
-	return &mockAuthService_RegisterChatSession_Call{Call: _e.mock.On("RegisterChatSession", ctx, authCookie, cfg)}
+//   - sessCfg func(sess *state.Session)
+func (_e *mockAuthService_Expecter) RegisterChatSession(ctx interface{}, authCookie interface{}, sessCfg interface{}) *mockAuthService_RegisterChatSession_Call {
+	return &mockAuthService_RegisterChatSession_Call{Call: _e.mock.On("RegisterChatSession", ctx, authCookie, sessCfg)}
 }
 }
 
 
-func (_c *mockAuthService_RegisterChatSession_Call) Run(run func(ctx context.Context, authCookie state.ServerCookie, cfg func(sess *state.Session))) *mockAuthService_RegisterChatSession_Call {
+func (_c *mockAuthService_RegisterChatSession_Call) Run(run func(ctx context.Context, authCookie state.ServerCookie, sessCfg func(sess *state.Session))) *mockAuthService_RegisterChatSession_Call {
 	_c.Call.Run(func(args mock.Arguments) {
 	_c.Call.Run(func(args mock.Arguments) {
 		var arg0 context.Context
 		var arg0 context.Context
 		if args[0] != nil {
 		if args[0] != nil {
@@ -531,7 +531,7 @@ func (_c *mockAuthService_RegisterChatSession_Call) Return(sessionInstance *stat
 	return _c
 	return _c
 }
 }
 
 
-func (_c *mockAuthService_RegisterChatSession_Call) RunAndReturn(run func(ctx context.Context, authCookie state.ServerCookie, cfg func(sess *state.Session)) (*state.SessionInstance, error)) *mockAuthService_RegisterChatSession_Call {
+func (_c *mockAuthService_RegisterChatSession_Call) RunAndReturn(run func(ctx context.Context, authCookie state.ServerCookie, sessCfg func(sess *state.Session)) (*state.SessionInstance, error)) *mockAuthService_RegisterChatSession_Call {
 	_c.Call.Return(run)
 	_c.Call.Return(run)
 	return _c
 	return _c
 }
 }

+ 11 - 11
server/oscar/mock_departure_notifier_test.go

@@ -103,16 +103,16 @@ func (_c *mockDepartureNotifier_BroadcastBuddyArrived_Call) RunAndReturn(run fun
 }
 }
 
 
 // BroadcastBuddyDeparted provides a mock function for the type mockDepartureNotifier
 // BroadcastBuddyDeparted provides a mock function for the type mockDepartureNotifier
-func (_mock *mockDepartureNotifier) BroadcastBuddyDeparted(ctx context.Context, instance *state.SessionInstance) error {
-	ret := _mock.Called(ctx, instance)
+func (_mock *mockDepartureNotifier) BroadcastBuddyDeparted(ctx context.Context, screenName state.IdentScreenName) error {
+	ret := _mock.Called(ctx, screenName)
 
 
 	if len(ret) == 0 {
 	if len(ret) == 0 {
 		panic("no return value specified for BroadcastBuddyDeparted")
 		panic("no return value specified for BroadcastBuddyDeparted")
 	}
 	}
 
 
 	var r0 error
 	var r0 error
-	if returnFunc, ok := ret.Get(0).(func(context.Context, *state.SessionInstance) error); ok {
-		r0 = returnFunc(ctx, instance)
+	if returnFunc, ok := ret.Get(0).(func(context.Context, state.IdentScreenName) error); ok {
+		r0 = returnFunc(ctx, screenName)
 	} else {
 	} else {
 		r0 = ret.Error(0)
 		r0 = ret.Error(0)
 	}
 	}
@@ -126,20 +126,20 @@ type mockDepartureNotifier_BroadcastBuddyDeparted_Call struct {
 
 
 // BroadcastBuddyDeparted is a helper method to define mock.On call
 // BroadcastBuddyDeparted is a helper method to define mock.On call
 //   - ctx context.Context
 //   - ctx context.Context
-//   - instance *state.SessionInstance
-func (_e *mockDepartureNotifier_Expecter) BroadcastBuddyDeparted(ctx interface{}, instance interface{}) *mockDepartureNotifier_BroadcastBuddyDeparted_Call {
-	return &mockDepartureNotifier_BroadcastBuddyDeparted_Call{Call: _e.mock.On("BroadcastBuddyDeparted", ctx, instance)}
+//   - screenName state.IdentScreenName
+func (_e *mockDepartureNotifier_Expecter) BroadcastBuddyDeparted(ctx interface{}, screenName interface{}) *mockDepartureNotifier_BroadcastBuddyDeparted_Call {
+	return &mockDepartureNotifier_BroadcastBuddyDeparted_Call{Call: _e.mock.On("BroadcastBuddyDeparted", ctx, screenName)}
 }
 }
 
 
-func (_c *mockDepartureNotifier_BroadcastBuddyDeparted_Call) Run(run func(ctx context.Context, instance *state.SessionInstance)) *mockDepartureNotifier_BroadcastBuddyDeparted_Call {
+func (_c *mockDepartureNotifier_BroadcastBuddyDeparted_Call) Run(run func(ctx context.Context, screenName state.IdentScreenName)) *mockDepartureNotifier_BroadcastBuddyDeparted_Call {
 	_c.Call.Run(func(args mock.Arguments) {
 	_c.Call.Run(func(args mock.Arguments) {
 		var arg0 context.Context
 		var arg0 context.Context
 		if args[0] != nil {
 		if args[0] != nil {
 			arg0 = args[0].(context.Context)
 			arg0 = args[0].(context.Context)
 		}
 		}
-		var arg1 *state.SessionInstance
+		var arg1 state.IdentScreenName
 		if args[1] != nil {
 		if args[1] != nil {
-			arg1 = args[1].(*state.SessionInstance)
+			arg1 = args[1].(state.IdentScreenName)
 		}
 		}
 		run(
 		run(
 			arg0,
 			arg0,
@@ -154,7 +154,7 @@ func (_c *mockDepartureNotifier_BroadcastBuddyDeparted_Call) Return(err error) *
 	return _c
 	return _c
 }
 }
 
 
-func (_c *mockDepartureNotifier_BroadcastBuddyDeparted_Call) RunAndReturn(run func(ctx context.Context, instance *state.SessionInstance) error) *mockDepartureNotifier_BroadcastBuddyDeparted_Call {
+func (_c *mockDepartureNotifier_BroadcastBuddyDeparted_Call) RunAndReturn(run func(ctx context.Context, screenName state.IdentScreenName) error) *mockDepartureNotifier_BroadcastBuddyDeparted_Call {
 	_c.Call.Return(run)
 	_c.Call.Return(run)
 	return _c
 	return _c
 }
 }

+ 3 - 6
server/oscar/server.go

@@ -253,11 +253,8 @@ func (s oscarServer) connectToOSCARService(
 		sessCfg := func(sess *state.Session) {
 		sessCfg := func(sess *state.Session) {
 			sess.OnSessionClose(func() {
 			sess.OnSessionClose(func() {
 				if !shuttingDown(ctx) {
 				if !shuttingDown(ctx) {
-					instances := sess.Instances()
-					if len(instances) > 0 {
-						if err := s.DepartureNotifier.BroadcastBuddyDeparted(ctx, instances[0]); err != nil {
-							s.Logger.ErrorContext(ctx, "error sending buddy departure notifications", "err", err.Error())
-						}
+					if err := s.DepartureNotifier.BroadcastBuddyDeparted(ctx, sess.IdentScreenName()); err != nil {
+						s.Logger.ErrorContext(ctx, "error sending buddy departure notifications", "err", err.Error())
 					}
 					}
 				}
 				}
 
 
@@ -321,7 +318,7 @@ func (s oscarServer) connectToOSCARService(
 				return
 				return
 			}
 			}
 			if instance.Session().Invisible() {
 			if instance.Session().Invisible() {
-				if err := s.DepartureNotifier.BroadcastBuddyDeparted(ctx, instance); err != nil {
+				if err := s.DepartureNotifier.BroadcastBuddyDeparted(ctx, instance.IdentScreenName()); err != nil {
 					s.Logger.ErrorContext(ctx, "error sending buddy departure notifications", "err", err.Error())
 					s.Logger.ErrorContext(ctx, "error sending buddy departure notifications", "err", err.Error())
 				}
 				}
 			} else {
 			} else {

+ 1 - 1
server/oscar/types.go

@@ -31,7 +31,7 @@ type BuddyListRegistry interface {
 // when a client disconnects.
 // when a client disconnects.
 type DepartureNotifier interface {
 type DepartureNotifier interface {
 	BroadcastBuddyArrived(ctx context.Context, screenName state.IdentScreenName, userInfo wire.TLVUserInfo) error
 	BroadcastBuddyArrived(ctx context.Context, screenName state.IdentScreenName, userInfo wire.TLVUserInfo) error
-	BroadcastBuddyDeparted(ctx context.Context, instance *state.SessionInstance) error
+	BroadcastBuddyDeparted(ctx context.Context, screenName state.IdentScreenName) error
 }
 }
 
 
 // ChatSessionManager is the interface for closing chat sessions
 // ChatSessionManager is the interface for closing chat sessions

+ 3 - 6
server/toc/cmd_client.go

@@ -2354,11 +2354,8 @@ func (s OSCARProxy) Signon(ctx context.Context, args []byte, recalcWarning func(
 	fnCfg := func(sess *state.Session) {
 	fnCfg := func(sess *state.Session) {
 		sess.OnSessionClose(func() {
 		sess.OnSessionClose(func() {
 			if !shuttingDown(ctx) {
 			if !shuttingDown(ctx) {
-				instances := sess.Instances()
-				if len(instances) > 0 {
-					if err := s.BuddyService.BroadcastBuddyDeparted(ctx, instances[0]); err != nil {
-						s.Logger.ErrorContext(ctx, "error sending buddy departure notifications", "err", err.Error())
-					}
+				if err := s.BuddyService.BroadcastBuddyDeparted(ctx, sess.IdentScreenName()); err != nil {
+					s.Logger.ErrorContext(ctx, "error sending buddy departure notifications", "err", err.Error())
 				}
 				}
 			}
 			}
 
 
@@ -2406,7 +2403,7 @@ func (s OSCARProxy) Signon(ctx context.Context, args []byte, recalcWarning func(
 			return
 			return
 		}
 		}
 		if instance.Session().Invisible() {
 		if instance.Session().Invisible() {
-			if err := s.BuddyService.BroadcastBuddyDeparted(ctx, instance); err != nil {
+			if err := s.BuddyService.BroadcastBuddyDeparted(ctx, instance.IdentScreenName()); err != nil {
 				s.Logger.ErrorContext(ctx, "error sending buddy departure notifications", "err", err.Error())
 				s.Logger.ErrorContext(ctx, "error sending buddy departure notifications", "err", err.Error())
 			}
 			}
 		} else {
 		} else {

+ 11 - 11
server/toc/mock_buddy_service_test.go

@@ -166,16 +166,16 @@ func (_c *mockBuddyService_BroadcastBuddyArrived_Call) RunAndReturn(run func(ctx
 }
 }
 
 
 // BroadcastBuddyDeparted provides a mock function for the type mockBuddyService
 // BroadcastBuddyDeparted provides a mock function for the type mockBuddyService
-func (_mock *mockBuddyService) BroadcastBuddyDeparted(ctx context.Context, instance *state.SessionInstance) error {
-	ret := _mock.Called(ctx, instance)
+func (_mock *mockBuddyService) BroadcastBuddyDeparted(ctx context.Context, screenName state.IdentScreenName) error {
+	ret := _mock.Called(ctx, screenName)
 
 
 	if len(ret) == 0 {
 	if len(ret) == 0 {
 		panic("no return value specified for BroadcastBuddyDeparted")
 		panic("no return value specified for BroadcastBuddyDeparted")
 	}
 	}
 
 
 	var r0 error
 	var r0 error
-	if returnFunc, ok := ret.Get(0).(func(context.Context, *state.SessionInstance) error); ok {
-		r0 = returnFunc(ctx, instance)
+	if returnFunc, ok := ret.Get(0).(func(context.Context, state.IdentScreenName) error); ok {
+		r0 = returnFunc(ctx, screenName)
 	} else {
 	} else {
 		r0 = ret.Error(0)
 		r0 = ret.Error(0)
 	}
 	}
@@ -189,20 +189,20 @@ type mockBuddyService_BroadcastBuddyDeparted_Call struct {
 
 
 // BroadcastBuddyDeparted is a helper method to define mock.On call
 // BroadcastBuddyDeparted is a helper method to define mock.On call
 //   - ctx context.Context
 //   - ctx context.Context
-//   - instance *state.SessionInstance
-func (_e *mockBuddyService_Expecter) BroadcastBuddyDeparted(ctx interface{}, instance interface{}) *mockBuddyService_BroadcastBuddyDeparted_Call {
-	return &mockBuddyService_BroadcastBuddyDeparted_Call{Call: _e.mock.On("BroadcastBuddyDeparted", ctx, instance)}
+//   - screenName state.IdentScreenName
+func (_e *mockBuddyService_Expecter) BroadcastBuddyDeparted(ctx interface{}, screenName interface{}) *mockBuddyService_BroadcastBuddyDeparted_Call {
+	return &mockBuddyService_BroadcastBuddyDeparted_Call{Call: _e.mock.On("BroadcastBuddyDeparted", ctx, screenName)}
 }
 }
 
 
-func (_c *mockBuddyService_BroadcastBuddyDeparted_Call) Run(run func(ctx context.Context, instance *state.SessionInstance)) *mockBuddyService_BroadcastBuddyDeparted_Call {
+func (_c *mockBuddyService_BroadcastBuddyDeparted_Call) Run(run func(ctx context.Context, screenName state.IdentScreenName)) *mockBuddyService_BroadcastBuddyDeparted_Call {
 	_c.Call.Run(func(args mock.Arguments) {
 	_c.Call.Run(func(args mock.Arguments) {
 		var arg0 context.Context
 		var arg0 context.Context
 		if args[0] != nil {
 		if args[0] != nil {
 			arg0 = args[0].(context.Context)
 			arg0 = args[0].(context.Context)
 		}
 		}
-		var arg1 *state.SessionInstance
+		var arg1 state.IdentScreenName
 		if args[1] != nil {
 		if args[1] != nil {
-			arg1 = args[1].(*state.SessionInstance)
+			arg1 = args[1].(state.IdentScreenName)
 		}
 		}
 		run(
 		run(
 			arg0,
 			arg0,
@@ -217,7 +217,7 @@ func (_c *mockBuddyService_BroadcastBuddyDeparted_Call) Return(err error) *mockB
 	return _c
 	return _c
 }
 }
 
 
-func (_c *mockBuddyService_BroadcastBuddyDeparted_Call) RunAndReturn(run func(ctx context.Context, instance *state.SessionInstance) error) *mockBuddyService_BroadcastBuddyDeparted_Call {
+func (_c *mockBuddyService_BroadcastBuddyDeparted_Call) RunAndReturn(run func(ctx context.Context, screenName state.IdentScreenName) error) *mockBuddyService_BroadcastBuddyDeparted_Call {
 	_c.Call.Return(run)
 	_c.Call.Return(run)
 	return _c
 	return _c
 }
 }

+ 1 - 1
server/toc/types.go

@@ -14,7 +14,7 @@ import (
 type BuddyService interface {
 type BuddyService interface {
 	AddBuddies(ctx context.Context, instance *state.SessionInstance, inBody wire.SNAC_0x03_0x04_BuddyAddBuddies) error
 	AddBuddies(ctx context.Context, instance *state.SessionInstance, inBody wire.SNAC_0x03_0x04_BuddyAddBuddies) error
 	BroadcastBuddyArrived(ctx context.Context, screenName state.IdentScreenName, userInfo wire.TLVUserInfo) error
 	BroadcastBuddyArrived(ctx context.Context, screenName state.IdentScreenName, userInfo wire.TLVUserInfo) error
-	BroadcastBuddyDeparted(ctx context.Context, instance *state.SessionInstance) error
+	BroadcastBuddyDeparted(ctx context.Context, screenName state.IdentScreenName) error
 	DelBuddies(ctx context.Context, instance *state.SessionInstance, inBody wire.SNAC_0x03_0x05_BuddyDelBuddies) error
 	DelBuddies(ctx context.Context, instance *state.SessionInstance, inBody wire.SNAC_0x03_0x05_BuddyDelBuddies) error
 	RightsQuery(ctx context.Context, inFrame wire.SNACFrame) wire.SNACMessage
 	RightsQuery(ctx context.Context, inFrame wire.SNACFrame) wire.SNACMessage
 }
 }

+ 2 - 2
server/webapi/handlers/presence.go

@@ -26,7 +26,7 @@ type PresenceHandler struct {
 // BuddyBroadcaster broadcasts buddy presence updates
 // BuddyBroadcaster broadcasts buddy presence updates
 type BuddyBroadcaster interface {
 type BuddyBroadcaster interface {
 	BroadcastBuddyArrived(ctx context.Context, screenName state.IdentScreenName, userInfo wire.TLVUserInfo) error
 	BroadcastBuddyArrived(ctx context.Context, screenName state.IdentScreenName, userInfo wire.TLVUserInfo) error
-	BroadcastBuddyDeparted(ctx context.Context, instance *state.SessionInstance) error
+	BroadcastBuddyDeparted(ctx context.Context, screenName state.IdentScreenName) error
 }
 }
 
 
 // ProfileManager manages user profiles (uses types.ProfileManager)
 // ProfileManager manages user profiles (uses types.ProfileManager)
@@ -403,7 +403,7 @@ func (h *PresenceHandler) SetState(w http.ResponseWriter, r *http.Request) {
 	// Broadcast presence update
 	// Broadcast presence update
 	if statusBitmask&wire.OServiceUserStatusInvisible != 0 {
 	if statusBitmask&wire.OServiceUserStatusInvisible != 0 {
 		// User going invisible - broadcast departure
 		// User going invisible - broadcast departure
-		if err := h.BuddyBroadcaster.BroadcastBuddyDeparted(ctx, oscarSession); err != nil {
+		if err := h.BuddyBroadcaster.BroadcastBuddyDeparted(ctx, oscarSession.IdentScreenName()); err != nil {
 			h.Logger.ErrorContext(ctx, "failed to broadcast buddy departed", "err", err.Error())
 			h.Logger.ErrorContext(ctx, "failed to broadcast buddy departed", "err", err.Error())
 		}
 		}
 	} else {
 	} else {

+ 2 - 2
server/webapi/handlers/presence_test.go

@@ -46,8 +46,8 @@ func (m *MockBuddyBroadcaster) BroadcastBuddyArrived(ctx context.Context, screen
 	return args.Error(0)
 	return args.Error(0)
 }
 }
 
 
-func (m *MockBuddyBroadcaster) BroadcastBuddyDeparted(ctx context.Context, instance *state.SessionInstance) error {
-	args := m.Called(ctx, instance)
+func (m *MockBuddyBroadcaster) BroadcastBuddyDeparted(ctx context.Context, screenName state.IdentScreenName) error {
+	args := m.Called(ctx, screenName)
 	return args.Error(0)
 	return args.Error(0)
 }
 }
 
 

+ 1 - 1
server/webapi/handlers/session.go

@@ -511,7 +511,7 @@ func (h *SessionHandler) EndSession(w http.ResponseWriter, r *http.Request) {
 	if session.OSCARSession != nil && h.OSCARSessionManager != nil {
 	if session.OSCARSession != nil && h.OSCARSessionManager != nil {
 		// Broadcast departure to OSCAR clients
 		// Broadcast departure to OSCAR clients
 		if h.BuddyBroadcaster != nil {
 		if h.BuddyBroadcaster != nil {
-			if err := h.BuddyBroadcaster.BroadcastBuddyDeparted(ctx, session.OSCARSession); err != nil {
+			if err := h.BuddyBroadcaster.BroadcastBuddyDeparted(ctx, session.OSCARSession.IdentScreenName()); err != nil {
 				h.Logger.ErrorContext(ctx, "failed to broadcast buddy departure", "err", err.Error())
 				h.Logger.ErrorContext(ctx, "failed to broadcast buddy departure", "err", err.Error())
 			}
 			}
 		}
 		}

+ 2 - 2
server/webapi/types.go

@@ -13,7 +13,7 @@ import (
 
 
 type BuddyService interface {
 type BuddyService interface {
 	AddBuddies(ctx context.Context, instance *state.SessionInstance, inBody wire.SNAC_0x03_0x04_BuddyAddBuddies) error
 	AddBuddies(ctx context.Context, instance *state.SessionInstance, inBody wire.SNAC_0x03_0x04_BuddyAddBuddies) error
-	BroadcastBuddyDeparted(ctx context.Context, instance *state.SessionInstance) error
+	BroadcastBuddyDeparted(ctx context.Context, screenName state.IdentScreenName) error
 	DelBuddies(ctx context.Context, instance *state.SessionInstance, inBody wire.SNAC_0x03_0x05_BuddyDelBuddies) error
 	DelBuddies(ctx context.Context, instance *state.SessionInstance, inBody wire.SNAC_0x03_0x05_BuddyDelBuddies) error
 	RightsQuery(ctx context.Context, inFrame wire.SNACFrame) wire.SNACMessage
 	RightsQuery(ctx context.Context, inFrame wire.SNACFrame) wire.SNACMessage
 }
 }
@@ -143,7 +143,7 @@ type OfflineMessageManager interface {
 // BuddyBroadcaster broadcasts buddy presence updates
 // BuddyBroadcaster broadcasts buddy presence updates
 type BuddyBroadcaster interface {
 type BuddyBroadcaster interface {
 	BroadcastBuddyArrived(ctx context.Context, screenName state.IdentScreenName, userInfo wire.TLVUserInfo) error
 	BroadcastBuddyArrived(ctx context.Context, screenName state.IdentScreenName, userInfo wire.TLVUserInfo) error
-	BroadcastBuddyDeparted(ctx context.Context, instance *state.SessionInstance) error
+	BroadcastBuddyDeparted(ctx context.Context, screenName state.IdentScreenName) error
 }
 }
 
 
 // ProfileManager manages user profiles
 // ProfileManager manages user profiles

+ 3 - 0
state/session_test.go

@@ -1744,9 +1744,12 @@ func TestSession_CloseInstance(t *testing.T) {
 	instance3 := s.AddInstance()
 	instance3 := s.AddInstance()
 
 
 	instance1.OnClose(func() {
 	instance1.OnClose(func() {
+		// ensure instance is removed from the session before calling this func
+		assert.Equal(t, 2, s.InstanceCount())
 		instance1CloseCount++
 		instance1CloseCount++
 	})
 	})
 	instance2.OnClose(func() {
 	instance2.OnClose(func() {
+		assert.Equal(t, 1, s.InstanceCount())
 		instance2CloseCount++
 		instance2CloseCount++
 	})
 	})
 	instance3.OnClose(func() {
 	instance3.OnClose(func() {