فهرست منبع

add support for user-defined screen name formatting

This commit introduces two distinct screen name data types:
- IdentScreenName: A normalized version of the screen name used as a
unique identifier. This is stored in the database and used for login
validation, ensuring that users can log in regardless of the format
they use.
- DisplayScreenName: Maintains the user-defined formatting, including
case and spaces, allowing for personalized display.

Additionally, a database migration is included, which adds a new field
identScreenName, for storing the IdentScreenName. The screenName
field is renamed to displayScreenName, which stores the value of
DisplayScreenName.

These changes allow users to register and log in with any screen name
format, as long as the normalized version matches the identifier
stored in the database.

From the API perspective, users continue to have the "screen_name"
field, which is the display screen name.
Mike 2 سال پیش
والد
کامیت
e217539ed1
47فایلهای تغییر یافته به همراه1373 افزوده شده و 1057 حذف شده
  1. 4 4
      README.md
  2. 24 13
      foodgroup/auth.go
  3. 102 85
      foodgroup/auth_test.go
  4. 2 2
      foodgroup/bart_test.go
  5. 12 12
      foodgroup/buddy.go
  6. 44 38
      foodgroup/buddy_test.go
  7. 2 2
      foodgroup/chat.go
  8. 1 1
      foodgroup/chat_nav.go
  9. 2 1
      foodgroup/chat_nav_test.go
  10. 12 12
      foodgroup/feedbag.go
  11. 73 70
      foodgroup/feedbag_test.go
  12. 14 12
      foodgroup/icbm.go
  13. 22 29
      foodgroup/icbm_test.go
  14. 6 4
      foodgroup/locate.go
  15. 22 22
      foodgroup/locate_test.go
  16. 16 16
      foodgroup/mock_chat_message_relayer_test.go
  17. 59 59
      foodgroup/mock_feedbag_manager_test.go
  18. 39 36
      foodgroup/mock_legacy_buddy_list_manager_test.go
  19. 16 16
      foodgroup/mock_message_relayer_test.go
  20. 18 15
      foodgroup/mock_profile_manager_test.go
  21. 17 17
      foodgroup/mock_session_manager_test.go
  22. 8 8
      foodgroup/mock_user_manager_manager_test.go
  23. 5 5
      foodgroup/oservice.go
  24. 36 30
      foodgroup/oservice_test.go
  25. 37 37
      foodgroup/test_helpers.go
  26. 20 20
      foodgroup/types.go
  27. 38 54
      server/http/mgmt_api.go
  28. 48 30
      server/http/mgmt_api_test.go
  29. 14 14
      server/http/mock_user_manager_test.go
  30. 3 3
      server/oscar/auth.go
  31. 4 3
      server/oscar/bos.go
  32. 1 1
      server/oscar/bos_test.go
  33. 1 1
      server/oscar/chat.go
  34. 1 1
      server/oscar/connection_test.go
  35. 1 1
      server/oscar/middleware/logger.go
  36. 38 38
      server/oscar/mock_auth_test.go
  37. 10 10
      state/buddy_list_store.go
  38. 49 38
      state/buddy_list_store_test.go
  39. 16 0
      state/migrations/0003_formatted_screen_name.down.sql
  40. 27 0
      state/migrations/0003_formatted_screen_name.up.sql
  41. 37 22
      state/session.go
  42. 20 18
      state/session_manager.go
  43. 43 52
      state/session_manager_test.go
  44. 6 5
      state/session_test.go
  45. 122 0
      state/user.go
  46. 68 123
      state/user_store.go
  47. 213 77
      state/user_store_test.go

+ 4 - 4
README.md

@@ -65,7 +65,7 @@ Invoke-WebRequest -Uri http://localhost:8080/user -Method Get
 
 ```powershell
 Invoke-WebRequest -Uri http://localhost:8080/user `
-  -Body '{"screen_name":"myscreenname", "password":"thepassword"}' `
+  -Body '{"screen_name":"MyScreenName", "password":"thepassword"}' `
   -Method Post `
   -ContentType "application/json"
 ```
@@ -83,7 +83,7 @@ Invoke-WebRequest -Uri http://localhost:8080/user `
 
 ```powershell
 Invoke-WebRequest -Uri http://localhost:8080/user/password `
-  -Body '{"screen_name":"myscreenname", "password":"thenewpassword"}' `
+  -Body '{"screen_name":"MyScreenName", "password":"thenewpassword"}' `
   -Method Put `
   -ContentType "application/json"
 ```
@@ -107,7 +107,7 @@ curl http://localhost:8080/user
 #### Create Users
 
 ```shell
-curl -d'{"screen_name":"myscreenname", "password":"thepassword"}' http://localhost:8080/user
+curl -d'{"screen_name":"MyScreenName", "password":"thepassword"}' http://localhost:8080/user
 ```
 
 #### Delete Users
@@ -119,7 +119,7 @@ curl -X DELETE -d '{"screen_name": "user123"}' http://localhost:8080/user
 #### Change Password
 
 ```shell
-curl -X PUT -d'{"screen_name":"myscreenname", "password":"thenewpassword"}' http://localhost:8080/user/password
+curl -X PUT -d'{"screen_name":"MyScreenName", "password":"thenewpassword"}' http://localhost:8080/user/password
 ```
 
 #### List Active Sessions

+ 24 - 13
foodgroup/auth.go

@@ -58,14 +58,28 @@ func (s AuthService) RegisterChatSession(loginCookie []byte) (*state.Session, er
 	if err != nil {
 		return nil, err
 	}
-	chatSess := chatSessMgr.(SessionManager).AddSession(c.ScreenName)
+	u, err := s.userManager.User(state.NewIdentScreenName(c.ScreenName))
+	if err != nil {
+		return nil, fmt.Errorf("failed to retrieve user: %w", err)
+	}
+	if u == nil {
+		return nil, fmt.Errorf("user not found")
+	}
+	chatSess := chatSessMgr.(SessionManager).AddSession(u.DisplayScreenName)
 	chatSess.SetChatRoomCookie(room.Cookie)
 	return chatSess, nil
 }
 
 // RegisterBOSSession creates and returns a user's session.
-func (s AuthService) RegisterBOSSession(sessionID string) (*state.Session, error) {
-	return s.sessionManager.AddSession(sessionID), nil
+func (s AuthService) RegisterBOSSession(screenName state.IdentScreenName) (*state.Session, error) {
+	u, err := s.userManager.User(screenName)
+	if err != nil {
+		return nil, fmt.Errorf("failed to retrieve user: %w", err)
+	}
+	if u == nil {
+		return nil, fmt.Errorf("user not found")
+	}
+	return s.sessionManager.AddSession(u.DisplayScreenName), nil
 }
 
 // Signout removes this user's session and notifies users who have this user on
@@ -75,7 +89,7 @@ func (s AuthService) Signout(ctx context.Context, sess *state.Session) error {
 		return err
 	}
 	s.sessionManager.RemoveSession(sess)
-	s.legacyBuddyListManager.DeleteUser(sess.ScreenName())
+	s.legacyBuddyListManager.DeleteUser(sess.IdentScreenName())
 	return nil
 }
 
@@ -113,7 +127,7 @@ func (s AuthService) BUCPChallenge(
 
 	var authKey string
 
-	user, err := s.userManager.User(screenName)
+	user, err := s.userManager.User(state.NewIdentScreenName(screenName))
 	if err != nil {
 		return wire.SNACMessage{}, err
 	}
@@ -163,10 +177,7 @@ func (s AuthService) BUCPChallenge(
 // (wire.LoginTLVTagsReconnectHere) and an authorization cookie
 // (wire.LoginTLVTagsAuthorizationCookie). Else, an error code is set
 // (wire.LoginTLVTagsErrorSubcode).
-func (s AuthService) BUCPLogin(
-	bodyIn wire.SNAC_0x17_0x02_BUCPLoginRequest,
-	newUserFn func(screenName string) (state.User, error),
-) (wire.SNACMessage, error) {
+func (s AuthService) BUCPLogin(bodyIn wire.SNAC_0x17_0x02_BUCPLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error)) (wire.SNACMessage, error) {
 
 	block, err := s.login(bodyIn.TLVList, newUserFn)
 	if err != nil {
@@ -194,7 +205,7 @@ func (s AuthService) BUCPLogin(
 // (wire.LoginTLVTagsReconnectHere) and an authorization cookie
 // (wire.LoginTLVTagsAuthorizationCookie). Else, an error code is set
 // (wire.LoginTLVTagsErrorSubcode).
-func (s AuthService) FLAPLogin(frame wire.FLAPSignonFrame, newUserFn func(screenName string) (state.User, error)) (wire.TLVRestBlock, error) {
+func (s AuthService) FLAPLogin(frame wire.FLAPSignonFrame, newUserFn func(screenName state.DisplayScreenName) (state.User, error)) (wire.TLVRestBlock, error) {
 	return s.login(frame.TLVList, newUserFn)
 }
 
@@ -202,7 +213,7 @@ func (s AuthService) FLAPLogin(frame wire.FLAPSignonFrame, newUserFn func(screen
 // metadata used in both BUCP and FLAP authentication responses.
 func (s AuthService) login(
 	TLVList wire.TLVList,
-	newUserFn func(screenName string) (state.User, error),
+	newUserFn func(screenName state.DisplayScreenName) (state.User, error),
 ) (wire.TLVRestBlock, error) {
 
 	screenName, found := TLVList.String(wire.LoginTLVTagsScreenName)
@@ -210,7 +221,7 @@ func (s AuthService) login(
 		return wire.TLVRestBlock{}, errors.New("screen name doesn't exist in tlv")
 	}
 
-	user, err := s.userManager.User(screenName)
+	user, err := s.userManager.User(state.NewIdentScreenName(screenName))
 	if err != nil {
 		return wire.TLVRestBlock{}, err
 	}
@@ -230,7 +241,7 @@ func (s AuthService) login(
 		if !loginOK {
 			// make login succeed anyway. create new user if the account
 			// doesn't already exist.
-			newUser, err := newUserFn(screenName)
+			newUser, err := newUserFn(state.DisplayScreenName(screenName))
 			if err != nil {
 				return wire.TLVRestBlock{}, err
 			}

+ 102 - 85
foodgroup/auth_test.go

@@ -16,8 +16,8 @@ import (
 
 func TestAuthService_BUCPLoginRequest(t *testing.T) {
 	user := state.User{
-		ScreenName: "screen_name",
-		AuthKey:    "auth_key",
+		IdentScreenName: state.NewIdentScreenName("screen_name"),
+		AuthKey:         "auth_key",
 	}
 	assert.NoError(t, user.HashPassword("the_password"))
 
@@ -32,7 +32,7 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 		// method's dependencies
 		mockParams mockParams
 		// newUserFn is the function that registers a new user account
-		newUserFn func(screenName string) (state.User, error)
+		newUserFn func(screenName state.DisplayScreenName) (state.User, error)
 		// expectOutput is the SNAC sent from the server to client
 		expectOutput wire.SNACMessage
 		// wantErr is the error we expect from the method
@@ -48,7 +48,7 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
 						wire.NewTLV(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
-						wire.NewTLV(wire.LoginTLVTagsScreenName, user.ScreenName),
+						wire.NewTLV(wire.LoginTLVTagsScreenName, user.IdentScreenName),
 					},
 				},
 			},
@@ -56,14 +56,14 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				userManagerParams: userManagerParams{
 					getUserParams: getUserParams{
 						{
-							screenName: user.ScreenName,
+							screenName: user.IdentScreenName,
 							result:     &user,
 						},
 					},
 				},
 				cookieIssuerParams: cookieIssuerParams{
 					{
-						data:   []byte(user.ScreenName),
+						data:   []byte(user.IdentScreenName.String()),
 						cookie: []byte("the-cookie"),
 					},
 				},
@@ -76,7 +76,7 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.LoginTLVTagsScreenName, user.ScreenName),
+							wire.NewTLV(wire.LoginTLVTagsScreenName, user.IdentScreenName),
 							wire.NewTLV(wire.LoginTLVTagsReconnectHere, "127.0.0.1:1234"),
 							wire.NewTLV(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
 						},
@@ -95,7 +95,7 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
 						wire.NewTLV(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
-						wire.NewTLV(wire.LoginTLVTagsScreenName, user.ScreenName),
+						wire.NewTLV(wire.LoginTLVTagsScreenName, user.IdentScreenName),
 					},
 				},
 			},
@@ -103,7 +103,7 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				userManagerParams: userManagerParams{
 					getUserParams: getUserParams{
 						{
-							screenName: user.ScreenName,
+							screenName: user.IdentScreenName,
 							result:     nil,
 						},
 					},
@@ -115,12 +115,12 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				},
 				cookieIssuerParams: cookieIssuerParams{
 					{
-						data:   []byte(user.ScreenName),
+						data:   []byte(user.IdentScreenName.String()),
 						cookie: []byte("the-cookie"),
 					},
 				},
 			},
-			newUserFn: func(screenName string) (state.User, error) {
+			newUserFn: func(screenName state.DisplayScreenName) (state.User, error) {
 				return user, nil
 			},
 			expectOutput: wire.SNACMessage{
@@ -131,7 +131,7 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.LoginTLVTagsScreenName, user.ScreenName),
+							wire.NewTLV(wire.LoginTLVTagsScreenName, user.IdentScreenName),
 							wire.NewTLV(wire.LoginTLVTagsReconnectHere, "127.0.0.1:1234"),
 							wire.NewTLV(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
 						},
@@ -150,7 +150,7 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
 						wire.NewTLV(wire.LoginTLVTagsPasswordHash, []byte("bad-password-hash")),
-						wire.NewTLV(wire.LoginTLVTagsScreenName, user.ScreenName),
+						wire.NewTLV(wire.LoginTLVTagsScreenName, user.IdentScreenName),
 					},
 				},
 			},
@@ -158,7 +158,7 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				userManagerParams: userManagerParams{
 					getUserParams: getUserParams{
 						{
-							screenName: user.ScreenName,
+							screenName: user.IdentScreenName,
 							result:     &user,
 						},
 					},
@@ -170,12 +170,12 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				},
 				cookieIssuerParams: cookieIssuerParams{
 					{
-						data:   []byte(user.ScreenName),
+						data:   []byte(user.IdentScreenName.String()),
 						cookie: []byte("the-cookie"),
 					},
 				},
 			},
-			newUserFn: func(screenName string) (state.User, error) {
+			newUserFn: func(screenName state.DisplayScreenName) (state.User, error) {
 				return user, nil
 			},
 			expectOutput: wire.SNACMessage{
@@ -186,7 +186,7 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.LoginTLVTagsScreenName, user.ScreenName),
+							wire.NewTLV(wire.LoginTLVTagsScreenName, user.IdentScreenName),
 							wire.NewTLV(wire.LoginTLVTagsReconnectHere, "127.0.0.1:1234"),
 							wire.NewTLV(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
 						},
@@ -205,7 +205,7 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
 						wire.NewTLV(wire.LoginTLVTagsPasswordHash, []byte("bad-password-hash")),
-						wire.NewTLV(wire.LoginTLVTagsScreenName, user.ScreenName),
+						wire.NewTLV(wire.LoginTLVTagsScreenName, user.IdentScreenName),
 					},
 				},
 			},
@@ -213,7 +213,7 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				userManagerParams: userManagerParams{
 					getUserParams: getUserParams{
 						{
-							screenName: user.ScreenName,
+							screenName: user.IdentScreenName,
 							result:     &user,
 						},
 					},
@@ -226,12 +226,12 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				},
 				cookieIssuerParams: cookieIssuerParams{
 					{
-						data:   []byte(user.ScreenName),
+						data:   []byte(user.IdentScreenName.String()),
 						cookie: []byte("the-cookie"),
 					},
 				},
 			},
-			newUserFn: func(screenName string) (state.User, error) {
+			newUserFn: func(screenName state.DisplayScreenName) (state.User, error) {
 				return user, nil
 			},
 			expectOutput: wire.SNACMessage{
@@ -242,7 +242,7 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.LoginTLVTagsScreenName, user.ScreenName),
+							wire.NewTLV(wire.LoginTLVTagsScreenName, user.IdentScreenName),
 							wire.NewTLV(wire.LoginTLVTagsReconnectHere, "127.0.0.1:1234"),
 							wire.NewTLV(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
 						},
@@ -259,7 +259,7 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
 						wire.NewTLV(wire.LoginTLVTagsPasswordHash, []byte("bad-password-hash")),
-						wire.NewTLV(wire.LoginTLVTagsScreenName, user.ScreenName),
+						wire.NewTLV(wire.LoginTLVTagsScreenName, user.IdentScreenName),
 					},
 				},
 			},
@@ -267,13 +267,13 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				userManagerParams: userManagerParams{
 					getUserParams: getUserParams{
 						{
-							screenName: user.ScreenName,
+							screenName: user.IdentScreenName,
 							result:     &user,
 						},
 					},
 				},
 			},
-			newUserFn: func(screenName string) (state.User, error) {
+			newUserFn: func(screenName state.DisplayScreenName) (state.User, error) {
 				return user, io.EOF
 			},
 			wantErr: io.EOF,
@@ -287,7 +287,7 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
 						wire.NewTLV(wire.LoginTLVTagsPasswordHash, []byte("bad-password-hash")),
-						wire.NewTLV(wire.LoginTLVTagsScreenName, user.ScreenName),
+						wire.NewTLV(wire.LoginTLVTagsScreenName, user.IdentScreenName),
 					},
 				},
 			},
@@ -295,7 +295,7 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				userManagerParams: userManagerParams{
 					getUserParams: getUserParams{
 						{
-							screenName: user.ScreenName,
+							screenName: user.IdentScreenName,
 							result:     &user,
 						},
 					},
@@ -307,7 +307,7 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 					},
 				},
 			},
-			newUserFn: func(screenName string) (state.User, error) {
+			newUserFn: func(screenName state.DisplayScreenName) (state.User, error) {
 				return user, nil
 			},
 			wantErr: io.EOF,
@@ -322,7 +322,7 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
 						wire.NewTLV(wire.LoginTLVTagsPasswordHash, []byte("bad_password")),
-						wire.NewTLV(wire.LoginTLVTagsScreenName, user.ScreenName),
+						wire.NewTLV(wire.LoginTLVTagsScreenName, user.IdentScreenName),
 					},
 				},
 			},
@@ -330,7 +330,7 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				userManagerParams: userManagerParams{
 					getUserParams: getUserParams{
 						{
-							screenName: user.ScreenName,
+							screenName: user.IdentScreenName,
 							result:     nil,
 						},
 					},
@@ -344,7 +344,7 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.LoginTLVTagsScreenName, user.ScreenName),
+							wire.NewTLV(wire.LoginTLVTagsScreenName, user.IdentScreenName),
 							wire.NewTLV(wire.LoginTLVTagsErrorSubcode, uint16(0x01)),
 						},
 					},
@@ -357,7 +357,7 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
 						wire.NewTLV(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
-						wire.NewTLV(wire.LoginTLVTagsScreenName, user.ScreenName),
+						wire.NewTLV(wire.LoginTLVTagsScreenName, user.IdentScreenName),
 					},
 				},
 			},
@@ -365,7 +365,7 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				userManagerParams: userManagerParams{
 					getUserParams: getUserParams{
 						{
-							screenName: user.ScreenName,
+							screenName: user.IdentScreenName,
 							err:        io.EOF,
 						},
 					},
@@ -411,8 +411,8 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 
 func TestAuthService_FLAPLoginResponse(t *testing.T) {
 	user := state.User{
-		ScreenName: "screen_name",
-		AuthKey:    "auth_key",
+		IdentScreenName: state.NewIdentScreenName("screen_name"),
+		AuthKey:         "auth_key",
 	}
 	assert.NoError(t, user.HashPassword("the_password"))
 
@@ -430,7 +430,7 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 		// method's dependencies
 		mockParams mockParams
 		// newUserFn is the function that registers a new user account
-		newUserFn func(screenName string) (state.User, error)
+		newUserFn func(screenName state.DisplayScreenName) (state.User, error)
 		// expectOutput is the response sent from the server to client
 		expectOutput wire.TLVRestBlock
 		// wantErr is the error we expect from the method
@@ -446,7 +446,7 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
 						wire.NewTLV(wire.LoginTLVTagsRoastedPassword, roastedPassword),
-						wire.NewTLV(wire.LoginTLVTagsScreenName, user.ScreenName),
+						wire.NewTLV(wire.LoginTLVTagsScreenName, user.IdentScreenName),
 					},
 				},
 			},
@@ -454,21 +454,21 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 				userManagerParams: userManagerParams{
 					getUserParams: getUserParams{
 						{
-							screenName: user.ScreenName,
+							screenName: user.IdentScreenName,
 							result:     &user,
 						},
 					},
 				},
 				cookieIssuerParams: cookieIssuerParams{
 					{
-						data:   []byte(user.ScreenName),
+						data:   []byte(user.IdentScreenName.String()),
 						cookie: []byte("the-cookie"),
 					},
 				},
 			},
 			expectOutput: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(wire.LoginTLVTagsScreenName, user.ScreenName),
+					wire.NewTLV(wire.LoginTLVTagsScreenName, user.IdentScreenName),
 					wire.NewTLV(wire.LoginTLVTagsReconnectHere, "127.0.0.1:1234"),
 					wire.NewTLV(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
 				},
@@ -485,7 +485,7 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
 						wire.NewTLV(wire.LoginTLVTagsRoastedPassword, roastedPassword),
-						wire.NewTLV(wire.LoginTLVTagsScreenName, user.ScreenName),
+						wire.NewTLV(wire.LoginTLVTagsScreenName, user.IdentScreenName),
 					},
 				},
 			},
@@ -493,7 +493,7 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 				userManagerParams: userManagerParams{
 					getUserParams: getUserParams{
 						{
-							screenName: user.ScreenName,
+							screenName: user.IdentScreenName,
 							result:     nil,
 						},
 					},
@@ -505,17 +505,17 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 				},
 				cookieIssuerParams: cookieIssuerParams{
 					{
-						data:   []byte(user.ScreenName),
+						data:   []byte(user.IdentScreenName.String()),
 						cookie: []byte("the-cookie"),
 					},
 				},
 			},
-			newUserFn: func(screenName string) (state.User, error) {
+			newUserFn: func(screenName state.DisplayScreenName) (state.User, error) {
 				return user, nil
 			},
 			expectOutput: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(wire.LoginTLVTagsScreenName, user.ScreenName),
+					wire.NewTLV(wire.LoginTLVTagsScreenName, user.IdentScreenName),
 					wire.NewTLV(wire.LoginTLVTagsReconnectHere, "127.0.0.1:1234"),
 					wire.NewTLV(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
 				},
@@ -532,7 +532,7 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
 						wire.NewTLV(wire.LoginTLVTagsPasswordHash, []byte("bad-roasted-password")),
-						wire.NewTLV(wire.LoginTLVTagsScreenName, user.ScreenName),
+						wire.NewTLV(wire.LoginTLVTagsScreenName, user.IdentScreenName),
 					},
 				},
 			},
@@ -540,7 +540,7 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 				userManagerParams: userManagerParams{
 					getUserParams: getUserParams{
 						{
-							screenName: user.ScreenName,
+							screenName: user.IdentScreenName,
 							result:     &user,
 						},
 					},
@@ -552,17 +552,17 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 				},
 				cookieIssuerParams: cookieIssuerParams{
 					{
-						data:   []byte(user.ScreenName),
+						data:   []byte(user.IdentScreenName.String()),
 						cookie: []byte("the-cookie"),
 					},
 				},
 			},
-			newUserFn: func(screenName string) (state.User, error) {
+			newUserFn: func(screenName state.DisplayScreenName) (state.User, error) {
 				return user, nil
 			},
 			expectOutput: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(wire.LoginTLVTagsScreenName, user.ScreenName),
+					wire.NewTLV(wire.LoginTLVTagsScreenName, user.IdentScreenName),
 					wire.NewTLV(wire.LoginTLVTagsReconnectHere, "127.0.0.1:1234"),
 					wire.NewTLV(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
 				},
@@ -579,7 +579,7 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
 						wire.NewTLV(wire.LoginTLVTagsPasswordHash, []byte("bad-roasted-password")),
-						wire.NewTLV(wire.LoginTLVTagsScreenName, user.ScreenName),
+						wire.NewTLV(wire.LoginTLVTagsScreenName, user.IdentScreenName),
 					},
 				},
 			},
@@ -587,7 +587,7 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 				userManagerParams: userManagerParams{
 					getUserParams: getUserParams{
 						{
-							screenName: user.ScreenName,
+							screenName: user.IdentScreenName,
 							result:     &user,
 						},
 					},
@@ -600,17 +600,17 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 				},
 				cookieIssuerParams: cookieIssuerParams{
 					{
-						data:   []byte(user.ScreenName),
+						data:   []byte(user.IdentScreenName.String()),
 						cookie: []byte("the-cookie"),
 					},
 				},
 			},
-			newUserFn: func(screenName string) (state.User, error) {
+			newUserFn: func(screenName state.DisplayScreenName) (state.User, error) {
 				return user, nil
 			},
 			expectOutput: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(wire.LoginTLVTagsScreenName, user.ScreenName),
+					wire.NewTLV(wire.LoginTLVTagsScreenName, user.IdentScreenName),
 					wire.NewTLV(wire.LoginTLVTagsReconnectHere, "127.0.0.1:1234"),
 					wire.NewTLV(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
 				},
@@ -625,7 +625,7 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
 						wire.NewTLV(wire.LoginTLVTagsPasswordHash, []byte("bad-roasted-password")),
-						wire.NewTLV(wire.LoginTLVTagsScreenName, user.ScreenName),
+						wire.NewTLV(wire.LoginTLVTagsScreenName, user.IdentScreenName),
 					},
 				},
 			},
@@ -633,13 +633,13 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 				userManagerParams: userManagerParams{
 					getUserParams: getUserParams{
 						{
-							screenName: user.ScreenName,
+							screenName: user.IdentScreenName,
 							result:     &user,
 						},
 					},
 				},
 			},
-			newUserFn: func(screenName string) (state.User, error) {
+			newUserFn: func(screenName state.DisplayScreenName) (state.User, error) {
 				return user, io.EOF
 			},
 			wantErr: io.EOF,
@@ -653,7 +653,7 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
 						wire.NewTLV(wire.LoginTLVTagsPasswordHash, []byte("bad-roasted-password")),
-						wire.NewTLV(wire.LoginTLVTagsScreenName, user.ScreenName),
+						wire.NewTLV(wire.LoginTLVTagsScreenName, user.IdentScreenName),
 					},
 				},
 			},
@@ -661,7 +661,7 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 				userManagerParams: userManagerParams{
 					getUserParams: getUserParams{
 						{
-							screenName: user.ScreenName,
+							screenName: user.IdentScreenName,
 							result:     &user,
 						},
 					},
@@ -673,7 +673,7 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 					},
 				},
 			},
-			newUserFn: func(screenName string) (state.User, error) {
+			newUserFn: func(screenName state.DisplayScreenName) (state.User, error) {
 				return user, nil
 			},
 			wantErr: io.EOF,
@@ -688,7 +688,7 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
 						wire.NewTLV(wire.LoginTLVTagsPasswordHash, []byte("bad-roasted-password")),
-						wire.NewTLV(wire.LoginTLVTagsScreenName, user.ScreenName),
+						wire.NewTLV(wire.LoginTLVTagsScreenName, user.IdentScreenName),
 					},
 				},
 			},
@@ -696,7 +696,7 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 				userManagerParams: userManagerParams{
 					getUserParams: getUserParams{
 						{
-							screenName: user.ScreenName,
+							screenName: user.IdentScreenName,
 							result:     nil,
 						},
 					},
@@ -704,7 +704,7 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 			},
 			expectOutput: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(wire.LoginTLVTagsScreenName, user.ScreenName),
+					wire.NewTLV(wire.LoginTLVTagsScreenName, user.IdentScreenName),
 					wire.NewTLV(wire.LoginTLVTagsErrorSubcode, uint16(0x01)),
 				},
 			},
@@ -715,7 +715,7 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
 						wire.NewTLV(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
-						wire.NewTLV(wire.LoginTLVTagsScreenName, user.ScreenName),
+						wire.NewTLV(wire.LoginTLVTagsScreenName, user.IdentScreenName),
 					},
 				},
 			},
@@ -723,7 +723,7 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 				userManagerParams: userManagerParams{
 					getUserParams: getUserParams{
 						{
-							screenName: user.ScreenName,
+							screenName: user.IdentScreenName,
 							err:        io.EOF,
 						},
 					},
@@ -800,10 +800,10 @@ func TestAuthService_BUCPChallengeRequest(t *testing.T) {
 				userManagerParams: userManagerParams{
 					getUserParams: getUserParams{
 						{
-							screenName: "sn_user_a",
+							screenName: state.NewIdentScreenName("sn_user_a"),
 							result: &state.User{
-								ScreenName: "sn_user_a",
-								AuthKey:    "auth_key_user_a",
+								IdentScreenName: state.NewIdentScreenName("sn_user_a"),
+								AuthKey:         "auth_key_user_a",
 							},
 						},
 					},
@@ -837,7 +837,7 @@ func TestAuthService_BUCPChallengeRequest(t *testing.T) {
 				userManagerParams: userManagerParams{
 					getUserParams: getUserParams{
 						{
-							screenName: "sn_user_b",
+							screenName: state.NewIdentScreenName("sn_user_b"),
 							result:     nil,
 						},
 					},
@@ -870,7 +870,7 @@ func TestAuthService_BUCPChallengeRequest(t *testing.T) {
 				userManagerParams: userManagerParams{
 					getUserParams: getUserParams{
 						{
-							screenName: "sn_user_b",
+							screenName: state.NewIdentScreenName("sn_user_b"),
 							result:     nil,
 						},
 					},
@@ -903,7 +903,7 @@ func TestAuthService_BUCPChallengeRequest(t *testing.T) {
 				userManagerParams: userManagerParams{
 					getUserParams: getUserParams{
 						{
-							screenName: "sn_user_b",
+							screenName: state.NewIdentScreenName("sn_user_b"),
 							err:        io.EOF,
 						},
 					},
@@ -941,16 +941,23 @@ func TestAuthService_RegisterChatSession_HappyPath(t *testing.T) {
 
 	c := chatLoginCookie{
 		ChatCookie: cookie,
-		ScreenName: sess.ScreenName(),
+		ScreenName: sess.IdentScreenName().String(),
 	}
 	buf := &bytes.Buffer{}
 	assert.NoError(t, wire.Marshal(c, buf))
 
 	sessionManager := newMockSessionManager(t)
 	sessionManager.EXPECT().
-		AddSession(sess.ScreenName()).
+		AddSession(sess.DisplayScreenName()).
 		Return(sess)
 
+	userManager := newMockUserManager(t)
+	userManager.EXPECT().
+		User(sess.IdentScreenName()).
+		Return(&state.User{
+			DisplayScreenName: sess.DisplayScreenName(),
+		}, nil)
+
 	chatRegistry := newMockChatRegistry(t)
 	chatRegistry.EXPECT().
 		Retrieve(cookie).
@@ -958,7 +965,7 @@ func TestAuthService_RegisterChatSession_HappyPath(t *testing.T) {
 
 	cookieIssuer := newMockCookieIssuer(t)
 
-	svc := NewAuthService(config.Config{}, nil, nil, chatRegistry, nil, cookieIssuer, nil)
+	svc := NewAuthService(config.Config{}, nil, userManager, chatRegistry, nil, cookieIssuer, nil)
 
 	have, err := svc.RegisterChatSession(buf.Bytes())
 	assert.NoError(t, err)
@@ -971,7 +978,7 @@ func TestAuthService_RegisterBOSSession_ChatNotFound(t *testing.T) {
 
 	c := chatLoginCookie{
 		ChatCookie: cookie,
-		ScreenName: sess.ScreenName(),
+		ScreenName: sess.IdentScreenName().String(),
 	}
 	loginCookie := &bytes.Buffer{}
 	assert.NoError(t, wire.Marshal(c, loginCookie))
@@ -993,14 +1000,19 @@ func TestAuthService_RegisterBOSSession_HappyPath(t *testing.T) {
 
 	sessionManager := newMockSessionManager(t)
 	sessionManager.EXPECT().
-		AddSession(sess.ScreenName()).
+		AddSession(sess.DisplayScreenName()).
 		Return(sess)
 
 	cookieIssuer := newMockCookieIssuer(t)
 
-	svc := NewAuthService(config.Config{}, sessionManager, nil, nil, nil, cookieIssuer, nil)
+	userManager := newMockUserManager(t)
+	userManager.EXPECT().
+		User(sess.IdentScreenName()).
+		Return(&state.User{DisplayScreenName: sess.DisplayScreenName()}, nil)
 
-	have, err := svc.RegisterBOSSession(sess.ScreenName())
+	svc := NewAuthService(config.Config{}, sessionManager, userManager, nil, nil, cookieIssuer, nil)
+
+	have, err := svc.RegisterBOSSession(sess.IdentScreenName())
 	assert.NoError(t, err)
 	assert.Equal(t, sess, have)
 }
@@ -1010,14 +1022,19 @@ func TestAuthService_RegisterBOSSession_SessionNotFound(t *testing.T) {
 
 	sessionManager := newMockSessionManager(t)
 	sessionManager.EXPECT().
-		AddSession(sess.ScreenName()).
+		AddSession(sess.DisplayScreenName()).
 		Return(nil)
 
 	cookieIssuer := newMockCookieIssuer(t)
 
-	svc := NewAuthService(config.Config{}, sessionManager, nil, nil, nil, cookieIssuer, nil)
+	userManager := newMockUserManager(t)
+	userManager.EXPECT().
+		User(sess.IdentScreenName()).
+		Return(&state.User{DisplayScreenName: sess.DisplayScreenName()}, nil)
+
+	svc := NewAuthService(config.Config{}, sessionManager, userManager, nil, nil, cookieIssuer, nil)
 
-	have, err := svc.RegisterBOSSession(sess.ScreenName())
+	have, err := svc.RegisterBOSSession(sess.IdentScreenName())
 	assert.NoError(t, err)
 	assert.Nil(t, have)
 }
@@ -1193,14 +1210,14 @@ func TestAuthService_Signout(t *testing.T) {
 				legacyBuddyListManagerParams: legacyBuddyListManagerParams{
 					deleteUserParams: deleteUserParams{
 						{
-							userScreenName: "user_screen_name",
+							userScreenName: state.NewIdentScreenName("user_screen_name"),
 						},
 					},
 				},
 				buddyBroadcasterParams: buddyBroadcasterParams{
 					broadcastBuddyDepartedParams: broadcastBuddyDepartedParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 						},
 					},
 				},
@@ -1222,7 +1239,7 @@ func TestAuthService_Signout(t *testing.T) {
 				p := params
 				buddyUpdateBroadcaster.EXPECT().
 					BroadcastBuddyDeparted(mock.Anything, mock.MatchedBy(func(s *state.Session) bool {
-						return s.ScreenName() == p.screenName
+						return s.IdentScreenName() == p.screenName
 					})).
 					Return(nil)
 			}

+ 2 - 2
foodgroup/bart_test.go

@@ -49,7 +49,7 @@ func TestBARTService_UpsertItem(t *testing.T) {
 				buddyBroadcasterParams: buddyBroadcasterParams{
 					broadcastBuddyArrivedParams: broadcastBuddyArrivedParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 						},
 					},
 				},
@@ -87,7 +87,7 @@ func TestBARTService_UpsertItem(t *testing.T) {
 				p := params
 				buddyUpdateBroadcaster.EXPECT().
 					BroadcastBuddyArrived(mock.Anything, mock.MatchedBy(func(s *state.Session) bool {
-						return s.ScreenName() == p.screenName
+						return s.IdentScreenName() == p.screenName
 					})).
 					Return(nil)
 			}

+ 12 - 12
foodgroup/buddy.go

@@ -57,13 +57,13 @@ func (s BuddyService) RightsQuery(_ context.Context, frameIn wire.SNACFrame) wir
 
 func (s BuddyService) AddBuddies(ctx context.Context, sess *state.Session, inBody wire.SNAC_0x03_0x04_BuddyAddBuddies) error {
 	for _, entry := range inBody.Buddies {
-		s.legacyBuddyListManager.AddBuddy(sess.ScreenName(), entry.ScreenName)
+		s.legacyBuddyListManager.AddBuddy(sess.IdentScreenName(), state.NewIdentScreenName(entry.ScreenName))
 		if !sess.SignonComplete() {
 			// client has not completed sign-on sequence, so any arrival
 			// messages sent at this point would be ignored by the client.
 			continue
 		}
-		buddy := s.messageRelayer.RetrieveByScreenName(entry.ScreenName)
+		buddy := s.messageRelayer.RetrieveByScreenName(state.NewIdentScreenName(entry.ScreenName))
 		if buddy == nil || buddy.Invisible() {
 			continue
 		}
@@ -77,7 +77,7 @@ func (s BuddyService) AddBuddies(ctx context.Context, sess *state.Session, inBod
 
 func (s BuddyService) DelBuddies(_ context.Context, sess *state.Session, inBody wire.SNAC_0x03_0x05_BuddyDelBuddies) {
 	for _, entry := range inBody.Buddies {
-		s.legacyBuddyListManager.DeleteBuddy(sess.ScreenName(), entry.ScreenName)
+		s.legacyBuddyListManager.DeleteBuddy(sess.IdentScreenName(), state.NewIdentScreenName(entry.ScreenName))
 	}
 }
 
@@ -94,7 +94,7 @@ func (s BuddyService) UnicastBuddyArrived(ctx context.Context, from *state.Sessi
 	case icon != nil:
 		userInfo.Append(wire.NewTLV(wire.OServiceUserInfoBARTInfo, *icon))
 	}
-	s.messageRelayer.RelayToScreenName(ctx, to.ScreenName(), wire.SNACMessage{
+	s.messageRelayer.RelayToScreenName(ctx, to.IdentScreenName(), wire.SNACMessage{
 		Frame: wire.SNACFrame{
 			FoodGroup: wire.Buddy,
 			SubGroup:  wire.BuddyArrived,
@@ -112,13 +112,13 @@ func (s BuddyService) UnicastBuddyArrived(ctx context.Context, from *state.Sessi
 // buddy icons, warning levels, invisibility status, etc.
 func (s BuddyService) BroadcastBuddyArrived(ctx context.Context, sess *state.Session) error {
 	// find users who have this user on their server-side buddy list
-	recipients, err := s.feedbagManager.AdjacentUsers(sess.ScreenName())
+	recipients, err := s.feedbagManager.AdjacentUsers(sess.IdentScreenName())
 	if err != nil {
 		return err
 	}
 
 	// find users who have this user on their client-side buddy list
-	legacyUsers := s.legacyBuddyListManager.WhoAddedUser(sess.ScreenName())
+	legacyUsers := s.legacyBuddyListManager.WhoAddedUser(sess.IdentScreenName())
 	recipients = append(recipients, legacyUsers...)
 
 	userInfo := sess.TLVUserInfo()
@@ -147,7 +147,7 @@ func (s BuddyService) BroadcastBuddyArrived(ctx context.Context, sess *state.Ses
 // from their feedbag. If it exists, the buddy icon is the feedbag item of
 // class wire.FeedbagClassIdBart with BART type wire.BARTTypesBuddyIcon.
 func getBuddyIconRefFromFeedbag(sess *state.Session, feedbagManager FeedbagManager) (*wire.BARTID, error) {
-	items, err := feedbagManager.Feedbag(sess.ScreenName())
+	items, err := feedbagManager.Feedbag(sess.IdentScreenName())
 	if err != nil {
 		return nil, err
 	}
@@ -198,12 +198,12 @@ func extractBARTItemType(item wire.FeedbagItem) (uint16, error) {
 }
 
 func (s BuddyService) BroadcastBuddyDeparted(ctx context.Context, sess *state.Session) error {
-	recipients, err := s.feedbagManager.AdjacentUsers(sess.ScreenName())
+	recipients, err := s.feedbagManager.AdjacentUsers(sess.IdentScreenName())
 	if err != nil {
 		return err
 	}
 
-	legacyUsers := s.legacyBuddyListManager.WhoAddedUser(sess.ScreenName())
+	legacyUsers := s.legacyBuddyListManager.WhoAddedUser(sess.IdentScreenName())
 	recipients = append(recipients, legacyUsers...)
 
 	s.messageRelayer.RelayToScreenNames(ctx, recipients, wire.SNACMessage{
@@ -215,7 +215,7 @@ func (s BuddyService) BroadcastBuddyDeparted(ctx context.Context, sess *state.Se
 			TLVUserInfo: wire.TLVUserInfo{
 				// don't include the TLV block, otherwise the AIM client fails
 				// to process the block event
-				ScreenName:   sess.ScreenName(),
+				ScreenName:   string(sess.DisplayScreenName()),
 				WarningLevel: sess.Warning(),
 			},
 		},
@@ -225,7 +225,7 @@ func (s BuddyService) BroadcastBuddyDeparted(ctx context.Context, sess *state.Se
 }
 
 func (s BuddyService) UnicastBuddyDeparted(ctx context.Context, from *state.Session, to *state.Session) {
-	s.messageRelayer.RelayToScreenName(ctx, to.ScreenName(), wire.SNACMessage{
+	s.messageRelayer.RelayToScreenName(ctx, to.IdentScreenName(), wire.SNACMessage{
 		Frame: wire.SNACFrame{
 			FoodGroup: wire.Buddy,
 			SubGroup:  wire.BuddyDeparted,
@@ -234,7 +234,7 @@ func (s BuddyService) UnicastBuddyDeparted(ctx context.Context, from *state.Sess
 			TLVUserInfo: wire.TLVUserInfo{
 				// don't include the TLV block, otherwise the AIM client fails
 				// to process the block event
-				ScreenName:   from.ScreenName(),
+				ScreenName:   string(from.DisplayScreenName()),
 				WarningLevel: from.Warning(),
 			},
 		},

+ 44 - 38
foodgroup/buddy_test.go

@@ -70,29 +70,29 @@ func TestBuddyService_AddBuddies(t *testing.T) {
 				legacyBuddyListManagerParams: legacyBuddyListManagerParams{
 					addBuddyParams: addBuddyParams{
 						{
-							userScreenName:  "user_screen_name",
-							buddyScreenName: "buddy_1_online",
+							userScreenName:  state.NewIdentScreenName("user_screen_name"),
+							buddyScreenName: state.NewIdentScreenName("buddy_1_online"),
 						},
 						{
-							userScreenName:  "user_screen_name",
-							buddyScreenName: "buddy_2_offline",
+							userScreenName:  state.NewIdentScreenName("user_screen_name"),
+							buddyScreenName: state.NewIdentScreenName("buddy_2_offline"),
 						},
 					},
 				},
 				messageRelayerParams: messageRelayerParams{
 					retrieveByScreenNameParams: retrieveByScreenNameParams{
 						{
-							screenName: "buddy_1_online",
+							screenName: state.NewIdentScreenName("buddy_1_online"),
 							sess:       newTestSession("buddy_1_online", sessOptCannedSignonTime),
 						},
 						{
-							screenName: "buddy_2_offline",
+							screenName: state.NewIdentScreenName("buddy_2_offline"),
 							sess:       nil,
 						},
 					},
 					relayToScreenNameParams: relayToScreenNameParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 							message: wire.SNACMessage{
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.Buddy,
@@ -108,7 +108,7 @@ func TestBuddyService_AddBuddies(t *testing.T) {
 				feedbagManagerParams: feedbagManagerParams{
 					feedbagParams: feedbagParams{
 						{
-							screenName: "buddy_1_online",
+							screenName: state.NewIdentScreenName("buddy_1_online"),
 						},
 					},
 				},
@@ -133,12 +133,12 @@ func TestBuddyService_AddBuddies(t *testing.T) {
 				legacyBuddyListManagerParams: legacyBuddyListManagerParams{
 					addBuddyParams: addBuddyParams{
 						{
-							userScreenName:  "user_screen_name",
-							buddyScreenName: "buddy_1_online",
+							userScreenName:  state.NewIdentScreenName("user_screen_name"),
+							buddyScreenName: state.NewIdentScreenName("buddy_1_online"),
 						},
 						{
-							userScreenName:  "user_screen_name",
-							buddyScreenName: "buddy_2_offline",
+							userScreenName:  state.NewIdentScreenName("user_screen_name"),
+							buddyScreenName: state.NewIdentScreenName("buddy_2_offline"),
 						},
 					},
 				},
@@ -212,12 +212,12 @@ func TestBuddyService_DelBuddies(t *testing.T) {
 				legacyBuddyListManagerParams: legacyBuddyListManagerParams{
 					deleteBuddyParams: deleteBuddyParams{
 						{
-							userScreenName:  "user_screen_name",
-							buddyScreenName: "buddy_1_online",
+							userScreenName:  state.NewIdentScreenName("user_screen_name"),
+							buddyScreenName: state.NewIdentScreenName("buddy_1_online"),
 						},
 						{
-							userScreenName:  "user_screen_name",
-							buddyScreenName: "buddy_2_offline",
+							userScreenName:  state.NewIdentScreenName("user_screen_name"),
+							buddyScreenName: state.NewIdentScreenName("buddy_2_offline"),
 						},
 					},
 				},
@@ -256,13 +256,13 @@ func TestBuddyService_BroadcastBuddyArrived(t *testing.T) {
 				feedbagManagerParams: feedbagManagerParams{
 					adjacentUsersParams: adjacentUsersParams{
 						{
-							screenName: "user_screen_name",
-							users:      []string{"friend1"},
+							screenName: state.NewIdentScreenName("user_screen_name"),
+							users:      []state.IdentScreenName{state.NewIdentScreenName("friend1")},
 						},
 					},
 					feedbagParams: feedbagParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 							results:    []wire.FeedbagItem{},
 						},
 					},
@@ -270,7 +270,10 @@ func TestBuddyService_BroadcastBuddyArrived(t *testing.T) {
 				messageRelayerParams: messageRelayerParams{
 					relayToScreenNamesParams: relayToScreenNamesParams{
 						{
-							screenNames: []string{"friend1", "friend2"},
+							screenNames: []state.IdentScreenName{
+								state.NewIdentScreenName("friend1"),
+								state.NewIdentScreenName("friend2"),
+							},
 							message: wire.SNACMessage{
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.Buddy,
@@ -286,8 +289,8 @@ func TestBuddyService_BroadcastBuddyArrived(t *testing.T) {
 				legacyBuddyListManagerParams: legacyBuddyListManagerParams{
 					whoAddedUserParams: whoAddedUserParams{
 						{
-							userScreenName: "user_screen_name",
-							result:         []string{"friend2"},
+							userScreenName: state.NewIdentScreenName("user_screen_name"),
+							result:         []state.IdentScreenName{state.NewIdentScreenName("friend2")},
 						},
 					},
 				},
@@ -300,13 +303,13 @@ func TestBuddyService_BroadcastBuddyArrived(t *testing.T) {
 				feedbagManagerParams: feedbagManagerParams{
 					adjacentUsersParams: adjacentUsersParams{
 						{
-							screenName: "user_screen_name",
-							users:      []string{"friend1"},
+							screenName: state.NewIdentScreenName("user_screen_name"),
+							users:      []state.IdentScreenName{state.NewIdentScreenName("friend1")},
 						},
 					},
 					feedbagParams: feedbagParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 							results: []wire.FeedbagItem{
 								{
 									ClassID: wire.FeedbagClassIdBuddy,
@@ -334,7 +337,7 @@ func TestBuddyService_BroadcastBuddyArrived(t *testing.T) {
 				messageRelayerParams: messageRelayerParams{
 					relayToScreenNamesParams: relayToScreenNamesParams{
 						{
-							screenNames: []string{"friend1"},
+							screenNames: []state.IdentScreenName{state.NewIdentScreenName("friend1")},
 							message: wire.SNACMessage{
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.Buddy,
@@ -359,8 +362,8 @@ func TestBuddyService_BroadcastBuddyArrived(t *testing.T) {
 				legacyBuddyListManagerParams: legacyBuddyListManagerParams{
 					whoAddedUserParams: whoAddedUserParams{
 						{
-							userScreenName: "user_screen_name",
-							result:         []string{},
+							userScreenName: state.NewIdentScreenName("user_screen_name"),
+							result:         []state.IdentScreenName{},
 						},
 					},
 				},
@@ -418,15 +421,18 @@ func TestBuddyService_BroadcastDeparture(t *testing.T) {
 				feedbagManagerParams: feedbagManagerParams{
 					adjacentUsersParams: adjacentUsersParams{
 						{
-							screenName: "user_screen_name",
-							users:      []string{"friend1"},
+							screenName: state.NewIdentScreenName("user_screen_name"),
+							users:      []state.IdentScreenName{state.NewIdentScreenName("friend1")},
 						},
 					},
 				},
 				messageRelayerParams: messageRelayerParams{
 					relayToScreenNamesParams: relayToScreenNamesParams{
 						{
-							screenNames: []string{"friend1", "friend2"},
+							screenNames: []state.IdentScreenName{
+								state.NewIdentScreenName("friend1"),
+								state.NewIdentScreenName("friend2"),
+							},
 							message: wire.SNACMessage{
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.Buddy,
@@ -445,8 +451,8 @@ func TestBuddyService_BroadcastDeparture(t *testing.T) {
 				legacyBuddyListManagerParams: legacyBuddyListManagerParams{
 					whoAddedUserParams: whoAddedUserParams{
 						{
-							userScreenName: "user_screen_name",
-							result:         []string{"friend2"},
+							userScreenName: state.NewIdentScreenName("user_screen_name"),
+							result:         []state.IdentScreenName{state.NewIdentScreenName("friend2")},
 						},
 					},
 				},
@@ -502,7 +508,7 @@ func TestBuddyService_UnicastBuddyDeparted(t *testing.T) {
 				messageRelayerParams: messageRelayerParams{
 					relayToScreenNameParams: relayToScreenNameParams{
 						{
-							screenName: "dest_screen_name",
+							screenName: state.NewIdentScreenName("dest_screen_name"),
 							message: wire.SNACMessage{
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.Buddy,
@@ -557,7 +563,7 @@ func TestBuddyService_UnicastBuddyArrived(t *testing.T) {
 				feedbagManagerParams: feedbagManagerParams{
 					feedbagParams: feedbagParams{
 						{
-							screenName: "src_screen_name",
+							screenName: state.NewIdentScreenName("src_screen_name"),
 							results:    []wire.FeedbagItem{},
 						},
 					},
@@ -565,7 +571,7 @@ func TestBuddyService_UnicastBuddyArrived(t *testing.T) {
 				messageRelayerParams: messageRelayerParams{
 					relayToScreenNameParams: relayToScreenNameParams{
 						{
-							screenName: "dest_screen_name",
+							screenName: state.NewIdentScreenName("dest_screen_name"),
 							message: wire.SNACMessage{
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.Buddy,
@@ -588,7 +594,7 @@ func TestBuddyService_UnicastBuddyArrived(t *testing.T) {
 				feedbagManagerParams: feedbagManagerParams{
 					feedbagParams: feedbagParams{
 						{
-							screenName: "src_screen_name",
+							screenName: state.NewIdentScreenName("src_screen_name"),
 							results: []wire.FeedbagItem{
 								{
 									ClassID: wire.FeedbagClassIdBuddy,
@@ -616,7 +622,7 @@ func TestBuddyService_UnicastBuddyArrived(t *testing.T) {
 				messageRelayerParams: messageRelayerParams{
 					relayToScreenNameParams: relayToScreenNameParams{
 						{
-							screenName: "dest_screen_name",
+							screenName: state.NewIdentScreenName("dest_screen_name"),
 							message: wire.SNACMessage{
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.Buddy,

+ 2 - 2
foodgroup/chat.go

@@ -81,7 +81,7 @@ func setOnlineChatUsers(ctx context.Context, sess *state.Session, chatMessageRel
 		snacPayloadOut.Users = append(snacPayloadOut.Users, uSess.TLVUserInfo())
 	}
 
-	chatMessageRelayer.RelayToScreenName(ctx, sess.ScreenName(), wire.SNACMessage{
+	chatMessageRelayer.RelayToScreenName(ctx, sess.IdentScreenName(), wire.SNACMessage{
 		Frame: wire.SNACFrame{
 			FoodGroup: wire.Chat,
 			SubGroup:  wire.ChatUsersJoined,
@@ -119,7 +119,7 @@ func alertUserLeft(ctx context.Context, sess *state.Session, chatMessageRelayer
 }
 
 func sendChatRoomInfoUpdate(ctx context.Context, sess *state.Session, chatMessageRelayer ChatMessageRelayer, room state.ChatRoom) {
-	chatMessageRelayer.RelayToScreenName(ctx, sess.ScreenName(), wire.SNACMessage{
+	chatMessageRelayer.RelayToScreenName(ctx, sess.IdentScreenName(), wire.SNACMessage{
 		Frame: wire.SNACFrame{
 			FoodGroup: wire.Chat,
 			SubGroup:  wire.ChatRoomInfoUpdate,

+ 1 - 1
foodgroup/chat_nav.go

@@ -87,7 +87,7 @@ func (s ChatNavService) CreateRoom(_ context.Context, sess *state.Session, inFra
 	s.chatRegistry.Register(room, chatSessMgr)
 
 	// add user to chat room
-	chatSess := chatSessMgr.AddSession(sess.ScreenName())
+	chatSess := chatSessMgr.AddSession(sess.DisplayScreenName())
 	chatSess.SetChatRoomCookie(room.Cookie)
 
 	return wire.SNACMessage{

+ 2 - 1
foodgroup/chat_nav_test.go

@@ -18,7 +18,8 @@ func TestChatNavService_CreateRoom(t *testing.T) {
 	chatRegistry := state.NewChatRegistry()
 
 	sessionManager := newMockSessionManager(t)
-	sessionManager.EXPECT().AddSession(bosSess.ScreenName()).
+	sessionManager.EXPECT().
+		AddSession(bosSess.DisplayScreenName()).
 		Return(chatSess)
 
 	newChatRoom := func() state.ChatRoom {

+ 12 - 12
foodgroup/feedbag.go

@@ -91,7 +91,7 @@ func (s FeedbagService) RightsQuery(_ context.Context, inFrame wire.SNACFrame) w
 // Query fetches the user's feedbag (aka buddy list). It returns
 // wire.FeedbagReply, which contains feedbag entries.
 func (s FeedbagService) Query(_ context.Context, sess *state.Session, inFrame wire.SNACFrame) (wire.SNACMessage, error) {
-	fb, err := s.feedbagManager.Feedbag(sess.ScreenName())
+	fb, err := s.feedbagManager.Feedbag(sess.IdentScreenName())
 	if err != nil {
 		return wire.SNACMessage{}, err
 	}
@@ -99,7 +99,7 @@ func (s FeedbagService) Query(_ context.Context, sess *state.Session, inFrame wi
 	lm := time.UnixMilli(0)
 
 	if len(fb) > 0 {
-		lm, err = s.feedbagManager.FeedbagLastModified(sess.ScreenName())
+		lm, err = s.feedbagManager.FeedbagLastModified(sess.IdentScreenName())
 		if err != nil {
 			return wire.SNACMessage{}, err
 		}
@@ -124,7 +124,7 @@ func (s FeedbagService) Query(_ context.Context, sess *state.Session, inFrame wi
 // inBody.LastUpdate, else return wire.FeedbagReply, which contains feedbag
 // entries.
 func (s FeedbagService) QueryIfModified(_ context.Context, sess *state.Session, inFrame wire.SNACFrame, inBody wire.SNAC_0x13_0x05_FeedbagQueryIfModified) (wire.SNACMessage, error) {
-	fb, err := s.feedbagManager.Feedbag(sess.ScreenName())
+	fb, err := s.feedbagManager.Feedbag(sess.IdentScreenName())
 	if err != nil {
 		return wire.SNACMessage{}, err
 	}
@@ -132,7 +132,7 @@ func (s FeedbagService) QueryIfModified(_ context.Context, sess *state.Session,
 	lm := time.UnixMilli(0)
 
 	if len(fb) > 0 {
-		lm, err = s.feedbagManager.FeedbagLastModified(sess.ScreenName())
+		lm, err = s.feedbagManager.FeedbagLastModified(sess.IdentScreenName())
 		if err != nil {
 			return wire.SNACMessage{}, err
 		}
@@ -177,7 +177,7 @@ func (s FeedbagService) UpsertItem(ctx context.Context, sess *state.Session, inF
 	for _, item := range items {
 		// don't let users block themselves, it causes the AIM client to go
 		// into a weird state.
-		if item.ClassID == 3 && item.Name == sess.ScreenName() {
+		if item.ClassID == 3 && state.NewIdentScreenName(item.Name) == sess.IdentScreenName() {
 			return wire.SNACMessage{
 				Frame: wire.SNACFrame{
 					FoodGroup: wire.Feedbag,
@@ -191,14 +191,14 @@ func (s FeedbagService) UpsertItem(ctx context.Context, sess *state.Session, inF
 		}
 	}
 
-	if err := s.feedbagManager.FeedbagUpsert(sess.ScreenName(), items); err != nil {
+	if err := s.feedbagManager.FeedbagUpsert(sess.IdentScreenName(), items); err != nil {
 		return wire.SNACMessage{}, err
 	}
 
 	for _, item := range items {
 		switch item.ClassID {
 		case wire.FeedbagClassIdBuddy, wire.FeedbagClassIDPermit: // add new buddy
-			buddy := s.messageRelayer.RetrieveByScreenName(item.Name)
+			buddy := s.messageRelayer.RetrieveByScreenName(state.NewIdentScreenName(item.Name))
 			if buddy == nil || buddy.Invisible() {
 				continue
 			}
@@ -209,7 +209,7 @@ func (s FeedbagService) UpsertItem(ctx context.Context, sess *state.Session, inF
 			if sess.Invisible() {
 				continue // user's offline, don't send departure notification
 			}
-			blockedSess := s.messageRelayer.RetrieveByScreenName(item.Name)
+			blockedSess := s.messageRelayer.RetrieveByScreenName(state.NewIdentScreenName(item.Name))
 			if blockedSess == nil {
 				continue // blocked buddy is offline, nothing to do here
 			}
@@ -282,7 +282,7 @@ func (s FeedbagService) broadcastIconUpdate(ctx context.Context, sess *state.Ses
 		}
 	}
 
-	s.messageRelayer.RelayToScreenName(ctx, sess.ScreenName(), wire.SNACMessage{
+	s.messageRelayer.RelayToScreenName(ctx, sess.IdentScreenName(), wire.SNACMessage{
 		Frame: wire.SNACFrame{
 			FoodGroup: wire.OService,
 			SubGroup:  wire.OServiceBartReply,
@@ -300,13 +300,13 @@ func (s FeedbagService) broadcastIconUpdate(ctx context.Context, sess *state.Ses
 // Sends buddy arrival notifications to each unblocked buddy if current user is
 // visible. It returns wire.FeedbagStatus, which contains update confirmation.
 func (s FeedbagService) DeleteItem(ctx context.Context, sess *state.Session, inFrame wire.SNACFrame, inBody wire.SNAC_0x13_0x0A_FeedbagDeleteItem) (wire.SNACMessage, error) {
-	if err := s.feedbagManager.FeedbagDelete(sess.ScreenName(), inBody.Items); err != nil {
+	if err := s.feedbagManager.FeedbagDelete(sess.IdentScreenName(), inBody.Items); err != nil {
 		return wire.SNACMessage{}, err
 	}
 
 	for _, item := range inBody.Items {
 		if item.ClassID == wire.FeedbagClassIDDeny {
-			unblockedSess := s.messageRelayer.RetrieveByScreenName(item.Name)
+			unblockedSess := s.messageRelayer.RetrieveByScreenName(state.NewIdentScreenName(item.Name))
 			if unblockedSess == nil {
 				continue // unblocked user is offline, nothing to do here
 			}
@@ -349,7 +349,7 @@ func (s FeedbagService) StartCluster(context.Context, wire.SNACFrame, wire.SNAC_
 // by AIM clients that use the feedbag food group for buddy list management (as
 // opposed to client-side management).
 func (s FeedbagService) Use(ctx context.Context, sess *state.Session) error {
-	buddies, err := s.feedbagManager.Buddies(sess.ScreenName())
+	buddies, err := s.feedbagManager.Buddies(sess.IdentScreenName())
 	if err != nil {
 		return err
 	}

+ 73 - 70
foodgroup/feedbag_test.go

@@ -38,7 +38,7 @@ func TestFeedbagService_Query(t *testing.T) {
 				feedbagManagerParams: feedbagManagerParams{
 					feedbagParams: feedbagParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 							results:    []wire.FeedbagItem{},
 						},
 					},
@@ -68,7 +68,7 @@ func TestFeedbagService_Query(t *testing.T) {
 				feedbagManagerParams: feedbagManagerParams{
 					feedbagParams: feedbagParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 							results: []wire.FeedbagItem{
 								{
 									Name: "buddy_1",
@@ -81,7 +81,7 @@ func TestFeedbagService_Query(t *testing.T) {
 					},
 					feedbagLastModifiedParams: feedbagLastModifiedParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 							result:     time.UnixMilli(1696472198082),
 						},
 					},
@@ -162,7 +162,7 @@ func TestFeedbagService_QueryIfModified(t *testing.T) {
 				feedbagManagerParams: feedbagManagerParams{
 					feedbagParams: feedbagParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 							results:    []wire.FeedbagItem{},
 						},
 					},
@@ -194,7 +194,7 @@ func TestFeedbagService_QueryIfModified(t *testing.T) {
 				feedbagManagerParams: feedbagManagerParams{
 					feedbagParams: feedbagParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 							results: []wire.FeedbagItem{
 								{
 									Name: "buddy_1",
@@ -207,7 +207,7 @@ func TestFeedbagService_QueryIfModified(t *testing.T) {
 					},
 					feedbagLastModifiedParams: feedbagLastModifiedParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 							result:     time.UnixMilli(200000),
 						},
 					},
@@ -248,7 +248,7 @@ func TestFeedbagService_QueryIfModified(t *testing.T) {
 				feedbagManagerParams: feedbagManagerParams{
 					feedbagParams: feedbagParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 							results: []wire.FeedbagItem{
 								{
 									Name: "buddy_1",
@@ -261,7 +261,7 @@ func TestFeedbagService_QueryIfModified(t *testing.T) {
 					},
 					feedbagLastModifiedParams: feedbagLastModifiedParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 							result:     time.UnixMilli(100000),
 						},
 					},
@@ -406,7 +406,7 @@ func TestFeedbagService_UpsertItem(t *testing.T) {
 				feedbagManagerParams: feedbagManagerParams{
 					feedbagUpsertParams: feedbagUpsertParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 							items: []wire.FeedbagItem{
 								{
 									ClassID: wire.FeedbagClassIDPermit,
@@ -423,11 +423,11 @@ func TestFeedbagService_UpsertItem(t *testing.T) {
 				messageRelayerParams: messageRelayerParams{
 					retrieveByScreenNameParams: retrieveByScreenNameParams{
 						{
-							screenName: "buddy_1_online",
+							screenName: state.NewIdentScreenName("buddy_1_online"),
 							sess:       newTestSession("buddy_1_online", sessOptCannedSignonTime),
 						},
 						{
-							screenName: "buddy_2_online",
+							screenName: state.NewIdentScreenName("buddy_2_online"),
 							sess:       newTestSession("buddy_2_online", sessOptCannedSignonTime),
 						},
 					},
@@ -435,12 +435,12 @@ func TestFeedbagService_UpsertItem(t *testing.T) {
 				buddyBroadcasterParams: buddyBroadcasterParams{
 					unicastBuddyArrivedParams: unicastBuddyArrivedParams{
 						{
-							from: "buddy_1_online",
-							to:   "user_screen_name",
+							from: state.NewIdentScreenName("buddy_1_online"),
+							to:   state.NewIdentScreenName("user_screen_name"),
 						},
 						{
-							from: "buddy_2_online",
-							to:   "user_screen_name",
+							from: state.NewIdentScreenName("buddy_2_online"),
+							to:   state.NewIdentScreenName("user_screen_name"),
 						},
 					},
 				},
@@ -476,7 +476,7 @@ func TestFeedbagService_UpsertItem(t *testing.T) {
 				feedbagManagerParams: feedbagManagerParams{
 					feedbagUpsertParams: feedbagUpsertParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 							items: []wire.FeedbagItem{
 								{
 									ClassID: wire.FeedbagClassIDPermit,
@@ -489,7 +489,7 @@ func TestFeedbagService_UpsertItem(t *testing.T) {
 				messageRelayerParams: messageRelayerParams{
 					retrieveByScreenNameParams: retrieveByScreenNameParams{
 						{
-							screenName: "buddy_offline",
+							screenName: state.NewIdentScreenName("buddy_offline"),
 							sess:       nil,
 						},
 					},
@@ -526,7 +526,7 @@ func TestFeedbagService_UpsertItem(t *testing.T) {
 				feedbagManagerParams: feedbagManagerParams{
 					feedbagUpsertParams: feedbagUpsertParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 							items: []wire.FeedbagItem{
 								{
 									ClassID: wire.FeedbagClassIDPermit,
@@ -539,7 +539,7 @@ func TestFeedbagService_UpsertItem(t *testing.T) {
 				messageRelayerParams: messageRelayerParams{
 					retrieveByScreenNameParams: retrieveByScreenNameParams{
 						{
-							screenName: "invisible_buddy_online",
+							screenName: state.NewIdentScreenName("invisible_buddy_online"),
 							sess:       newTestSession("invisible_buddy_online", sessOptInvisible),
 						},
 					},
@@ -576,7 +576,7 @@ func TestFeedbagService_UpsertItem(t *testing.T) {
 				feedbagManagerParams: feedbagManagerParams{
 					feedbagUpsertParams: feedbagUpsertParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 							items: []wire.FeedbagItem{
 								{
 									ClassID: wire.FeedbagClassIDDeny,
@@ -589,7 +589,7 @@ func TestFeedbagService_UpsertItem(t *testing.T) {
 				messageRelayerParams: messageRelayerParams{
 					retrieveByScreenNameParams: retrieveByScreenNameParams{
 						{
-							screenName: "buddy_1",
+							screenName: state.NewIdentScreenName("buddy_1"),
 							sess:       newTestSession("buddy_1"),
 						},
 					},
@@ -597,12 +597,12 @@ func TestFeedbagService_UpsertItem(t *testing.T) {
 				buddyBroadcasterParams: buddyBroadcasterParams{
 					unicastBuddyDepartedParams: unicastBuddyDepartedParams{
 						{
-							from: "user_screen_name",
-							to:   "buddy_1",
+							from: state.NewIdentScreenName("user_screen_name"),
+							to:   state.NewIdentScreenName("buddy_1"),
 						},
 						{
-							from: "buddy_1",
-							to:   "user_screen_name",
+							from: state.NewIdentScreenName("buddy_1"),
+							to:   state.NewIdentScreenName("user_screen_name"),
 						},
 					},
 				},
@@ -638,7 +638,7 @@ func TestFeedbagService_UpsertItem(t *testing.T) {
 				feedbagManagerParams: feedbagManagerParams{
 					feedbagUpsertParams: feedbagUpsertParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 							items: []wire.FeedbagItem{
 								{
 									ClassID: wire.FeedbagClassIDDeny,
@@ -651,7 +651,7 @@ func TestFeedbagService_UpsertItem(t *testing.T) {
 				messageRelayerParams: messageRelayerParams{
 					retrieveByScreenNameParams: retrieveByScreenNameParams{
 						{
-							screenName: "buddy_1",
+							screenName: state.NewIdentScreenName("buddy_1"),
 							sess:       nil,
 						},
 					},
@@ -688,7 +688,7 @@ func TestFeedbagService_UpsertItem(t *testing.T) {
 				feedbagManagerParams: feedbagManagerParams{
 					feedbagUpsertParams: feedbagUpsertParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 							items: []wire.FeedbagItem{
 								{
 									ClassID: wire.FeedbagClassIDDeny,
@@ -771,7 +771,7 @@ func TestFeedbagService_UpsertItem(t *testing.T) {
 				feedbagManagerParams: feedbagManagerParams{
 					feedbagUpsertParams: feedbagUpsertParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 							items: []wire.FeedbagItem{
 								{
 									ClassID: wire.FeedbagClassIdBart,
@@ -790,7 +790,7 @@ func TestFeedbagService_UpsertItem(t *testing.T) {
 				messageRelayerParams: messageRelayerParams{
 					relayToScreenNameParams: relayToScreenNameParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 							message: wire.SNACMessage{
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.OService,
@@ -855,7 +855,7 @@ func TestFeedbagService_UpsertItem(t *testing.T) {
 				feedbagManagerParams: feedbagManagerParams{
 					feedbagUpsertParams: feedbagUpsertParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 							items: []wire.FeedbagItem{
 								{
 									ClassID: wire.FeedbagClassIdBart,
@@ -876,7 +876,7 @@ func TestFeedbagService_UpsertItem(t *testing.T) {
 				messageRelayerParams: messageRelayerParams{
 					relayToScreenNameParams: relayToScreenNameParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 							message: wire.SNACMessage{
 								Frame: wire.SNACFrame{
 									FoodGroup: wire.OService,
@@ -898,7 +898,7 @@ func TestFeedbagService_UpsertItem(t *testing.T) {
 				buddyBroadcasterParams: buddyBroadcasterParams{
 					broadcastBuddyArrivedParams: broadcastBuddyArrivedParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 						},
 					},
 				},
@@ -940,7 +940,7 @@ func TestFeedbagService_UpsertItem(t *testing.T) {
 				feedbagManagerParams: feedbagManagerParams{
 					feedbagUpsertParams: feedbagUpsertParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 							items: []wire.FeedbagItem{
 								{
 									ClassID: wire.FeedbagClassIdBart,
@@ -959,7 +959,7 @@ func TestFeedbagService_UpsertItem(t *testing.T) {
 				buddyBroadcasterParams: buddyBroadcasterParams{
 					broadcastBuddyArrivedParams: broadcastBuddyArrivedParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 						},
 					},
 				},
@@ -1021,7 +1021,7 @@ func TestFeedbagService_UpsertItem(t *testing.T) {
 				p := params
 				buddyUpdateBroadcaster.EXPECT().
 					BroadcastBuddyArrived(mock.Anything, mock.MatchedBy(func(s *state.Session) bool {
-						return s.ScreenName() == p.screenName
+						return s.IdentScreenName() == p.screenName
 					})).
 					Return(nil)
 			}
@@ -1030,10 +1030,10 @@ func TestFeedbagService_UpsertItem(t *testing.T) {
 				buddyUpdateBroadcaster.EXPECT().
 					UnicastBuddyArrived(mock.Anything,
 						mock.MatchedBy(func(s *state.Session) bool {
-							return s.ScreenName() == p.from
+							return s.IdentScreenName() == p.from
 						}),
 						mock.MatchedBy(func(s *state.Session) bool {
-							return s.ScreenName() == p.to
+							return s.IdentScreenName() == p.to
 						})).
 					Return(nil)
 			}
@@ -1042,10 +1042,10 @@ func TestFeedbagService_UpsertItem(t *testing.T) {
 				buddyUpdateBroadcaster.EXPECT().
 					UnicastBuddyDeparted(mock.Anything,
 						mock.MatchedBy(func(s *state.Session) bool {
-							return s.ScreenName() == p.from
+							return s.IdentScreenName() == p.from
 						}),
 						mock.MatchedBy(func(s *state.Session) bool {
-							return s.ScreenName() == p.to
+							return s.IdentScreenName() == p.to
 						}))
 			}
 
@@ -1092,7 +1092,7 @@ func TestFeedbagService_DeleteItem(t *testing.T) {
 				feedbagManagerParams: feedbagManagerParams{
 					feedbagDeleteParams: feedbagDeleteParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 							items: []wire.FeedbagItem{
 								{
 									ClassID: wire.FeedbagClassIdBuddy,
@@ -1138,7 +1138,7 @@ func TestFeedbagService_DeleteItem(t *testing.T) {
 				feedbagManagerParams: feedbagManagerParams{
 					feedbagDeleteParams: feedbagDeleteParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 							items: []wire.FeedbagItem{
 								{
 									ClassID: wire.FeedbagClassIDDeny,
@@ -1155,11 +1155,11 @@ func TestFeedbagService_DeleteItem(t *testing.T) {
 				messageRelayerParams: messageRelayerParams{
 					retrieveByScreenNameParams: retrieveByScreenNameParams{
 						{
-							screenName: "buddy_1_online",
+							screenName: state.NewIdentScreenName("buddy_1_online"),
 							sess:       newTestSession("buddy_1_online", sessOptCannedSignonTime),
 						},
 						{
-							screenName: "buddy_2_online",
+							screenName: state.NewIdentScreenName("buddy_2_online"),
 							sess:       newTestSession("buddy_2_online", sessOptCannedSignonTime),
 						},
 					},
@@ -1167,20 +1167,20 @@ func TestFeedbagService_DeleteItem(t *testing.T) {
 				buddyBroadcasterParams: buddyBroadcasterParams{
 					unicastBuddyArrivedParams: unicastBuddyArrivedParams{
 						{
-							from: "buddy_1_online",
-							to:   "user_screen_name",
+							from: state.NewIdentScreenName("buddy_1_online"),
+							to:   state.NewIdentScreenName("user_screen_name"),
 						},
 						{
-							from: "buddy_2_online",
-							to:   "user_screen_name",
+							from: state.NewIdentScreenName("buddy_2_online"),
+							to:   state.NewIdentScreenName("user_screen_name"),
 						},
 						{
-							from: "user_screen_name",
-							to:   "buddy_1_online",
+							from: state.NewIdentScreenName("user_screen_name"),
+							to:   state.NewIdentScreenName("buddy_1_online"),
 						},
 						{
-							from: "user_screen_name",
-							to:   "buddy_2_online",
+							from: state.NewIdentScreenName("user_screen_name"),
+							to:   state.NewIdentScreenName("buddy_2_online"),
 						},
 					},
 				},
@@ -1216,7 +1216,7 @@ func TestFeedbagService_DeleteItem(t *testing.T) {
 				feedbagManagerParams: feedbagManagerParams{
 					feedbagDeleteParams: feedbagDeleteParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 							items: []wire.FeedbagItem{
 								{
 									ClassID: wire.FeedbagClassIDDeny,
@@ -1229,7 +1229,7 @@ func TestFeedbagService_DeleteItem(t *testing.T) {
 				messageRelayerParams: messageRelayerParams{
 					retrieveByScreenNameParams: retrieveByScreenNameParams{
 						{
-							screenName: "buddy_offline",
+							screenName: state.NewIdentScreenName("buddy_offline"),
 							sess:       nil,
 						},
 					},
@@ -1266,7 +1266,7 @@ func TestFeedbagService_DeleteItem(t *testing.T) {
 				feedbagManagerParams: feedbagManagerParams{
 					feedbagDeleteParams: feedbagDeleteParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 							items: []wire.FeedbagItem{
 								{
 									ClassID: wire.FeedbagClassIDDeny,
@@ -1279,7 +1279,7 @@ func TestFeedbagService_DeleteItem(t *testing.T) {
 				messageRelayerParams: messageRelayerParams{
 					retrieveByScreenNameParams: retrieveByScreenNameParams{
 						{
-							screenName: "invisible_buddy_online",
+							screenName: state.NewIdentScreenName("invisible_buddy_online"),
 							sess:       newTestSession("invisible_buddy_online", sessOptInvisible),
 						},
 					},
@@ -1287,8 +1287,8 @@ func TestFeedbagService_DeleteItem(t *testing.T) {
 				buddyBroadcasterParams: buddyBroadcasterParams{
 					unicastBuddyArrivedParams: unicastBuddyArrivedParams{
 						{
-							from: "user_screen_name",
-							to:   "invisible_buddy_online",
+							from: state.NewIdentScreenName("user_screen_name"),
+							to:   state.NewIdentScreenName("invisible_buddy_online"),
 						},
 					},
 				},
@@ -1326,10 +1326,10 @@ func TestFeedbagService_DeleteItem(t *testing.T) {
 				buddyUpdateBroadcast.EXPECT().
 					UnicastBuddyArrived(mock.Anything,
 						mock.MatchedBy(func(s *state.Session) bool {
-							return s.ScreenName() == p.from
+							return s.IdentScreenName() == p.from
 						}),
 						mock.MatchedBy(func(s *state.Session) bool {
-							return s.ScreenName() == p.to
+							return s.IdentScreenName() == p.to
 						})).
 					Return(nil)
 			}
@@ -1369,19 +1369,22 @@ func TestFeedbagService_Use(t *testing.T) {
 				feedbagManagerParams: feedbagManagerParams{
 					buddiesParams: buddiesParams{
 						{
-							screenName: "test-user",
-							results:    []string{"buddy1", "buddy3"},
+							screenName: state.NewIdentScreenName("test-user"),
+							results: []state.IdentScreenName{
+								state.NewIdentScreenName("buddy1"),
+								state.NewIdentScreenName("buddy3"),
+							},
 						},
 					},
 				},
 				messageRelayerParams: messageRelayerParams{
 					retrieveByScreenNameParams: retrieveByScreenNameParams{
 						{
-							screenName: "buddy1",
+							screenName: state.NewIdentScreenName("buddy1"),
 							sess:       newTestSession("buddy1"),
 						},
 						{
-							screenName: "buddy3",
+							screenName: state.NewIdentScreenName("buddy3"),
 							sess:       newTestSession("buddy3"),
 						},
 					},
@@ -1389,12 +1392,12 @@ func TestFeedbagService_Use(t *testing.T) {
 				buddyBroadcasterParams: buddyBroadcasterParams{
 					unicastBuddyArrivedParams: unicastBuddyArrivedParams{
 						{
-							from: "buddy1",
-							to:   "test-user",
+							from: state.NewIdentScreenName("buddy1"),
+							to:   state.NewIdentScreenName("test-user"),
 						},
 						{
-							from: "buddy3",
-							to:   "test-user",
+							from: state.NewIdentScreenName("buddy3"),
+							to:   state.NewIdentScreenName("test-user"),
 						},
 					},
 				},
@@ -1421,10 +1424,10 @@ func TestFeedbagService_Use(t *testing.T) {
 				buddyUpdateBroadcast.EXPECT().
 					UnicastBuddyArrived(mock.Anything,
 						mock.MatchedBy(func(s *state.Session) bool {
-							return s.ScreenName() == p.from
+							return s.IdentScreenName() == p.from
 						}),
 						mock.MatchedBy(func(s *state.Session) bool {
-							return s.ScreenName() == p.to
+							return s.IdentScreenName() == p.to
 						})).
 					Return(nil)
 			}

+ 14 - 12
foodgroup/icbm.go

@@ -58,7 +58,7 @@ func (s ICBMService) ParameterQuery(_ context.Context, inFrame wire.SNACFrame) w
 // the wire.ICBMChannelMsgToHost message contains a request acknowledgement
 // flag.
 func (s ICBMService) ChannelMsgToHost(ctx context.Context, sess *state.Session, inFrame wire.SNACFrame, inBody wire.SNAC_0x04_0x06_ICBMChannelMsgToHost) (*wire.SNACMessage, error) {
-	blocked, err := s.feedbagManager.BlockedState(sess.ScreenName(), inBody.ScreenName)
+	blocked, err := s.feedbagManager.BlockedState(sess.IdentScreenName(), state.NewIdentScreenName(inBody.ScreenName))
 	if err != nil {
 		return nil, err
 	}
@@ -80,7 +80,7 @@ func (s ICBMService) ChannelMsgToHost(ctx context.Context, sess *state.Session,
 		}, nil
 	}
 
-	recipSess := s.messageRelayer.RetrieveByScreenName(inBody.ScreenName)
+	recipSess := s.messageRelayer.RetrieveByScreenName(state.NewIdentScreenName(inBody.ScreenName))
 	if recipSess == nil {
 		return &wire.SNACMessage{
 			Frame: wire.SNACFrame{
@@ -98,7 +98,7 @@ func (s ICBMService) ChannelMsgToHost(ctx context.Context, sess *state.Session,
 		Cookie:    inBody.Cookie,
 		ChannelID: inBody.ChannelID,
 		TLVUserInfo: wire.TLVUserInfo{
-			ScreenName:   sess.ScreenName(),
+			ScreenName:   string(sess.DisplayScreenName()),
 			WarningLevel: sess.Warning(),
 		},
 		TLVRestBlock: wire.TLVRestBlock{
@@ -115,7 +115,7 @@ func (s ICBMService) ChannelMsgToHost(ctx context.Context, sess *state.Session,
 	// far as I can tell.
 	clientIM.AppendList(inBody.TLVRestBlock.TLVList)
 
-	s.messageRelayer.RelayToScreenName(ctx, recipSess.ScreenName(), wire.SNACMessage{
+	s.messageRelayer.RelayToScreenName(ctx, recipSess.IdentScreenName(), wire.SNACMessage{
 		Frame: wire.SNACFrame{
 			FoodGroup: wire.ICBM,
 			SubGroup:  wire.ICBMChannelMsgToClient,
@@ -146,7 +146,7 @@ func (s ICBMService) ChannelMsgToHost(ctx context.Context, sess *state.Session,
 // ClientEvent relays SNAC wire.ICBMClientEvent typing events from the
 // sender to the recipient.
 func (s ICBMService) ClientEvent(ctx context.Context, sess *state.Session, inFrame wire.SNACFrame, inBody wire.SNAC_0x04_0x14_ICBMClientEvent) error {
-	blocked, err := s.feedbagManager.BlockedState(sess.ScreenName(), inBody.ScreenName)
+	blocked, err := s.feedbagManager.BlockedState(sess.IdentScreenName(), state.NewIdentScreenName(inBody.ScreenName))
 
 	switch {
 	case err != nil:
@@ -154,7 +154,7 @@ func (s ICBMService) ClientEvent(ctx context.Context, sess *state.Session, inFra
 	case blocked != state.BlockedNo:
 		return nil
 	default:
-		s.messageRelayer.RelayToScreenName(ctx, inBody.ScreenName, wire.SNACMessage{
+		s.messageRelayer.RelayToScreenName(ctx, state.NewIdentScreenName(inBody.ScreenName), wire.SNACMessage{
 			Frame: wire.SNACFrame{
 				FoodGroup: wire.ICBM,
 				SubGroup:  wire.ICBMClientEvent,
@@ -163,7 +163,7 @@ func (s ICBMService) ClientEvent(ctx context.Context, sess *state.Session, inFra
 			Body: wire.SNAC_0x04_0x14_ICBMClientEvent{
 				Cookie:     inBody.Cookie,
 				ChannelID:  inBody.ChannelID,
-				ScreenName: sess.ScreenName(),
+				ScreenName: string(sess.DisplayScreenName()),
 				Event:      inBody.Event,
 			},
 		})
@@ -179,9 +179,11 @@ func (s ICBMService) ClientEvent(ctx context.Context, sess *state.Session, inFra
 // warning was sent. Users may not warn themselves or warn users they have
 // blocked or are blocked by.
 func (s ICBMService) EvilRequest(ctx context.Context, sess *state.Session, inFrame wire.SNACFrame, inBody wire.SNAC_0x04_0x08_ICBMEvilRequest) (wire.SNACMessage, error) {
+	identScreenName := state.NewIdentScreenName(inBody.ScreenName)
+
 	// don't let users warn themselves, it causes the AIM client to go into a
 	// weird state.
-	if inBody.ScreenName == sess.ScreenName() {
+	if identScreenName == sess.IdentScreenName() {
 		return wire.SNACMessage{
 			Frame: wire.SNACFrame{
 				FoodGroup: wire.ICBM,
@@ -194,7 +196,7 @@ func (s ICBMService) EvilRequest(ctx context.Context, sess *state.Session, inFra
 		}, nil
 	}
 
-	blocked, err := s.feedbagManager.BlockedState(sess.ScreenName(), inBody.ScreenName)
+	blocked, err := s.feedbagManager.BlockedState(sess.IdentScreenName(), identScreenName)
 	if err != nil {
 		return wire.SNACMessage{}, err
 	}
@@ -211,7 +213,7 @@ func (s ICBMService) EvilRequest(ctx context.Context, sess *state.Session, inFra
 		}, nil
 	}
 
-	recipSess := s.messageRelayer.RetrieveByScreenName(inBody.ScreenName)
+	recipSess := s.messageRelayer.RetrieveByScreenName(identScreenName)
 	if recipSess == nil {
 		return wire.SNACMessage{
 			Frame: wire.SNACFrame{
@@ -236,7 +238,7 @@ func (s ICBMService) EvilRequest(ctx context.Context, sess *state.Session, inFra
 		notif = wire.SNAC_0x01_0x10_OServiceEvilNotification{
 			NewEvil: recipSess.Warning(),
 			TLVUserInfo: wire.TLVUserInfo{
-				ScreenName:   sess.ScreenName(),
+				ScreenName:   sess.IdentScreenName().String(),
 				WarningLevel: recipSess.Warning(),
 			},
 		}
@@ -246,7 +248,7 @@ func (s ICBMService) EvilRequest(ctx context.Context, sess *state.Session, inFra
 		}
 	}
 
-	s.messageRelayer.RelayToScreenName(ctx, recipSess.ScreenName(), wire.SNACMessage{
+	s.messageRelayer.RelayToScreenName(ctx, recipSess.IdentScreenName(), wire.SNACMessage{
 		Frame: wire.SNACFrame{
 			FoodGroup: wire.OService,
 			SubGroup:  wire.OServiceEvilNotification,

+ 22 - 29
foodgroup/icbm_test.go

@@ -229,18 +229,18 @@ func TestICBMService_ChannelMsgToHost(t *testing.T) {
 			//
 			feedbagManager := newMockFeedbagManager(t)
 			feedbagManager.EXPECT().
-				BlockedState(tc.senderSession.ScreenName(),
-					tc.inputSNAC.Body.(wire.SNAC_0x04_0x06_ICBMChannelMsgToHost).ScreenName).
+				BlockedState(tc.senderSession.IdentScreenName(),
+					state.NewIdentScreenName(tc.inputSNAC.Body.(wire.SNAC_0x04_0x06_ICBMChannelMsgToHost).ScreenName)).
 				Return(tc.blockedState, nil).
 				Maybe()
 			messageRelayer := newMockMessageRelayer(t)
 			messageRelayer.EXPECT().
-				RetrieveByScreenName(tc.inputSNAC.Body.(wire.SNAC_0x04_0x06_ICBMChannelMsgToHost).ScreenName).
+				RetrieveByScreenName(state.NewIdentScreenName(tc.inputSNAC.Body.(wire.SNAC_0x04_0x06_ICBMChannelMsgToHost).ScreenName)).
 				Return(tc.recipientSession).
 				Maybe()
 			if tc.recipientSession != nil {
 				messageRelayer.EXPECT().
-					RelayToScreenName(mock.Anything, tc.recipientSession.ScreenName(), tc.expectSNACToClient).
+					RelayToScreenName(mock.Anything, tc.recipientSession.IdentScreenName(), tc.expectSNACToClient).
 					Maybe()
 			}
 			//
@@ -265,7 +265,7 @@ func TestICBMService_ClientEvent(t *testing.T) {
 		// blockedState is the response to the sender/recipient block check
 		blockedState state.BlockedState
 		// senderScreenName is the screen name of the user sending the event
-		senderScreenName string
+		senderScreenName state.DisplayScreenName
 		// inputSNAC is the SNAC sent by the sender client
 		inputSNAC wire.SNACMessage
 		// expectSNACToClient is the SNAC sent from the server to the
@@ -323,13 +323,15 @@ func TestICBMService_ClientEvent(t *testing.T) {
 			//
 			feedbagManager := newMockFeedbagManager(t)
 			feedbagManager.EXPECT().
-				BlockedState(tc.senderScreenName, tc.inputSNAC.Body.(wire.SNAC_0x04_0x14_ICBMClientEvent).ScreenName).
+				BlockedState(tc.senderScreenName.IdentScreenName(),
+					state.NewIdentScreenName(tc.inputSNAC.Body.(wire.SNAC_0x04_0x14_ICBMClientEvent).ScreenName)).
 				Return(tc.blockedState, nil).
 				Maybe()
 			messageRelayer := newMockMessageRelayer(t)
 			if tc.blockedState == state.BlockedNo {
 				messageRelayer.EXPECT().
-					RelayToScreenName(mock.Anything, tc.inputSNAC.Body.(wire.SNAC_0x04_0x14_ICBMClientEvent).ScreenName,
+					RelayToScreenName(mock.Anything,
+						state.NewIdentScreenName(tc.inputSNAC.Body.(wire.SNAC_0x04_0x14_ICBMClientEvent).ScreenName),
 						tc.expectSNACToClient)
 			}
 			//
@@ -357,10 +359,7 @@ func TestICBMService_EvilRequest(t *testing.T) {
 		// recipientSession is the session of the user receiving the EvilNotification
 		recipientSession *state.Session
 		// recipientScreenName is the screen name of the user receiving the EvilNotification
-		recipientScreenName string
-		// recipientBuddies is a list of the recipient's buddies that get
-		// updated warning level
-		recipientBuddies []string
+		recipientScreenName state.IdentScreenName
 		// inputSNAC is the SNAC sent by the sender client
 		inputSNAC wire.SNACMessage
 		// expectSNACToClient is the SNAC sent from the server to the
@@ -377,8 +376,7 @@ func TestICBMService_EvilRequest(t *testing.T) {
 			blockedState:        state.BlockedNo,
 			senderSession:       newTestSession("sender-screen-name"),
 			recipientSession:    newTestSession("recipient-screen-name", sessOptCannedSignonTime),
-			recipientScreenName: "recipient-screen-name",
-			recipientBuddies:    []string{"buddy1", "buddy2"},
+			recipientScreenName: state.NewIdentScreenName("recipient-screen-name"),
 			inputSNAC: wire.SNACMessage{
 				Frame: wire.SNACFrame{
 					RequestID: 1234,
@@ -412,7 +410,7 @@ func TestICBMService_EvilRequest(t *testing.T) {
 				buddyBroadcasterParams: buddyBroadcasterParams{
 					broadcastBuddyArrivedParams: broadcastBuddyArrivedParams{
 						{
-							screenName: "recipient-screen-name",
+							screenName: state.NewIdentScreenName("recipient-screen-name"),
 						},
 					},
 				},
@@ -423,8 +421,7 @@ func TestICBMService_EvilRequest(t *testing.T) {
 			blockedState:        state.BlockedNo,
 			senderSession:       newTestSession("sender-screen-name"),
 			recipientSession:    newTestSession("recipient-screen-name", sessOptCannedSignonTime),
-			recipientScreenName: "recipient-screen-name",
-			recipientBuddies:    []string{"buddy1", "buddy2"},
+			recipientScreenName: state.NewIdentScreenName("recipient-screen-name"),
 			inputSNAC: wire.SNACMessage{
 				Frame: wire.SNACFrame{
 					RequestID: 1234,
@@ -462,7 +459,7 @@ func TestICBMService_EvilRequest(t *testing.T) {
 				buddyBroadcasterParams: buddyBroadcasterParams{
 					broadcastBuddyArrivedParams: broadcastBuddyArrivedParams{
 						{
-							screenName: "recipient-screen-name",
+							screenName: state.NewIdentScreenName("recipient-screen-name"),
 						},
 					},
 				},
@@ -473,8 +470,7 @@ func TestICBMService_EvilRequest(t *testing.T) {
 			blockedState:        state.BlockedA,
 			senderSession:       newTestSession("sender-screen-name"),
 			recipientSession:    nil,
-			recipientScreenName: "recipient-screen-name",
-			recipientBuddies:    []string{"buddy1", "buddy2"},
+			recipientScreenName: state.NewIdentScreenName("recipient-screen-name"),
 			inputSNAC: wire.SNACMessage{
 				Frame: wire.SNACFrame{
 					RequestID: 1234,
@@ -500,8 +496,7 @@ func TestICBMService_EvilRequest(t *testing.T) {
 			blockedState:        state.BlockedB,
 			senderSession:       newTestSession("sender-screen-name"),
 			recipientSession:    nil,
-			recipientScreenName: "recipient-screen-name",
-			recipientBuddies:    []string{"buddy1", "buddy2"},
+			recipientScreenName: state.NewIdentScreenName("recipient-screen-name"),
 			inputSNAC: wire.SNACMessage{
 				Frame: wire.SNACFrame{
 					RequestID: 1234,
@@ -526,7 +521,7 @@ func TestICBMService_EvilRequest(t *testing.T) {
 			name:                "don't let users warn themselves",
 			senderSession:       newTestSession("sender-screen-name"),
 			recipientSession:    nil,
-			recipientScreenName: "sender-screen-name",
+			recipientScreenName: state.NewIdentScreenName("sender-screen-name"),
 			inputSNAC: wire.SNACMessage{
 				Frame: wire.SNACFrame{
 					RequestID: 1234,
@@ -552,8 +547,7 @@ func TestICBMService_EvilRequest(t *testing.T) {
 			blockedState:        state.BlockedNo,
 			senderSession:       newTestSession("sender-screen-name"),
 			recipientSession:    nil,
-			recipientScreenName: "recipient-screen-name",
-			recipientBuddies:    []string{"buddy1", "buddy2"},
+			recipientScreenName: state.NewIdentScreenName("recipient-screen-name"),
 			inputSNAC: wire.SNACMessage{
 				Frame: wire.SNACFrame{
 					RequestID: 1234,
@@ -579,8 +573,7 @@ func TestICBMService_EvilRequest(t *testing.T) {
 			blockedState:        state.BlockedNo,
 			senderSession:       newTestSession("sender-screen-name"),
 			recipientSession:    nil,
-			recipientScreenName: "recipient-screen-name",
-			recipientBuddies:    []string{"buddy1", "buddy2"},
+			recipientScreenName: state.NewIdentScreenName("recipient-screen-name"),
 			inputSNAC: wire.SNACMessage{
 				Frame: wire.SNACFrame{
 					RequestID: 1234,
@@ -610,7 +603,7 @@ func TestICBMService_EvilRequest(t *testing.T) {
 			//
 			feedbagManager := newMockFeedbagManager(t)
 			feedbagManager.EXPECT().
-				BlockedState(tc.senderSession.ScreenName(), tc.recipientScreenName).
+				BlockedState(tc.senderSession.IdentScreenName(), tc.recipientScreenName).
 				Return(tc.blockedState, nil).
 				Maybe()
 			messageRelayer := newMockMessageRelayer(t)
@@ -626,14 +619,14 @@ func TestICBMService_EvilRequest(t *testing.T) {
 				p := params
 				buddyUpdateBroadcaster.EXPECT().
 					BroadcastBuddyArrived(mock.Anything, mock.MatchedBy(func(s *state.Session) bool {
-						return s.ScreenName() == p.screenName
+						return s.IdentScreenName() == p.screenName
 					})).
 					Return(nil)
 			}
 			//
 			// send input SNAC
 			//
-			senderSession := newTestSession(tc.senderSession.ScreenName())
+			senderSession := newTestSession(tc.senderSession.DisplayScreenName())
 			svc := NewICBMService(messageRelayer, feedbagManager, buddyUpdateBroadcaster)
 			outputSNAC, err := svc.EvilRequest(nil, senderSession, tc.inputSNAC.Frame,
 				tc.inputSNAC.Body.(wire.SNAC_0x04_0x08_ICBMEvilRequest))

+ 6 - 4
foodgroup/locate.go

@@ -73,7 +73,7 @@ func (s LocateService) RightsQuery(_ context.Context, inFrame wire.SNACFrame) wi
 func (s LocateService) SetInfo(ctx context.Context, sess *state.Session, inBody wire.SNAC_0x02_0x04_LocateSetInfo) error {
 	// update profile
 	if profile, hasProfile := inBody.String(wire.LocateTLVTagsInfoSigData); hasProfile {
-		if err := s.profileManager.SetProfile(sess.ScreenName(), profile); err != nil {
+		if err := s.profileManager.SetProfile(sess.IdentScreenName(), profile); err != nil {
 			return err
 		}
 	}
@@ -111,7 +111,9 @@ func (s LocateService) SetInfo(ctx context.Context, sess *state.Session, inBody
 // profile, if requested, and/or the away message, if requested. This is a v2
 // of UserInfoQuery.
 func (s LocateService) UserInfoQuery(_ context.Context, sess *state.Session, inFrame wire.SNACFrame, inBody wire.SNAC_0x02_0x05_LocateUserInfoQuery) (wire.SNACMessage, error) {
-	blocked, err := s.feedbagManager.BlockedState(sess.ScreenName(), inBody.ScreenName)
+	identScreenName := state.NewIdentScreenName(inBody.ScreenName)
+
+	blocked, err := s.feedbagManager.BlockedState(sess.IdentScreenName(), identScreenName)
 	switch {
 	case err != nil:
 		return wire.SNACMessage{}, err
@@ -128,7 +130,7 @@ func (s LocateService) UserInfoQuery(_ context.Context, sess *state.Session, inF
 		}, nil
 	}
 
-	buddySess := s.sessionManager.RetrieveByScreenName(inBody.ScreenName)
+	buddySess := s.sessionManager.RetrieveByScreenName(identScreenName)
 	if buddySess == nil {
 		return wire.SNACMessage{
 			Frame: wire.SNACFrame{
@@ -145,7 +147,7 @@ func (s LocateService) UserInfoQuery(_ context.Context, sess *state.Session, inF
 	var list wire.TLVList
 
 	if inBody.RequestProfile() {
-		profile, err := s.profileManager.Profile(inBody.ScreenName)
+		profile, err := s.profileManager.Profile(identScreenName)
 		if err != nil {
 			return wire.SNACMessage{}, err
 		}

+ 22 - 22
foodgroup/locate_test.go

@@ -31,8 +31,8 @@ func TestLocateService_UserInfoQuery(t *testing.T) {
 				feedbagManagerParams: feedbagManagerParams{
 					blockedStateParams: blockedStateParams{
 						{
-							screenName1: "user_screen_name",
-							screenName2: "requested-user",
+							screenName1: state.NewIdentScreenName("user_screen_name"),
+							screenName2: state.NewIdentScreenName("requested-user"),
 							result:      state.BlockedNo,
 						},
 					},
@@ -40,7 +40,7 @@ func TestLocateService_UserInfoQuery(t *testing.T) {
 				messageRelayerParams: messageRelayerParams{
 					retrieveByScreenNameParams: retrieveByScreenNameParams{
 						{
-							screenName: "requested-user",
+							screenName: state.NewIdentScreenName("requested-user"),
 							sess: newTestSession("requested-user",
 								sessOptCannedSignonTime,
 								sessOptCannedAwayMessage),
@@ -79,8 +79,8 @@ func TestLocateService_UserInfoQuery(t *testing.T) {
 				feedbagManagerParams: feedbagManagerParams{
 					blockedStateParams: blockedStateParams{
 						{
-							screenName1: "user_screen_name",
-							screenName2: "requested-user",
+							screenName1: state.NewIdentScreenName("user_screen_name"),
+							screenName2: state.NewIdentScreenName("requested-user"),
 							result:      state.BlockedNo,
 						},
 					},
@@ -88,7 +88,7 @@ func TestLocateService_UserInfoQuery(t *testing.T) {
 				messageRelayerParams: messageRelayerParams{
 					retrieveByScreenNameParams: retrieveByScreenNameParams{
 						{
-							screenName: "requested-user",
+							screenName: state.NewIdentScreenName("requested-user"),
 							sess: newTestSession("requested-user",
 								sessOptCannedSignonTime,
 								sessOptCannedAwayMessage),
@@ -98,7 +98,7 @@ func TestLocateService_UserInfoQuery(t *testing.T) {
 				profileManagerParams: profileManagerParams{
 					retrieveProfileParams: retrieveProfileParams{
 						{
-							screenName: "requested-user",
+							screenName: state.NewIdentScreenName("requested-user"),
 							result:     "this is my profile!",
 						},
 					},
@@ -141,8 +141,8 @@ func TestLocateService_UserInfoQuery(t *testing.T) {
 				feedbagManagerParams: feedbagManagerParams{
 					blockedStateParams: blockedStateParams{
 						{
-							screenName1: "user_screen_name",
-							screenName2: "requested-user",
+							screenName1: state.NewIdentScreenName("user_screen_name"),
+							screenName2: state.NewIdentScreenName("requested-user"),
 							result:      state.BlockedNo,
 						},
 					},
@@ -150,7 +150,7 @@ func TestLocateService_UserInfoQuery(t *testing.T) {
 				messageRelayerParams: messageRelayerParams{
 					retrieveByScreenNameParams: retrieveByScreenNameParams{
 						{
-							screenName: "requested-user",
+							screenName: state.NewIdentScreenName("requested-user"),
 							sess: newTestSession("requested-user",
 								sessOptCannedSignonTime,
 								sessOptCannedAwayMessage),
@@ -160,7 +160,7 @@ func TestLocateService_UserInfoQuery(t *testing.T) {
 				profileManagerParams: profileManagerParams{
 					retrieveProfileParams: retrieveProfileParams{
 						{
-							screenName: "requested-user",
+							screenName: state.NewIdentScreenName("requested-user"),
 							result:     "this is my profile!",
 						},
 					},
@@ -203,8 +203,8 @@ func TestLocateService_UserInfoQuery(t *testing.T) {
 				feedbagManagerParams: feedbagManagerParams{
 					blockedStateParams: blockedStateParams{
 						{
-							screenName1: "user_screen_name",
-							screenName2: "requested-user",
+							screenName1: state.NewIdentScreenName("user_screen_name"),
+							screenName2: state.NewIdentScreenName("requested-user"),
 							result:      state.BlockedNo,
 						},
 					},
@@ -212,7 +212,7 @@ func TestLocateService_UserInfoQuery(t *testing.T) {
 				messageRelayerParams: messageRelayerParams{
 					retrieveByScreenNameParams: retrieveByScreenNameParams{
 						{
-							screenName: "requested-user",
+							screenName: state.NewIdentScreenName("requested-user"),
 							sess: newTestSession("requested-user",
 								sessOptCannedSignonTime,
 								sessOptCannedAwayMessage),
@@ -257,8 +257,8 @@ func TestLocateService_UserInfoQuery(t *testing.T) {
 				feedbagManagerParams: feedbagManagerParams{
 					blockedStateParams: blockedStateParams{
 						{
-							screenName1: "user_screen_name",
-							screenName2: "requested-user",
+							screenName1: state.NewIdentScreenName("user_screen_name"),
+							screenName2: state.NewIdentScreenName("requested-user"),
 							result:      state.BlockedB,
 						},
 					},
@@ -290,8 +290,8 @@ func TestLocateService_UserInfoQuery(t *testing.T) {
 				feedbagManagerParams: feedbagManagerParams{
 					blockedStateParams: blockedStateParams{
 						{
-							screenName1: "user_screen_name",
-							screenName2: "non_existent_requested_user",
+							screenName1: state.NewIdentScreenName("user_screen_name"),
+							screenName2: state.NewIdentScreenName("non_existent_requested_user"),
 							result:      state.BlockedNo,
 						},
 					},
@@ -299,7 +299,7 @@ func TestLocateService_UserInfoQuery(t *testing.T) {
 				messageRelayerParams: messageRelayerParams{
 					retrieveByScreenNameParams: retrieveByScreenNameParams{
 						{
-							screenName: "non_existent_requested_user",
+							screenName: state.NewIdentScreenName("non_existent_requested_user"),
 							sess:       nil,
 						},
 					},
@@ -425,7 +425,7 @@ func TestLocateService_SetInfo(t *testing.T) {
 				profileManagerParams: profileManagerParams{
 					setProfileParams: setProfileParams{
 						{
-							screenName: "test-user",
+							screenName: state.NewIdentScreenName("test-user"),
 							body:       "profile-result",
 						},
 					},
@@ -446,7 +446,7 @@ func TestLocateService_SetInfo(t *testing.T) {
 				buddyBroadcasterParams: buddyBroadcasterParams{
 					broadcastBuddyArrivedParams: broadcastBuddyArrivedParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 						},
 					},
 				},
@@ -466,7 +466,7 @@ func TestLocateService_SetInfo(t *testing.T) {
 				p := params
 				buddyUpdateBroadcaster.EXPECT().
 					BroadcastBuddyArrived(mock.Anything, mock.MatchedBy(func(s *state.Session) bool {
-						return s.ScreenName() == p.screenName
+						return s.IdentScreenName() == p.screenName
 					})).
 					Return(nil)
 			}

+ 16 - 16
foodgroup/mock_chat_message_relayer_test.go

@@ -107,7 +107,7 @@ func (_c *mockChatMessageRelayer_RelayToAllExcept_Call) RunAndReturn(run func(co
 }
 
 // RelayToScreenName provides a mock function with given fields: ctx, screenName, msg
-func (_m *mockChatMessageRelayer) RelayToScreenName(ctx context.Context, screenName string, msg wire.SNACMessage) {
+func (_m *mockChatMessageRelayer) RelayToScreenName(ctx context.Context, screenName state.IdentScreenName, msg wire.SNACMessage) {
 	_m.Called(ctx, screenName, msg)
 }
 
@@ -118,15 +118,15 @@ type mockChatMessageRelayer_RelayToScreenName_Call struct {
 
 // RelayToScreenName is a helper method to define mock.On call
 //   - ctx context.Context
-//   - screenName string
+//   - screenName state.IdentScreenName
 //   - msg wire.SNACMessage
 func (_e *mockChatMessageRelayer_Expecter) RelayToScreenName(ctx interface{}, screenName interface{}, msg interface{}) *mockChatMessageRelayer_RelayToScreenName_Call {
 	return &mockChatMessageRelayer_RelayToScreenName_Call{Call: _e.mock.On("RelayToScreenName", ctx, screenName, msg)}
 }
 
-func (_c *mockChatMessageRelayer_RelayToScreenName_Call) Run(run func(ctx context.Context, screenName string, msg wire.SNACMessage)) *mockChatMessageRelayer_RelayToScreenName_Call {
+func (_c *mockChatMessageRelayer_RelayToScreenName_Call) Run(run func(ctx context.Context, screenName state.IdentScreenName, msg wire.SNACMessage)) *mockChatMessageRelayer_RelayToScreenName_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(context.Context), args[1].(string), args[2].(wire.SNACMessage))
+		run(args[0].(context.Context), args[1].(state.IdentScreenName), args[2].(wire.SNACMessage))
 	})
 	return _c
 }
@@ -136,13 +136,13 @@ func (_c *mockChatMessageRelayer_RelayToScreenName_Call) Return() *mockChatMessa
 	return _c
 }
 
-func (_c *mockChatMessageRelayer_RelayToScreenName_Call) RunAndReturn(run func(context.Context, string, wire.SNACMessage)) *mockChatMessageRelayer_RelayToScreenName_Call {
+func (_c *mockChatMessageRelayer_RelayToScreenName_Call) RunAndReturn(run func(context.Context, state.IdentScreenName, wire.SNACMessage)) *mockChatMessageRelayer_RelayToScreenName_Call {
 	_c.Call.Return(run)
 	return _c
 }
 
 // RelayToScreenNames provides a mock function with given fields: ctx, screenNames, msg
-func (_m *mockChatMessageRelayer) RelayToScreenNames(ctx context.Context, screenNames []string, msg wire.SNACMessage) {
+func (_m *mockChatMessageRelayer) RelayToScreenNames(ctx context.Context, screenNames []state.IdentScreenName, msg wire.SNACMessage) {
 	_m.Called(ctx, screenNames, msg)
 }
 
@@ -153,15 +153,15 @@ type mockChatMessageRelayer_RelayToScreenNames_Call struct {
 
 // RelayToScreenNames is a helper method to define mock.On call
 //   - ctx context.Context
-//   - screenNames []string
+//   - screenNames []state.IdentScreenName
 //   - msg wire.SNACMessage
 func (_e *mockChatMessageRelayer_Expecter) RelayToScreenNames(ctx interface{}, screenNames interface{}, msg interface{}) *mockChatMessageRelayer_RelayToScreenNames_Call {
 	return &mockChatMessageRelayer_RelayToScreenNames_Call{Call: _e.mock.On("RelayToScreenNames", ctx, screenNames, msg)}
 }
 
-func (_c *mockChatMessageRelayer_RelayToScreenNames_Call) Run(run func(ctx context.Context, screenNames []string, msg wire.SNACMessage)) *mockChatMessageRelayer_RelayToScreenNames_Call {
+func (_c *mockChatMessageRelayer_RelayToScreenNames_Call) Run(run func(ctx context.Context, screenNames []state.IdentScreenName, msg wire.SNACMessage)) *mockChatMessageRelayer_RelayToScreenNames_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(context.Context), args[1].([]string), args[2].(wire.SNACMessage))
+		run(args[0].(context.Context), args[1].([]state.IdentScreenName), args[2].(wire.SNACMessage))
 	})
 	return _c
 }
@@ -171,13 +171,13 @@ func (_c *mockChatMessageRelayer_RelayToScreenNames_Call) Return() *mockChatMess
 	return _c
 }
 
-func (_c *mockChatMessageRelayer_RelayToScreenNames_Call) RunAndReturn(run func(context.Context, []string, wire.SNACMessage)) *mockChatMessageRelayer_RelayToScreenNames_Call {
+func (_c *mockChatMessageRelayer_RelayToScreenNames_Call) RunAndReturn(run func(context.Context, []state.IdentScreenName, wire.SNACMessage)) *mockChatMessageRelayer_RelayToScreenNames_Call {
 	_c.Call.Return(run)
 	return _c
 }
 
 // RetrieveByScreenName provides a mock function with given fields: screenName
-func (_m *mockChatMessageRelayer) RetrieveByScreenName(screenName string) *state.Session {
+func (_m *mockChatMessageRelayer) RetrieveByScreenName(screenName state.IdentScreenName) *state.Session {
 	ret := _m.Called(screenName)
 
 	if len(ret) == 0 {
@@ -185,7 +185,7 @@ func (_m *mockChatMessageRelayer) RetrieveByScreenName(screenName string) *state
 	}
 
 	var r0 *state.Session
-	if rf, ok := ret.Get(0).(func(string) *state.Session); ok {
+	if rf, ok := ret.Get(0).(func(state.IdentScreenName) *state.Session); ok {
 		r0 = rf(screenName)
 	} else {
 		if ret.Get(0) != nil {
@@ -202,14 +202,14 @@ type mockChatMessageRelayer_RetrieveByScreenName_Call struct {
 }
 
 // RetrieveByScreenName is a helper method to define mock.On call
-//   - screenName string
+//   - screenName state.IdentScreenName
 func (_e *mockChatMessageRelayer_Expecter) RetrieveByScreenName(screenName interface{}) *mockChatMessageRelayer_RetrieveByScreenName_Call {
 	return &mockChatMessageRelayer_RetrieveByScreenName_Call{Call: _e.mock.On("RetrieveByScreenName", screenName)}
 }
 
-func (_c *mockChatMessageRelayer_RetrieveByScreenName_Call) Run(run func(screenName string)) *mockChatMessageRelayer_RetrieveByScreenName_Call {
+func (_c *mockChatMessageRelayer_RetrieveByScreenName_Call) Run(run func(screenName state.IdentScreenName)) *mockChatMessageRelayer_RetrieveByScreenName_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(string))
+		run(args[0].(state.IdentScreenName))
 	})
 	return _c
 }
@@ -219,7 +219,7 @@ func (_c *mockChatMessageRelayer_RetrieveByScreenName_Call) Return(_a0 *state.Se
 	return _c
 }
 
-func (_c *mockChatMessageRelayer_RetrieveByScreenName_Call) RunAndReturn(run func(string) *state.Session) *mockChatMessageRelayer_RetrieveByScreenName_Call {
+func (_c *mockChatMessageRelayer_RetrieveByScreenName_Call) RunAndReturn(run func(state.IdentScreenName) *state.Session) *mockChatMessageRelayer_RetrieveByScreenName_Call {
 	_c.Call.Return(run)
 	return _c
 }

+ 59 - 59
foodgroup/mock_feedbag_manager_test.go

@@ -25,27 +25,27 @@ func (_m *mockFeedbagManager) EXPECT() *mockFeedbagManager_Expecter {
 }
 
 // AdjacentUsers provides a mock function with given fields: screenName
-func (_m *mockFeedbagManager) AdjacentUsers(screenName string) ([]string, error) {
+func (_m *mockFeedbagManager) AdjacentUsers(screenName state.IdentScreenName) ([]state.IdentScreenName, error) {
 	ret := _m.Called(screenName)
 
 	if len(ret) == 0 {
 		panic("no return value specified for AdjacentUsers")
 	}
 
-	var r0 []string
+	var r0 []state.IdentScreenName
 	var r1 error
-	if rf, ok := ret.Get(0).(func(string) ([]string, error)); ok {
+	if rf, ok := ret.Get(0).(func(state.IdentScreenName) ([]state.IdentScreenName, error)); ok {
 		return rf(screenName)
 	}
-	if rf, ok := ret.Get(0).(func(string) []string); ok {
+	if rf, ok := ret.Get(0).(func(state.IdentScreenName) []state.IdentScreenName); ok {
 		r0 = rf(screenName)
 	} else {
 		if ret.Get(0) != nil {
-			r0 = ret.Get(0).([]string)
+			r0 = ret.Get(0).([]state.IdentScreenName)
 		}
 	}
 
-	if rf, ok := ret.Get(1).(func(string) error); ok {
+	if rf, ok := ret.Get(1).(func(state.IdentScreenName) error); ok {
 		r1 = rf(screenName)
 	} else {
 		r1 = ret.Error(1)
@@ -60,30 +60,30 @@ type mockFeedbagManager_AdjacentUsers_Call struct {
 }
 
 // AdjacentUsers is a helper method to define mock.On call
-//   - screenName string
+//   - screenName state.IdentScreenName
 func (_e *mockFeedbagManager_Expecter) AdjacentUsers(screenName interface{}) *mockFeedbagManager_AdjacentUsers_Call {
 	return &mockFeedbagManager_AdjacentUsers_Call{Call: _e.mock.On("AdjacentUsers", screenName)}
 }
 
-func (_c *mockFeedbagManager_AdjacentUsers_Call) Run(run func(screenName string)) *mockFeedbagManager_AdjacentUsers_Call {
+func (_c *mockFeedbagManager_AdjacentUsers_Call) Run(run func(screenName state.IdentScreenName)) *mockFeedbagManager_AdjacentUsers_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(string))
+		run(args[0].(state.IdentScreenName))
 	})
 	return _c
 }
 
-func (_c *mockFeedbagManager_AdjacentUsers_Call) Return(_a0 []string, _a1 error) *mockFeedbagManager_AdjacentUsers_Call {
+func (_c *mockFeedbagManager_AdjacentUsers_Call) Return(_a0 []state.IdentScreenName, _a1 error) *mockFeedbagManager_AdjacentUsers_Call {
 	_c.Call.Return(_a0, _a1)
 	return _c
 }
 
-func (_c *mockFeedbagManager_AdjacentUsers_Call) RunAndReturn(run func(string) ([]string, error)) *mockFeedbagManager_AdjacentUsers_Call {
+func (_c *mockFeedbagManager_AdjacentUsers_Call) RunAndReturn(run func(state.IdentScreenName) ([]state.IdentScreenName, error)) *mockFeedbagManager_AdjacentUsers_Call {
 	_c.Call.Return(run)
 	return _c
 }
 
 // BlockedState provides a mock function with given fields: screenName1, screenName2
-func (_m *mockFeedbagManager) BlockedState(screenName1 string, screenName2 string) (state.BlockedState, error) {
+func (_m *mockFeedbagManager) BlockedState(screenName1 state.IdentScreenName, screenName2 state.IdentScreenName) (state.BlockedState, error) {
 	ret := _m.Called(screenName1, screenName2)
 
 	if len(ret) == 0 {
@@ -92,16 +92,16 @@ func (_m *mockFeedbagManager) BlockedState(screenName1 string, screenName2 strin
 
 	var r0 state.BlockedState
 	var r1 error
-	if rf, ok := ret.Get(0).(func(string, string) (state.BlockedState, error)); ok {
+	if rf, ok := ret.Get(0).(func(state.IdentScreenName, state.IdentScreenName) (state.BlockedState, error)); ok {
 		return rf(screenName1, screenName2)
 	}
-	if rf, ok := ret.Get(0).(func(string, string) state.BlockedState); ok {
+	if rf, ok := ret.Get(0).(func(state.IdentScreenName, state.IdentScreenName) state.BlockedState); ok {
 		r0 = rf(screenName1, screenName2)
 	} else {
 		r0 = ret.Get(0).(state.BlockedState)
 	}
 
-	if rf, ok := ret.Get(1).(func(string, string) error); ok {
+	if rf, ok := ret.Get(1).(func(state.IdentScreenName, state.IdentScreenName) error); ok {
 		r1 = rf(screenName1, screenName2)
 	} else {
 		r1 = ret.Error(1)
@@ -116,15 +116,15 @@ type mockFeedbagManager_BlockedState_Call struct {
 }
 
 // BlockedState is a helper method to define mock.On call
-//   - screenName1 string
-//   - screenName2 string
+//   - screenName1 state.IdentScreenName
+//   - screenName2 state.IdentScreenName
 func (_e *mockFeedbagManager_Expecter) BlockedState(screenName1 interface{}, screenName2 interface{}) *mockFeedbagManager_BlockedState_Call {
 	return &mockFeedbagManager_BlockedState_Call{Call: _e.mock.On("BlockedState", screenName1, screenName2)}
 }
 
-func (_c *mockFeedbagManager_BlockedState_Call) Run(run func(screenName1 string, screenName2 string)) *mockFeedbagManager_BlockedState_Call {
+func (_c *mockFeedbagManager_BlockedState_Call) Run(run func(screenName1 state.IdentScreenName, screenName2 state.IdentScreenName)) *mockFeedbagManager_BlockedState_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(string), args[1].(string))
+		run(args[0].(state.IdentScreenName), args[1].(state.IdentScreenName))
 	})
 	return _c
 }
@@ -134,33 +134,33 @@ func (_c *mockFeedbagManager_BlockedState_Call) Return(_a0 state.BlockedState, _
 	return _c
 }
 
-func (_c *mockFeedbagManager_BlockedState_Call) RunAndReturn(run func(string, string) (state.BlockedState, error)) *mockFeedbagManager_BlockedState_Call {
+func (_c *mockFeedbagManager_BlockedState_Call) RunAndReturn(run func(state.IdentScreenName, state.IdentScreenName) (state.BlockedState, error)) *mockFeedbagManager_BlockedState_Call {
 	_c.Call.Return(run)
 	return _c
 }
 
 // Buddies provides a mock function with given fields: screenName
-func (_m *mockFeedbagManager) Buddies(screenName string) ([]string, error) {
+func (_m *mockFeedbagManager) Buddies(screenName state.IdentScreenName) ([]state.IdentScreenName, error) {
 	ret := _m.Called(screenName)
 
 	if len(ret) == 0 {
 		panic("no return value specified for Buddies")
 	}
 
-	var r0 []string
+	var r0 []state.IdentScreenName
 	var r1 error
-	if rf, ok := ret.Get(0).(func(string) ([]string, error)); ok {
+	if rf, ok := ret.Get(0).(func(state.IdentScreenName) ([]state.IdentScreenName, error)); ok {
 		return rf(screenName)
 	}
-	if rf, ok := ret.Get(0).(func(string) []string); ok {
+	if rf, ok := ret.Get(0).(func(state.IdentScreenName) []state.IdentScreenName); ok {
 		r0 = rf(screenName)
 	} else {
 		if ret.Get(0) != nil {
-			r0 = ret.Get(0).([]string)
+			r0 = ret.Get(0).([]state.IdentScreenName)
 		}
 	}
 
-	if rf, ok := ret.Get(1).(func(string) error); ok {
+	if rf, ok := ret.Get(1).(func(state.IdentScreenName) error); ok {
 		r1 = rf(screenName)
 	} else {
 		r1 = ret.Error(1)
@@ -175,30 +175,30 @@ type mockFeedbagManager_Buddies_Call struct {
 }
 
 // Buddies is a helper method to define mock.On call
-//   - screenName string
+//   - screenName state.IdentScreenName
 func (_e *mockFeedbagManager_Expecter) Buddies(screenName interface{}) *mockFeedbagManager_Buddies_Call {
 	return &mockFeedbagManager_Buddies_Call{Call: _e.mock.On("Buddies", screenName)}
 }
 
-func (_c *mockFeedbagManager_Buddies_Call) Run(run func(screenName string)) *mockFeedbagManager_Buddies_Call {
+func (_c *mockFeedbagManager_Buddies_Call) Run(run func(screenName state.IdentScreenName)) *mockFeedbagManager_Buddies_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(string))
+		run(args[0].(state.IdentScreenName))
 	})
 	return _c
 }
 
-func (_c *mockFeedbagManager_Buddies_Call) Return(_a0 []string, _a1 error) *mockFeedbagManager_Buddies_Call {
+func (_c *mockFeedbagManager_Buddies_Call) Return(_a0 []state.IdentScreenName, _a1 error) *mockFeedbagManager_Buddies_Call {
 	_c.Call.Return(_a0, _a1)
 	return _c
 }
 
-func (_c *mockFeedbagManager_Buddies_Call) RunAndReturn(run func(string) ([]string, error)) *mockFeedbagManager_Buddies_Call {
+func (_c *mockFeedbagManager_Buddies_Call) RunAndReturn(run func(state.IdentScreenName) ([]state.IdentScreenName, error)) *mockFeedbagManager_Buddies_Call {
 	_c.Call.Return(run)
 	return _c
 }
 
 // Feedbag provides a mock function with given fields: screenName
-func (_m *mockFeedbagManager) Feedbag(screenName string) ([]wire.FeedbagItem, error) {
+func (_m *mockFeedbagManager) Feedbag(screenName state.IdentScreenName) ([]wire.FeedbagItem, error) {
 	ret := _m.Called(screenName)
 
 	if len(ret) == 0 {
@@ -207,10 +207,10 @@ func (_m *mockFeedbagManager) Feedbag(screenName string) ([]wire.FeedbagItem, er
 
 	var r0 []wire.FeedbagItem
 	var r1 error
-	if rf, ok := ret.Get(0).(func(string) ([]wire.FeedbagItem, error)); ok {
+	if rf, ok := ret.Get(0).(func(state.IdentScreenName) ([]wire.FeedbagItem, error)); ok {
 		return rf(screenName)
 	}
-	if rf, ok := ret.Get(0).(func(string) []wire.FeedbagItem); ok {
+	if rf, ok := ret.Get(0).(func(state.IdentScreenName) []wire.FeedbagItem); ok {
 		r0 = rf(screenName)
 	} else {
 		if ret.Get(0) != nil {
@@ -218,7 +218,7 @@ func (_m *mockFeedbagManager) Feedbag(screenName string) ([]wire.FeedbagItem, er
 		}
 	}
 
-	if rf, ok := ret.Get(1).(func(string) error); ok {
+	if rf, ok := ret.Get(1).(func(state.IdentScreenName) error); ok {
 		r1 = rf(screenName)
 	} else {
 		r1 = ret.Error(1)
@@ -233,14 +233,14 @@ type mockFeedbagManager_Feedbag_Call struct {
 }
 
 // Feedbag is a helper method to define mock.On call
-//   - screenName string
+//   - screenName state.IdentScreenName
 func (_e *mockFeedbagManager_Expecter) Feedbag(screenName interface{}) *mockFeedbagManager_Feedbag_Call {
 	return &mockFeedbagManager_Feedbag_Call{Call: _e.mock.On("Feedbag", screenName)}
 }
 
-func (_c *mockFeedbagManager_Feedbag_Call) Run(run func(screenName string)) *mockFeedbagManager_Feedbag_Call {
+func (_c *mockFeedbagManager_Feedbag_Call) Run(run func(screenName state.IdentScreenName)) *mockFeedbagManager_Feedbag_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(string))
+		run(args[0].(state.IdentScreenName))
 	})
 	return _c
 }
@@ -250,13 +250,13 @@ func (_c *mockFeedbagManager_Feedbag_Call) Return(_a0 []wire.FeedbagItem, _a1 er
 	return _c
 }
 
-func (_c *mockFeedbagManager_Feedbag_Call) RunAndReturn(run func(string) ([]wire.FeedbagItem, error)) *mockFeedbagManager_Feedbag_Call {
+func (_c *mockFeedbagManager_Feedbag_Call) RunAndReturn(run func(state.IdentScreenName) ([]wire.FeedbagItem, error)) *mockFeedbagManager_Feedbag_Call {
 	_c.Call.Return(run)
 	return _c
 }
 
 // FeedbagDelete provides a mock function with given fields: screenName, items
-func (_m *mockFeedbagManager) FeedbagDelete(screenName string, items []wire.FeedbagItem) error {
+func (_m *mockFeedbagManager) FeedbagDelete(screenName state.IdentScreenName, items []wire.FeedbagItem) error {
 	ret := _m.Called(screenName, items)
 
 	if len(ret) == 0 {
@@ -264,7 +264,7 @@ func (_m *mockFeedbagManager) FeedbagDelete(screenName string, items []wire.Feed
 	}
 
 	var r0 error
-	if rf, ok := ret.Get(0).(func(string, []wire.FeedbagItem) error); ok {
+	if rf, ok := ret.Get(0).(func(state.IdentScreenName, []wire.FeedbagItem) error); ok {
 		r0 = rf(screenName, items)
 	} else {
 		r0 = ret.Error(0)
@@ -279,15 +279,15 @@ type mockFeedbagManager_FeedbagDelete_Call struct {
 }
 
 // FeedbagDelete is a helper method to define mock.On call
-//   - screenName string
+//   - screenName state.IdentScreenName
 //   - items []wire.FeedbagItem
 func (_e *mockFeedbagManager_Expecter) FeedbagDelete(screenName interface{}, items interface{}) *mockFeedbagManager_FeedbagDelete_Call {
 	return &mockFeedbagManager_FeedbagDelete_Call{Call: _e.mock.On("FeedbagDelete", screenName, items)}
 }
 
-func (_c *mockFeedbagManager_FeedbagDelete_Call) Run(run func(screenName string, items []wire.FeedbagItem)) *mockFeedbagManager_FeedbagDelete_Call {
+func (_c *mockFeedbagManager_FeedbagDelete_Call) Run(run func(screenName state.IdentScreenName, items []wire.FeedbagItem)) *mockFeedbagManager_FeedbagDelete_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(string), args[1].([]wire.FeedbagItem))
+		run(args[0].(state.IdentScreenName), args[1].([]wire.FeedbagItem))
 	})
 	return _c
 }
@@ -297,13 +297,13 @@ func (_c *mockFeedbagManager_FeedbagDelete_Call) Return(_a0 error) *mockFeedbagM
 	return _c
 }
 
-func (_c *mockFeedbagManager_FeedbagDelete_Call) RunAndReturn(run func(string, []wire.FeedbagItem) error) *mockFeedbagManager_FeedbagDelete_Call {
+func (_c *mockFeedbagManager_FeedbagDelete_Call) RunAndReturn(run func(state.IdentScreenName, []wire.FeedbagItem) error) *mockFeedbagManager_FeedbagDelete_Call {
 	_c.Call.Return(run)
 	return _c
 }
 
 // FeedbagLastModified provides a mock function with given fields: screenName
-func (_m *mockFeedbagManager) FeedbagLastModified(screenName string) (time.Time, error) {
+func (_m *mockFeedbagManager) FeedbagLastModified(screenName state.IdentScreenName) (time.Time, error) {
 	ret := _m.Called(screenName)
 
 	if len(ret) == 0 {
@@ -312,16 +312,16 @@ func (_m *mockFeedbagManager) FeedbagLastModified(screenName string) (time.Time,
 
 	var r0 time.Time
 	var r1 error
-	if rf, ok := ret.Get(0).(func(string) (time.Time, error)); ok {
+	if rf, ok := ret.Get(0).(func(state.IdentScreenName) (time.Time, error)); ok {
 		return rf(screenName)
 	}
-	if rf, ok := ret.Get(0).(func(string) time.Time); ok {
+	if rf, ok := ret.Get(0).(func(state.IdentScreenName) time.Time); ok {
 		r0 = rf(screenName)
 	} else {
 		r0 = ret.Get(0).(time.Time)
 	}
 
-	if rf, ok := ret.Get(1).(func(string) error); ok {
+	if rf, ok := ret.Get(1).(func(state.IdentScreenName) error); ok {
 		r1 = rf(screenName)
 	} else {
 		r1 = ret.Error(1)
@@ -336,14 +336,14 @@ type mockFeedbagManager_FeedbagLastModified_Call struct {
 }
 
 // FeedbagLastModified is a helper method to define mock.On call
-//   - screenName string
+//   - screenName state.IdentScreenName
 func (_e *mockFeedbagManager_Expecter) FeedbagLastModified(screenName interface{}) *mockFeedbagManager_FeedbagLastModified_Call {
 	return &mockFeedbagManager_FeedbagLastModified_Call{Call: _e.mock.On("FeedbagLastModified", screenName)}
 }
 
-func (_c *mockFeedbagManager_FeedbagLastModified_Call) Run(run func(screenName string)) *mockFeedbagManager_FeedbagLastModified_Call {
+func (_c *mockFeedbagManager_FeedbagLastModified_Call) Run(run func(screenName state.IdentScreenName)) *mockFeedbagManager_FeedbagLastModified_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(string))
+		run(args[0].(state.IdentScreenName))
 	})
 	return _c
 }
@@ -353,13 +353,13 @@ func (_c *mockFeedbagManager_FeedbagLastModified_Call) Return(_a0 time.Time, _a1
 	return _c
 }
 
-func (_c *mockFeedbagManager_FeedbagLastModified_Call) RunAndReturn(run func(string) (time.Time, error)) *mockFeedbagManager_FeedbagLastModified_Call {
+func (_c *mockFeedbagManager_FeedbagLastModified_Call) RunAndReturn(run func(state.IdentScreenName) (time.Time, error)) *mockFeedbagManager_FeedbagLastModified_Call {
 	_c.Call.Return(run)
 	return _c
 }
 
 // FeedbagUpsert provides a mock function with given fields: screenName, items
-func (_m *mockFeedbagManager) FeedbagUpsert(screenName string, items []wire.FeedbagItem) error {
+func (_m *mockFeedbagManager) FeedbagUpsert(screenName state.IdentScreenName, items []wire.FeedbagItem) error {
 	ret := _m.Called(screenName, items)
 
 	if len(ret) == 0 {
@@ -367,7 +367,7 @@ func (_m *mockFeedbagManager) FeedbagUpsert(screenName string, items []wire.Feed
 	}
 
 	var r0 error
-	if rf, ok := ret.Get(0).(func(string, []wire.FeedbagItem) error); ok {
+	if rf, ok := ret.Get(0).(func(state.IdentScreenName, []wire.FeedbagItem) error); ok {
 		r0 = rf(screenName, items)
 	} else {
 		r0 = ret.Error(0)
@@ -382,15 +382,15 @@ type mockFeedbagManager_FeedbagUpsert_Call struct {
 }
 
 // FeedbagUpsert is a helper method to define mock.On call
-//   - screenName string
+//   - screenName state.IdentScreenName
 //   - items []wire.FeedbagItem
 func (_e *mockFeedbagManager_Expecter) FeedbagUpsert(screenName interface{}, items interface{}) *mockFeedbagManager_FeedbagUpsert_Call {
 	return &mockFeedbagManager_FeedbagUpsert_Call{Call: _e.mock.On("FeedbagUpsert", screenName, items)}
 }
 
-func (_c *mockFeedbagManager_FeedbagUpsert_Call) Run(run func(screenName string, items []wire.FeedbagItem)) *mockFeedbagManager_FeedbagUpsert_Call {
+func (_c *mockFeedbagManager_FeedbagUpsert_Call) Run(run func(screenName state.IdentScreenName, items []wire.FeedbagItem)) *mockFeedbagManager_FeedbagUpsert_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(string), args[1].([]wire.FeedbagItem))
+		run(args[0].(state.IdentScreenName), args[1].([]wire.FeedbagItem))
 	})
 	return _c
 }
@@ -400,7 +400,7 @@ func (_c *mockFeedbagManager_FeedbagUpsert_Call) Return(_a0 error) *mockFeedbagM
 	return _c
 }
 
-func (_c *mockFeedbagManager_FeedbagUpsert_Call) RunAndReturn(run func(string, []wire.FeedbagItem) error) *mockFeedbagManager_FeedbagUpsert_Call {
+func (_c *mockFeedbagManager_FeedbagUpsert_Call) RunAndReturn(run func(state.IdentScreenName, []wire.FeedbagItem) error) *mockFeedbagManager_FeedbagUpsert_Call {
 	_c.Call.Return(run)
 	return _c
 }

+ 39 - 36
foodgroup/mock_legacy_buddy_list_manager_test.go

@@ -2,7 +2,10 @@
 
 package foodgroup
 
-import mock "github.com/stretchr/testify/mock"
+import (
+	state "github.com/mk6i/retro-aim-server/state"
+	mock "github.com/stretchr/testify/mock"
+)
 
 // mockLegacyBuddyListManager is an autogenerated mock type for the LegacyBuddyListManager type
 type mockLegacyBuddyListManager struct {
@@ -18,7 +21,7 @@ func (_m *mockLegacyBuddyListManager) EXPECT() *mockLegacyBuddyListManager_Expec
 }
 
 // AddBuddy provides a mock function with given fields: userScreenName, buddyScreenName
-func (_m *mockLegacyBuddyListManager) AddBuddy(userScreenName string, buddyScreenName string) {
+func (_m *mockLegacyBuddyListManager) AddBuddy(userScreenName state.IdentScreenName, buddyScreenName state.IdentScreenName) {
 	_m.Called(userScreenName, buddyScreenName)
 }
 
@@ -28,15 +31,15 @@ type mockLegacyBuddyListManager_AddBuddy_Call struct {
 }
 
 // AddBuddy is a helper method to define mock.On call
-//   - userScreenName string
-//   - buddyScreenName string
+//   - userScreenName state.IdentScreenName
+//   - buddyScreenName state.IdentScreenName
 func (_e *mockLegacyBuddyListManager_Expecter) AddBuddy(userScreenName interface{}, buddyScreenName interface{}) *mockLegacyBuddyListManager_AddBuddy_Call {
 	return &mockLegacyBuddyListManager_AddBuddy_Call{Call: _e.mock.On("AddBuddy", userScreenName, buddyScreenName)}
 }
 
-func (_c *mockLegacyBuddyListManager_AddBuddy_Call) Run(run func(userScreenName string, buddyScreenName string)) *mockLegacyBuddyListManager_AddBuddy_Call {
+func (_c *mockLegacyBuddyListManager_AddBuddy_Call) Run(run func(userScreenName state.IdentScreenName, buddyScreenName state.IdentScreenName)) *mockLegacyBuddyListManager_AddBuddy_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(string), args[1].(string))
+		run(args[0].(state.IdentScreenName), args[1].(state.IdentScreenName))
 	})
 	return _c
 }
@@ -46,25 +49,25 @@ func (_c *mockLegacyBuddyListManager_AddBuddy_Call) Return() *mockLegacyBuddyLis
 	return _c
 }
 
-func (_c *mockLegacyBuddyListManager_AddBuddy_Call) RunAndReturn(run func(string, string)) *mockLegacyBuddyListManager_AddBuddy_Call {
+func (_c *mockLegacyBuddyListManager_AddBuddy_Call) RunAndReturn(run func(state.IdentScreenName, state.IdentScreenName)) *mockLegacyBuddyListManager_AddBuddy_Call {
 	_c.Call.Return(run)
 	return _c
 }
 
 // Buddies provides a mock function with given fields: userScreenName
-func (_m *mockLegacyBuddyListManager) Buddies(userScreenName string) []string {
+func (_m *mockLegacyBuddyListManager) Buddies(userScreenName state.IdentScreenName) []state.IdentScreenName {
 	ret := _m.Called(userScreenName)
 
 	if len(ret) == 0 {
 		panic("no return value specified for Buddies")
 	}
 
-	var r0 []string
-	if rf, ok := ret.Get(0).(func(string) []string); ok {
+	var r0 []state.IdentScreenName
+	if rf, ok := ret.Get(0).(func(state.IdentScreenName) []state.IdentScreenName); ok {
 		r0 = rf(userScreenName)
 	} else {
 		if ret.Get(0) != nil {
-			r0 = ret.Get(0).([]string)
+			r0 = ret.Get(0).([]state.IdentScreenName)
 		}
 	}
 
@@ -77,30 +80,30 @@ type mockLegacyBuddyListManager_Buddies_Call struct {
 }
 
 // Buddies is a helper method to define mock.On call
-//   - userScreenName string
+//   - userScreenName state.IdentScreenName
 func (_e *mockLegacyBuddyListManager_Expecter) Buddies(userScreenName interface{}) *mockLegacyBuddyListManager_Buddies_Call {
 	return &mockLegacyBuddyListManager_Buddies_Call{Call: _e.mock.On("Buddies", userScreenName)}
 }
 
-func (_c *mockLegacyBuddyListManager_Buddies_Call) Run(run func(userScreenName string)) *mockLegacyBuddyListManager_Buddies_Call {
+func (_c *mockLegacyBuddyListManager_Buddies_Call) Run(run func(userScreenName state.IdentScreenName)) *mockLegacyBuddyListManager_Buddies_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(string))
+		run(args[0].(state.IdentScreenName))
 	})
 	return _c
 }
 
-func (_c *mockLegacyBuddyListManager_Buddies_Call) Return(_a0 []string) *mockLegacyBuddyListManager_Buddies_Call {
+func (_c *mockLegacyBuddyListManager_Buddies_Call) Return(_a0 []state.IdentScreenName) *mockLegacyBuddyListManager_Buddies_Call {
 	_c.Call.Return(_a0)
 	return _c
 }
 
-func (_c *mockLegacyBuddyListManager_Buddies_Call) RunAndReturn(run func(string) []string) *mockLegacyBuddyListManager_Buddies_Call {
+func (_c *mockLegacyBuddyListManager_Buddies_Call) RunAndReturn(run func(state.IdentScreenName) []state.IdentScreenName) *mockLegacyBuddyListManager_Buddies_Call {
 	_c.Call.Return(run)
 	return _c
 }
 
 // DeleteBuddy provides a mock function with given fields: userScreenName, buddyScreenName
-func (_m *mockLegacyBuddyListManager) DeleteBuddy(userScreenName string, buddyScreenName string) {
+func (_m *mockLegacyBuddyListManager) DeleteBuddy(userScreenName state.IdentScreenName, buddyScreenName state.IdentScreenName) {
 	_m.Called(userScreenName, buddyScreenName)
 }
 
@@ -110,15 +113,15 @@ type mockLegacyBuddyListManager_DeleteBuddy_Call struct {
 }
 
 // DeleteBuddy is a helper method to define mock.On call
-//   - userScreenName string
-//   - buddyScreenName string
+//   - userScreenName state.IdentScreenName
+//   - buddyScreenName state.IdentScreenName
 func (_e *mockLegacyBuddyListManager_Expecter) DeleteBuddy(userScreenName interface{}, buddyScreenName interface{}) *mockLegacyBuddyListManager_DeleteBuddy_Call {
 	return &mockLegacyBuddyListManager_DeleteBuddy_Call{Call: _e.mock.On("DeleteBuddy", userScreenName, buddyScreenName)}
 }
 
-func (_c *mockLegacyBuddyListManager_DeleteBuddy_Call) Run(run func(userScreenName string, buddyScreenName string)) *mockLegacyBuddyListManager_DeleteBuddy_Call {
+func (_c *mockLegacyBuddyListManager_DeleteBuddy_Call) Run(run func(userScreenName state.IdentScreenName, buddyScreenName state.IdentScreenName)) *mockLegacyBuddyListManager_DeleteBuddy_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(string), args[1].(string))
+		run(args[0].(state.IdentScreenName), args[1].(state.IdentScreenName))
 	})
 	return _c
 }
@@ -128,13 +131,13 @@ func (_c *mockLegacyBuddyListManager_DeleteBuddy_Call) Return() *mockLegacyBuddy
 	return _c
 }
 
-func (_c *mockLegacyBuddyListManager_DeleteBuddy_Call) RunAndReturn(run func(string, string)) *mockLegacyBuddyListManager_DeleteBuddy_Call {
+func (_c *mockLegacyBuddyListManager_DeleteBuddy_Call) RunAndReturn(run func(state.IdentScreenName, state.IdentScreenName)) *mockLegacyBuddyListManager_DeleteBuddy_Call {
 	_c.Call.Return(run)
 	return _c
 }
 
 // DeleteUser provides a mock function with given fields: userScreenName
-func (_m *mockLegacyBuddyListManager) DeleteUser(userScreenName string) {
+func (_m *mockLegacyBuddyListManager) DeleteUser(userScreenName state.IdentScreenName) {
 	_m.Called(userScreenName)
 }
 
@@ -144,14 +147,14 @@ type mockLegacyBuddyListManager_DeleteUser_Call struct {
 }
 
 // DeleteUser is a helper method to define mock.On call
-//   - userScreenName string
+//   - userScreenName state.IdentScreenName
 func (_e *mockLegacyBuddyListManager_Expecter) DeleteUser(userScreenName interface{}) *mockLegacyBuddyListManager_DeleteUser_Call {
 	return &mockLegacyBuddyListManager_DeleteUser_Call{Call: _e.mock.On("DeleteUser", userScreenName)}
 }
 
-func (_c *mockLegacyBuddyListManager_DeleteUser_Call) Run(run func(userScreenName string)) *mockLegacyBuddyListManager_DeleteUser_Call {
+func (_c *mockLegacyBuddyListManager_DeleteUser_Call) Run(run func(userScreenName state.IdentScreenName)) *mockLegacyBuddyListManager_DeleteUser_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(string))
+		run(args[0].(state.IdentScreenName))
 	})
 	return _c
 }
@@ -161,25 +164,25 @@ func (_c *mockLegacyBuddyListManager_DeleteUser_Call) Return() *mockLegacyBuddyL
 	return _c
 }
 
-func (_c *mockLegacyBuddyListManager_DeleteUser_Call) RunAndReturn(run func(string)) *mockLegacyBuddyListManager_DeleteUser_Call {
+func (_c *mockLegacyBuddyListManager_DeleteUser_Call) RunAndReturn(run func(state.IdentScreenName)) *mockLegacyBuddyListManager_DeleteUser_Call {
 	_c.Call.Return(run)
 	return _c
 }
 
 // WhoAddedUser provides a mock function with given fields: userScreenName
-func (_m *mockLegacyBuddyListManager) WhoAddedUser(userScreenName string) []string {
+func (_m *mockLegacyBuddyListManager) WhoAddedUser(userScreenName state.IdentScreenName) []state.IdentScreenName {
 	ret := _m.Called(userScreenName)
 
 	if len(ret) == 0 {
 		panic("no return value specified for WhoAddedUser")
 	}
 
-	var r0 []string
-	if rf, ok := ret.Get(0).(func(string) []string); ok {
+	var r0 []state.IdentScreenName
+	if rf, ok := ret.Get(0).(func(state.IdentScreenName) []state.IdentScreenName); ok {
 		r0 = rf(userScreenName)
 	} else {
 		if ret.Get(0) != nil {
-			r0 = ret.Get(0).([]string)
+			r0 = ret.Get(0).([]state.IdentScreenName)
 		}
 	}
 
@@ -192,24 +195,24 @@ type mockLegacyBuddyListManager_WhoAddedUser_Call struct {
 }
 
 // WhoAddedUser is a helper method to define mock.On call
-//   - userScreenName string
+//   - userScreenName state.IdentScreenName
 func (_e *mockLegacyBuddyListManager_Expecter) WhoAddedUser(userScreenName interface{}) *mockLegacyBuddyListManager_WhoAddedUser_Call {
 	return &mockLegacyBuddyListManager_WhoAddedUser_Call{Call: _e.mock.On("WhoAddedUser", userScreenName)}
 }
 
-func (_c *mockLegacyBuddyListManager_WhoAddedUser_Call) Run(run func(userScreenName string)) *mockLegacyBuddyListManager_WhoAddedUser_Call {
+func (_c *mockLegacyBuddyListManager_WhoAddedUser_Call) Run(run func(userScreenName state.IdentScreenName)) *mockLegacyBuddyListManager_WhoAddedUser_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(string))
+		run(args[0].(state.IdentScreenName))
 	})
 	return _c
 }
 
-func (_c *mockLegacyBuddyListManager_WhoAddedUser_Call) Return(_a0 []string) *mockLegacyBuddyListManager_WhoAddedUser_Call {
+func (_c *mockLegacyBuddyListManager_WhoAddedUser_Call) Return(_a0 []state.IdentScreenName) *mockLegacyBuddyListManager_WhoAddedUser_Call {
 	_c.Call.Return(_a0)
 	return _c
 }
 
-func (_c *mockLegacyBuddyListManager_WhoAddedUser_Call) RunAndReturn(run func(string) []string) *mockLegacyBuddyListManager_WhoAddedUser_Call {
+func (_c *mockLegacyBuddyListManager_WhoAddedUser_Call) RunAndReturn(run func(state.IdentScreenName) []state.IdentScreenName) *mockLegacyBuddyListManager_WhoAddedUser_Call {
 	_c.Call.Return(run)
 	return _c
 }

+ 16 - 16
foodgroup/mock_message_relayer_test.go

@@ -25,7 +25,7 @@ func (_m *mockMessageRelayer) EXPECT() *mockMessageRelayer_Expecter {
 }
 
 // RelayToScreenName provides a mock function with given fields: ctx, screenName, msg
-func (_m *mockMessageRelayer) RelayToScreenName(ctx context.Context, screenName string, msg wire.SNACMessage) {
+func (_m *mockMessageRelayer) RelayToScreenName(ctx context.Context, screenName state.IdentScreenName, msg wire.SNACMessage) {
 	_m.Called(ctx, screenName, msg)
 }
 
@@ -36,15 +36,15 @@ type mockMessageRelayer_RelayToScreenName_Call struct {
 
 // RelayToScreenName is a helper method to define mock.On call
 //   - ctx context.Context
-//   - screenName string
+//   - screenName state.IdentScreenName
 //   - msg wire.SNACMessage
 func (_e *mockMessageRelayer_Expecter) RelayToScreenName(ctx interface{}, screenName interface{}, msg interface{}) *mockMessageRelayer_RelayToScreenName_Call {
 	return &mockMessageRelayer_RelayToScreenName_Call{Call: _e.mock.On("RelayToScreenName", ctx, screenName, msg)}
 }
 
-func (_c *mockMessageRelayer_RelayToScreenName_Call) Run(run func(ctx context.Context, screenName string, msg wire.SNACMessage)) *mockMessageRelayer_RelayToScreenName_Call {
+func (_c *mockMessageRelayer_RelayToScreenName_Call) Run(run func(ctx context.Context, screenName state.IdentScreenName, msg wire.SNACMessage)) *mockMessageRelayer_RelayToScreenName_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(context.Context), args[1].(string), args[2].(wire.SNACMessage))
+		run(args[0].(context.Context), args[1].(state.IdentScreenName), args[2].(wire.SNACMessage))
 	})
 	return _c
 }
@@ -54,13 +54,13 @@ func (_c *mockMessageRelayer_RelayToScreenName_Call) Return() *mockMessageRelaye
 	return _c
 }
 
-func (_c *mockMessageRelayer_RelayToScreenName_Call) RunAndReturn(run func(context.Context, string, wire.SNACMessage)) *mockMessageRelayer_RelayToScreenName_Call {
+func (_c *mockMessageRelayer_RelayToScreenName_Call) RunAndReturn(run func(context.Context, state.IdentScreenName, wire.SNACMessage)) *mockMessageRelayer_RelayToScreenName_Call {
 	_c.Call.Return(run)
 	return _c
 }
 
 // RelayToScreenNames provides a mock function with given fields: ctx, screenNames, msg
-func (_m *mockMessageRelayer) RelayToScreenNames(ctx context.Context, screenNames []string, msg wire.SNACMessage) {
+func (_m *mockMessageRelayer) RelayToScreenNames(ctx context.Context, screenNames []state.IdentScreenName, msg wire.SNACMessage) {
 	_m.Called(ctx, screenNames, msg)
 }
 
@@ -71,15 +71,15 @@ type mockMessageRelayer_RelayToScreenNames_Call struct {
 
 // RelayToScreenNames is a helper method to define mock.On call
 //   - ctx context.Context
-//   - screenNames []string
+//   - screenNames []state.IdentScreenName
 //   - msg wire.SNACMessage
 func (_e *mockMessageRelayer_Expecter) RelayToScreenNames(ctx interface{}, screenNames interface{}, msg interface{}) *mockMessageRelayer_RelayToScreenNames_Call {
 	return &mockMessageRelayer_RelayToScreenNames_Call{Call: _e.mock.On("RelayToScreenNames", ctx, screenNames, msg)}
 }
 
-func (_c *mockMessageRelayer_RelayToScreenNames_Call) Run(run func(ctx context.Context, screenNames []string, msg wire.SNACMessage)) *mockMessageRelayer_RelayToScreenNames_Call {
+func (_c *mockMessageRelayer_RelayToScreenNames_Call) Run(run func(ctx context.Context, screenNames []state.IdentScreenName, msg wire.SNACMessage)) *mockMessageRelayer_RelayToScreenNames_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(context.Context), args[1].([]string), args[2].(wire.SNACMessage))
+		run(args[0].(context.Context), args[1].([]state.IdentScreenName), args[2].(wire.SNACMessage))
 	})
 	return _c
 }
@@ -89,13 +89,13 @@ func (_c *mockMessageRelayer_RelayToScreenNames_Call) Return() *mockMessageRelay
 	return _c
 }
 
-func (_c *mockMessageRelayer_RelayToScreenNames_Call) RunAndReturn(run func(context.Context, []string, wire.SNACMessage)) *mockMessageRelayer_RelayToScreenNames_Call {
+func (_c *mockMessageRelayer_RelayToScreenNames_Call) RunAndReturn(run func(context.Context, []state.IdentScreenName, wire.SNACMessage)) *mockMessageRelayer_RelayToScreenNames_Call {
 	_c.Call.Return(run)
 	return _c
 }
 
 // RetrieveByScreenName provides a mock function with given fields: screenName
-func (_m *mockMessageRelayer) RetrieveByScreenName(screenName string) *state.Session {
+func (_m *mockMessageRelayer) RetrieveByScreenName(screenName state.IdentScreenName) *state.Session {
 	ret := _m.Called(screenName)
 
 	if len(ret) == 0 {
@@ -103,7 +103,7 @@ func (_m *mockMessageRelayer) RetrieveByScreenName(screenName string) *state.Ses
 	}
 
 	var r0 *state.Session
-	if rf, ok := ret.Get(0).(func(string) *state.Session); ok {
+	if rf, ok := ret.Get(0).(func(state.IdentScreenName) *state.Session); ok {
 		r0 = rf(screenName)
 	} else {
 		if ret.Get(0) != nil {
@@ -120,14 +120,14 @@ type mockMessageRelayer_RetrieveByScreenName_Call struct {
 }
 
 // RetrieveByScreenName is a helper method to define mock.On call
-//   - screenName string
+//   - screenName state.IdentScreenName
 func (_e *mockMessageRelayer_Expecter) RetrieveByScreenName(screenName interface{}) *mockMessageRelayer_RetrieveByScreenName_Call {
 	return &mockMessageRelayer_RetrieveByScreenName_Call{Call: _e.mock.On("RetrieveByScreenName", screenName)}
 }
 
-func (_c *mockMessageRelayer_RetrieveByScreenName_Call) Run(run func(screenName string)) *mockMessageRelayer_RetrieveByScreenName_Call {
+func (_c *mockMessageRelayer_RetrieveByScreenName_Call) Run(run func(screenName state.IdentScreenName)) *mockMessageRelayer_RetrieveByScreenName_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(string))
+		run(args[0].(state.IdentScreenName))
 	})
 	return _c
 }
@@ -137,7 +137,7 @@ func (_c *mockMessageRelayer_RetrieveByScreenName_Call) Return(_a0 *state.Sessio
 	return _c
 }
 
-func (_c *mockMessageRelayer_RetrieveByScreenName_Call) RunAndReturn(run func(string) *state.Session) *mockMessageRelayer_RetrieveByScreenName_Call {
+func (_c *mockMessageRelayer_RetrieveByScreenName_Call) RunAndReturn(run func(state.IdentScreenName) *state.Session) *mockMessageRelayer_RetrieveByScreenName_Call {
 	_c.Call.Return(run)
 	return _c
 }

+ 18 - 15
foodgroup/mock_profile_manager_test.go

@@ -2,7 +2,10 @@
 
 package foodgroup
 
-import mock "github.com/stretchr/testify/mock"
+import (
+	state "github.com/mk6i/retro-aim-server/state"
+	mock "github.com/stretchr/testify/mock"
+)
 
 // mockProfileManager is an autogenerated mock type for the ProfileManager type
 type mockProfileManager struct {
@@ -18,7 +21,7 @@ func (_m *mockProfileManager) EXPECT() *mockProfileManager_Expecter {
 }
 
 // Profile provides a mock function with given fields: screenName
-func (_m *mockProfileManager) Profile(screenName string) (string, error) {
+func (_m *mockProfileManager) Profile(screenName state.IdentScreenName) (string, error) {
 	ret := _m.Called(screenName)
 
 	if len(ret) == 0 {
@@ -27,16 +30,16 @@ func (_m *mockProfileManager) Profile(screenName string) (string, error) {
 
 	var r0 string
 	var r1 error
-	if rf, ok := ret.Get(0).(func(string) (string, error)); ok {
+	if rf, ok := ret.Get(0).(func(state.IdentScreenName) (string, error)); ok {
 		return rf(screenName)
 	}
-	if rf, ok := ret.Get(0).(func(string) string); ok {
+	if rf, ok := ret.Get(0).(func(state.IdentScreenName) string); ok {
 		r0 = rf(screenName)
 	} else {
 		r0 = ret.Get(0).(string)
 	}
 
-	if rf, ok := ret.Get(1).(func(string) error); ok {
+	if rf, ok := ret.Get(1).(func(state.IdentScreenName) error); ok {
 		r1 = rf(screenName)
 	} else {
 		r1 = ret.Error(1)
@@ -51,14 +54,14 @@ type mockProfileManager_Profile_Call struct {
 }
 
 // Profile is a helper method to define mock.On call
-//   - screenName string
+//   - screenName state.IdentScreenName
 func (_e *mockProfileManager_Expecter) Profile(screenName interface{}) *mockProfileManager_Profile_Call {
 	return &mockProfileManager_Profile_Call{Call: _e.mock.On("Profile", screenName)}
 }
 
-func (_c *mockProfileManager_Profile_Call) Run(run func(screenName string)) *mockProfileManager_Profile_Call {
+func (_c *mockProfileManager_Profile_Call) Run(run func(screenName state.IdentScreenName)) *mockProfileManager_Profile_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(string))
+		run(args[0].(state.IdentScreenName))
 	})
 	return _c
 }
@@ -68,13 +71,13 @@ func (_c *mockProfileManager_Profile_Call) Return(_a0 string, _a1 error) *mockPr
 	return _c
 }
 
-func (_c *mockProfileManager_Profile_Call) RunAndReturn(run func(string) (string, error)) *mockProfileManager_Profile_Call {
+func (_c *mockProfileManager_Profile_Call) RunAndReturn(run func(state.IdentScreenName) (string, error)) *mockProfileManager_Profile_Call {
 	_c.Call.Return(run)
 	return _c
 }
 
 // SetProfile provides a mock function with given fields: screenName, body
-func (_m *mockProfileManager) SetProfile(screenName string, body string) error {
+func (_m *mockProfileManager) SetProfile(screenName state.IdentScreenName, body string) error {
 	ret := _m.Called(screenName, body)
 
 	if len(ret) == 0 {
@@ -82,7 +85,7 @@ func (_m *mockProfileManager) SetProfile(screenName string, body string) error {
 	}
 
 	var r0 error
-	if rf, ok := ret.Get(0).(func(string, string) error); ok {
+	if rf, ok := ret.Get(0).(func(state.IdentScreenName, string) error); ok {
 		r0 = rf(screenName, body)
 	} else {
 		r0 = ret.Error(0)
@@ -97,15 +100,15 @@ type mockProfileManager_SetProfile_Call struct {
 }
 
 // SetProfile is a helper method to define mock.On call
-//   - screenName string
+//   - screenName state.IdentScreenName
 //   - body string
 func (_e *mockProfileManager_Expecter) SetProfile(screenName interface{}, body interface{}) *mockProfileManager_SetProfile_Call {
 	return &mockProfileManager_SetProfile_Call{Call: _e.mock.On("SetProfile", screenName, body)}
 }
 
-func (_c *mockProfileManager_SetProfile_Call) Run(run func(screenName string, body string)) *mockProfileManager_SetProfile_Call {
+func (_c *mockProfileManager_SetProfile_Call) Run(run func(screenName state.IdentScreenName, body string)) *mockProfileManager_SetProfile_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(string), args[1].(string))
+		run(args[0].(state.IdentScreenName), args[1].(string))
 	})
 	return _c
 }
@@ -115,7 +118,7 @@ func (_c *mockProfileManager_SetProfile_Call) Return(_a0 error) *mockProfileMana
 	return _c
 }
 
-func (_c *mockProfileManager_SetProfile_Call) RunAndReturn(run func(string, string) error) *mockProfileManager_SetProfile_Call {
+func (_c *mockProfileManager_SetProfile_Call) RunAndReturn(run func(state.IdentScreenName, string) error) *mockProfileManager_SetProfile_Call {
 	_c.Call.Return(run)
 	return _c
 }

+ 17 - 17
foodgroup/mock_session_manager_test.go

@@ -21,7 +21,7 @@ func (_m *mockSessionManager) EXPECT() *mockSessionManager_Expecter {
 }
 
 // AddSession provides a mock function with given fields: screenName
-func (_m *mockSessionManager) AddSession(screenName string) *state.Session {
+func (_m *mockSessionManager) AddSession(screenName state.DisplayScreenName) *state.Session {
 	ret := _m.Called(screenName)
 
 	if len(ret) == 0 {
@@ -29,7 +29,7 @@ func (_m *mockSessionManager) AddSession(screenName string) *state.Session {
 	}
 
 	var r0 *state.Session
-	if rf, ok := ret.Get(0).(func(string) *state.Session); ok {
+	if rf, ok := ret.Get(0).(func(state.DisplayScreenName) *state.Session); ok {
 		r0 = rf(screenName)
 	} else {
 		if ret.Get(0) != nil {
@@ -46,14 +46,14 @@ type mockSessionManager_AddSession_Call struct {
 }
 
 // AddSession is a helper method to define mock.On call
-//   - screenName string
+//   - screenName state.DisplayScreenName
 func (_e *mockSessionManager_Expecter) AddSession(screenName interface{}) *mockSessionManager_AddSession_Call {
 	return &mockSessionManager_AddSession_Call{Call: _e.mock.On("AddSession", screenName)}
 }
 
-func (_c *mockSessionManager_AddSession_Call) Run(run func(screenName string)) *mockSessionManager_AddSession_Call {
+func (_c *mockSessionManager_AddSession_Call) Run(run func(screenName state.DisplayScreenName)) *mockSessionManager_AddSession_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(string))
+		run(args[0].(state.DisplayScreenName))
 	})
 	return _c
 }
@@ -63,7 +63,7 @@ func (_c *mockSessionManager_AddSession_Call) Return(_a0 *state.Session) *mockSe
 	return _c
 }
 
-func (_c *mockSessionManager_AddSession_Call) RunAndReturn(run func(string) *state.Session) *mockSessionManager_AddSession_Call {
+func (_c *mockSessionManager_AddSession_Call) RunAndReturn(run func(state.DisplayScreenName) *state.Session) *mockSessionManager_AddSession_Call {
 	_c.Call.Return(run)
 	return _c
 }
@@ -146,17 +146,17 @@ func (_c *mockSessionManager_RemoveSession_Call) RunAndReturn(run func(*state.Se
 	return _c
 }
 
-// RetrieveSession provides a mock function with given fields: ID
-func (_m *mockSessionManager) RetrieveSession(ID string) *state.Session {
-	ret := _m.Called(ID)
+// RetrieveSession provides a mock function with given fields: screenName
+func (_m *mockSessionManager) RetrieveSession(screenName state.IdentScreenName) *state.Session {
+	ret := _m.Called(screenName)
 
 	if len(ret) == 0 {
 		panic("no return value specified for RetrieveSession")
 	}
 
 	var r0 *state.Session
-	if rf, ok := ret.Get(0).(func(string) *state.Session); ok {
-		r0 = rf(ID)
+	if rf, ok := ret.Get(0).(func(state.IdentScreenName) *state.Session); ok {
+		r0 = rf(screenName)
 	} else {
 		if ret.Get(0) != nil {
 			r0 = ret.Get(0).(*state.Session)
@@ -172,14 +172,14 @@ type mockSessionManager_RetrieveSession_Call struct {
 }
 
 // RetrieveSession is a helper method to define mock.On call
-//   - ID string
-func (_e *mockSessionManager_Expecter) RetrieveSession(ID interface{}) *mockSessionManager_RetrieveSession_Call {
-	return &mockSessionManager_RetrieveSession_Call{Call: _e.mock.On("RetrieveSession", ID)}
+//   - screenName state.IdentScreenName
+func (_e *mockSessionManager_Expecter) RetrieveSession(screenName interface{}) *mockSessionManager_RetrieveSession_Call {
+	return &mockSessionManager_RetrieveSession_Call{Call: _e.mock.On("RetrieveSession", screenName)}
 }
 
-func (_c *mockSessionManager_RetrieveSession_Call) Run(run func(ID string)) *mockSessionManager_RetrieveSession_Call {
+func (_c *mockSessionManager_RetrieveSession_Call) Run(run func(screenName state.IdentScreenName)) *mockSessionManager_RetrieveSession_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(string))
+		run(args[0].(state.IdentScreenName))
 	})
 	return _c
 }
@@ -189,7 +189,7 @@ func (_c *mockSessionManager_RetrieveSession_Call) Return(_a0 *state.Session) *m
 	return _c
 }
 
-func (_c *mockSessionManager_RetrieveSession_Call) RunAndReturn(run func(string) *state.Session) *mockSessionManager_RetrieveSession_Call {
+func (_c *mockSessionManager_RetrieveSession_Call) RunAndReturn(run func(state.IdentScreenName) *state.Session) *mockSessionManager_RetrieveSession_Call {
 	_c.Call.Return(run)
 	return _c
 }

+ 8 - 8
foodgroup/mock_user_manager_manager_test.go

@@ -67,7 +67,7 @@ func (_c *mockUserManager_InsertUser_Call) RunAndReturn(run func(state.User) err
 }
 
 // User provides a mock function with given fields: screenName
-func (_m *mockUserManager) User(screenName string) (*state.User, error) {
+func (_m *mockUserManager) User(screenName state.IdentScreenName) (*state.User, error) {
 	ret := _m.Called(screenName)
 
 	if len(ret) == 0 {
@@ -76,10 +76,10 @@ func (_m *mockUserManager) User(screenName string) (*state.User, error) {
 
 	var r0 *state.User
 	var r1 error
-	if rf, ok := ret.Get(0).(func(string) (*state.User, error)); ok {
+	if rf, ok := ret.Get(0).(func(state.IdentScreenName) (*state.User, error)); ok {
 		return rf(screenName)
 	}
-	if rf, ok := ret.Get(0).(func(string) *state.User); ok {
+	if rf, ok := ret.Get(0).(func(state.IdentScreenName) *state.User); ok {
 		r0 = rf(screenName)
 	} else {
 		if ret.Get(0) != nil {
@@ -87,7 +87,7 @@ func (_m *mockUserManager) User(screenName string) (*state.User, error) {
 		}
 	}
 
-	if rf, ok := ret.Get(1).(func(string) error); ok {
+	if rf, ok := ret.Get(1).(func(state.IdentScreenName) error); ok {
 		r1 = rf(screenName)
 	} else {
 		r1 = ret.Error(1)
@@ -102,14 +102,14 @@ type mockUserManager_User_Call struct {
 }
 
 // User is a helper method to define mock.On call
-//   - screenName string
+//   - screenName state.IdentScreenName
 func (_e *mockUserManager_Expecter) User(screenName interface{}) *mockUserManager_User_Call {
 	return &mockUserManager_User_Call{Call: _e.mock.On("User", screenName)}
 }
 
-func (_c *mockUserManager_User_Call) Run(run func(screenName string)) *mockUserManager_User_Call {
+func (_c *mockUserManager_User_Call) Run(run func(screenName state.IdentScreenName)) *mockUserManager_User_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(string))
+		run(args[0].(state.IdentScreenName))
 	})
 	return _c
 }
@@ -119,7 +119,7 @@ func (_c *mockUserManager_User_Call) Return(_a0 *state.User, _a1 error) *mockUse
 	return _c
 }
 
-func (_c *mockUserManager_User_Call) RunAndReturn(run func(string) (*state.User, error)) *mockUserManager_User_Call {
+func (_c *mockUserManager_User_Call) RunAndReturn(run func(state.IdentScreenName) (*state.User, error)) *mockUserManager_User_Call {
 	_c.Call.Return(run)
 	return _c
 }

+ 5 - 5
foodgroup/oservice.go

@@ -562,7 +562,7 @@ type chatLoginCookie struct {
 func (s OServiceServiceForBOS) ServiceRequest(ctx context.Context, sess *state.Session, inFrame wire.SNACFrame, inBody wire.SNAC_0x01_0x04_OServiceServiceRequest) (wire.SNACMessage, error) {
 	switch inBody.FoodGroup {
 	case wire.Alert:
-		cookie, err := s.cookieIssuer.Issue([]byte(sess.ScreenName()))
+		cookie, err := s.cookieIssuer.Issue([]byte(sess.IdentScreenName().String()))
 		if err != nil {
 			return wire.SNACMessage{}, err
 		}
@@ -585,7 +585,7 @@ func (s OServiceServiceForBOS) ServiceRequest(ctx context.Context, sess *state.S
 			},
 		}, nil
 	case wire.BART:
-		cookie, err := s.cookieIssuer.Issue([]byte(sess.ScreenName()))
+		cookie, err := s.cookieIssuer.Issue([]byte(sess.IdentScreenName().String()))
 		if err != nil {
 			return wire.SNACMessage{}, err
 		}
@@ -608,7 +608,7 @@ func (s OServiceServiceForBOS) ServiceRequest(ctx context.Context, sess *state.S
 			},
 		}, nil
 	case wire.ChatNav:
-		cookie, err := s.cookieIssuer.Issue([]byte(sess.ScreenName()))
+		cookie, err := s.cookieIssuer.Issue([]byte(sess.IdentScreenName().String()))
 		if err != nil {
 			return wire.SNACMessage{}, err
 		}
@@ -648,7 +648,7 @@ func (s OServiceServiceForBOS) ServiceRequest(ctx context.Context, sess *state.S
 
 		loginCookie := chatLoginCookie{
 			ChatCookie: room.Cookie,
-			ScreenName: sess.ScreenName(),
+			ScreenName: sess.IdentScreenName().String(),
 		}
 		buf := &bytes.Buffer{}
 		if err := wire.Marshal(loginCookie, buf); err != nil {
@@ -703,7 +703,7 @@ func (s OServiceServiceForBOS) ClientOnline(ctx context.Context, _ wire.SNAC_0x0
 	}
 
 	// send buddy arrival events to client-side buddy list
-	buddies := s.legacyBuddyListManager.Buddies(sess.ScreenName())
+	buddies := s.legacyBuddyListManager.Buddies(sess.IdentScreenName())
 	for _, buddy := range buddies {
 		buddySess := s.messageRelayer.RetrieveByScreenName(buddy)
 		if buddySess == nil || buddySess.Invisible() {

+ 36 - 30
foodgroup/oservice_test.go

@@ -245,7 +245,7 @@ func TestOServiceServiceForBOS_ServiceRequest(t *testing.T) {
 			chatSess := &state.Session{}
 			if tc.chatRoom != nil {
 				sessionManager.EXPECT().
-					AddSession(tc.userSession.ScreenName()).
+					AddSession(tc.userSession.IdentScreenName()).
 					Return(chatSess).
 					Maybe()
 				chatRegistry.Register(*tc.chatRoom, sessionManager)
@@ -329,7 +329,7 @@ func TestSetUserInfoFields(t *testing.T) {
 				buddyBroadcasterParams: buddyBroadcasterParams{
 					broadcastBuddyArrivedParams: broadcastBuddyArrivedParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 						},
 					},
 				},
@@ -364,7 +364,7 @@ func TestSetUserInfoFields(t *testing.T) {
 				buddyBroadcasterParams: buddyBroadcasterParams{
 					broadcastBuddyDepartedParams: broadcastBuddyDepartedParams{
 						{
-							screenName: "user_screen_name",
+							screenName: state.NewIdentScreenName("user_screen_name"),
 						},
 					},
 				},
@@ -379,7 +379,7 @@ func TestSetUserInfoFields(t *testing.T) {
 				p := params
 				buddyUpdateBroadcaster.EXPECT().
 					BroadcastBuddyArrived(mock.Anything, mock.MatchedBy(func(s *state.Session) bool {
-						return s.ScreenName() == p.screenName
+						return s.IdentScreenName() == p.screenName
 					})).
 					Return(nil)
 			}
@@ -387,7 +387,7 @@ func TestSetUserInfoFields(t *testing.T) {
 				p := params
 				buddyUpdateBroadcaster.EXPECT().
 					BroadcastBuddyDeparted(mock.Anything, mock.MatchedBy(func(s *state.Session) bool {
-						return s.ScreenName() == p.screenName
+						return s.IdentScreenName() == p.screenName
 					})).
 					Return(nil)
 			}
@@ -1452,7 +1452,7 @@ func TestOServiceService_IdleNotification(t *testing.T) {
 				buddyBroadcasterParams: buddyBroadcasterParams{
 					broadcastBuddyArrivedParams: broadcastBuddyArrivedParams{
 						{
-							screenName: "test-user",
+							screenName: state.NewIdentScreenName("test-user"),
 						},
 					},
 				},
@@ -1468,7 +1468,7 @@ func TestOServiceService_IdleNotification(t *testing.T) {
 				buddyBroadcasterParams: buddyBroadcasterParams{
 					broadcastBuddyArrivedParams: broadcastBuddyArrivedParams{
 						{
-							screenName: "test-user",
+							screenName: state.NewIdentScreenName("test-user"),
 						},
 					},
 				},
@@ -1482,7 +1482,7 @@ func TestOServiceService_IdleNotification(t *testing.T) {
 				p := params
 				buddyUpdateBroadcaster.EXPECT().
 					BroadcastBuddyArrived(mock.Anything, mock.MatchedBy(func(s *state.Session) bool {
-						return s.ScreenName() == p.screenName
+						return s.IdentScreenName() == p.screenName
 					})).
 					Return(nil)
 			}
@@ -1522,19 +1522,22 @@ func TestOServiceServiceForBOS_ClientOnline(t *testing.T) {
 				legacyBuddyListManagerParams: legacyBuddyListManagerParams{
 					legacyBuddiesParams: legacyBuddiesParams{
 						{
-							userScreenName: "test-user",
-							result:         []string{"buddy1", "buddy2"},
+							userScreenName: state.NewIdentScreenName("test-user"),
+							result: []state.IdentScreenName{
+								state.NewIdentScreenName("buddy1"),
+								state.NewIdentScreenName("buddy2"),
+							},
 						},
 					},
 				},
 				messageRelayerParams: messageRelayerParams{
 					retrieveByScreenNameParams: retrieveByScreenNameParams{
 						{
-							screenName: "buddy1",
+							screenName: state.NewIdentScreenName("buddy1"),
 							sess:       newTestSession("buddy1", sessOptCannedSignonTime),
 						},
 						{
-							screenName: "buddy2",
+							screenName: state.NewIdentScreenName("buddy2"),
 							sess:       newTestSession("buddy2", sessOptCannedSignonTime),
 						},
 					},
@@ -1542,17 +1545,17 @@ func TestOServiceServiceForBOS_ClientOnline(t *testing.T) {
 				buddyBroadcasterParams: buddyBroadcasterParams{
 					broadcastBuddyArrivedParams: broadcastBuddyArrivedParams{
 						{
-							screenName: "test-user",
+							screenName: state.NewIdentScreenName("test-user"),
 						},
 					},
 					unicastBuddyArrivedParams: unicastBuddyArrivedParams{
 						{
-							from: "buddy1",
-							to:   "test-user",
+							from: state.NewIdentScreenName("buddy1"),
+							to:   state.NewIdentScreenName("test-user"),
 						},
 						{
-							from: "buddy2",
-							to:   "test-user",
+							from: state.NewIdentScreenName("buddy2"),
+							to:   state.NewIdentScreenName("test-user"),
 						},
 					},
 				},
@@ -1566,19 +1569,22 @@ func TestOServiceServiceForBOS_ClientOnline(t *testing.T) {
 				legacyBuddyListManagerParams: legacyBuddyListManagerParams{
 					legacyBuddiesParams: legacyBuddiesParams{
 						{
-							userScreenName: "test-user",
-							result:         []string{"buddy1", "buddy2"},
+							userScreenName: state.NewIdentScreenName("test-user"),
+							result: []state.IdentScreenName{
+								state.NewIdentScreenName("buddy1"),
+								state.NewIdentScreenName("buddy2"),
+							},
 						},
 					},
 				},
 				messageRelayerParams: messageRelayerParams{
 					retrieveByScreenNameParams: retrieveByScreenNameParams{
 						{
-							screenName: "buddy1",
+							screenName: state.NewIdentScreenName("buddy1"),
 							sess:       newTestSession("buddy1", sessOptCannedSignonTime, sessOptInvisible),
 						},
 						{
-							screenName: "buddy2",
+							screenName: state.NewIdentScreenName("buddy2"),
 							sess:       newTestSession("buddy2", sessOptCannedSignonTime),
 						},
 					},
@@ -1586,13 +1592,13 @@ func TestOServiceServiceForBOS_ClientOnline(t *testing.T) {
 				buddyBroadcasterParams: buddyBroadcasterParams{
 					broadcastBuddyArrivedParams: broadcastBuddyArrivedParams{
 						{
-							screenName: "test-user",
+							screenName: state.NewIdentScreenName("test-user"),
 						},
 					},
 					unicastBuddyArrivedParams: unicastBuddyArrivedParams{
 						{
-							from: "buddy2",
-							to:   "test-user",
+							from: state.NewIdentScreenName("buddy2"),
+							to:   state.NewIdentScreenName("test-user"),
 						},
 					},
 				},
@@ -1618,7 +1624,7 @@ func TestOServiceServiceForBOS_ClientOnline(t *testing.T) {
 				p := params
 				buddyUpdateBroadcaster.EXPECT().
 					BroadcastBuddyArrived(mock.Anything, mock.MatchedBy(func(s *state.Session) bool {
-						return s.ScreenName() == p.screenName
+						return s.IdentScreenName() == p.screenName
 					})).
 					Return(nil)
 			}
@@ -1627,10 +1633,10 @@ func TestOServiceServiceForBOS_ClientOnline(t *testing.T) {
 				buddyUpdateBroadcaster.EXPECT().
 					UnicastBuddyArrived(mock.Anything,
 						mock.MatchedBy(func(s *state.Session) bool {
-							return s.ScreenName() == p.from
+							return s.IdentScreenName() == p.from
 						}),
 						mock.MatchedBy(func(s *state.Session) bool {
-							return s.ScreenName() == p.to
+							return s.IdentScreenName() == p.to
 						})).
 					Return(p.err)
 			}
@@ -1659,7 +1665,7 @@ func TestOServiceServiceForChat_ClientOnline(t *testing.T) {
 		message wire.SNACMessage
 	}
 	type sendToScreenNameParams []struct {
-		screenName string
+		screenName state.IdentScreenName
 		message    wire.SNACMessage
 	}
 
@@ -1710,7 +1716,7 @@ func TestOServiceServiceForChat_ClientOnline(t *testing.T) {
 			},
 			sendToScreenNameParams: sendToScreenNameParams{
 				{
-					screenName: chatter1.ScreenName(),
+					screenName: chatter1.IdentScreenName(),
 					message: wire.SNACMessage{
 						Frame: wire.SNACFrame{
 							FoodGroup: wire.Chat,
@@ -1728,7 +1734,7 @@ func TestOServiceServiceForChat_ClientOnline(t *testing.T) {
 					},
 				},
 				{
-					screenName: chatter1.ScreenName(),
+					screenName: chatter1.IdentScreenName(),
 					message: wire.SNACMessage{
 						Frame: wire.SNACFrame{
 							FoodGroup: wire.Chat,

+ 37 - 37
foodgroup/test_helpers.go

@@ -69,7 +69,7 @@ type userManagerParams struct {
 // getUserParams is the list of parameters passed at the mock
 // UserManager.User call site
 type getUserParams []struct {
-	screenName string
+	screenName state.IdentScreenName
 	result     *state.User
 	err        error
 }
@@ -92,8 +92,7 @@ type sessionManagerParams struct {
 // addSessionParams is the list of parameters passed at the mock
 // SessionManager.AddSession call site
 type addSessionParams []struct {
-	sessID     string
-	screenName string
+	screenName state.DisplayScreenName
 	result     *state.Session
 }
 
@@ -124,8 +123,8 @@ type feedbagManagerParams struct {
 // blockedStateParams is the list of parameters passed at the mock
 // FeedbagManager.BlockedState call site
 type blockedStateParams []struct {
-	screenName1 string
-	screenName2 string
+	screenName1 state.IdentScreenName
+	screenName2 state.IdentScreenName
 	result      state.BlockedState
 	err         error
 }
@@ -133,43 +132,43 @@ type blockedStateParams []struct {
 // adjacentUsersParams is the list of parameters passed at the mock
 // FeedbagManager.AdjacentUsers call site
 type adjacentUsersParams []struct {
-	screenName string
-	users      []string
+	screenName state.IdentScreenName
+	users      []state.IdentScreenName
 	err        error
 }
 
 // feedbagUpsertParams is the list of parameters passed at the mock
 // FeedbagManager.FeedbagUpsert call site
 type feedbagUpsertParams []struct {
-	screenName string
+	screenName state.IdentScreenName
 	items      []wire.FeedbagItem
 }
 
 // buddiesParams is the list of parameters passed at the mock
 // FeedbagManager.Buddies call site
 type buddiesParams []struct {
-	screenName string
-	results    []string
+	screenName state.IdentScreenName
+	results    []state.IdentScreenName
 }
 
 // feedbagParams is the list of parameters passed at the mock
 // FeedbagManager.Feedbag call site
 type feedbagParams []struct {
-	screenName string
+	screenName state.IdentScreenName
 	results    []wire.FeedbagItem
 }
 
 // feedbagLastModifiedParams is the list of parameters passed at the mock
 // FeedbagManager.FeedbagLastModified call site
 type feedbagLastModifiedParams []struct {
-	screenName string
+	screenName state.IdentScreenName
 	result     time.Time
 }
 
 // feedbagDeleteParams is the list of parameters passed at the mock
 // FeedbagManager.FeedbagDelete call site
 type feedbagDeleteParams []struct {
-	screenName string
+	screenName state.IdentScreenName
 	items      []wire.FeedbagItem
 }
 
@@ -184,21 +183,21 @@ type messageRelayerParams struct {
 // retrieveByScreenNameParams is the list of parameters passed at the mock
 // MessageRelayer.RetrieveByScreenName call site
 type retrieveByScreenNameParams []struct {
-	screenName string
+	screenName state.IdentScreenName
 	sess       *state.Session
 }
 
 // relayToScreenNamesParams is the list of parameters passed at the mock
 // MessageRelayer.RelayToScreenNames call site
 type relayToScreenNamesParams []struct {
-	screenNames []string
+	screenNames []state.IdentScreenName
 	message     wire.SNACMessage
 }
 
 // relayToScreenNameParams is the list of parameters passed at the mock
 // MessageRelayer.RelayToScreenName call site
 type relayToScreenNameParams []struct {
-	screenName string
+	screenName state.IdentScreenName
 	message    wire.SNACMessage
 }
 
@@ -210,17 +209,17 @@ type profileManagerParams struct {
 }
 
 // retrieveByScreenNameParams is the list of parameters passed at the mock
-// ProfileManager.RetrieveProfile call site
+// ProfileManager.Profile call site
 type retrieveProfileParams []struct {
-	screenName string
+	screenName state.IdentScreenName
 	result     string
 	err        error
 }
 
 // setProfileParams is the list of parameters passed at the mock
-// ProfileManager.UpsertProfile call site
+// ProfileManager.SetProfile call site
 type setProfileParams []struct {
-	screenName string
+	screenName state.IdentScreenName
 	body       any
 }
 
@@ -250,35 +249,35 @@ type legacyBuddyListManagerParams struct {
 // legacyBuddiesParams is the list of parameters passed at the mock
 // LegacyBuddyListManager.AddBuddy call site
 type addBuddyParams []struct {
-	userScreenName  string
-	buddyScreenName string
+	userScreenName  state.IdentScreenName
+	buddyScreenName state.IdentScreenName
 }
 
 // legacyBuddiesParams is the list of parameters passed at the mock
 // LegacyBuddyListManager.DeleteBuddy call site
 type deleteBuddyParams []struct {
-	userScreenName  string
-	buddyScreenName string
+	userScreenName  state.IdentScreenName
+	buddyScreenName state.IdentScreenName
 }
 
 // deleteUserParams is the list of parameters passed at the mock
 // LegacyBuddyListManager.DeleteUser call site
 type deleteUserParams []struct {
-	userScreenName string
+	userScreenName state.IdentScreenName
 }
 
 // legacyBuddiesParams is the list of parameters passed at the mock
 // LegacyBuddyListManager.Buddies call site
 type legacyBuddiesParams []struct {
-	userScreenName string
-	result         []string
+	userScreenName state.IdentScreenName
+	result         []state.IdentScreenName
 }
 
 // whoAddedUserParams is the list of parameters passed at the mock
 // LegacyBuddyListManager.WhoAddedUser call site
 type whoAddedUserParams []struct {
-	userScreenName string
-	result         []string
+	userScreenName state.IdentScreenName
+	result         []state.IdentScreenName
 }
 
 // cookieIssuerParams is the list of parameters passed at the mock
@@ -301,30 +300,30 @@ type buddyBroadcasterParams struct {
 // broadcastBuddyArrivedParams is the list of parameters passed at the mock
 // BuddyBroadcaster.BroadcastBuddyArrived call site
 type broadcastBuddyArrivedParams []struct {
-	screenName string
+	screenName state.IdentScreenName
 	err        error
 }
 
 // broadcastBuddyDepartedParams is the list of parameters passed at the mock
 // BuddyBroadcaster.BroadcastBuddyDeparted call site
 type broadcastBuddyDepartedParams []struct {
-	screenName string
+	screenName state.IdentScreenName
 	err        error
 }
 
 // unicastBuddyArrivedParams is the list of parameters passed at the mock
 // BuddyBroadcaster.UnicastBuddyArrived call site
 type unicastBuddyArrivedParams []struct {
-	from string
-	to   string
+	from state.IdentScreenName
+	to   state.IdentScreenName
 	err  error
 }
 
 // unicastBuddyDepartedParams is the list of parameters passed at the mock
 // BuddyBroadcaster.UnicastBuddyDeparted call site
 type unicastBuddyDepartedParams []struct {
-	from string
-	to   string
+	from state.IdentScreenName
+	to   state.IdentScreenName
 	err  error
 }
 
@@ -388,9 +387,10 @@ func sessOptCaps(caps [][16]byte) func(session *state.Session) {
 
 // newTestSession creates a session object with 0 or more functional options
 // applied
-func newTestSession(screenName string, options ...func(session *state.Session)) *state.Session {
+func newTestSession(screenName state.DisplayScreenName, options ...func(session *state.Session)) *state.Session {
 	s := state.NewSession()
-	s.SetScreenName(screenName)
+	s.SetIdentScreenName(screenName.IdentScreenName())
+	s.SetDisplayScreenName(screenName)
 	for _, op := range options {
 		op(s)
 	}

+ 20 - 20
foodgroup/types.go

@@ -42,13 +42,13 @@ import (
 )
 
 type FeedbagManager interface {
-	BlockedState(screenName1, screenName2 string) (state.BlockedState, error)
-	Buddies(screenName string) ([]string, error)
-	FeedbagDelete(screenName string, items []wire.FeedbagItem) error
-	AdjacentUsers(screenName string) ([]string, error)
-	FeedbagLastModified(screenName string) (time.Time, error)
-	Feedbag(screenName string) ([]wire.FeedbagItem, error)
-	FeedbagUpsert(screenName string, items []wire.FeedbagItem) error
+	BlockedState(screenName1, screenName2 state.IdentScreenName) (state.BlockedState, error)
+	Buddies(screenName state.IdentScreenName) ([]state.IdentScreenName, error)
+	FeedbagDelete(screenName state.IdentScreenName, items []wire.FeedbagItem) error
+	AdjacentUsers(screenName state.IdentScreenName) ([]state.IdentScreenName, error)
+	FeedbagLastModified(screenName state.IdentScreenName) (time.Time, error)
+	Feedbag(screenName state.IdentScreenName) ([]wire.FeedbagItem, error)
+	FeedbagUpsert(screenName state.IdentScreenName, items []wire.FeedbagItem) error
 }
 
 // LegacyBuddyListManager defines operations for tracking user relationships
@@ -56,44 +56,44 @@ type FeedbagManager interface {
 // 4.3.
 type LegacyBuddyListManager interface {
 	// AddBuddy adds buddyScreenName to userScreenName's buddy list.
-	AddBuddy(userScreenName, buddyScreenName string)
+	AddBuddy(userScreenName, buddyScreenName state.IdentScreenName)
 
 	// Buddies returns a list of all buddies associated with the specified
 	// userScreenName.
-	Buddies(userScreenName string) []string
+	Buddies(userScreenName state.IdentScreenName) []state.IdentScreenName
 
 	// DeleteBuddy removes buddyScreenName from userScreenName's buddy list.
-	DeleteBuddy(userScreenName, buddyScreenName string)
+	DeleteBuddy(userScreenName, buddyScreenName state.IdentScreenName)
 
 	// DeleteUser removes userScreenName's buddy list.
-	DeleteUser(userScreenName string)
+	DeleteUser(userScreenName state.IdentScreenName)
 
 	// WhoAddedUser returns a list of screen names who have userScreenName in
 	// their buddy lists.
-	WhoAddedUser(userScreenName string) []string
+	WhoAddedUser(userScreenName state.IdentScreenName) []state.IdentScreenName
 }
 
 type UserManager interface {
-	User(screenName string) (*state.User, error)
+	User(screenName state.IdentScreenName) (*state.User, error)
 	InsertUser(u state.User) error
 }
 
 type SessionManager interface {
 	Empty() bool
-	AddSession(screenName string) *state.Session
+	AddSession(screenName state.DisplayScreenName) *state.Session
 	RemoveSession(sess *state.Session)
-	RetrieveSession(ID string) *state.Session
+	RetrieveSession(screenName state.IdentScreenName) *state.Session
 }
 
 type ProfileManager interface {
-	Profile(screenName string) (string, error)
-	SetProfile(screenName string, body string) error
+	Profile(screenName state.IdentScreenName) (string, error)
+	SetProfile(screenName state.IdentScreenName, body string) error
 }
 
 type MessageRelayer interface {
-	RelayToScreenNames(ctx context.Context, screenNames []string, msg wire.SNACMessage)
-	RetrieveByScreenName(screenName string) *state.Session
-	RelayToScreenName(ctx context.Context, screenName string, msg wire.SNACMessage)
+	RelayToScreenNames(ctx context.Context, screenNames []state.IdentScreenName, msg wire.SNACMessage)
+	RetrieveByScreenName(screenName state.IdentScreenName) *state.Session
+	RelayToScreenName(ctx context.Context, screenName state.IdentScreenName, msg wire.SNACMessage)
 }
 
 type ChatMessageRelayer interface {

+ 38 - 54
server/http/mgmt_api.go

@@ -34,10 +34,10 @@ type onlineUsers struct {
 
 type UserManager interface {
 	AllUsers() ([]state.User, error)
-	DeleteUser(screenName string) error
+	DeleteUser(screenName state.IdentScreenName) error
 	InsertUser(u state.User) error
 	SetUserPassword(u state.User) error
-	User(screenName string) (*state.User, error)
+	User(screenName state.IdentScreenName) (*state.User, error)
 }
 
 type SessionRetriever interface {
@@ -46,14 +46,11 @@ type SessionRetriever interface {
 
 func StartManagementAPI(cfg config.Config, userManager UserManager, sessionRetriever SessionRetriever, logger *slog.Logger) {
 	mux := http.NewServeMux()
-	newUser := func() state.User {
-		return state.User{AuthKey: uuid.New().String()}
-	}
 	mux.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) {
-		userHandler(w, r, userManager, newUser, logger)
+		userHandler(w, r, userManager, uuid.New, logger)
 	})
 	mux.HandleFunc("/user/password", func(w http.ResponseWriter, r *http.Request) {
-		userPasswordHandler(w, r, userManager, newUser, logger)
+		userPasswordHandler(w, r, userManager, uuid.New, logger)
 	})
 	mux.HandleFunc("/user/login", func(w http.ResponseWriter, r *http.Request) {
 		loginHandler(w, r, userManager, logger)
@@ -70,32 +67,27 @@ func StartManagementAPI(cfg config.Config, userManager UserManager, sessionRetri
 	}
 }
 
-func userHandler(
-	w http.ResponseWriter,
-	r *http.Request,
-	userManager UserManager,
-	newUser func() state.User,
-	logger *slog.Logger,
-) {
+func userHandler(w http.ResponseWriter, r *http.Request, userManager UserManager, newUUID func() uuid.UUID, logger *slog.Logger) {
 	switch r.Method {
 	case http.MethodDelete:
 		deleteUserHandler(w, r, userManager, logger)
 	case http.MethodGet:
 		getUserHandler(w, r, userManager, logger)
 	case http.MethodPost:
-		postUserHandler(w, r, userManager, newUser, logger)
+		postUserHandler(w, r, userManager, newUUID, logger)
 	default:
 		http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
 	}
 }
 
 func deleteUserHandler(w http.ResponseWriter, r *http.Request, manager UserManager, logger *slog.Logger) {
-	user := state.User{}
-	if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
-		http.Error(w, "malformed input", http.StatusBadRequest)
+	user, err := userFromBody(r)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusBadRequest)
 		return
 	}
-	err := manager.DeleteUser(user.ScreenName)
+
+	err = manager.DeleteUser(user.DisplayScreenName.IdentScreenName())
 	switch {
 	case errors.Is(err, state.ErrNoUser):
 		http.Error(w, "user does not exist", http.StatusNotFound)
@@ -110,36 +102,25 @@ func deleteUserHandler(w http.ResponseWriter, r *http.Request, manager UserManag
 	fmt.Fprintln(w, "User account successfully deleted.")
 }
 
-func userPasswordHandler(
-	w http.ResponseWriter,
-	r *http.Request,
-	userManager UserManager,
-	userFactory func() state.User,
-	logger *slog.Logger,
-) {
+func userPasswordHandler(w http.ResponseWriter, r *http.Request, userManager UserManager, newUUID func() uuid.UUID, logger *slog.Logger) {
 	switch r.Method {
 	case http.MethodPut:
-		putUserPasswordHandler(w, r, userManager, userFactory, logger)
+		putUserPasswordHandler(w, r, userManager, newUUID, logger)
 	default:
 		http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
 	}
 }
 
 // putUserPasswordHandler handles the PUT /user/password endpoint.
-func putUserPasswordHandler(
-	w http.ResponseWriter,
-	r *http.Request,
-	userManager UserManager,
-	newUser func() state.User,
-	logger *slog.Logger,
-) {
-	user := userWithPassword{
-		User: newUser(),
-	}
-	if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
-		http.Error(w, "malformed input", http.StatusBadRequest)
+func putUserPasswordHandler(w http.ResponseWriter, r *http.Request, userManager UserManager, newUUID func() uuid.UUID, logger *slog.Logger) {
+	user, err := userFromBody(r)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusBadRequest)
 		return
 	}
+	user.AuthKey = newUUID().String()
+
+	user.IdentScreenName = user.DisplayScreenName.IdentScreenName()
 	if err := user.HashPassword(user.Password); err != nil {
 		logger.Error("error hashing user password in PUT /user/password", "err", err.Error())
 		http.Error(w, "internal server error", http.StatusInternalServerError)
@@ -178,7 +159,7 @@ func sessionHandler(w http.ResponseWriter, r *http.Request, sessionRetriever Ses
 
 	for _, s := range allUsers {
 		ou.Sessions = append(ou.Sessions, userSession{
-			ScreenName: s.ScreenName(),
+			ScreenName: s.DisplayScreenName().String(),
 		})
 	}
 
@@ -204,27 +185,21 @@ func getUserHandler(w http.ResponseWriter, _ *http.Request, userManager UserMana
 }
 
 // postUserHandler handles the POST /user endpoint.
-func postUserHandler(
-	w http.ResponseWriter,
-	r *http.Request,
-	userManager UserManager,
-	newUser func() state.User,
-	logger *slog.Logger,
-) {
-	user := userWithPassword{
-		User: newUser(),
-	}
-	if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
-		http.Error(w, "malformed input", http.StatusBadRequest)
+func postUserHandler(w http.ResponseWriter, r *http.Request, userManager UserManager, newUUID func() uuid.UUID, logger *slog.Logger) {
+	user, err := userFromBody(r)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusBadRequest)
 		return
 	}
+	user.AuthKey = newUUID().String()
+
 	if err := user.HashPassword(user.Password); err != nil {
 		logger.Error("error hashing user password in POST /user", "err", err.Error())
 		http.Error(w, "internal server error", http.StatusInternalServerError)
 		return
 	}
 
-	err := userManager.InsertUser(user.User)
+	err = userManager.InsertUser(user.User)
 	switch {
 	case errors.Is(err, state.ErrDupUser):
 		http.Error(w, "user already exists", http.StatusConflict)
@@ -239,6 +214,15 @@ func postUserHandler(
 	fmt.Fprintln(w, "User account created successfully.")
 }
 
+func userFromBody(r *http.Request) (userWithPassword, error) {
+	user := userWithPassword{}
+	if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
+		return userWithPassword{}, errors.New("malformed input")
+	}
+	user.IdentScreenName = user.DisplayScreenName.IdentScreenName()
+	return user, nil
+}
+
 // loginHandler is a temporary endpoint for validating user credentials for
 // chivanet. do not rely on this endpoint, as it will be eventually removed.
 func loginHandler(w http.ResponseWriter, r *http.Request, userManager UserManager, logger *slog.Logger) {
@@ -272,7 +256,7 @@ func loginHandler(w http.ResponseWriter, r *http.Request, userManager UserManage
 		return
 	}
 
-	username, password := pair[0], pair[1]
+	username, password := state.NewIdentScreenName(pair[0]), pair[1]
 
 	user, err := userManager.User(username)
 	if err != nil {

+ 48 - 30
server/http/mgmt_api_test.go

@@ -8,6 +8,7 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/google/uuid"
 	"github.com/stretchr/testify/assert"
 
 	"github.com/mk6i/retro-aim-server/state"
@@ -16,7 +17,8 @@ import (
 func TestSessionHandler_GET(t *testing.T) {
 	fnNewSess := func(screenName string) *state.Session {
 		sess := state.NewSession()
-		sess.SetScreenName(screenName)
+		sess.SetIdentScreenName(state.NewIdentScreenName(screenName))
+		sess.SetDisplayScreenName(state.DisplayScreenName(screenName))
 		return sess
 	}
 	tt := []struct {
@@ -100,8 +102,14 @@ func TestUserHandler_GET(t *testing.T) {
 		{
 			name: "user store containing 2 users",
 			users: []state.User{
-				{ScreenName: "userA"},
-				{ScreenName: "userB"},
+				{
+					DisplayScreenName: "userA",
+					IdentScreenName:   state.NewIdentScreenName("userA"),
+				},
+				{
+					DisplayScreenName: "userB",
+					IdentScreenName:   state.NewIdentScreenName("userB"),
+				},
 			},
 			want:       `[{"screen_name":"userA"},{"screen_name":"userB"}]`,
 			statusCode: http.StatusOK,
@@ -142,6 +150,7 @@ func TestUserHandler_POST(t *testing.T) {
 	tt := []struct {
 		name           string
 		body           string
+		UUID           uuid.UUID
 		user           state.User
 		userHandlerErr error
 		want           string
@@ -150,10 +159,12 @@ func TestUserHandler_POST(t *testing.T) {
 		{
 			name: "with valid user",
 			body: `{"screen_name":"userA", "password":"thepassword"}`,
+			UUID: uuid.MustParse("07c70701-ba68-49a9-9f9b-67a53816e37b"),
 			user: func() state.User {
 				user := state.User{
-					AuthKey:    "theAuthKey",
-					ScreenName: "userA",
+					AuthKey:           uuid.MustParse("07c70701-ba68-49a9-9f9b-67a53816e37b").String(),
+					DisplayScreenName: "userA",
+					IdentScreenName:   state.NewIdentScreenName("userA"),
 				}
 				assert.NoError(t, user.HashPassword("thepassword"))
 				return user
@@ -171,10 +182,12 @@ func TestUserHandler_POST(t *testing.T) {
 		{
 			name: "user handler error",
 			body: `{"screen_name":"userA", "password":"thepassword"}`,
+			UUID: uuid.MustParse("07c70701-ba68-49a9-9f9b-67a53816e37b"),
 			user: func() state.User {
 				user := state.User{
-					AuthKey:    "theAuthKey",
-					ScreenName: "userA",
+					AuthKey:           uuid.MustParse("07c70701-ba68-49a9-9f9b-67a53816e37b").String(),
+					DisplayScreenName: "userA",
+					IdentScreenName:   state.NewIdentScreenName("userA"),
 				}
 				assert.NoError(t, user.HashPassword("thepassword"))
 				return user
@@ -186,10 +199,12 @@ func TestUserHandler_POST(t *testing.T) {
 		{
 			name: "duplicate user",
 			body: `{"screen_name":"userA", "password":"thepassword"}`,
+			UUID: uuid.MustParse("07c70701-ba68-49a9-9f9b-67a53816e37b"),
 			user: func() state.User {
 				user := state.User{
-					AuthKey:    "theAuthKey",
-					ScreenName: "userA",
+					AuthKey:           uuid.MustParse("07c70701-ba68-49a9-9f9b-67a53816e37b").String(),
+					DisplayScreenName: "userA",
+					IdentScreenName:   state.NewIdentScreenName("userA"),
 				}
 				assert.NoError(t, user.HashPassword("thepassword"))
 				return user
@@ -206,16 +221,14 @@ func TestUserHandler_POST(t *testing.T) {
 			responseRecorder := httptest.NewRecorder()
 
 			userManager := newMockUserManager(t)
-			if tc.user.ScreenName != "" {
+			if tc.user.IdentScreenName.String() != "" {
 				userManager.EXPECT().
 					InsertUser(tc.user).
 					Return(tc.userHandlerErr)
 			}
 
-			newUser := func() state.User {
-				return tc.user
-			}
-			userHandler(responseRecorder, request, userManager, newUser, slog.Default())
+			newUUID := func() uuid.UUID { return tc.UUID }
+			userHandler(responseRecorder, request, userManager, newUUID, slog.Default())
 
 			if responseRecorder.Code != tc.statusCode {
 				t.Errorf("want status '%d', got '%d'", tc.statusCode, responseRecorder.Code)
@@ -241,7 +254,7 @@ func TestUserHandler_DELETE(t *testing.T) {
 			name: "with valid user",
 			body: `{"screen_name":"userA"}`,
 			user: state.User{
-				ScreenName: "userA",
+				IdentScreenName: state.NewIdentScreenName("userA"),
 			},
 			want:       `User account successfully deleted.`,
 			statusCode: http.StatusNoContent,
@@ -250,7 +263,7 @@ func TestUserHandler_DELETE(t *testing.T) {
 			name: "with non-existent user",
 			body: `{"screen_name":"userA"}`,
 			user: state.User{
-				ScreenName: "userA",
+				IdentScreenName: state.NewIdentScreenName("userA"),
 			},
 			userHandlerErr: state.ErrNoUser,
 			want:           `user does not exist`,
@@ -267,7 +280,7 @@ func TestUserHandler_DELETE(t *testing.T) {
 			name: "user handler error",
 			body: `{"screen_name":"userA"}`,
 			user: state.User{
-				ScreenName: "userA",
+				IdentScreenName: state.NewIdentScreenName("userA"),
 			},
 			userHandlerErr: io.EOF,
 			want:           `internal server error`,
@@ -281,9 +294,9 @@ func TestUserHandler_DELETE(t *testing.T) {
 			responseRecorder := httptest.NewRecorder()
 
 			userManager := newMockUserManager(t)
-			if tc.user.ScreenName != "" {
+			if tc.user.IdentScreenName.String() != "" {
 				userManager.EXPECT().
-					DeleteUser(tc.user.ScreenName).
+					DeleteUser(tc.user.IdentScreenName).
 					Return(tc.userHandlerErr)
 			}
 
@@ -305,6 +318,7 @@ func TestUserPasswordHandler_PUT(t *testing.T) {
 		name           string
 		body           string
 		user           state.User
+		UUID           uuid.UUID
 		userHandlerErr error
 		want           string
 		statusCode     int
@@ -312,10 +326,12 @@ func TestUserPasswordHandler_PUT(t *testing.T) {
 		{
 			name: "with valid password",
 			body: `{"screen_name":"userA", "password":"thepassword"}`,
+			UUID: uuid.MustParse("07c70701-ba68-49a9-9f9b-67a53816e37b"),
 			user: func() state.User {
 				user := state.User{
-					AuthKey:    "theAuthKey",
-					ScreenName: "userA",
+					AuthKey:           uuid.MustParse("07c70701-ba68-49a9-9f9b-67a53816e37b").String(),
+					DisplayScreenName: "userA",
+					IdentScreenName:   state.NewIdentScreenName("userA"),
 				}
 				assert.NoError(t, user.HashPassword("thepassword"))
 				return user
@@ -333,10 +349,12 @@ func TestUserPasswordHandler_PUT(t *testing.T) {
 		{
 			name: "user password handler error",
 			body: `{"screen_name":"userA", "password":"thepassword"}`,
+			UUID: uuid.MustParse("07c70701-ba68-49a9-9f9b-67a53816e37b"),
 			user: func() state.User {
 				user := state.User{
-					AuthKey:    "theAuthKey",
-					ScreenName: "userA",
+					AuthKey:           uuid.MustParse("07c70701-ba68-49a9-9f9b-67a53816e37b").String(),
+					DisplayScreenName: "userA",
+					IdentScreenName:   state.NewIdentScreenName("userA"),
 				}
 				assert.NoError(t, user.HashPassword("thepassword"))
 				return user
@@ -348,10 +366,12 @@ func TestUserPasswordHandler_PUT(t *testing.T) {
 		{
 			name: "user doesn't exist",
 			body: `{"screen_name":"userA", "password":"thepassword"}`,
+			UUID: uuid.MustParse("07c70701-ba68-49a9-9f9b-67a53816e37b"),
 			user: func() state.User {
 				user := state.User{
-					AuthKey:    "theAuthKey",
-					ScreenName: "userA",
+					AuthKey:           uuid.MustParse("07c70701-ba68-49a9-9f9b-67a53816e37b").String(),
+					DisplayScreenName: "userA",
+					IdentScreenName:   state.NewIdentScreenName("userA"),
 				}
 				assert.NoError(t, user.HashPassword("thepassword"))
 				return user
@@ -368,16 +388,14 @@ func TestUserPasswordHandler_PUT(t *testing.T) {
 			responseRecorder := httptest.NewRecorder()
 
 			userManager := newMockUserManager(t)
-			if tc.user.ScreenName != "" {
+			if tc.user.IdentScreenName.String() != "" {
 				userManager.EXPECT().
 					SetUserPassword(tc.user).
 					Return(tc.userHandlerErr)
 			}
 
-			uf := func() state.User {
-				return tc.user
-			}
-			userPasswordHandler(responseRecorder, request, userManager, uf, slog.Default())
+			newUUID := func() uuid.UUID { return tc.UUID }
+			userPasswordHandler(responseRecorder, request, userManager, newUUID, slog.Default())
 
 			if responseRecorder.Code != tc.statusCode {
 				t.Errorf("want status '%d', got '%d'", tc.statusCode, responseRecorder.Code)

+ 14 - 14
server/http/mock_user_manager_test.go

@@ -78,7 +78,7 @@ func (_c *mockUserManager_AllUsers_Call) RunAndReturn(run func() ([]state.User,
 }
 
 // DeleteUser provides a mock function with given fields: screenName
-func (_m *mockUserManager) DeleteUser(screenName string) error {
+func (_m *mockUserManager) DeleteUser(screenName state.IdentScreenName) error {
 	ret := _m.Called(screenName)
 
 	if len(ret) == 0 {
@@ -86,7 +86,7 @@ func (_m *mockUserManager) DeleteUser(screenName string) error {
 	}
 
 	var r0 error
-	if rf, ok := ret.Get(0).(func(string) error); ok {
+	if rf, ok := ret.Get(0).(func(state.IdentScreenName) error); ok {
 		r0 = rf(screenName)
 	} else {
 		r0 = ret.Error(0)
@@ -101,14 +101,14 @@ type mockUserManager_DeleteUser_Call struct {
 }
 
 // DeleteUser is a helper method to define mock.On call
-//   - screenName string
+//   - screenName state.IdentScreenName
 func (_e *mockUserManager_Expecter) DeleteUser(screenName interface{}) *mockUserManager_DeleteUser_Call {
 	return &mockUserManager_DeleteUser_Call{Call: _e.mock.On("DeleteUser", screenName)}
 }
 
-func (_c *mockUserManager_DeleteUser_Call) Run(run func(screenName string)) *mockUserManager_DeleteUser_Call {
+func (_c *mockUserManager_DeleteUser_Call) Run(run func(screenName state.IdentScreenName)) *mockUserManager_DeleteUser_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(string))
+		run(args[0].(state.IdentScreenName))
 	})
 	return _c
 }
@@ -118,7 +118,7 @@ func (_c *mockUserManager_DeleteUser_Call) Return(_a0 error) *mockUserManager_De
 	return _c
 }
 
-func (_c *mockUserManager_DeleteUser_Call) RunAndReturn(run func(string) error) *mockUserManager_DeleteUser_Call {
+func (_c *mockUserManager_DeleteUser_Call) RunAndReturn(run func(state.IdentScreenName) error) *mockUserManager_DeleteUser_Call {
 	_c.Call.Return(run)
 	return _c
 }
@@ -216,7 +216,7 @@ func (_c *mockUserManager_SetUserPassword_Call) RunAndReturn(run func(state.User
 }
 
 // User provides a mock function with given fields: screenName
-func (_m *mockUserManager) User(screenName string) (*state.User, error) {
+func (_m *mockUserManager) User(screenName state.IdentScreenName) (*state.User, error) {
 	ret := _m.Called(screenName)
 
 	if len(ret) == 0 {
@@ -225,10 +225,10 @@ func (_m *mockUserManager) User(screenName string) (*state.User, error) {
 
 	var r0 *state.User
 	var r1 error
-	if rf, ok := ret.Get(0).(func(string) (*state.User, error)); ok {
+	if rf, ok := ret.Get(0).(func(state.IdentScreenName) (*state.User, error)); ok {
 		return rf(screenName)
 	}
-	if rf, ok := ret.Get(0).(func(string) *state.User); ok {
+	if rf, ok := ret.Get(0).(func(state.IdentScreenName) *state.User); ok {
 		r0 = rf(screenName)
 	} else {
 		if ret.Get(0) != nil {
@@ -236,7 +236,7 @@ func (_m *mockUserManager) User(screenName string) (*state.User, error) {
 		}
 	}
 
-	if rf, ok := ret.Get(1).(func(string) error); ok {
+	if rf, ok := ret.Get(1).(func(state.IdentScreenName) error); ok {
 		r1 = rf(screenName)
 	} else {
 		r1 = ret.Error(1)
@@ -251,14 +251,14 @@ type mockUserManager_User_Call struct {
 }
 
 // User is a helper method to define mock.On call
-//   - screenName string
+//   - screenName state.IdentScreenName
 func (_e *mockUserManager_Expecter) User(screenName interface{}) *mockUserManager_User_Call {
 	return &mockUserManager_User_Call{Call: _e.mock.On("User", screenName)}
 }
 
-func (_c *mockUserManager_User_Call) Run(run func(screenName string)) *mockUserManager_User_Call {
+func (_c *mockUserManager_User_Call) Run(run func(screenName state.IdentScreenName)) *mockUserManager_User_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(string))
+		run(args[0].(state.IdentScreenName))
 	})
 	return _c
 }
@@ -268,7 +268,7 @@ func (_c *mockUserManager_User_Call) Return(_a0 *state.User, _a1 error) *mockUse
 	return _c
 }
 
-func (_c *mockUserManager_User_Call) RunAndReturn(run func(string) (*state.User, error)) *mockUserManager_User_Call {
+func (_c *mockUserManager_User_Call) RunAndReturn(run func(state.IdentScreenName) (*state.User, error)) *mockUserManager_User_Call {
 	_c.Call.Return(run)
 	return _c
 }

+ 3 - 3
server/oscar/auth.go

@@ -16,9 +16,9 @@ import (
 
 type AuthService interface {
 	BUCPChallenge(bodyIn wire.SNAC_0x17_0x06_BUCPChallengeRequest, newUUID func() uuid.UUID) (wire.SNACMessage, error)
-	BUCPLogin(bodyIn wire.SNAC_0x17_0x02_BUCPLoginRequest, fn func(screenName string) (state.User, error)) (wire.SNACMessage, error)
-	FLAPLogin(frame wire.FLAPSignonFrame, newUserFn func(screenName string) (state.User, error)) (wire.TLVRestBlock, error)
-	RegisterBOSSession(sessionID string) (*state.Session, error)
+	BUCPLogin(bodyIn wire.SNAC_0x17_0x02_BUCPLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error)) (wire.SNACMessage, error)
+	FLAPLogin(frame wire.FLAPSignonFrame, newUserFn func(screenName state.DisplayScreenName) (state.User, error)) (wire.TLVRestBlock, error)
+	RegisterBOSSession(screenName state.IdentScreenName) (*state.Session, error)
 	RegisterChatSession(loginCookie []byte) (*state.Session, error)
 	Signout(ctx context.Context, sess *state.Session) error
 	SignoutChat(ctx context.Context, sess *state.Session) error

+ 4 - 3
server/oscar/bos.go

@@ -9,6 +9,7 @@ import (
 	"os"
 
 	"github.com/mk6i/retro-aim-server/config"
+	"github.com/mk6i/retro-aim-server/state"
 	"github.com/mk6i/retro-aim-server/wire"
 )
 
@@ -77,12 +78,12 @@ func (rt BOSServer) handleNewConnection(ctx context.Context, rwc io.ReadWriteClo
 		return errors.New("unable to get session id from payload")
 	}
 
-	token, err := rt.CookieCracker.Crack(authCookie)
+	screenName, err := rt.CookieCracker.Crack(authCookie)
 	if err != nil {
 		return err
 	}
 
-	sess, err := rt.RegisterBOSSession(string(token))
+	sess, err := rt.RegisterBOSSession(state.NewIdentScreenName(string(screenName)))
 	if err != nil {
 		return err
 	}
@@ -98,7 +99,7 @@ func (rt BOSServer) handleNewConnection(ctx context.Context, rwc io.ReadWriteClo
 		}
 	}()
 
-	ctx = context.WithValue(ctx, "screenName", sess.ScreenName())
+	ctx = context.WithValue(ctx, "screenName", sess.IdentScreenName())
 
 	msg := rt.OnlineNotifier.HostOnline()
 	if err := flapc.SendSNAC(msg.Frame, msg.Body); err != nil {

+ 1 - 1
server/oscar/bos_test.go

@@ -81,7 +81,7 @@ func TestBOSService_handleNewConnection(t *testing.T) {
 
 	authService := newMockAuthService(t)
 	authService.EXPECT().
-		RegisterBOSSession("user_screen_name").
+		RegisterBOSSession(state.NewIdentScreenName("user_screen_name")).
 		Return(sess, nil)
 	authService.EXPECT().
 		Signout(mock.Anything, sess).

+ 1 - 1
server/oscar/chat.go

@@ -95,6 +95,6 @@ func (rt ChatServer) handleNewConnection(ctx context.Context, rwc io.ReadWriteCl
 		return err
 	}
 
-	ctx = context.WithValue(ctx, "screenName", chatSess.ScreenName())
+	ctx = context.WithValue(ctx, "screenName", chatSess.IdentScreenName())
 	return dispatchIncomingMessages(ctx, chatSess, flapc, rwc, rt.Logger, rt.Handler, rt.Config)
 }

+ 1 - 1
server/oscar/connection_test.go

@@ -60,7 +60,7 @@ func TestHandleChatConnection_MessageRelay(t *testing.T) {
 
 	// relay messages to user session
 	for _, msg := range inboundMsgs {
-		sessionManager.RelayToScreenName(context.Background(), "bob", msg)
+		sessionManager.RelayToScreenName(context.Background(), state.NewIdentScreenName("bob"), msg)
 	}
 
 	// consume and verify the relayed messages

+ 1 - 1
server/oscar/middleware/logger.go

@@ -59,7 +59,7 @@ type handler struct {
 
 func (h handler) Handle(ctx context.Context, r slog.Record) error {
 	if sn := ctx.Value("screenName"); sn != nil {
-		r.AddAttrs(slog.Attr{Key: "screenName", Value: slog.StringValue(sn.(string))})
+		//r.AddAttrs(slog.Attr{Key: "screenName", Value: slog.StringValue(sn.(string))})
 	}
 	if ip := ctx.Value("ip"); ip != nil {
 		r.AddAttrs(slog.Attr{Key: "ip", Value: slog.StringValue(ip.(string))})

+ 38 - 38
server/oscar/mock_auth_test.go

@@ -83,9 +83,9 @@ func (_c *mockAuthService_BUCPChallenge_Call) RunAndReturn(run func(wire.SNAC_0x
 	return _c
 }
 
-// BUCPLogin provides a mock function with given fields: bodyIn, fn
-func (_m *mockAuthService) BUCPLogin(bodyIn wire.SNAC_0x17_0x02_BUCPLoginRequest, fn func(string) (state.User, error)) (wire.SNACMessage, error) {
-	ret := _m.Called(bodyIn, fn)
+// BUCPLogin provides a mock function with given fields: bodyIn, newUserFn
+func (_m *mockAuthService) BUCPLogin(bodyIn wire.SNAC_0x17_0x02_BUCPLoginRequest, newUserFn func(state.DisplayScreenName) (state.User, error)) (wire.SNACMessage, error) {
+	ret := _m.Called(bodyIn, newUserFn)
 
 	if len(ret) == 0 {
 		panic("no return value specified for BUCPLogin")
@@ -93,17 +93,17 @@ func (_m *mockAuthService) BUCPLogin(bodyIn wire.SNAC_0x17_0x02_BUCPLoginRequest
 
 	var r0 wire.SNACMessage
 	var r1 error
-	if rf, ok := ret.Get(0).(func(wire.SNAC_0x17_0x02_BUCPLoginRequest, func(string) (state.User, error)) (wire.SNACMessage, error)); ok {
-		return rf(bodyIn, fn)
+	if rf, ok := ret.Get(0).(func(wire.SNAC_0x17_0x02_BUCPLoginRequest, func(state.DisplayScreenName) (state.User, error)) (wire.SNACMessage, error)); ok {
+		return rf(bodyIn, newUserFn)
 	}
-	if rf, ok := ret.Get(0).(func(wire.SNAC_0x17_0x02_BUCPLoginRequest, func(string) (state.User, error)) wire.SNACMessage); ok {
-		r0 = rf(bodyIn, fn)
+	if rf, ok := ret.Get(0).(func(wire.SNAC_0x17_0x02_BUCPLoginRequest, func(state.DisplayScreenName) (state.User, error)) wire.SNACMessage); ok {
+		r0 = rf(bodyIn, newUserFn)
 	} else {
 		r0 = ret.Get(0).(wire.SNACMessage)
 	}
 
-	if rf, ok := ret.Get(1).(func(wire.SNAC_0x17_0x02_BUCPLoginRequest, func(string) (state.User, error)) error); ok {
-		r1 = rf(bodyIn, fn)
+	if rf, ok := ret.Get(1).(func(wire.SNAC_0x17_0x02_BUCPLoginRequest, func(state.DisplayScreenName) (state.User, error)) error); ok {
+		r1 = rf(bodyIn, newUserFn)
 	} else {
 		r1 = ret.Error(1)
 	}
@@ -118,14 +118,14 @@ type mockAuthService_BUCPLogin_Call struct {
 
 // BUCPLogin is a helper method to define mock.On call
 //   - bodyIn wire.SNAC_0x17_0x02_BUCPLoginRequest
-//   - fn func(string)(state.User , error)
-func (_e *mockAuthService_Expecter) BUCPLogin(bodyIn interface{}, fn interface{}) *mockAuthService_BUCPLogin_Call {
-	return &mockAuthService_BUCPLogin_Call{Call: _e.mock.On("BUCPLogin", bodyIn, fn)}
+//   - newUserFn func(state.DisplayScreenName)(state.User , error)
+func (_e *mockAuthService_Expecter) BUCPLogin(bodyIn interface{}, newUserFn interface{}) *mockAuthService_BUCPLogin_Call {
+	return &mockAuthService_BUCPLogin_Call{Call: _e.mock.On("BUCPLogin", bodyIn, newUserFn)}
 }
 
-func (_c *mockAuthService_BUCPLogin_Call) Run(run func(bodyIn wire.SNAC_0x17_0x02_BUCPLoginRequest, fn func(string) (state.User, error))) *mockAuthService_BUCPLogin_Call {
+func (_c *mockAuthService_BUCPLogin_Call) Run(run func(bodyIn wire.SNAC_0x17_0x02_BUCPLoginRequest, newUserFn func(state.DisplayScreenName) (state.User, error))) *mockAuthService_BUCPLogin_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(wire.SNAC_0x17_0x02_BUCPLoginRequest), args[1].(func(string) (state.User, error)))
+		run(args[0].(wire.SNAC_0x17_0x02_BUCPLoginRequest), args[1].(func(state.DisplayScreenName) (state.User, error)))
 	})
 	return _c
 }
@@ -135,13 +135,13 @@ func (_c *mockAuthService_BUCPLogin_Call) Return(_a0 wire.SNACMessage, _a1 error
 	return _c
 }
 
-func (_c *mockAuthService_BUCPLogin_Call) RunAndReturn(run func(wire.SNAC_0x17_0x02_BUCPLoginRequest, func(string) (state.User, error)) (wire.SNACMessage, error)) *mockAuthService_BUCPLogin_Call {
+func (_c *mockAuthService_BUCPLogin_Call) RunAndReturn(run func(wire.SNAC_0x17_0x02_BUCPLoginRequest, func(state.DisplayScreenName) (state.User, error)) (wire.SNACMessage, error)) *mockAuthService_BUCPLogin_Call {
 	_c.Call.Return(run)
 	return _c
 }
 
 // FLAPLogin provides a mock function with given fields: frame, newUserFn
-func (_m *mockAuthService) FLAPLogin(frame wire.FLAPSignonFrame, newUserFn func(string) (state.User, error)) (wire.TLVRestBlock, error) {
+func (_m *mockAuthService) FLAPLogin(frame wire.FLAPSignonFrame, newUserFn func(state.DisplayScreenName) (state.User, error)) (wire.TLVRestBlock, error) {
 	ret := _m.Called(frame, newUserFn)
 
 	if len(ret) == 0 {
@@ -150,16 +150,16 @@ func (_m *mockAuthService) FLAPLogin(frame wire.FLAPSignonFrame, newUserFn func(
 
 	var r0 wire.TLVRestBlock
 	var r1 error
-	if rf, ok := ret.Get(0).(func(wire.FLAPSignonFrame, func(string) (state.User, error)) (wire.TLVRestBlock, error)); ok {
+	if rf, ok := ret.Get(0).(func(wire.FLAPSignonFrame, func(state.DisplayScreenName) (state.User, error)) (wire.TLVRestBlock, error)); ok {
 		return rf(frame, newUserFn)
 	}
-	if rf, ok := ret.Get(0).(func(wire.FLAPSignonFrame, func(string) (state.User, error)) wire.TLVRestBlock); ok {
+	if rf, ok := ret.Get(0).(func(wire.FLAPSignonFrame, func(state.DisplayScreenName) (state.User, error)) wire.TLVRestBlock); ok {
 		r0 = rf(frame, newUserFn)
 	} else {
 		r0 = ret.Get(0).(wire.TLVRestBlock)
 	}
 
-	if rf, ok := ret.Get(1).(func(wire.FLAPSignonFrame, func(string) (state.User, error)) error); ok {
+	if rf, ok := ret.Get(1).(func(wire.FLAPSignonFrame, func(state.DisplayScreenName) (state.User, error)) error); ok {
 		r1 = rf(frame, newUserFn)
 	} else {
 		r1 = ret.Error(1)
@@ -175,14 +175,14 @@ type mockAuthService_FLAPLogin_Call struct {
 
 // FLAPLogin is a helper method to define mock.On call
 //   - frame wire.FLAPSignonFrame
-//   - newUserFn func(string)(state.User , error)
+//   - newUserFn func(state.DisplayScreenName)(state.User , error)
 func (_e *mockAuthService_Expecter) FLAPLogin(frame interface{}, newUserFn interface{}) *mockAuthService_FLAPLogin_Call {
 	return &mockAuthService_FLAPLogin_Call{Call: _e.mock.On("FLAPLogin", frame, newUserFn)}
 }
 
-func (_c *mockAuthService_FLAPLogin_Call) Run(run func(frame wire.FLAPSignonFrame, newUserFn func(string) (state.User, error))) *mockAuthService_FLAPLogin_Call {
+func (_c *mockAuthService_FLAPLogin_Call) Run(run func(frame wire.FLAPSignonFrame, newUserFn func(state.DisplayScreenName) (state.User, error))) *mockAuthService_FLAPLogin_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(wire.FLAPSignonFrame), args[1].(func(string) (state.User, error)))
+		run(args[0].(wire.FLAPSignonFrame), args[1].(func(state.DisplayScreenName) (state.User, error)))
 	})
 	return _c
 }
@@ -192,14 +192,14 @@ func (_c *mockAuthService_FLAPLogin_Call) Return(_a0 wire.TLVRestBlock, _a1 erro
 	return _c
 }
 
-func (_c *mockAuthService_FLAPLogin_Call) RunAndReturn(run func(wire.FLAPSignonFrame, func(string) (state.User, error)) (wire.TLVRestBlock, error)) *mockAuthService_FLAPLogin_Call {
+func (_c *mockAuthService_FLAPLogin_Call) RunAndReturn(run func(wire.FLAPSignonFrame, func(state.DisplayScreenName) (state.User, error)) (wire.TLVRestBlock, error)) *mockAuthService_FLAPLogin_Call {
 	_c.Call.Return(run)
 	return _c
 }
 
-// RegisterBOSSession provides a mock function with given fields: sessionID
-func (_m *mockAuthService) RegisterBOSSession(sessionID string) (*state.Session, error) {
-	ret := _m.Called(sessionID)
+// RegisterBOSSession provides a mock function with given fields: screenName
+func (_m *mockAuthService) RegisterBOSSession(screenName state.IdentScreenName) (*state.Session, error) {
+	ret := _m.Called(screenName)
 
 	if len(ret) == 0 {
 		panic("no return value specified for RegisterBOSSession")
@@ -207,19 +207,19 @@ func (_m *mockAuthService) RegisterBOSSession(sessionID string) (*state.Session,
 
 	var r0 *state.Session
 	var r1 error
-	if rf, ok := ret.Get(0).(func(string) (*state.Session, error)); ok {
-		return rf(sessionID)
+	if rf, ok := ret.Get(0).(func(state.IdentScreenName) (*state.Session, error)); ok {
+		return rf(screenName)
 	}
-	if rf, ok := ret.Get(0).(func(string) *state.Session); ok {
-		r0 = rf(sessionID)
+	if rf, ok := ret.Get(0).(func(state.IdentScreenName) *state.Session); ok {
+		r0 = rf(screenName)
 	} else {
 		if ret.Get(0) != nil {
 			r0 = ret.Get(0).(*state.Session)
 		}
 	}
 
-	if rf, ok := ret.Get(1).(func(string) error); ok {
-		r1 = rf(sessionID)
+	if rf, ok := ret.Get(1).(func(state.IdentScreenName) error); ok {
+		r1 = rf(screenName)
 	} else {
 		r1 = ret.Error(1)
 	}
@@ -233,14 +233,14 @@ type mockAuthService_RegisterBOSSession_Call struct {
 }
 
 // RegisterBOSSession is a helper method to define mock.On call
-//   - sessionID string
-func (_e *mockAuthService_Expecter) RegisterBOSSession(sessionID interface{}) *mockAuthService_RegisterBOSSession_Call {
-	return &mockAuthService_RegisterBOSSession_Call{Call: _e.mock.On("RegisterBOSSession", sessionID)}
+//   - screenName state.IdentScreenName
+func (_e *mockAuthService_Expecter) RegisterBOSSession(screenName interface{}) *mockAuthService_RegisterBOSSession_Call {
+	return &mockAuthService_RegisterBOSSession_Call{Call: _e.mock.On("RegisterBOSSession", screenName)}
 }
 
-func (_c *mockAuthService_RegisterBOSSession_Call) Run(run func(sessionID string)) *mockAuthService_RegisterBOSSession_Call {
+func (_c *mockAuthService_RegisterBOSSession_Call) Run(run func(screenName state.IdentScreenName)) *mockAuthService_RegisterBOSSession_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(string))
+		run(args[0].(state.IdentScreenName))
 	})
 	return _c
 }
@@ -250,7 +250,7 @@ func (_c *mockAuthService_RegisterBOSSession_Call) Return(_a0 *state.Session, _a
 	return _c
 }
 
-func (_c *mockAuthService_RegisterBOSSession_Call) RunAndReturn(run func(string) (*state.Session, error)) *mockAuthService_RegisterBOSSession_Call {
+func (_c *mockAuthService_RegisterBOSSession_Call) RunAndReturn(run func(state.IdentScreenName) (*state.Session, error)) *mockAuthService_RegisterBOSSession_Call {
 	_c.Call.Return(run)
 	return _c
 }

+ 10 - 10
state/buddy_list_store.go

@@ -4,34 +4,34 @@ import "sync"
 
 // AdjListBuddyListStore implements a buddy list using an adjacency list.
 type AdjListBuddyListStore struct {
-	buddies map[string]map[string]bool
+	buddies map[IdentScreenName]map[IdentScreenName]bool
 	mu      sync.RWMutex // ensures thread-safe access
 }
 
 // NewAdjListBuddyListStore initializes a new instance of AdjListBuddyListStore.
 func NewAdjListBuddyListStore() *AdjListBuddyListStore {
 	return &AdjListBuddyListStore{
-		buddies: make(map[string]map[string]bool),
+		buddies: make(map[IdentScreenName]map[IdentScreenName]bool),
 	}
 }
 
 // AddBuddy adds buddyScreenName to userScreenName's buddy list.
-func (store *AdjListBuddyListStore) AddBuddy(userScreenName, buddyScreenName string) {
+func (store *AdjListBuddyListStore) AddBuddy(userScreenName, buddyScreenName IdentScreenName) {
 	store.mu.Lock()
 	defer store.mu.Unlock()
 
 	if _, exists := store.buddies[userScreenName]; !exists {
-		store.buddies[userScreenName] = make(map[string]bool)
+		store.buddies[userScreenName] = make(map[IdentScreenName]bool)
 	}
 	store.buddies[userScreenName][buddyScreenName] = true
 }
 
 // WhoAddedUser returns a list of screen names who have userScreenName in their buddy lists.
-func (store *AdjListBuddyListStore) WhoAddedUser(userScreenName string) []string {
+func (store *AdjListBuddyListStore) WhoAddedUser(userScreenName IdentScreenName) []IdentScreenName {
 	store.mu.RLock()
 	defer store.mu.RUnlock()
 
-	var users []string
+	var users []IdentScreenName
 	for user, buddies := range store.buddies {
 		if buddies[userScreenName] {
 			users = append(users, user)
@@ -41,12 +41,12 @@ func (store *AdjListBuddyListStore) WhoAddedUser(userScreenName string) []string
 }
 
 // Buddies returns a list of all buddies associated with the specified userScreenName.
-func (store *AdjListBuddyListStore) Buddies(userScreenName string) []string {
+func (store *AdjListBuddyListStore) Buddies(userScreenName IdentScreenName) []IdentScreenName {
 	store.mu.RLock()
 	defer store.mu.RUnlock()
 
 	if buddies, exists := store.buddies[userScreenName]; exists {
-		users := make([]string, 0, len(buddies))
+		users := make([]IdentScreenName, 0, len(buddies))
 		for buddy := range buddies {
 			users = append(users, buddy)
 		}
@@ -56,7 +56,7 @@ func (store *AdjListBuddyListStore) Buddies(userScreenName string) []string {
 }
 
 // DeleteBuddy removes buddyScreenName from userScreenName's buddy list.
-func (store *AdjListBuddyListStore) DeleteBuddy(userScreenName, buddyScreenName string) {
+func (store *AdjListBuddyListStore) DeleteBuddy(userScreenName, buddyScreenName IdentScreenName) {
 	store.mu.Lock()
 	defer store.mu.Unlock()
 
@@ -69,7 +69,7 @@ func (store *AdjListBuddyListStore) DeleteBuddy(userScreenName, buddyScreenName
 }
 
 // DeleteUser removes userScreenName's buddy list.
-func (store *AdjListBuddyListStore) DeleteUser(userScreenName string) {
+func (store *AdjListBuddyListStore) DeleteUser(userScreenName IdentScreenName) {
 	store.mu.Lock()
 	defer store.mu.Unlock()
 

+ 49 - 38
state/buddy_list_store_test.go

@@ -8,68 +8,79 @@ import (
 
 func TestAdjListBuddyListStore_AddBuddy_Buddies(t *testing.T) {
 	store := NewAdjListBuddyListStore()
-	store.AddBuddy("alice", "bob")
-	store.AddBuddy("bob", "alice")
-	store.AddBuddy("alice", "charlie")
-	store.AddBuddy("alice", "dave")
-	store.AddBuddy("dave", "bob")
+	store.AddBuddy(NewIdentScreenName("alice"), NewIdentScreenName("bob"))
+	store.AddBuddy(NewIdentScreenName("bob"), NewIdentScreenName("alice"))
+	store.AddBuddy(NewIdentScreenName("alice"), NewIdentScreenName("charlie"))
+	store.AddBuddy(NewIdentScreenName("alice"), NewIdentScreenName("dave"))
+	store.AddBuddy(NewIdentScreenName("dave"), NewIdentScreenName("bob"))
 
-	buddies := store.Buddies("alice")
-	assert.ElementsMatch(t, []string{"bob", "charlie", "dave"}, buddies)
+	buddies := store.Buddies(NewIdentScreenName("alice"))
+	assert.ElementsMatch(t, []IdentScreenName{
+		NewIdentScreenName("bob"),
+		NewIdentScreenName("charlie"),
+		NewIdentScreenName("dave"),
+	}, buddies)
 }
 
 func TestAdjListBuddyListStore_DeleteBuddy_OneBuddy(t *testing.T) {
 	store := NewAdjListBuddyListStore()
-	store.AddBuddy("alice", "bob")
-	store.AddBuddy("bob", "alice")
-	store.AddBuddy("alice", "charlie")
-	store.AddBuddy("alice", "dave")
-	store.AddBuddy("dave", "bob")
+	store.AddBuddy(NewIdentScreenName("alice"), NewIdentScreenName("bob"))
+	store.AddBuddy(NewIdentScreenName("bob"), NewIdentScreenName("alice"))
+	store.AddBuddy(NewIdentScreenName("alice"), NewIdentScreenName("charlie"))
+	store.AddBuddy(NewIdentScreenName("alice"), NewIdentScreenName("dave"))
+	store.AddBuddy(NewIdentScreenName("dave"), NewIdentScreenName("bob"))
 
-	store.DeleteBuddy("alice", "bob")
+	store.DeleteBuddy(NewIdentScreenName("alice"), NewIdentScreenName("bob"))
 
-	buddies := store.Buddies("alice")
-	assert.ElementsMatch(t, []string{"charlie", "dave"}, buddies)
+	buddies := store.Buddies(NewIdentScreenName("alice"))
+	assert.ElementsMatch(t, []IdentScreenName{
+		NewIdentScreenName("charlie"),
+		NewIdentScreenName("dave"),
+	}, buddies)
 }
 
 func TestAdjListBuddyListStore_DeleteBuddy_AllBuddies(t *testing.T) {
 	store := NewAdjListBuddyListStore()
-	store.AddBuddy("alice", "bob")
-	store.AddBuddy("bob", "alice")
-	store.AddBuddy("alice", "charlie")
-	store.AddBuddy("alice", "dave")
-	store.AddBuddy("dave", "bob")
+	store.AddBuddy(NewIdentScreenName("alice"), NewIdentScreenName("bob"))
+	store.AddBuddy(NewIdentScreenName("bob"), NewIdentScreenName("alice"))
+	store.AddBuddy(NewIdentScreenName("alice"), NewIdentScreenName("charlie"))
+	store.AddBuddy(NewIdentScreenName("alice"), NewIdentScreenName("dave"))
+	store.AddBuddy(NewIdentScreenName("dave"), NewIdentScreenName("bob"))
 
-	store.DeleteBuddy("alice", "bob")
-	store.DeleteBuddy("alice", "charlie")
-	store.DeleteBuddy("alice", "dave")
+	store.DeleteBuddy(NewIdentScreenName("alice"), NewIdentScreenName("bob"))
+	store.DeleteBuddy(NewIdentScreenName("alice"), NewIdentScreenName("charlie"))
+	store.DeleteBuddy(NewIdentScreenName("alice"), NewIdentScreenName("dave"))
 
-	buddies := store.Buddies("alice")
+	buddies := store.Buddies(NewIdentScreenName("alice"))
 	assert.Nil(t, buddies)
 }
 
 func TestAdjListBuddyListStore_DeleteUser(t *testing.T) {
 	store := NewAdjListBuddyListStore()
-	store.AddBuddy("alice", "bob")
-	store.AddBuddy("bob", "alice")
-	store.AddBuddy("alice", "charlie")
-	store.AddBuddy("alice", "dave")
-	store.AddBuddy("dave", "bob")
+	store.AddBuddy(NewIdentScreenName("alice"), NewIdentScreenName("bob"))
+	store.AddBuddy(NewIdentScreenName("bob"), NewIdentScreenName("alice"))
+	store.AddBuddy(NewIdentScreenName("alice"), NewIdentScreenName("charlie"))
+	store.AddBuddy(NewIdentScreenName("alice"), NewIdentScreenName("dave"))
+	store.AddBuddy(NewIdentScreenName("dave"), NewIdentScreenName("bob"))
 
-	store.DeleteUser("alice")
+	store.DeleteUser(NewIdentScreenName("alice"))
 
-	buddies := store.Buddies("alice")
+	buddies := store.Buddies(NewIdentScreenName("alice"))
 	assert.Nil(t, buddies)
 }
 
 func TestAdjListBuddyListStore_WhoAddedUser(t *testing.T) {
 	store := NewAdjListBuddyListStore()
-	store.AddBuddy("alice", "bob")
-	store.AddBuddy("alice", "charlie")
-	store.AddBuddy("charlie", "bob")
-	store.AddBuddy("dave", "bob")
-	store.AddBuddy("dave", "alive")
+	store.AddBuddy(NewIdentScreenName("alice"), NewIdentScreenName("bob"))
+	store.AddBuddy(NewIdentScreenName("alice"), NewIdentScreenName("charlie"))
+	store.AddBuddy(NewIdentScreenName("charlie"), NewIdentScreenName("bob"))
+	store.AddBuddy(NewIdentScreenName("dave"), NewIdentScreenName("bob"))
+	store.AddBuddy(NewIdentScreenName("dave"), NewIdentScreenName("alive"))
 
-	whoAddedBob := store.WhoAddedUser("bob")
-	assert.ElementsMatch(t, []string{"alice", "charlie", "dave"}, whoAddedBob)
+	whoAddedBob := store.WhoAddedUser(NewIdentScreenName("bob"))
+	assert.ElementsMatch(t, []IdentScreenName{
+		NewIdentScreenName("alice"),
+		NewIdentScreenName("charlie"),
+		NewIdentScreenName("dave"),
+	}, whoAddedBob)
 }

+ 16 - 0
state/migrations/0003_formatted_screen_name.down.sql

@@ -0,0 +1,16 @@
+CREATE TABLE user
+(
+    screenName    VARCHAR(16) PRIMARY KEY,
+    authKey       TEXT,
+    strongMD5Pass TEXT,
+    weakMD5Pass   TEXT
+);
+
+INSERT INTO user
+SELECT displayScreenName,
+       authKey,
+       strongMD5Pass,
+       weakMD5Pass
+FROM users;
+
+DROP TABLE users;

+ 27 - 0
state/migrations/0003_formatted_screen_name.up.sql

@@ -0,0 +1,27 @@
+-- call the new table "users" since it's not a reserved word that we'll need to
+-- deal with for postgres in the future.
+CREATE TABLE users
+(
+    identScreenName   VARCHAR(16) PRIMARY KEY,
+    displayScreenName TEXT,
+    authKey           TEXT,
+    strongMD5Pass     TEXT,
+    weakMD5Pass       TEXT
+);
+
+INSERT INTO users
+SELECT LOWER(REPLACE(screenName, ' ', '')),
+       screenName,
+       authKey,
+       strongMD5Pass,
+       weakMD5Pass
+FROM user;
+
+DROP TABLE user;
+
+UPDATE feedbag
+SET name = LOWER(REPLACE(name, ' ', ''))
+WHERE classID IN (0, 2, 3);
+
+UPDATE feedbag
+SET screenName = LOWER(REPLACE(screenName, ' ', ''));

+ 37 - 22
state/session.go

@@ -23,21 +23,22 @@ const (
 // Session represents a user's current session. Unless stated otherwise, all
 // methods may be safely accessed by multiple goroutines.
 type Session struct {
-	awayMessage    string
-	chatRoomCookie string
-	closed         bool
-	idle           bool
-	idleTime       time.Time
-	invisible      bool
-	msgCh          chan wire.SNACMessage
-	mutex          sync.RWMutex
-	nowFn          func() time.Time
-	signonComplete bool
-	screenName     string
-	signonTime     time.Time
-	stopCh         chan struct{}
-	warning        uint16
-	caps           [][16]byte
+	awayMessage       string
+	caps              [][16]byte
+	chatRoomCookie    string
+	closed            bool
+	displayScreenName DisplayScreenName
+	identScreenName   IdentScreenName
+	idle              bool
+	idleTime          time.Time
+	invisible         bool
+	msgCh             chan wire.SNACMessage
+	mutex             sync.RWMutex
+	nowFn             func() time.Time
+	signonComplete    bool
+	signonTime        time.Time
+	stopCh            chan struct{}
+	warning           uint16
 }
 
 // NewSession returns a new instance of Session. By default, the user may have
@@ -74,18 +75,32 @@ func (s *Session) Invisible() bool {
 	return s.invisible
 }
 
-// SetScreenName sets the user's screen name.
-func (s *Session) SetScreenName(screenName string) {
+// SetIdentScreenName sets the user's screen name.
+func (s *Session) SetIdentScreenName(screenName IdentScreenName) {
 	s.mutex.Lock()
 	defer s.mutex.Unlock()
-	s.screenName = screenName
+	s.identScreenName = screenName
 }
 
-// ScreenName returns the user's screen name.
-func (s *Session) ScreenName() string {
+// IdentScreenName returns the user's screen name.
+func (s *Session) IdentScreenName() IdentScreenName {
 	s.mutex.RLock()
 	defer s.mutex.RUnlock()
-	return s.screenName
+	return s.identScreenName
+}
+
+// SetDisplayScreenName sets the user's screen name.
+func (s *Session) SetDisplayScreenName(displayScreenName DisplayScreenName) {
+	s.mutex.Lock()
+	defer s.mutex.Unlock()
+	s.displayScreenName = displayScreenName
+}
+
+// DisplayScreenName returns the user's screen name.
+func (s *Session) DisplayScreenName() DisplayScreenName {
+	s.mutex.RLock()
+	defer s.mutex.RUnlock()
+	return s.displayScreenName
 }
 
 // SetSignonTime sets the user's sign-ontime.
@@ -159,7 +174,7 @@ func (s *Session) TLVUserInfo() wire.TLVUserInfo {
 	s.mutex.RLock()
 	defer s.mutex.RUnlock()
 	return wire.TLVUserInfo{
-		ScreenName:   s.screenName,
+		ScreenName:   string(s.displayScreenName),
 		WarningLevel: s.warning,
 		TLVBlock: wire.TLVBlock{
 			TLVList: s.userInfo(),

+ 20 - 18
state/session_manager.go

@@ -12,7 +12,7 @@ import (
 // synchronized message relay between sessions in the session pool. An
 // InMemorySessionManager is safe for concurrent use by multiple goroutines.
 type InMemorySessionManager struct {
-	store    map[string]*Session
+	store    map[IdentScreenName]*Session
 	mapMutex sync.RWMutex
 	logger   *slog.Logger
 }
@@ -21,7 +21,7 @@ type InMemorySessionManager struct {
 func NewInMemorySessionManager(logger *slog.Logger) *InMemorySessionManager {
 	return &InMemorySessionManager{
 		logger: logger,
-		store:  make(map[string]*Session),
+		store:  make(map[IdentScreenName]*Session),
 	}
 }
 
@@ -48,7 +48,7 @@ func (s *InMemorySessionManager) RelayToAllExcept(ctx context.Context, except *S
 }
 
 // RelayToScreenName relays a message to a session with a matching screen name.
-func (s *InMemorySessionManager) RelayToScreenName(ctx context.Context, screenName string, msg wire.SNACMessage) {
+func (s *InMemorySessionManager) RelayToScreenName(ctx context.Context, screenName IdentScreenName, msg wire.SNACMessage) {
 	sess := s.RetrieveByScreenName(screenName)
 	if sess == nil {
 		s.logger.WarnContext(ctx, "can't send notification because user is not online", "recipient", screenName, "message", msg)
@@ -58,7 +58,7 @@ func (s *InMemorySessionManager) RelayToScreenName(ctx context.Context, screenNa
 }
 
 // RelayToScreenNames relays a message to sessions with matching screenNames.
-func (s *InMemorySessionManager) RelayToScreenNames(ctx context.Context, screenNames []string, msg wire.SNACMessage) {
+func (s *InMemorySessionManager) RelayToScreenNames(ctx context.Context, screenNames []IdentScreenName, msg wire.SNACMessage) {
 	for _, sess := range s.retrieveByScreenNames(screenNames) {
 		s.maybeRelayMessage(ctx, msg, sess)
 	}
@@ -67,9 +67,9 @@ func (s *InMemorySessionManager) RelayToScreenNames(ctx context.Context, screenN
 func (s *InMemorySessionManager) maybeRelayMessage(ctx context.Context, msg wire.SNACMessage, sess *Session) {
 	switch sess.RelayMessage(msg) {
 	case SessSendClosed:
-		s.logger.WarnContext(ctx, "can't send notification because the user's session is closed", "recipient", sess.ScreenName(), "message", msg)
+		s.logger.WarnContext(ctx, "can't send notification because the user's session is closed", "recipient", sess.IdentScreenName(), "message", msg)
 	case SessQueueFull:
-		s.logger.WarnContext(ctx, "can't send notification because queue is full", "recipient", sess.ScreenName(), "message", msg)
+		s.logger.WarnContext(ctx, "can't send notification because queue is full", "recipient", sess.IdentScreenName(), "message", msg)
 		sess.Close()
 	}
 }
@@ -77,26 +77,28 @@ func (s *InMemorySessionManager) maybeRelayMessage(ctx context.Context, msg wire
 // AddSession adds a new session to the pool. It replaces an existing session
 // with a matching screen name, ensuring that each screen name is unique in the
 // pool.
-func (s *InMemorySessionManager) AddSession(screenName string) *Session {
+func (s *InMemorySessionManager) AddSession(displayScreenName DisplayScreenName) *Session {
 	s.mapMutex.Lock()
 	defer s.mapMutex.Unlock()
 
+	identScreenName := NewIdentScreenName(string(displayScreenName))
 	// Only allow one session at a time per screen name. A session may already
 	// exist because:
 	// 1) the user is signing on using an already logged-on screen name.
 	// 2) the session might be orphaned due to an undetected client
 	// disconnection.
 	for _, sess := range s.store {
-		if screenName == sess.ScreenName() {
+		if identScreenName == sess.IdentScreenName() {
 			sess.Close()
-			delete(s.store, screenName)
+			delete(s.store, identScreenName)
 			break
 		}
 	}
 
 	sess := NewSession()
-	sess.SetScreenName(screenName)
-	s.store[sess.ScreenName()] = sess
+	sess.SetIdentScreenName(identScreenName)
+	sess.SetDisplayScreenName(displayScreenName)
+	s.store[identScreenName] = sess
 	return sess
 }
 
@@ -104,37 +106,37 @@ func (s *InMemorySessionManager) AddSession(screenName string) *Session {
 func (s *InMemorySessionManager) RemoveSession(sess *Session) {
 	s.mapMutex.Lock()
 	defer s.mapMutex.Unlock()
-	delete(s.store, sess.ScreenName())
+	delete(s.store, sess.IdentScreenName())
 }
 
 // RetrieveSession finds a session with a matching sessionID. Returns nil if
 // session is not found.
-func (s *InMemorySessionManager) RetrieveSession(sessionID string) *Session {
+func (s *InMemorySessionManager) RetrieveSession(screenName IdentScreenName) *Session {
 	s.mapMutex.RLock()
 	defer s.mapMutex.RUnlock()
-	return s.store[sessionID]
+	return s.store[screenName]
 }
 
 // RetrieveByScreenName find a session with a matching screen name. Returns nil
 // if session is not found.
-func (s *InMemorySessionManager) RetrieveByScreenName(screenName string) *Session {
+func (s *InMemorySessionManager) RetrieveByScreenName(screenName IdentScreenName) *Session {
 	s.mapMutex.RLock()
 	defer s.mapMutex.RUnlock()
 	for _, sess := range s.store {
-		if screenName == sess.ScreenName() {
+		if screenName == sess.IdentScreenName() {
 			return sess
 		}
 	}
 	return nil
 }
 
-func (s *InMemorySessionManager) retrieveByScreenNames(screenNames []string) []*Session {
+func (s *InMemorySessionManager) retrieveByScreenNames(screenNames []IdentScreenName) []*Session {
 	s.mapMutex.RLock()
 	defer s.mapMutex.RUnlock()
 	var ret []*Session
 	for _, sn := range screenNames {
 		for _, sess := range s.store {
-			if sn == sess.ScreenName() {
+			if sn == sess.IdentScreenName() {
 				ret = append(ret, sess)
 			}
 		}

+ 43 - 52
state/session_manager_test.go

@@ -14,11 +14,11 @@ func TestInMemorySessionManager_AddSession(t *testing.T) {
 	sm := NewInMemorySessionManager(slog.Default())
 
 	want1 := sm.AddSession("user-screen-name")
-	have1 := sm.RetrieveByScreenName("user-screen-name")
+	have1 := sm.RetrieveByScreenName(NewIdentScreenName("user-screen-name"))
 	assert.Same(t, want1, have1)
 
 	want2 := sm.AddSession("user-screen-name")
-	have2 := sm.RetrieveByScreenName("user-screen-name")
+	have2 := sm.RetrieveByScreenName(NewIdentScreenName("user-screen-name"))
 	assert.Same(t, want2, have2)
 
 	// ensure that the second session created with the same screen name as the
@@ -29,38 +29,34 @@ func TestInMemorySessionManager_AddSession(t *testing.T) {
 func TestInMemorySessionManager_Remove(t *testing.T) {
 	tests := []struct {
 		name   string
-		given  []*Session
-		remove string
-		want   []string
+		given  []DisplayScreenName
+		remove IdentScreenName
+		want   []IdentScreenName
 	}{
 		{
 			name: "remove user that exists",
-			given: []*Session{
-				{
-					screenName: "user-screen-name-1",
-				},
-				{
-					screenName: "user-screen-name-2",
-				},
-			},
-			remove: "user-screen-name-1",
-			want: []string{
+			given: []DisplayScreenName{
+				"user-screen-name-1",
 				"user-screen-name-2",
 			},
+			remove: NewIdentScreenName("user-screen-name-1"),
+			want: []IdentScreenName{
+				NewIdentScreenName("user-screen-name-2"),
+			},
 		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			sm := NewInMemorySessionManager(slog.Default())
 
-			for _, sess := range tt.given {
-				sm.AddSession(sess.screenName)
+			for _, screenName := range tt.given {
+				sm.AddSession(screenName)
 			}
 
 			sm.RemoveSession(sm.RetrieveByScreenName(tt.remove))
 
 			for i, sess := range sm.AllSessions() {
-				assert.Equal(t, tt.want[i], sess.screenName)
+				assert.Equal(t, tt.want[i], sess.identScreenName)
 			}
 		})
 	}
@@ -68,23 +64,20 @@ func TestInMemorySessionManager_Remove(t *testing.T) {
 
 func TestInMemorySessionManager_Empty(t *testing.T) {
 	tests := []struct {
-		name   string
-		given  []*Session
-		remove string
-		want   bool
+		name  string
+		given []DisplayScreenName
+		want  bool
 	}{
 		{
 			name: "session manager is not empty",
-			given: []*Session{
-				{
-					screenName: "user-screen-name-1",
-				},
+			given: []DisplayScreenName{
+				"user-screen-name-1",
 			},
 			want: false,
 		},
 		{
 			name:  "session manager is empty",
-			given: []*Session{},
+			given: []DisplayScreenName{},
 			want:  true,
 		},
 	}
@@ -92,8 +85,8 @@ func TestInMemorySessionManager_Empty(t *testing.T) {
 		t.Run(tt.name, func(t *testing.T) {
 			sm := NewInMemorySessionManager(slog.Default())
 
-			for _, sess := range tt.given {
-				sm.AddSession(sess.screenName)
+			for _, screenName := range tt.given {
+				sm.AddSession(screenName)
 			}
 
 			have := sm.Empty()
@@ -105,44 +98,39 @@ func TestInMemorySessionManager_Empty(t *testing.T) {
 func TestInMemorySessionManager_Retrieve(t *testing.T) {
 	tests := []struct {
 		name             string
-		given            []*Session
-		lookupScreenName string
-		remove           string
-		wantScreenName   string
+		given            []DisplayScreenName
+		lookupScreenName IdentScreenName
+		wantScreenName   IdentScreenName
 	}{
 		{
 			name: "lookup finds match",
-			given: []*Session{
-				{
-					screenName: "user-screen-name-1",
-				},
-				{
-					screenName: "user-screen-name-2",
-				},
+			given: []DisplayScreenName{
+				"user-screen-name-1",
+				"user-screen-name-2",
 			},
-			lookupScreenName: "user-screen-name-2",
-			wantScreenName:   "user-screen-name-2",
+			lookupScreenName: NewIdentScreenName("user-screen-name-2"),
+			wantScreenName:   NewIdentScreenName("user-screen-name-2"),
 		},
 		{
 			name:             "lookup does not find match",
-			given:            []*Session{},
-			lookupScreenName: "user-screen-name-3",
-			wantScreenName:   "",
+			given:            []DisplayScreenName{},
+			lookupScreenName: NewIdentScreenName("user-screen-name-3"),
+			wantScreenName:   NewIdentScreenName(""),
 		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			sm := NewInMemorySessionManager(slog.Default())
 
-			for _, sess := range tt.given {
-				sm.AddSession(sess.screenName)
+			for _, screenName := range tt.given {
+				sm.AddSession(screenName)
 			}
 
 			have := sm.RetrieveSession(tt.lookupScreenName)
 			if have == nil {
 				assert.Empty(t, tt.wantScreenName)
 			} else {
-				assert.Equal(t, tt.wantScreenName, have.ScreenName())
+				assert.Equal(t, tt.wantScreenName, have.IdentScreenName())
 			}
 		})
 	}
@@ -157,7 +145,10 @@ func TestInMemorySessionManager_RelayToScreenNames(t *testing.T) {
 
 	want := wire.SNACMessage{Frame: wire.SNACFrame{FoodGroup: wire.ICBM}}
 
-	recips := []string{"user-screen-name-1", "user-screen-name-2"}
+	recips := []IdentScreenName{
+		NewIdentScreenName("user-screen-name-1"),
+		NewIdentScreenName("user-screen-name-2"),
+	}
 	sm.RelayToScreenNames(context.Background(), recips, want)
 
 	select {
@@ -229,7 +220,7 @@ func TestInMemorySessionManager_RelayToScreenName_SessionExists(t *testing.T) {
 
 	want := wire.SNACMessage{Frame: wire.SNACFrame{FoodGroup: wire.ICBM}}
 
-	recip := "user-screen-name-1"
+	recip := NewIdentScreenName("user-screen-name-1")
 	sm.RelayToScreenName(context.Background(), recip, want)
 
 	select {
@@ -251,7 +242,7 @@ func TestInMemorySessionManager_RelayToScreenName_SessionNotExist(t *testing.T)
 
 	want := wire.SNACMessage{Frame: wire.SNACFrame{FoodGroup: wire.ICBM}}
 
-	recip := "user-screen-name-2"
+	recip := NewIdentScreenName("user-screen-name-2")
 	sm.RelayToScreenName(context.Background(), recip, want)
 
 	select {
@@ -275,7 +266,7 @@ func TestInMemorySessionManager_RelayToScreenName_SkipFullSession(t *testing.T)
 		wantCount++
 	}
 
-	recip := "user-screen-name-1"
+	recip := NewIdentScreenName("user-screen-name-1")
 	sm.RelayToScreenName(context.Background(), recip, msg)
 
 	haveCount := 0

+ 6 - 5
state/session_test.go

@@ -38,10 +38,10 @@ func TestSession_SetAndGetInvisible(t *testing.T) {
 
 func TestSession_SetAndGetScreenName(t *testing.T) {
 	s := NewSession()
-	assert.Empty(t, s.ScreenName())
-	sn := "user-screen-name"
-	s.SetScreenName(sn)
-	assert.Equal(t, sn, s.ScreenName())
+	assert.Empty(t, s.IdentScreenName())
+	sn := NewIdentScreenName("user-screen-name")
+	s.SetIdentScreenName(sn)
+	assert.Equal(t, sn, s.IdentScreenName())
 }
 
 func TestSession_SetAndGetChatRoomCookie(t *testing.T) {
@@ -63,7 +63,8 @@ func TestSession_TLVUserInfo(t *testing.T) {
 			givenSessionFn: func() *Session {
 				s := NewSession()
 				s.SetSignonTime(time.Unix(1, 0))
-				s.SetScreenName("xXAIMUSERXx")
+				s.SetIdentScreenName(NewIdentScreenName("xXAIMUSERXx"))
+				s.SetDisplayScreenName("xXAIMUSERXx")
 				s.IncrementWarning(10)
 				return s
 			},

+ 122 - 0
state/user.go

@@ -0,0 +1,122 @@
+package state
+
+import (
+	"bytes"
+	"errors"
+	"strings"
+
+	"github.com/google/uuid"
+
+	"github.com/mk6i/retro-aim-server/wire"
+)
+
+// BlockedState represents the blocked status between two users
+type BlockedState int
+
+var (
+	// ErrDupUser indicates that a user already exists.
+	ErrDupUser = errors.New("user already exists")
+	// ErrNoUser indicates that a user does not exist.
+	ErrNoUser = errors.New("user does not exist")
+)
+
+const (
+	// BlockedNo indicates that neither user blocks the other.
+	BlockedNo BlockedState = iota
+	// BlockedA indicates that user A blocks user B.
+	BlockedA
+	// BlockedB indicates that user B blocks user A.
+	BlockedB
+)
+
+// IdentScreenName struct stores the normalized version of a user's screen name.
+// This format is used for uniformity in storage and comparison by removing spaces
+// and converting all characters to lowercase.
+type IdentScreenName struct {
+	// screenName contains the identifier screen name value. Do not assign this
+	// value directly. Rather, set it through NewIdentScreenName. This ensures
+	// that when an instance of IdentScreenName is present, it's guaranteed to
+	// have a normalized value.
+	screenName string
+}
+
+// String returns the string representation of the IdentScreenName.
+func (i IdentScreenName) String() string {
+	return i.screenName
+}
+
+// NewIdentScreenName creates a new IdentScreenName.
+func NewIdentScreenName(screenName string) IdentScreenName {
+	str := strings.ReplaceAll(screenName, " ", "")
+	str = strings.ToLower(str)
+	return IdentScreenName{screenName: str}
+}
+
+// DisplayScreenName type represents the screen name in the user-defined format.
+// This includes the original casing and spacing as defined by the user.
+type DisplayScreenName string
+
+// IdentScreenName converts the DisplayScreenName to an IdentScreenName by applying
+// the normalization process defined in NewIdentScreenName.
+func (s DisplayScreenName) IdentScreenName() IdentScreenName {
+	return NewIdentScreenName(string(s))
+}
+
+// String returns the original display string of the screen name, preserving the user-defined
+// casing and spaces.
+func (s DisplayScreenName) String() string {
+	return string(s)
+}
+
+// NewStubUser creates a new user with canned credentials. The default password
+// is "welcome1". This is typically used for development purposes.
+func NewStubUser(screenName DisplayScreenName) (User, error) {
+	uid, err := uuid.NewRandom()
+	if err != nil {
+		return User{}, err
+	}
+	u := User{
+		IdentScreenName:   NewIdentScreenName(string(screenName)),
+		DisplayScreenName: screenName,
+		AuthKey:           uid.String(),
+	}
+	err = u.HashPassword("welcome1")
+	return u, err
+}
+
+// User represents a user account.
+type User struct {
+	// IdentScreenName is the AIM screen name.
+	IdentScreenName IdentScreenName `json:"-"`
+	// DisplayScreenName is the formatted screen name.
+	DisplayScreenName DisplayScreenName `json:"screen_name"`
+	// AuthKey is the salt for the MD5 password hash.
+	AuthKey string `json:"-"`
+	// StrongMD5Pass is the MD5 password hash format used by AIM v4.8-v5.9.
+	StrongMD5Pass []byte `json:"-"`
+	// WeakMD5Pass is the MD5 password hash format used by AIM v3.5-v4.7. This
+	// hash is used to authenticate roasted passwords for AIM v1.0-v3.0.
+	WeakMD5Pass []byte `json:"-"`
+}
+
+// ValidateHash checks if md5Hash is identical to one of the password hashes.
+func (u *User) ValidateHash(md5Hash []byte) bool {
+	return bytes.Equal(u.StrongMD5Pass, md5Hash) || bytes.Equal(u.WeakMD5Pass, md5Hash)
+}
+
+// ValidateRoastedPass checks if the provided roasted password matches the MD5
+// hash of the user's actual password. A roasted password is a XOR-obfuscated
+// form of the real password, intended to add a simple layer of security.
+func (u *User) ValidateRoastedPass(roastedPass []byte) bool {
+	clearPass := wire.RoastPassword(roastedPass)
+	md5Hash := wire.WeakMD5PasswordHash(string(clearPass), u.AuthKey) // todo remove string conversion
+	return bytes.Equal(u.WeakMD5Pass, md5Hash)
+}
+
+// HashPassword computes MD5 hashes of the user's password. It computes both
+// weak and strong variants and stores them in the struct.
+func (u *User) HashPassword(passwd string) error {
+	u.WeakMD5Pass = wire.WeakMD5PasswordHash(passwd, u.AuthKey)
+	u.StrongMD5Pass = wire.StrongMD5PasswordHash(passwd, u.AuthKey)
+	return nil
+}

+ 68 - 123
state/user_store.go

@@ -15,67 +15,12 @@ import (
 	"github.com/golang-migrate/migrate/v4"
 	"github.com/golang-migrate/migrate/v4/database/sqlite3"
 	"github.com/golang-migrate/migrate/v4/source/httpfs"
-	"github.com/google/uuid"
 	_ "github.com/mattn/go-sqlite3"
 )
 
-// BlockedState represents the blocked status between two users
-type BlockedState int
-
 //go:embed migrations/*
 var migrations embed.FS
 
-var (
-	// ErrDupUser indicates that a user already exists.
-	ErrDupUser = errors.New("user already exists")
-	// ErrNoUser indicates that a user does not exist.
-	ErrNoUser = errors.New("user does not exist")
-)
-
-const (
-	// BlockedNo indicates that neither user blocks the other.
-	BlockedNo BlockedState = iota
-	// BlockedA indicates that user A blocks user B.
-	BlockedA
-	// BlockedB indicates that user B blocks user A.
-	BlockedB
-)
-
-// User represents a user account.
-type User struct {
-	// ScreenName is the AIM screen name.
-	ScreenName string `json:"screen_name"`
-	// AuthKey is the salt for the MD5 password hash.
-	AuthKey string `json:"-"`
-	// StrongMD5Pass is the MD5 password hash format used by AIM v4.8-v5.9.
-	StrongMD5Pass []byte `json:"-"`
-	// WeakMD5Pass is the MD5 password hash format used by AIM v3.5-v4.7. This
-	// hash is used to authenticate roasted passwords for AIM v1.0-v3.0.
-	WeakMD5Pass []byte `json:"-"`
-}
-
-// ValidateHash checks if md5Hash is identical to one of the password hashes.
-func (u *User) ValidateHash(md5Hash []byte) bool {
-	return bytes.Equal(u.StrongMD5Pass, md5Hash) || bytes.Equal(u.WeakMD5Pass, md5Hash)
-}
-
-// ValidateRoastedPass checks if the provided roasted password matches the MD5
-// hash of the user's actual password. A roasted password is a XOR-obfuscated
-// form of the real password, intended to add a simple layer of security.
-func (u *User) ValidateRoastedPass(roastedPass []byte) bool {
-	clearPass := wire.RoastPassword(roastedPass)
-	md5Hash := wire.WeakMD5PasswordHash(string(clearPass), u.AuthKey) // todo remove string conversion
-	return bytes.Equal(u.WeakMD5Pass, md5Hash)
-}
-
-// HashPassword computes MD5 hashes of the user's password. It computes both
-// weak and strong variants and stores them in the struct.
-func (u *User) HashPassword(passwd string) error {
-	u.WeakMD5Pass = wire.WeakMD5PasswordHash(passwd, u.AuthKey)
-	u.StrongMD5Pass = wire.StrongMD5PasswordHash(passwd, u.AuthKey)
-	return nil
-}
-
 // SQLiteUserStore stores user feedbag (buddy list), profile, and
 // authentication credentials information in a SQLite database.
 type SQLiteUserStore struct {
@@ -122,10 +67,10 @@ func (f SQLiteUserStore) runMigrations() error {
 	return nil
 }
 
-// AllUsers returns all stored users. It only populates the User.ScreenName field
-// populated in the returned slice.
+// AllUsers returns all stored users. It only populates the User.IdentScreenName and
+// User.DisplayScreenName fields in the return slice.
 func (f SQLiteUserStore) AllUsers() ([]User, error) {
-	q := `SELECT screenName FROM user`
+	q := `SELECT identScreenName, displayScreenName FROM users`
 	rows, err := f.db.Query(q)
 	if err != nil {
 		return nil, err
@@ -134,11 +79,14 @@ func (f SQLiteUserStore) AllUsers() ([]User, error) {
 
 	var users []User
 	for rows.Next() {
-		u := User{}
-		if err := rows.Scan(&u.ScreenName); err != nil {
+		var identSN, displaySN string
+		if err := rows.Scan(&identSN, &displaySN); err != nil {
 			return nil, err
 		}
-		users = append(users, u)
+		users = append(users, User{
+			IdentScreenName:   NewIdentScreenName(identSN),
+			DisplayScreenName: DisplayScreenName(displaySN),
+		})
 	}
 
 	if err := rows.Err(); err != nil {
@@ -150,18 +98,22 @@ func (f SQLiteUserStore) AllUsers() ([]User, error) {
 
 // User looks up a user by screen name. It populates the User record with
 // credentials that can be used to validate the user's password.
-func (f SQLiteUserStore) User(screenName string) (*User, error) {
+func (f SQLiteUserStore) User(screenName IdentScreenName) (*User, error) {
 	q := `
 		SELECT
-			screenName, 
-			authKey, 
+			displayScreenName,
+			authKey,
 			weakMD5Pass,
 			strongMD5Pass
-		FROM user
-		WHERE screenName = ?
+		FROM users
+		WHERE identScreenName = ?
 	`
-	u := &User{}
-	err := f.db.QueryRow(q, screenName).Scan(&u.ScreenName, &u.AuthKey, &u.WeakMD5Pass, &u.StrongMD5Pass)
+	u := &User{
+		IdentScreenName: screenName,
+	}
+	err := f.db.QueryRow(q, screenName.String()).
+		Scan(&u.DisplayScreenName, &u.AuthKey, &u.WeakMD5Pass, &u.StrongMD5Pass)
+
 	if errors.Is(err, sql.ErrNoRows) {
 		return nil, nil
 	}
@@ -172,11 +124,11 @@ func (f SQLiteUserStore) User(screenName string) (*User, error) {
 // same screen name already exists.
 func (f SQLiteUserStore) InsertUser(u User) error {
 	q := `
-		INSERT INTO user (screenName, authKey, weakMD5Pass, strongMD5Pass)
-		VALUES (?, ?, ?, ?)
-		ON CONFLICT (screenName) DO NOTHING
+		INSERT INTO users (identScreenName, displayScreenName, authKey, weakMD5Pass, strongMD5Pass)
+		VALUES (?, ?, ?, ?, ?)
+		ON CONFLICT (identScreenName) DO NOTHING
 	`
-	result, err := f.db.Exec(q, u.ScreenName, u.AuthKey, u.WeakMD5Pass, u.StrongMD5Pass)
+	result, err := f.db.Exec(q, u.IdentScreenName.String(), u.DisplayScreenName, u.AuthKey, u.WeakMD5Pass, u.StrongMD5Pass)
 	if err != nil {
 		return err
 	}
@@ -194,11 +146,11 @@ func (f SQLiteUserStore) InsertUser(u User) error {
 
 // DeleteUser deletes a user from the store. Return ErrNoUser if the user did
 // not exist prior to deletion.
-func (f SQLiteUserStore) DeleteUser(screenName string) error {
+func (f SQLiteUserStore) DeleteUser(screenName IdentScreenName) error {
 	q := `
-		DELETE FROM user WHERE screenName = ?
+		DELETE FROM users WHERE identScreenName = ?
 	`
-	result, err := f.db.Exec(q, screenName)
+	result, err := f.db.Exec(q, screenName.String())
 	if err != nil {
 		return err
 	}
@@ -215,43 +167,44 @@ func (f SQLiteUserStore) DeleteUser(screenName string) error {
 }
 
 // SetUserPassword sets the user's password hashes and auth key.
-func (f SQLiteUserStore) SetUserPassword(u User) error {
+func (f SQLiteUserStore) SetUserPassword(u User) (err error) {
 	tx, err := f.db.Begin()
 	if err != nil {
-		return err
+		return
 	}
 
 	defer func() {
 		if err != nil {
-			tx.Rollback()
+			err = errors.Join(err, tx.Rollback())
 		}
 	}()
 
 	q := `
-		UPDATE user
+		UPDATE users
 		SET authKey = ?, weakMD5Pass = ?, strongMD5Pass = ?
-		WHERE screenName = ?
+		WHERE identScreenName = ?
 	`
-	result, err := tx.Exec(q, u.AuthKey, u.WeakMD5Pass, u.StrongMD5Pass, u.ScreenName)
+	result, err := tx.Exec(q, u.AuthKey, u.WeakMD5Pass, u.StrongMD5Pass, u.IdentScreenName.String())
 	if err != nil {
-		return err
+		return
 	}
 
 	rowsAffected, err := result.RowsAffected()
 	if err != nil {
-		return err
+		return
 	}
 
 	if rowsAffected == 0 {
 		// it's possible the user didn't change OR the user doesn't exist.
 		// check if the user exists.
 		var exists int
-		err = tx.QueryRow("SELECT COUNT(*) FROM user WHERE screenName = ?", u.ScreenName).Scan(&exists)
+		err = tx.QueryRow("SELECT COUNT(*) FROM users WHERE identScreenName = ?", u.IdentScreenName.String()).Scan(&exists)
 		if err != nil {
-			return err // Handle possible SQL errors during the select
+			return // Handle possible SQL errors during the select
 		}
 		if exists == 0 {
-			return ErrNoUser // User does not exist
+			err = ErrNoUser // User does not exist
+			return
 		}
 	}
 
@@ -259,7 +212,7 @@ func (f SQLiteUserStore) SetUserPassword(u User) error {
 }
 
 // Feedbag fetches the contents of a user's feedbag (buddy list).
-func (f SQLiteUserStore) Feedbag(screenName string) ([]wire.FeedbagItem, error) {
+func (f SQLiteUserStore) Feedbag(screenName IdentScreenName) ([]wire.FeedbagItem, error) {
 	q := `
 		SELECT 
 			groupID,
@@ -271,7 +224,7 @@ func (f SQLiteUserStore) Feedbag(screenName string) ([]wire.FeedbagItem, error)
 		WHERE screenName = ?
 	`
 
-	rows, err := f.db.Query(q, screenName)
+	rows, err := f.db.Query(q, screenName.String())
 	if err != nil {
 		return nil, err
 	}
@@ -295,20 +248,20 @@ func (f SQLiteUserStore) Feedbag(screenName string) ([]wire.FeedbagItem, error)
 
 // FeedbagLastModified returns the last time a user's feedbag (buddy list) was
 // updated.
-func (f SQLiteUserStore) FeedbagLastModified(screenName string) (time.Time, error) {
+func (f SQLiteUserStore) FeedbagLastModified(screenName IdentScreenName) (time.Time, error) {
 	var lastModified sql.NullInt64
 	q := `SELECT MAX(lastModified) FROM feedbag WHERE screenName = ?`
-	err := f.db.QueryRow(q, screenName).Scan(&lastModified)
+	err := f.db.QueryRow(q, screenName.String()).Scan(&lastModified)
 	return time.Unix(lastModified.Int64, 0), err
 }
 
 // FeedbagDelete deletes an entry from a user's feedbag (buddy list).
-func (f SQLiteUserStore) FeedbagDelete(screenName string, items []wire.FeedbagItem) error {
+func (f SQLiteUserStore) FeedbagDelete(screenName IdentScreenName, items []wire.FeedbagItem) error {
 	// todo add transaction
 	q := `DELETE FROM feedbag WHERE screenName = ? AND itemID = ?`
 
 	for _, item := range items {
-		if _, err := f.db.Exec(q, screenName, item.ItemID); err != nil {
+		if _, err := f.db.Exec(q, screenName.String(), item.ItemID); err != nil {
 			return err
 		}
 	}
@@ -318,7 +271,7 @@ func (f SQLiteUserStore) FeedbagDelete(screenName string, items []wire.FeedbagIt
 
 // FeedbagUpsert upserts an entry to a user's feedbag (buddy list). An entry is
 // created if it doesn't already exist, or modified if it already exists.
-func (f SQLiteUserStore) FeedbagUpsert(screenName string, items []wire.FeedbagItem) error {
+func (f SQLiteUserStore) FeedbagUpsert(screenName IdentScreenName, items []wire.FeedbagItem) error {
 	q := `
 		INSERT INTO feedbag (screenName, groupID, itemID, classID, name, attributes, lastModified)
 		VALUES (?, ?, ?, ?, ?, ?, UNIXEPOCH())
@@ -335,8 +288,15 @@ func (f SQLiteUserStore) FeedbagUpsert(screenName string, items []wire.FeedbagIt
 			return err
 		}
 
+		// todo are these all the right items?
+		if item.ClassID == wire.FeedbagClassIdBuddy ||
+			item.ClassID == wire.FeedbagClassIDPermit ||
+			item.ClassID == wire.FeedbagClassIDDeny {
+			// insert screen name identifier
+			item.Name = NewIdentScreenName(item.Name).String()
+		}
 		_, err := f.db.Exec(q,
-			screenName,
+			screenName.String(),
 			item.GroupID,
 			item.ItemID,
 			item.ClassID,
@@ -352,7 +312,7 @@ func (f SQLiteUserStore) FeedbagUpsert(screenName string, items []wire.FeedbagIt
 
 // AdjacentUsers returns all users who have screenName in their buddy list.
 // Exclude users who are on screenName's block list.
-func (f SQLiteUserStore) AdjacentUsers(screenName string) ([]string, error) {
+func (f SQLiteUserStore) AdjacentUsers(screenName IdentScreenName) ([]IdentScreenName, error) {
 	q := `
 		SELECT f.screenName
 		FROM feedbag f
@@ -364,19 +324,19 @@ func (f SQLiteUserStore) AdjacentUsers(screenName string) ([]string, error) {
 		AND NOT EXISTS(SELECT 1 FROM feedbag WHERE screenName = f.screenName AND name = f.name AND classID = 3)
 	`
 
-	rows, err := f.db.Query(q, screenName, screenName, screenName)
+	rows, err := f.db.Query(q, screenName.String(), screenName.String(), screenName.String())
 	if err != nil {
 		return nil, err
 	}
 	defer rows.Close()
 
-	var items []string
+	var items []IdentScreenName
 	for rows.Next() {
 		var sn string
 		if err := rows.Scan(&sn); err != nil {
 			return nil, err
 		}
-		items = append(items, sn)
+		items = append(items, NewIdentScreenName(sn))
 	}
 
 	return items, nil
@@ -384,7 +344,7 @@ func (f SQLiteUserStore) AdjacentUsers(screenName string) ([]string, error) {
 
 // Buddies returns all user's buddies. Don't return a buddy if the user has
 // them on their block list.
-func (f SQLiteUserStore) Buddies(screenName string) ([]string, error) {
+func (f SQLiteUserStore) Buddies(screenName IdentScreenName) ([]IdentScreenName, error) {
 	q := `
 		SELECT f.name
 		FROM feedbag f
@@ -395,26 +355,26 @@ func (f SQLiteUserStore) Buddies(screenName string) ([]string, error) {
 		AND NOT EXISTS(SELECT 1 FROM feedbag WHERE screenName = ? AND name = f.name AND classID = 3)
 	`
 
-	rows, err := f.db.Query(q, screenName, screenName, screenName)
+	rows, err := f.db.Query(q, screenName.String(), screenName.String(), screenName.String())
 	if err != nil {
 		return nil, err
 	}
 	defer rows.Close()
 
-	var items []string
+	var items []IdentScreenName
 	for rows.Next() {
 		var sn string
 		if err := rows.Scan(&sn); err != nil {
 			return nil, err
 		}
-		items = append(items, sn)
+		items = append(items, NewIdentScreenName(sn))
 	}
 
 	return items, nil
 }
 
 // BlockedState returns the BlockedState between two users.
-func (f SQLiteUserStore) BlockedState(screenNameA, screenNameB string) (BlockedState, error) {
+func (f SQLiteUserStore) BlockedState(screenName1, screenName2 IdentScreenName) (BlockedState, error) {
 	q := `
 		SELECT EXISTS(SELECT 1
 					  FROM feedbag f
@@ -428,7 +388,7 @@ func (f SQLiteUserStore) BlockedState(screenNameA, screenNameB string) (BlockedS
 						AND f.screenName = ?
 						AND f.name = ?)
 	`
-	row, err := f.db.Query(q, screenNameA, screenNameB, screenNameB, screenNameA)
+	row, err := f.db.Query(q, screenName1.String(), screenName2.String(), screenName2.String(), screenName1.String())
 	if err != nil {
 		return BlockedNo, err
 	}
@@ -460,14 +420,14 @@ func (f SQLiteUserStore) BlockedState(screenNameA, screenNameB string) (BlockedS
 
 // Profile fetches a user profile. Return empty string if the user
 // does not exist or has no profile.
-func (f SQLiteUserStore) Profile(screenName string) (string, error) {
+func (f SQLiteUserStore) Profile(screenName IdentScreenName) (string, error) {
 	q := `
 		SELECT IFNULL(body, '')
 		FROM profile
 		WHERE screenName = ?
 	`
 	var profile string
-	err := f.db.QueryRow(q, screenName).Scan(&profile)
+	err := f.db.QueryRow(q, screenName.String()).Scan(&profile)
 	if err != nil && !errors.Is(err, sql.ErrNoRows) {
 		return "", err
 	}
@@ -475,14 +435,14 @@ func (f SQLiteUserStore) Profile(screenName string) (string, error) {
 }
 
 // SetProfile sets the text contents of a user's profile.
-func (f SQLiteUserStore) SetProfile(screenName string, body string) error {
+func (f SQLiteUserStore) SetProfile(screenName IdentScreenName, body string) error {
 	q := `
 		INSERT INTO profile (screenName, body)
 		VALUES (?, ?)
 		ON CONFLICT (screenName)
 			DO UPDATE SET body = excluded.body
 	`
-	_, err := f.db.Exec(q, screenName, body)
+	_, err := f.db.Exec(q, screenName.String(), body)
 	return err
 }
 
@@ -509,18 +469,3 @@ func (f SQLiteUserStore) BARTRetrieve(hash []byte) ([]byte, error) {
 	}
 	return body, nil
 }
-
-// NewStubUser creates a new user with canned credentials. The default password
-// is "welcome1". This is typically used for development purposes.
-func NewStubUser(screenName string) (User, error) {
-	uid, err := uuid.NewRandom()
-	if err != nil {
-		return User{}, err
-	}
-	u := User{
-		ScreenName: screenName,
-		AuthKey:    uid.String(),
-	}
-	err = u.HashPassword("welcome1")
-	return u, err
-}

+ 213 - 77
state/user_store_test.go

@@ -14,7 +14,7 @@ const testFile string = "aim_test.db"
 
 func TestUserStore(t *testing.T) {
 
-	const screenName = "sn2day"
+	screenName := NewIdentScreenName("sn2day")
 
 	defer func() {
 		assert.NoError(t, os.Remove(testFile))
@@ -54,7 +54,7 @@ func TestUserStore(t *testing.T) {
 
 func TestFeedbagDelete(t *testing.T) {
 
-	const screenName = "sn2day"
+	screenName := NewIdentScreenName("sn2day")
 
 	defer func() {
 		assert.NoError(t, os.Remove(testFile))
@@ -111,7 +111,7 @@ func TestFeedbagDelete(t *testing.T) {
 
 func TestLastModifiedEmpty(t *testing.T) {
 
-	const screenName = "sn2day"
+	screenName := NewIdentScreenName("sn2day")
 
 	defer func() {
 		assert.NoError(t, os.Remove(testFile))
@@ -129,7 +129,7 @@ func TestLastModifiedEmpty(t *testing.T) {
 
 func TestLastModifiedNotEmpty(t *testing.T) {
 
-	const screenName = "sn2day"
+	screenName := NewIdentScreenName("sn2day")
 
 	defer func() {
 		assert.NoError(t, os.Remove(testFile))
@@ -159,7 +159,7 @@ func TestLastModifiedNotEmpty(t *testing.T) {
 
 func TestProfile(t *testing.T) {
 
-	const screenName = "sn2day"
+	screenName := NewIdentScreenName("sn2day")
 
 	defer func() {
 		assert.NoError(t, os.Remove(testFile))
@@ -169,7 +169,7 @@ func TestProfile(t *testing.T) {
 	assert.NoError(t, err)
 
 	u := User{
-		ScreenName: screenName,
+		IdentScreenName: screenName,
 	}
 	if err := f.InsertUser(u); err != nil {
 		t.Fatalf("failed to upsert new user: %s", err.Error())
@@ -215,7 +215,7 @@ func TestProfile(t *testing.T) {
 
 func TestProfileNonExistent(t *testing.T) {
 
-	const screenName = "sn2day"
+	screenName := NewIdentScreenName("sn2day")
 
 	defer func() {
 		assert.NoError(t, os.Remove(testFile))
@@ -242,12 +242,12 @@ func TestAdjacentUsers(t *testing.T) {
 	f.db.Exec(`INSERT INTO "feedbag" VALUES('userA',27631,4016,0,'userB',NULL,1690508233)`)
 	f.db.Exec(`INSERT INTO "feedbag" VALUES('userB',28330,8120,0,'userA',NULL,1691180328)`)
 
-	users, err := f.AdjacentUsers("userA")
+	users, err := f.AdjacentUsers(NewIdentScreenName("userA"))
 	if len(users) != 0 {
 		t.Fatalf("expected no interested users, got %v", users)
 	}
 
-	users, err = f.AdjacentUsers("userB")
+	users, err = f.AdjacentUsers(NewIdentScreenName("userB"))
 	if len(users) != 0 {
 		t.Fatalf("expected no interested users, got %v", users)
 	}
@@ -265,12 +265,12 @@ func TestUserStoreBuddiesBlockedUser(t *testing.T) {
 	f.db.Exec(`INSERT INTO "feedbag" VALUES('userA',27631,4016,0,'userB',NULL,1690508233)`)
 	f.db.Exec(`INSERT INTO "feedbag" VALUES('userB',28330,8120,0,'userA',NULL,1691180328)`)
 
-	users, err := f.Buddies("userA")
+	users, err := f.Buddies(NewIdentScreenName("userA"))
 	if len(users) != 0 {
 		t.Fatalf("expected no buddies, got %v", users)
 	}
 
-	users, err = f.Buddies("userB")
+	users, err = f.Buddies(NewIdentScreenName("userB"))
 	if len(users) != 0 {
 		t.Fatalf("expected no buddies, got %v", users)
 	}
@@ -285,12 +285,35 @@ func TestUserStoreBlockedA(t *testing.T) {
 	f, err := NewSQLiteUserStore(testFile)
 	assert.NoError(t, err)
 
-	f.db.Exec(`INSERT INTO "feedbag" VALUES('userA',0,13852,3,'userB',NULL,1691286176)`)
-	f.db.Exec(`INSERT INTO "feedbag" VALUES('userA',27631,4016,0,'userB',NULL,1690508233)`)
-	f.db.Exec(`INSERT INTO "feedbag" VALUES('userB',28330,8120,0,'userA',NULL,1691180328)`)
+	sn1 := NewIdentScreenName("userA")
+	sn2 := NewIdentScreenName("userB")
+
+	err = f.FeedbagUpsert(sn1, []wire.FeedbagItem{
+		{
+			GroupID: 0,
+			ItemID:  13852,
+			ClassID: 3,
+			Name:    "userB",
+		},
+		{
+			GroupID: 27631,
+			ItemID:  4016,
+			ClassID: 0,
+			Name:    "userB",
+		},
+	})
+	assert.NoError(t, err)
+
+	err = f.FeedbagUpsert(sn2, []wire.FeedbagItem{
+		{
+			GroupID: 28330,
+			ItemID:  8120,
+			ClassID: 0,
+			Name:    "userA",
+		},
+	})
+	assert.NoError(t, err)
 
-	sn1 := "userA"
-	sn2 := "userB"
 	blocked, err := f.BlockedState(sn1, sn2)
 	if err != nil {
 		t.Fatalf("db err: %s", err.Error())
@@ -308,12 +331,35 @@ func TestUserStoreBlockedB(t *testing.T) {
 	f, err := NewSQLiteUserStore(testFile)
 	assert.NoError(t, err)
 
-	f.db.Exec(`INSERT INTO "feedbag" VALUES('userB',0,13852,3,'userA',NULL,1691286176)`)
-	f.db.Exec(`INSERT INTO "feedbag" VALUES('userA',27631,4016,0,'userB',NULL,1690508233)`)
-	f.db.Exec(`INSERT INTO "feedbag" VALUES('userB',28330,8120,0,'userA',NULL,1691180328)`)
+	sn1 := NewIdentScreenName("userA")
+	sn2 := NewIdentScreenName("userB")
+
+	err = f.FeedbagUpsert(sn1, []wire.FeedbagItem{
+		{
+			GroupID: 27631,
+			ItemID:  4016,
+			ClassID: 0,
+			Name:    "userB",
+		},
+	})
+	assert.NoError(t, err)
+
+	err = f.FeedbagUpsert(sn2, []wire.FeedbagItem{
+		{
+			GroupID: 0,
+			ItemID:  13852,
+			ClassID: 3,
+			Name:    "userA",
+		},
+		{
+			GroupID: 28330,
+			ItemID:  8120,
+			ClassID: 0,
+			Name:    "userA",
+		},
+	})
+	assert.NoError(t, err)
 
-	sn1 := "userA"
-	sn2 := "userB"
 	blocked, err := f.BlockedState(sn1, sn2)
 	if err != nil {
 		t.Fatalf("db err: %s", err.Error())
@@ -334,8 +380,8 @@ func TestUserStoreBlockedNoBlocked(t *testing.T) {
 	f.db.Exec(`INSERT INTO "feedbag" VALUES('userA',27631,4016,0,'userB',NULL,1690508233)`)
 	f.db.Exec(`INSERT INTO "feedbag" VALUES('userB',28330,8120,0,'userA',NULL,1691180328)`)
 
-	sn1 := "userA"
-	sn2 := "userB"
+	sn1 := NewIdentScreenName("userA")
+	sn2 := NewIdentScreenName("userB")
 	blocked, err := f.BlockedState(sn1, sn2)
 	if err != nil {
 		t.Fatalf("db err: %s", err.Error())
@@ -353,24 +399,24 @@ func TestGetUser(t *testing.T) {
 	f, err := NewSQLiteUserStore(testFile)
 	assert.NoError(t, err)
 
-	expectUser := &User{
-		ScreenName:    "testscreenname",
-		AuthKey:       "theauthkey",
-		StrongMD5Pass: []byte("thepasshash"),
-	}
-	_, err = f.db.Exec(`INSERT INTO user (ScreenName, authKey, strongMD5Pass) VALUES(?, ?, ?)`,
-		expectUser.ScreenName, expectUser.AuthKey, expectUser.StrongMD5Pass)
-	if err != nil {
-		t.Fatalf("failed to insert user: %s", err.Error())
+	screenName := NewIdentScreenName("testscreenname")
+
+	insertedUser := &User{
+		IdentScreenName:   screenName,
+		DisplayScreenName: DisplayScreenName("testscreenname"),
+		AuthKey:           "theauthkey",
+		StrongMD5Pass:     []byte("thepasshash"),
 	}
+	err = f.InsertUser(*insertedUser)
+	assert.NoError(t, err)
 
-	actualUser, err := f.User(expectUser.ScreenName)
+	actualUser, err := f.User(screenName)
 	if err != nil {
 		t.Fatalf("failed to get user: %s", err.Error())
 	}
 
-	if !reflect.DeepEqual(expectUser, actualUser) {
-		t.Fatalf("users are not equal. expect: %v actual: %v", expectUser, actualUser)
+	if !reflect.DeepEqual(insertedUser, actualUser) {
+		t.Fatalf("users are not equal. expect: %v actual: %v", insertedUser, actualUser)
 	}
 }
 
@@ -382,7 +428,7 @@ func TestGetUserNotFound(t *testing.T) {
 	f, err := NewSQLiteUserStore(testFile)
 	assert.NoError(t, err)
 
-	actualUser, err := f.User("testscreenname")
+	actualUser, err := f.User(NewIdentScreenName("testscreenname"))
 	if err != nil {
 		t.Fatalf("failed to get user: %s", err.Error())
 	}
@@ -403,9 +449,18 @@ func TestSQLiteUserStore_Users(t *testing.T) {
 	}
 
 	want := []User{
-		{ScreenName: "userA"},
-		{ScreenName: "userB"},
-		{ScreenName: "userC"},
+		{
+			IdentScreenName:   NewIdentScreenName("userA"),
+			DisplayScreenName: "userA",
+		},
+		{
+			IdentScreenName:   NewIdentScreenName("userB"),
+			DisplayScreenName: "userB",
+		},
+		{
+			IdentScreenName:   NewIdentScreenName("userC"),
+			DisplayScreenName: "userC",
+		},
 	}
 
 	for _, u := range want {
@@ -427,18 +482,27 @@ func TestSQLiteUserStore_DeleteUser_DeleteExistentUser(t *testing.T) {
 	f, err := NewSQLiteUserStore(testFile)
 	assert.NoError(t, err)
 
-	err = f.InsertUser(User{ScreenName: "userA"})
+	err = f.InsertUser(User{
+		IdentScreenName:   NewIdentScreenName("userA"),
+		DisplayScreenName: "userA",
+	})
 	assert.NoError(t, err)
-	err = f.InsertUser(User{ScreenName: "userB"})
+	err = f.InsertUser(User{
+		IdentScreenName:   NewIdentScreenName("userB"),
+		DisplayScreenName: "userB",
+	})
 	assert.NoError(t, err)
 
-	err = f.DeleteUser("userA")
+	err = f.DeleteUser(NewIdentScreenName("userA"))
 	assert.NoError(t, err)
 
 	have, err := f.AllUsers()
 	assert.NoError(t, err)
 
-	want := []User{{ScreenName: "userB"}}
+	want := []User{{
+		IdentScreenName:   NewIdentScreenName("userB"),
+		DisplayScreenName: "userB",
+	}}
 	assert.Equal(t, want, have)
 }
 
@@ -450,7 +514,7 @@ func TestSQLiteUserStore_DeleteUser_DeleteNonExistentUser(t *testing.T) {
 	f, err := NewSQLiteUserStore(testFile)
 	assert.NoError(t, err)
 
-	err = f.DeleteUser("userA")
+	err = f.DeleteUser(NewIdentScreenName("userA"))
 	assert.ErrorIs(t, ErrNoUser, err)
 }
 
@@ -462,21 +526,29 @@ func TestSQLiteUserStore_Buddies(t *testing.T) {
 	feedbagStore, err := NewSQLiteUserStore(testFile)
 	assert.NoError(t, err)
 
-	assert.NoError(t, feedbagStore.FeedbagUpsert("userA", []wire.FeedbagItem{
-		{Name: "userB", ItemID: 1, ClassID: wire.FeedbagClassIdBuddy},
-		{Name: "userC", ItemID: 2, ClassID: wire.FeedbagClassIdBuddy},
-		{Name: "userD", ItemID: 3, ClassID: wire.FeedbagClassIdBuddy},
-	}))
-	assert.NoError(t, feedbagStore.FeedbagUpsert("userB", []wire.FeedbagItem{
-		{Name: "userA", ItemID: 1, ClassID: wire.FeedbagClassIdBuddy},
-		{Name: "userC", ItemID: 2, ClassID: wire.FeedbagClassIdBuddy},
-		{Name: "userD", ItemID: 3, ClassID: wire.FeedbagClassIdBuddy},
-	}))
-
-	want := []string{"userB", "userC", "userD"}
-	have, err := feedbagStore.Buddies("userA")
+	assert.NoError(t, feedbagStore.FeedbagUpsert(
+		NewIdentScreenName("userA"),
+		[]wire.FeedbagItem{
+			{Name: "userB", ItemID: 1, ClassID: wire.FeedbagClassIdBuddy},
+			{Name: "userC", ItemID: 2, ClassID: wire.FeedbagClassIdBuddy},
+			{Name: "userD", ItemID: 3, ClassID: wire.FeedbagClassIdBuddy},
+		}))
+	assert.NoError(t, feedbagStore.FeedbagUpsert(
+		NewIdentScreenName("userB"),
+		[]wire.FeedbagItem{
+			{Name: "userA", ItemID: 1, ClassID: wire.FeedbagClassIdBuddy},
+			{Name: "userC", ItemID: 2, ClassID: wire.FeedbagClassIdBuddy},
+			{Name: "userD", ItemID: 3, ClassID: wire.FeedbagClassIdBuddy},
+		}))
+
+	have, err := feedbagStore.Buddies(NewIdentScreenName("userA"))
 	assert.NoError(t, err)
 
+	want := []IdentScreenName{
+		NewIdentScreenName("userB"),
+		NewIdentScreenName("userC"),
+		NewIdentScreenName("userD"),
+	}
 	assert.Equal(t, want, have)
 }
 
@@ -485,8 +557,9 @@ func TestNewStubUser(t *testing.T) {
 	assert.NoError(t, err)
 
 	want := User{
-		ScreenName: "userA",
-		AuthKey:    have.AuthKey,
+		IdentScreenName:   NewIdentScreenName("userA"),
+		DisplayScreenName: "userA",
+		AuthKey:           have.AuthKey,
 	}
 	assert.NoError(t, want.HashPassword("welcome1"))
 
@@ -501,24 +574,33 @@ func TestSQLiteUserStore_AdjacentUsers(t *testing.T) {
 	feedbagStore, err := NewSQLiteUserStore(testFile)
 	assert.NoError(t, err)
 
-	assert.NoError(t, feedbagStore.FeedbagUpsert("userA", []wire.FeedbagItem{
-		{Name: "userB", ItemID: 1, ClassID: wire.FeedbagClassIdBuddy},
-		{Name: "userC", ItemID: 2, ClassID: wire.FeedbagClassIdBuddy},
-		{Name: "userD", ItemID: 3, ClassID: wire.FeedbagClassIdBuddy},
-	}))
-	assert.NoError(t, feedbagStore.FeedbagUpsert("userB", []wire.FeedbagItem{
-		{Name: "userA", ItemID: 1, ClassID: wire.FeedbagClassIdBuddy},
-		{Name: "userC", ItemID: 2, ClassID: wire.FeedbagClassIdBuddy},
-		{Name: "userD", ItemID: 3, ClassID: wire.FeedbagClassIdBuddy},
-	}))
-	assert.NoError(t, feedbagStore.FeedbagUpsert("userC", []wire.FeedbagItem{
-		{Name: "userA", ItemID: 1, ClassID: wire.FeedbagClassIdBuddy},
-		{Name: "userB", ItemID: 2, ClassID: wire.FeedbagClassIdBuddy},
-		{Name: "userD", ItemID: 3, ClassID: wire.FeedbagClassIdBuddy},
-	}))
-
-	want := []string{"userB", "userC"}
-	have, err := feedbagStore.AdjacentUsers("userA")
+	assert.NoError(t, feedbagStore.FeedbagUpsert(
+		NewIdentScreenName("userA"),
+		[]wire.FeedbagItem{
+			{Name: "userB", ItemID: 1, ClassID: wire.FeedbagClassIdBuddy},
+			{Name: "userC", ItemID: 2, ClassID: wire.FeedbagClassIdBuddy},
+			{Name: "userD", ItemID: 3, ClassID: wire.FeedbagClassIdBuddy},
+		}))
+	assert.NoError(t, feedbagStore.FeedbagUpsert(
+		NewIdentScreenName("userB"),
+		[]wire.FeedbagItem{
+			{Name: "userA", ItemID: 1, ClassID: wire.FeedbagClassIdBuddy},
+			{Name: "userC", ItemID: 2, ClassID: wire.FeedbagClassIdBuddy},
+			{Name: "userD", ItemID: 3, ClassID: wire.FeedbagClassIdBuddy},
+		}))
+	assert.NoError(t, feedbagStore.FeedbagUpsert(
+		NewIdentScreenName("userC"),
+		[]wire.FeedbagItem{
+			{Name: "userA", ItemID: 1, ClassID: wire.FeedbagClassIdBuddy},
+			{Name: "userB", ItemID: 2, ClassID: wire.FeedbagClassIdBuddy},
+			{Name: "userD", ItemID: 3, ClassID: wire.FeedbagClassIdBuddy},
+		}))
+
+	want := []IdentScreenName{
+		NewIdentScreenName("userB"),
+		NewIdentScreenName("userC"),
+	}
+	have, err := feedbagStore.AdjacentUsers(NewIdentScreenName("userA"))
 	assert.NoError(t, err)
 
 	assert.Equal(t, want, have)
@@ -546,3 +628,57 @@ func TestSQLiteUserStore_BARTUpsertAndRetrieve(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, item, b)
 }
+
+func TestSQLiteUserStore_SetUserPassword_UserExists(t *testing.T) {
+	defer func() {
+		assert.NoError(t, os.Remove(testFile))
+	}()
+
+	feedbagStore, err := NewSQLiteUserStore(testFile)
+	assert.NoError(t, err)
+
+	u := User{
+		IdentScreenName:   NewIdentScreenName("theuser"),
+		DisplayScreenName: "theUser",
+	}
+	err = u.HashPassword("thepassword")
+	assert.NoError(t, err)
+
+	err = feedbagStore.InsertUser(u)
+	assert.NoError(t, err)
+
+	err = u.HashPassword("thenewpassword")
+	assert.NoError(t, err)
+
+	err = feedbagStore.SetUserPassword(u)
+	assert.NoError(t, err)
+
+	gotUser, err := feedbagStore.User(u.IdentScreenName)
+	assert.NoError(t, err)
+
+	valid := gotUser.ValidateHash(u.StrongMD5Pass)
+	assert.True(t, valid)
+}
+
+func TestSQLiteUserStore_SetUserPassword_ErrNoUser(t *testing.T) {
+	defer func() {
+		assert.NoError(t, os.Remove(testFile))
+	}()
+
+	feedbagStore, err := NewSQLiteUserStore(testFile)
+	assert.NoError(t, err)
+
+	u := User{
+		IdentScreenName:   NewIdentScreenName("theuser"),
+		DisplayScreenName: "theUser",
+	}
+	err = u.HashPassword("thepassword")
+	assert.NoError(t, err)
+
+	err = feedbagStore.SetUserPassword(u)
+	assert.ErrorIs(t, err, ErrNoUser)
+
+	// make sure previous transaction previously closed
+	err = feedbagStore.SetUserPassword(u)
+	assert.ErrorIs(t, err, ErrNoUser)
+}