Просмотр исходного кода

add test coverage to user store

Mike 2 лет назад
Родитель
Сommit
14dbdc3e0b

+ 1 - 1
cmd/main.go

@@ -21,7 +21,7 @@ func main() {
 		os.Exit(1)
 	}
 
-	feedbagStore, err := state.NewSQLiteFeedbagStore(cfg.DBPath)
+	feedbagStore, err := state.NewSQLiteUserStore(cfg.DBPath)
 	if err != nil {
 		_, _ = fmt.Fprintf(os.Stderr, "unable to create feedbag store: %s", err.Error())
 		os.Exit(1)

+ 3 - 3
handler/auth.go

@@ -90,7 +90,7 @@ func (s AuthService) BUCPChallengeRequestHandler(bodyIn oscar.SNAC_0x17_0x06_BUC
 
 	var authKey string
 
-	u, err := s.userManager.GetUser(screenName)
+	u, err := s.userManager.User(screenName)
 	switch {
 	case err != nil:
 		return oscar.SNACMessage{}, err
@@ -147,7 +147,7 @@ func (s AuthService) BUCPLoginRequestHandler(bodyIn oscar.SNAC_0x17_0x02_BUCPLog
 
 	loginOK := false
 
-	u, err := s.userManager.GetUser(screenName)
+	u, err := s.userManager.User(screenName)
 	switch {
 	case err != nil:
 		return oscar.SNACMessage{}, err
@@ -160,7 +160,7 @@ func (s AuthService) BUCPLoginRequestHandler(bodyIn oscar.SNAC_0x17_0x02_BUCPLog
 		if err != nil {
 			return oscar.SNACMessage{}, err
 		}
-		if err := s.userManager.UpsertUser(user); err != nil {
+		if err := s.userManager.InsertUser(user); err != nil {
 			return oscar.SNACMessage{}, err
 		}
 		loginOK = true

+ 3 - 3
handler/auth_test.go

@@ -333,12 +333,12 @@ func TestAuthService_BUCPLoginRequestHandler(t *testing.T) {
 			userManager := newMockUserManager(t)
 			for _, params := range tc.mockParams.getUserParams {
 				userManager.EXPECT().
-					GetUser(params.screenName).
+					User(params.screenName).
 					Return(params.result, params.err)
 			}
 			for _, params := range tc.mockParams.upsertUserParams {
 				userManager.EXPECT().
-					UpsertUser(params.user).
+					InsertUser(params.user).
 					Return(params.err)
 			}
 			sessionManager := newMockSessionManager(t)
@@ -514,7 +514,7 @@ func TestAuthService_BUCPChallengeRequestHandler(t *testing.T) {
 			userManager := newMockUserManager(t)
 			for _, params := range tc.mockParams.getUserParams {
 				userManager.EXPECT().
-					GetUser(params.screenName).
+					User(params.screenName).
 					Return(params.result, params.err)
 			}
 			svc := AuthService{

+ 2 - 2
handler/buddy.go

@@ -35,7 +35,7 @@ func (s BuddyService) RightsQueryHandler(_ context.Context, frameIn oscar.SNACFr
 }
 
 func broadcastArrival(ctx context.Context, sess *state.Session, messageRelayer MessageRelayer, feedbagManager FeedbagManager) error {
-	screenNames, err := feedbagManager.InterestedUsers(sess.ScreenName())
+	screenNames, err := feedbagManager.AdjacentUsers(sess.ScreenName())
 	if err != nil {
 		return err
 	}
@@ -54,7 +54,7 @@ func broadcastArrival(ctx context.Context, sess *state.Session, messageRelayer M
 }
 
 func broadcastDeparture(ctx context.Context, sess *state.Session, messageRelayer MessageRelayer, feedbagManager FeedbagManager) error {
-	screenNames, err := feedbagManager.InterestedUsers(sess.ScreenName())
+	screenNames, err := feedbagManager.AdjacentUsers(sess.ScreenName())
 	if err != nil {
 		return err
 	}

+ 7 - 7
handler/feedbag.go

@@ -77,7 +77,7 @@ func (s FeedbagService) RightsQueryHandler(_ context.Context, inFrame oscar.SNAC
 // QueryHandler fetches the user's feedbag (aka buddy list). It returns
 // oscar.FeedbagReply, which contains feedbag entries.
 func (s FeedbagService) QueryHandler(_ context.Context, sess *state.Session, inFrame oscar.SNACFrame) (oscar.SNACMessage, error) {
-	fb, err := s.feedbagManager.Retrieve(sess.ScreenName())
+	fb, err := s.feedbagManager.Feedbag(sess.ScreenName())
 	if err != nil {
 		return oscar.SNACMessage{}, err
 	}
@@ -85,7 +85,7 @@ func (s FeedbagService) QueryHandler(_ context.Context, sess *state.Session, inF
 	lm := time.UnixMilli(0)
 
 	if len(fb) > 0 {
-		lm, err = s.feedbagManager.LastModified(sess.ScreenName())
+		lm, err = s.feedbagManager.FeedbagLastModified(sess.ScreenName())
 		if err != nil {
 			return oscar.SNACMessage{}, err
 		}
@@ -110,7 +110,7 @@ func (s FeedbagService) QueryHandler(_ context.Context, sess *state.Session, inF
 // before inBody.LastUpdate, else return oscar.FeedbagReply, which contains
 // feedbag entries.
 func (s FeedbagService) QueryIfModifiedHandler(_ context.Context, sess *state.Session, inFrame oscar.SNACFrame, inBody oscar.SNAC_0x13_0x05_FeedbagQueryIfModified) (oscar.SNACMessage, error) {
-	fb, err := s.feedbagManager.Retrieve(sess.ScreenName())
+	fb, err := s.feedbagManager.Feedbag(sess.ScreenName())
 	if err != nil {
 		return oscar.SNACMessage{}, err
 	}
@@ -118,7 +118,7 @@ func (s FeedbagService) QueryIfModifiedHandler(_ context.Context, sess *state.Se
 	lm := time.UnixMilli(0)
 
 	if len(fb) > 0 {
-		lm, err = s.feedbagManager.LastModified(sess.ScreenName())
+		lm, err = s.feedbagManager.FeedbagLastModified(sess.ScreenName())
 		if err != nil {
 			return oscar.SNACMessage{}, err
 		}
@@ -174,7 +174,7 @@ func (s FeedbagService) InsertItemHandler(ctx context.Context, sess *state.Sessi
 		}
 	}
 
-	if err := s.feedbagManager.Upsert(sess.ScreenName(), inBody.Items); err != nil {
+	if err := s.feedbagManager.FeedbagUpsert(sess.ScreenName(), inBody.Items); err != nil {
 		return oscar.SNACMessage{}, nil
 	}
 
@@ -219,7 +219,7 @@ func (s FeedbagService) InsertItemHandler(ctx context.Context, sess *state.Sessi
 // to the feedbag. It returns oscar.FeedbagStatus, which contains update
 // confirmation.
 func (s FeedbagService) UpdateItemHandler(ctx context.Context, sess *state.Session, inFrame oscar.SNACFrame, inBody oscar.SNAC_0x13_0x09_FeedbagUpdateItem) (oscar.SNACMessage, error) {
-	if err := s.feedbagManager.Upsert(sess.ScreenName(), inBody.Items); err != nil {
+	if err := s.feedbagManager.FeedbagUpsert(sess.ScreenName(), inBody.Items); err != nil {
 		return oscar.SNACMessage{}, nil
 	}
 
@@ -255,7 +255,7 @@ func (s FeedbagService) UpdateItemHandler(ctx context.Context, sess *state.Sessi
 // current user is visible. It returns oscar.FeedbagStatus, which contains update
 // confirmation.
 func (s FeedbagService) DeleteItemHandler(ctx context.Context, sess *state.Session, inFrame oscar.SNACFrame, inBody oscar.SNAC_0x13_0x0A_FeedbagDeleteItem) (oscar.SNACMessage, error) {
-	if err := s.feedbagManager.Delete(sess.ScreenName(), inBody.Items); err != nil {
+	if err := s.feedbagManager.FeedbagDelete(sess.ScreenName(), inBody.Items); err != nil {
 		return oscar.SNACMessage{}, err
 	}
 

+ 74 - 74
handler/feedbag_manager_mock_test.go

@@ -24,12 +24,12 @@ func (_m *mockFeedbagManager) EXPECT() *mockFeedbagManager_Expecter {
 	return &mockFeedbagManager_Expecter{mock: &_m.Mock}
 }
 
-// Blocked provides a mock function with given fields: screenName1, screenName2
-func (_m *mockFeedbagManager) Blocked(screenName1 string, screenName2 string) (state.BlockedState, error) {
+// BlockedState provides a mock function with given fields: screenName1, screenName2
+func (_m *mockFeedbagManager) BlockedState(screenName1 string, screenName2 string) (state.BlockedState, error) {
 	ret := _m.Called(screenName1, screenName2)
 
 	if len(ret) == 0 {
-		panic("no return value specified for Blocked")
+		panic("no return value specified for BlockedState")
 	}
 
 	var r0 state.BlockedState
@@ -52,31 +52,31 @@ func (_m *mockFeedbagManager) Blocked(screenName1 string, screenName2 string) (s
 	return r0, r1
 }
 
-// mockFeedbagManager_Blocked_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Blocked'
-type mockFeedbagManager_Blocked_Call struct {
+// mockFeedbagManager_BlockedState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockedState'
+type mockFeedbagManager_BlockedState_Call struct {
 	*mock.Call
 }
 
-// Blocked is a helper method to define mock.On call
+// BlockedState is a helper method to define mock.On call
 //   - screenName1 string
 //   - screenName2 string
-func (_e *mockFeedbagManager_Expecter) Blocked(screenName1 interface{}, screenName2 interface{}) *mockFeedbagManager_Blocked_Call {
-	return &mockFeedbagManager_Blocked_Call{Call: _e.mock.On("Blocked", screenName1, screenName2)}
+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_Blocked_Call) Run(run func(screenName1 string, screenName2 string)) *mockFeedbagManager_Blocked_Call {
+func (_c *mockFeedbagManager_BlockedState_Call) Run(run func(screenName1 string, screenName2 string)) *mockFeedbagManager_BlockedState_Call {
 	_c.Call.Run(func(args mock.Arguments) {
 		run(args[0].(string), args[1].(string))
 	})
 	return _c
 }
 
-func (_c *mockFeedbagManager_Blocked_Call) Return(_a0 state.BlockedState, _a1 error) *mockFeedbagManager_Blocked_Call {
+func (_c *mockFeedbagManager_BlockedState_Call) Return(_a0 state.BlockedState, _a1 error) *mockFeedbagManager_BlockedState_Call {
 	_c.Call.Return(_a0, _a1)
 	return _c
 }
 
-func (_c *mockFeedbagManager_Blocked_Call) RunAndReturn(run func(string, string) (state.BlockedState, error)) *mockFeedbagManager_Blocked_Call {
+func (_c *mockFeedbagManager_BlockedState_Call) RunAndReturn(run func(string, string) (state.BlockedState, error)) *mockFeedbagManager_BlockedState_Call {
 	_c.Call.Return(run)
 	return _c
 }
@@ -139,12 +139,12 @@ func (_c *mockFeedbagManager_Buddies_Call) RunAndReturn(run func(string) ([]stri
 	return _c
 }
 
-// Delete provides a mock function with given fields: screenName, items
-func (_m *mockFeedbagManager) Delete(screenName string, items []oscar.FeedbagItem) error {
+// DeleteUser provides a mock function with given fields: screenName, items
+func (_m *mockFeedbagManager) FeedbagDelete(screenName string, items []oscar.FeedbagItem) error {
 	ret := _m.Called(screenName, items)
 
 	if len(ret) == 0 {
-		panic("no return value specified for Delete")
+		panic("no return value specified for FeedbagDelete")
 	}
 
 	var r0 error
@@ -157,53 +157,53 @@ func (_m *mockFeedbagManager) Delete(screenName string, items []oscar.FeedbagIte
 	return r0
 }
 
-// mockFeedbagManager_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete'
-type mockFeedbagManager_Delete_Call struct {
+// mockFeedbagManager_DeleteUser_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FeedbagDelete'
+type mockFeedbagManager_DeleteUser_Call struct {
 	*mock.Call
 }
 
-// Delete is a helper method to define mock.On call
+// DeleteUser is a helper method to define mock.On call
 //   - screenName string
 //   - items []oscar.FeedbagItem
-func (_e *mockFeedbagManager_Expecter) Delete(screenName interface{}, items interface{}) *mockFeedbagManager_Delete_Call {
-	return &mockFeedbagManager_Delete_Call{Call: _e.mock.On("Delete", screenName, items)}
+func (_e *mockFeedbagManager_Expecter) DeleteUser(screenName interface{}, items interface{}) *mockFeedbagManager_DeleteUser_Call {
+	return &mockFeedbagManager_DeleteUser_Call{Call: _e.mock.On("FeedbagDelete", screenName, items)}
 }
 
-func (_c *mockFeedbagManager_Delete_Call) Run(run func(screenName string, items []oscar.FeedbagItem)) *mockFeedbagManager_Delete_Call {
+func (_c *mockFeedbagManager_DeleteUser_Call) Run(run func(screenName string, items []oscar.FeedbagItem)) *mockFeedbagManager_DeleteUser_Call {
 	_c.Call.Run(func(args mock.Arguments) {
 		run(args[0].(string), args[1].([]oscar.FeedbagItem))
 	})
 	return _c
 }
 
-func (_c *mockFeedbagManager_Delete_Call) Return(_a0 error) *mockFeedbagManager_Delete_Call {
+func (_c *mockFeedbagManager_DeleteUser_Call) Return(_a0 error) *mockFeedbagManager_DeleteUser_Call {
 	_c.Call.Return(_a0)
 	return _c
 }
 
-func (_c *mockFeedbagManager_Delete_Call) RunAndReturn(run func(string, []oscar.FeedbagItem) error) *mockFeedbagManager_Delete_Call {
+func (_c *mockFeedbagManager_DeleteUser_Call) RunAndReturn(run func(string, []oscar.FeedbagItem) error) *mockFeedbagManager_DeleteUser_Call {
 	_c.Call.Return(run)
 	return _c
 }
 
-// InterestedUsers provides a mock function with given fields: screenName
-func (_m *mockFeedbagManager) InterestedUsers(screenName string) ([]string, error) {
+// Feedbag provides a mock function with given fields: screenName
+func (_m *mockFeedbagManager) Feedbag(screenName string) ([]oscar.FeedbagItem, error) {
 	ret := _m.Called(screenName)
 
 	if len(ret) == 0 {
-		panic("no return value specified for InterestedUsers")
+		panic("no return value specified for Feedbag")
 	}
 
-	var r0 []string
+	var r0 []oscar.FeedbagItem
 	var r1 error
-	if rf, ok := ret.Get(0).(func(string) ([]string, error)); ok {
+	if rf, ok := ret.Get(0).(func(string) ([]oscar.FeedbagItem, error)); ok {
 		return rf(screenName)
 	}
-	if rf, ok := ret.Get(0).(func(string) []string); ok {
+	if rf, ok := ret.Get(0).(func(string) []oscar.FeedbagItem); ok {
 		r0 = rf(screenName)
 	} else {
 		if ret.Get(0) != nil {
-			r0 = ret.Get(0).([]string)
+			r0 = ret.Get(0).([]oscar.FeedbagItem)
 		}
 	}
 
@@ -216,40 +216,40 @@ func (_m *mockFeedbagManager) InterestedUsers(screenName string) ([]string, erro
 	return r0, r1
 }
 
-// mockFeedbagManager_InterestedUsers_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'InterestedUsers'
-type mockFeedbagManager_InterestedUsers_Call struct {
+// mockFeedbagManager_Feedbag_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Feedbag'
+type mockFeedbagManager_Feedbag_Call struct {
 	*mock.Call
 }
 
-// InterestedUsers is a helper method to define mock.On call
+// Feedbag is a helper method to define mock.On call
 //   - screenName string
-func (_e *mockFeedbagManager_Expecter) InterestedUsers(screenName interface{}) *mockFeedbagManager_InterestedUsers_Call {
-	return &mockFeedbagManager_InterestedUsers_Call{Call: _e.mock.On("InterestedUsers", screenName)}
+func (_e *mockFeedbagManager_Expecter) Feedbag(screenName interface{}) *mockFeedbagManager_Feedbag_Call {
+	return &mockFeedbagManager_Feedbag_Call{Call: _e.mock.On("Feedbag", screenName)}
 }
 
-func (_c *mockFeedbagManager_InterestedUsers_Call) Run(run func(screenName string)) *mockFeedbagManager_InterestedUsers_Call {
+func (_c *mockFeedbagManager_Feedbag_Call) Run(run func(screenName string)) *mockFeedbagManager_Feedbag_Call {
 	_c.Call.Run(func(args mock.Arguments) {
 		run(args[0].(string))
 	})
 	return _c
 }
 
-func (_c *mockFeedbagManager_InterestedUsers_Call) Return(_a0 []string, _a1 error) *mockFeedbagManager_InterestedUsers_Call {
+func (_c *mockFeedbagManager_Feedbag_Call) Return(_a0 []oscar.FeedbagItem, _a1 error) *mockFeedbagManager_Feedbag_Call {
 	_c.Call.Return(_a0, _a1)
 	return _c
 }
 
-func (_c *mockFeedbagManager_InterestedUsers_Call) RunAndReturn(run func(string) ([]string, error)) *mockFeedbagManager_InterestedUsers_Call {
+func (_c *mockFeedbagManager_Feedbag_Call) RunAndReturn(run func(string) ([]oscar.FeedbagItem, error)) *mockFeedbagManager_Feedbag_Call {
 	_c.Call.Return(run)
 	return _c
 }
 
-// LastModified provides a mock function with given fields: screenName
-func (_m *mockFeedbagManager) LastModified(screenName string) (time.Time, error) {
+// FeedbagLastModified provides a mock function with given fields: screenName
+func (_m *mockFeedbagManager) FeedbagLastModified(screenName string) (time.Time, error) {
 	ret := _m.Called(screenName)
 
 	if len(ret) == 0 {
-		panic("no return value specified for LastModified")
+		panic("no return value specified for FeedbagLastModified")
 	}
 
 	var r0 time.Time
@@ -272,52 +272,52 @@ func (_m *mockFeedbagManager) LastModified(screenName string) (time.Time, error)
 	return r0, r1
 }
 
-// mockFeedbagManager_LastModified_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LastModified'
-type mockFeedbagManager_LastModified_Call struct {
+// mockFeedbagManager_FeedbagLastModified_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FeedbagLastModified'
+type mockFeedbagManager_FeedbagLastModified_Call struct {
 	*mock.Call
 }
 
-// LastModified is a helper method to define mock.On call
+// FeedbagLastModified is a helper method to define mock.On call
 //   - screenName string
-func (_e *mockFeedbagManager_Expecter) LastModified(screenName interface{}) *mockFeedbagManager_LastModified_Call {
-	return &mockFeedbagManager_LastModified_Call{Call: _e.mock.On("LastModified", screenName)}
+func (_e *mockFeedbagManager_Expecter) FeedbagLastModified(screenName interface{}) *mockFeedbagManager_FeedbagLastModified_Call {
+	return &mockFeedbagManager_FeedbagLastModified_Call{Call: _e.mock.On("FeedbagLastModified", screenName)}
 }
 
-func (_c *mockFeedbagManager_LastModified_Call) Run(run func(screenName string)) *mockFeedbagManager_LastModified_Call {
+func (_c *mockFeedbagManager_FeedbagLastModified_Call) Run(run func(screenName string)) *mockFeedbagManager_FeedbagLastModified_Call {
 	_c.Call.Run(func(args mock.Arguments) {
 		run(args[0].(string))
 	})
 	return _c
 }
 
-func (_c *mockFeedbagManager_LastModified_Call) Return(_a0 time.Time, _a1 error) *mockFeedbagManager_LastModified_Call {
+func (_c *mockFeedbagManager_FeedbagLastModified_Call) Return(_a0 time.Time, _a1 error) *mockFeedbagManager_FeedbagLastModified_Call {
 	_c.Call.Return(_a0, _a1)
 	return _c
 }
 
-func (_c *mockFeedbagManager_LastModified_Call) RunAndReturn(run func(string) (time.Time, error)) *mockFeedbagManager_LastModified_Call {
+func (_c *mockFeedbagManager_FeedbagLastModified_Call) RunAndReturn(run func(string) (time.Time, error)) *mockFeedbagManager_FeedbagLastModified_Call {
 	_c.Call.Return(run)
 	return _c
 }
 
-// Retrieve provides a mock function with given fields: screenName
-func (_m *mockFeedbagManager) Retrieve(screenName string) ([]oscar.FeedbagItem, error) {
+// InterestedUsers provides a mock function with given fields: screenName
+func (_m *mockFeedbagManager) AdjacentUsers(screenName string) ([]string, error) {
 	ret := _m.Called(screenName)
 
 	if len(ret) == 0 {
-		panic("no return value specified for Retrieve")
+		panic("no return value specified for AdjacentUsers")
 	}
 
-	var r0 []oscar.FeedbagItem
+	var r0 []string
 	var r1 error
-	if rf, ok := ret.Get(0).(func(string) ([]oscar.FeedbagItem, error)); ok {
+	if rf, ok := ret.Get(0).(func(string) ([]string, error)); ok {
 		return rf(screenName)
 	}
-	if rf, ok := ret.Get(0).(func(string) []oscar.FeedbagItem); ok {
+	if rf, ok := ret.Get(0).(func(string) []string); ok {
 		r0 = rf(screenName)
 	} else {
 		if ret.Get(0) != nil {
-			r0 = ret.Get(0).([]oscar.FeedbagItem)
+			r0 = ret.Get(0).([]string)
 		}
 	}
 
@@ -330,40 +330,40 @@ func (_m *mockFeedbagManager) Retrieve(screenName string) ([]oscar.FeedbagItem,
 	return r0, r1
 }
 
-// mockFeedbagManager_Retrieve_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Retrieve'
-type mockFeedbagManager_Retrieve_Call struct {
+// mockFeedbagManager_InterestedUsers_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AdjacentUsers'
+type mockFeedbagManager_InterestedUsers_Call struct {
 	*mock.Call
 }
 
-// Retrieve is a helper method to define mock.On call
+// InterestedUsers is a helper method to define mock.On call
 //   - screenName string
-func (_e *mockFeedbagManager_Expecter) Retrieve(screenName interface{}) *mockFeedbagManager_Retrieve_Call {
-	return &mockFeedbagManager_Retrieve_Call{Call: _e.mock.On("Retrieve", screenName)}
+func (_e *mockFeedbagManager_Expecter) InterestedUsers(screenName interface{}) *mockFeedbagManager_InterestedUsers_Call {
+	return &mockFeedbagManager_InterestedUsers_Call{Call: _e.mock.On("AdjacentUsers", screenName)}
 }
 
-func (_c *mockFeedbagManager_Retrieve_Call) Run(run func(screenName string)) *mockFeedbagManager_Retrieve_Call {
+func (_c *mockFeedbagManager_InterestedUsers_Call) Run(run func(screenName string)) *mockFeedbagManager_InterestedUsers_Call {
 	_c.Call.Run(func(args mock.Arguments) {
 		run(args[0].(string))
 	})
 	return _c
 }
 
-func (_c *mockFeedbagManager_Retrieve_Call) Return(_a0 []oscar.FeedbagItem, _a1 error) *mockFeedbagManager_Retrieve_Call {
+func (_c *mockFeedbagManager_InterestedUsers_Call) Return(_a0 []string, _a1 error) *mockFeedbagManager_InterestedUsers_Call {
 	_c.Call.Return(_a0, _a1)
 	return _c
 }
 
-func (_c *mockFeedbagManager_Retrieve_Call) RunAndReturn(run func(string) ([]oscar.FeedbagItem, error)) *mockFeedbagManager_Retrieve_Call {
+func (_c *mockFeedbagManager_InterestedUsers_Call) RunAndReturn(run func(string) ([]string, error)) *mockFeedbagManager_InterestedUsers_Call {
 	_c.Call.Return(run)
 	return _c
 }
 
-// Upsert provides a mock function with given fields: screenName, items
-func (_m *mockFeedbagManager) Upsert(screenName string, items []oscar.FeedbagItem) error {
+// UpsertFeedbag provides a mock function with given fields: screenName, items
+func (_m *mockFeedbagManager) FeedbagUpsert(screenName string, items []oscar.FeedbagItem) error {
 	ret := _m.Called(screenName, items)
 
 	if len(ret) == 0 {
-		panic("no return value specified for Upsert")
+		panic("no return value specified for FeedbagUpsert")
 	}
 
 	var r0 error
@@ -376,31 +376,31 @@ func (_m *mockFeedbagManager) Upsert(screenName string, items []oscar.FeedbagIte
 	return r0
 }
 
-// mockFeedbagManager_Upsert_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Upsert'
-type mockFeedbagManager_Upsert_Call struct {
+// mockFeedbagManager_UpsertFeedbag_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FeedbagUpsert'
+type mockFeedbagManager_UpsertFeedbag_Call struct {
 	*mock.Call
 }
 
-// Upsert is a helper method to define mock.On call
+// UpsertFeedbag is a helper method to define mock.On call
 //   - screenName string
 //   - items []oscar.FeedbagItem
-func (_e *mockFeedbagManager_Expecter) Upsert(screenName interface{}, items interface{}) *mockFeedbagManager_Upsert_Call {
-	return &mockFeedbagManager_Upsert_Call{Call: _e.mock.On("Upsert", screenName, items)}
+func (_e *mockFeedbagManager_Expecter) UpsertFeedbag(screenName interface{}, items interface{}) *mockFeedbagManager_UpsertFeedbag_Call {
+	return &mockFeedbagManager_UpsertFeedbag_Call{Call: _e.mock.On("FeedbagUpsert", screenName, items)}
 }
 
-func (_c *mockFeedbagManager_Upsert_Call) Run(run func(screenName string, items []oscar.FeedbagItem)) *mockFeedbagManager_Upsert_Call {
+func (_c *mockFeedbagManager_UpsertFeedbag_Call) Run(run func(screenName string, items []oscar.FeedbagItem)) *mockFeedbagManager_UpsertFeedbag_Call {
 	_c.Call.Run(func(args mock.Arguments) {
 		run(args[0].(string), args[1].([]oscar.FeedbagItem))
 	})
 	return _c
 }
 
-func (_c *mockFeedbagManager_Upsert_Call) Return(_a0 error) *mockFeedbagManager_Upsert_Call {
+func (_c *mockFeedbagManager_UpsertFeedbag_Call) Return(_a0 error) *mockFeedbagManager_UpsertFeedbag_Call {
 	_c.Call.Return(_a0)
 	return _c
 }
 
-func (_c *mockFeedbagManager_Upsert_Call) RunAndReturn(run func(string, []oscar.FeedbagItem) error) *mockFeedbagManager_Upsert_Call {
+func (_c *mockFeedbagManager_UpsertFeedbag_Call) RunAndReturn(run func(string, []oscar.FeedbagItem) error) *mockFeedbagManager_UpsertFeedbag_Call {
 	_c.Call.Return(run)
 	return _c
 }

+ 36 - 36
handler/feedbag_test.go

@@ -34,13 +34,13 @@ func TestFeedbagService_QueryHandler(t *testing.T) {
 			},
 			mockParams: mockParams{
 				feedbagManagerParams: feedbagManagerParams{
-					retrieveParams: retrieveParams{
+					feedbagParams: feedbagParams{
 						{
 							screenName: "user_screen_name",
 							results:    []oscar.FeedbagItem{},
 						},
 					},
-					lastModifiedParams: lastModifiedParams{},
+					feedbagLastModifiedParams: feedbagLastModifiedParams{},
 				},
 			},
 			expectOutput: oscar.SNACMessage{
@@ -64,7 +64,7 @@ func TestFeedbagService_QueryHandler(t *testing.T) {
 			},
 			mockParams: mockParams{
 				feedbagManagerParams: feedbagManagerParams{
-					retrieveParams: retrieveParams{
+					feedbagParams: feedbagParams{
 						{
 							screenName: "user_screen_name",
 							results: []oscar.FeedbagItem{
@@ -77,7 +77,7 @@ func TestFeedbagService_QueryHandler(t *testing.T) {
 							},
 						},
 					},
-					lastModifiedParams: lastModifiedParams{
+					feedbagLastModifiedParams: feedbagLastModifiedParams{
 						{
 							screenName: "user_screen_name",
 							result:     time.UnixMilli(1696472198082),
@@ -110,14 +110,14 @@ func TestFeedbagService_QueryHandler(t *testing.T) {
 	for _, tc := range cases {
 		t.Run(tc.name, func(t *testing.T) {
 			feedbagManager := newMockFeedbagManager(t)
-			for _, params := range tc.mockParams.retrieveParams {
+			for _, params := range tc.mockParams.feedbagParams {
 				feedbagManager.EXPECT().
-					Retrieve(params.screenName).
+					Feedbag(params.screenName).
 					Return(params.results, nil)
 			}
-			for _, params := range tc.mockParams.lastModifiedParams {
+			for _, params := range tc.mockParams.feedbagLastModifiedParams {
 				feedbagManager.EXPECT().
-					LastModified(params.screenName).
+					FeedbagLastModified(params.screenName).
 					Return(params.result, nil)
 			}
 
@@ -158,7 +158,7 @@ func TestFeedbagService_QueryIfModifiedHandler(t *testing.T) {
 			},
 			mockParams: mockParams{
 				feedbagManagerParams: feedbagManagerParams{
-					retrieveParams: retrieveParams{
+					feedbagParams: feedbagParams{
 						{
 							screenName: "user_screen_name",
 							results:    []oscar.FeedbagItem{},
@@ -190,7 +190,7 @@ func TestFeedbagService_QueryIfModifiedHandler(t *testing.T) {
 			},
 			mockParams: mockParams{
 				feedbagManagerParams: feedbagManagerParams{
-					retrieveParams: retrieveParams{
+					feedbagParams: feedbagParams{
 						{
 							screenName: "user_screen_name",
 							results: []oscar.FeedbagItem{
@@ -203,7 +203,7 @@ func TestFeedbagService_QueryIfModifiedHandler(t *testing.T) {
 							},
 						},
 					},
-					lastModifiedParams: lastModifiedParams{
+					feedbagLastModifiedParams: feedbagLastModifiedParams{
 						{
 							screenName: "user_screen_name",
 							result:     time.UnixMilli(200000),
@@ -244,7 +244,7 @@ func TestFeedbagService_QueryIfModifiedHandler(t *testing.T) {
 			},
 			mockParams: mockParams{
 				feedbagManagerParams: feedbagManagerParams{
-					retrieveParams: retrieveParams{
+					feedbagParams: feedbagParams{
 						{
 							screenName: "user_screen_name",
 							results: []oscar.FeedbagItem{
@@ -257,7 +257,7 @@ func TestFeedbagService_QueryIfModifiedHandler(t *testing.T) {
 							},
 						},
 					},
-					lastModifiedParams: lastModifiedParams{
+					feedbagLastModifiedParams: feedbagLastModifiedParams{
 						{
 							screenName: "user_screen_name",
 							result:     time.UnixMilli(100000),
@@ -285,14 +285,14 @@ func TestFeedbagService_QueryIfModifiedHandler(t *testing.T) {
 			// initialize dependencies
 			//
 			feedbagManager := newMockFeedbagManager(t)
-			for _, params := range tc.mockParams.retrieveParams {
+			for _, params := range tc.mockParams.feedbagParams {
 				feedbagManager.EXPECT().
-					Retrieve(params.screenName).
+					Feedbag(params.screenName).
 					Return(params.results, nil)
 			}
-			for _, params := range tc.mockParams.lastModifiedParams {
+			for _, params := range tc.mockParams.feedbagLastModifiedParams {
 				feedbagManager.EXPECT().
-					LastModified(params.screenName).
+					FeedbagLastModified(params.screenName).
 					Return(params.result, nil)
 			}
 			//
@@ -402,7 +402,7 @@ func TestFeedbagService_InsertItemHandler(t *testing.T) {
 			},
 			mockParams: mockParams{
 				feedbagManagerParams: feedbagManagerParams{
-					upsertParams: upsertParams{
+					feedbagUpsertParams: feedbagUpsertParams{
 						{
 							screenName: "user_screen_name",
 							items: []oscar.FeedbagItem{
@@ -486,7 +486,7 @@ func TestFeedbagService_InsertItemHandler(t *testing.T) {
 			},
 			mockParams: mockParams{
 				feedbagManagerParams: feedbagManagerParams{
-					upsertParams: upsertParams{
+					feedbagUpsertParams: feedbagUpsertParams{
 						{
 							screenName: "user_screen_name",
 							items: []oscar.FeedbagItem{
@@ -536,7 +536,7 @@ func TestFeedbagService_InsertItemHandler(t *testing.T) {
 			},
 			mockParams: mockParams{
 				feedbagManagerParams: feedbagManagerParams{
-					upsertParams: upsertParams{
+					feedbagUpsertParams: feedbagUpsertParams{
 						{
 							screenName: "user_screen_name",
 							items: []oscar.FeedbagItem{
@@ -586,7 +586,7 @@ func TestFeedbagService_InsertItemHandler(t *testing.T) {
 			},
 			mockParams: mockParams{
 				feedbagManagerParams: feedbagManagerParams{
-					upsertParams: upsertParams{
+					feedbagUpsertParams: feedbagUpsertParams{
 						{
 							screenName: "user_screen_name",
 							items: []oscar.FeedbagItem{
@@ -653,7 +653,7 @@ func TestFeedbagService_InsertItemHandler(t *testing.T) {
 			},
 			mockParams: mockParams{
 				feedbagManagerParams: feedbagManagerParams{
-					upsertParams: upsertParams{
+					feedbagUpsertParams: feedbagUpsertParams{
 						{
 							screenName: "user_screen_name",
 							items: []oscar.FeedbagItem{
@@ -703,7 +703,7 @@ func TestFeedbagService_InsertItemHandler(t *testing.T) {
 			},
 			mockParams: mockParams{
 				feedbagManagerParams: feedbagManagerParams{
-					upsertParams: upsertParams{
+					feedbagUpsertParams: feedbagUpsertParams{
 						{
 							screenName: "user_screen_name",
 							items: []oscar.FeedbagItem{
@@ -759,9 +759,9 @@ func TestFeedbagService_InsertItemHandler(t *testing.T) {
 	for _, tc := range cases {
 		t.Run(tc.name, func(t *testing.T) {
 			feedbagManager := newMockFeedbagManager(t)
-			for _, params := range tc.mockParams.feedbagManagerParams.upsertParams {
+			for _, params := range tc.mockParams.feedbagManagerParams.feedbagUpsertParams {
 				feedbagManager.EXPECT().
-					Upsert(params.screenName, params.items).
+					UpsertFeedbag(params.screenName, params.items).
 					Return(nil)
 			}
 			messageRelayer := newMockMessageRelayer(t)
@@ -823,7 +823,7 @@ func TestFeedbagService_UpdateItemHandler(t *testing.T) {
 			},
 			mockParams: mockParams{
 				feedbagManagerParams: feedbagManagerParams{
-					upsertParams: upsertParams{
+					feedbagUpsertParams: feedbagUpsertParams{
 						{
 							screenName: "user_screen_name",
 							items: []oscar.FeedbagItem{
@@ -907,7 +907,7 @@ func TestFeedbagService_UpdateItemHandler(t *testing.T) {
 			},
 			mockParams: mockParams{
 				feedbagManagerParams: feedbagManagerParams{
-					upsertParams: upsertParams{
+					feedbagUpsertParams: feedbagUpsertParams{
 						{
 							screenName: "user_screen_name",
 							items: []oscar.FeedbagItem{
@@ -957,7 +957,7 @@ func TestFeedbagService_UpdateItemHandler(t *testing.T) {
 			},
 			mockParams: mockParams{
 				feedbagManagerParams: feedbagManagerParams{
-					upsertParams: upsertParams{
+					feedbagUpsertParams: feedbagUpsertParams{
 						{
 							screenName: "user_screen_name",
 							items: []oscar.FeedbagItem{
@@ -994,9 +994,9 @@ func TestFeedbagService_UpdateItemHandler(t *testing.T) {
 	for _, tc := range cases {
 		t.Run(tc.name, func(t *testing.T) {
 			feedbagManager := newMockFeedbagManager(t)
-			for _, params := range tc.mockParams.feedbagManagerParams.upsertParams {
+			for _, params := range tc.mockParams.feedbagManagerParams.feedbagUpsertParams {
 				feedbagManager.EXPECT().
-					Upsert(params.screenName, params.items).
+					UpsertFeedbag(params.screenName, params.items).
 					Return(nil)
 			}
 			messageRelayer := newMockMessageRelayer(t)
@@ -1054,7 +1054,7 @@ func TestFeedbagService_DeleteItemHandler(t *testing.T) {
 			},
 			mockParams: mockParams{
 				feedbagManagerParams: feedbagManagerParams{
-					deleteParams: deleteParams{
+					feedbagDeleteParams: feedbagDeleteParams{
 						{
 							screenName: "user_screen_name",
 							items: []oscar.FeedbagItem{
@@ -1100,7 +1100,7 @@ func TestFeedbagService_DeleteItemHandler(t *testing.T) {
 			},
 			mockParams: mockParams{
 				feedbagManagerParams: feedbagManagerParams{
-					deleteParams: deleteParams{
+					feedbagDeleteParams: feedbagDeleteParams{
 						{
 							screenName: "user_screen_name",
 							items: []oscar.FeedbagItem{
@@ -1208,7 +1208,7 @@ func TestFeedbagService_DeleteItemHandler(t *testing.T) {
 			},
 			mockParams: mockParams{
 				feedbagManagerParams: feedbagManagerParams{
-					deleteParams: deleteParams{
+					feedbagDeleteParams: feedbagDeleteParams{
 						{
 							screenName: "user_screen_name",
 							items: []oscar.FeedbagItem{
@@ -1258,7 +1258,7 @@ func TestFeedbagService_DeleteItemHandler(t *testing.T) {
 			},
 			mockParams: mockParams{
 				feedbagManagerParams: feedbagManagerParams{
-					deleteParams: deleteParams{
+					feedbagDeleteParams: feedbagDeleteParams{
 						{
 							screenName: "user_screen_name",
 							items: []oscar.FeedbagItem{
@@ -1309,9 +1309,9 @@ func TestFeedbagService_DeleteItemHandler(t *testing.T) {
 	for _, tc := range cases {
 		t.Run(tc.name, func(t *testing.T) {
 			feedbagManager := newMockFeedbagManager(t)
-			for _, params := range tc.mockParams.feedbagManagerParams.deleteParams {
+			for _, params := range tc.mockParams.feedbagManagerParams.feedbagDeleteParams {
 				feedbagManager.EXPECT().
-					Delete(params.screenName, params.items).
+					DeleteUser(params.screenName, params.items).
 					Return(nil)
 			}
 			messageRelayer := newMockMessageRelayer(t)

+ 3 - 3
handler/icbm.go

@@ -40,7 +40,7 @@ func (s ICBMService) ParameterQueryHandler(_ context.Context, inFrame oscar.SNAC
 }
 
 func (s ICBMService) ChannelMsgToHostHandler(ctx context.Context, sess *state.Session, inFrame oscar.SNACFrame, inBody oscar.SNAC_0x04_0x06_ICBMChannelMsgToHost) (*oscar.SNACMessage, error) {
-	blocked, err := s.feedbagManager.Blocked(sess.ScreenName(), inBody.ScreenName)
+	blocked, err := s.feedbagManager.BlockedState(sess.ScreenName(), inBody.ScreenName)
 	if err != nil {
 		return nil, err
 	}
@@ -126,7 +126,7 @@ func (s ICBMService) ChannelMsgToHostHandler(ctx context.Context, sess *state.Se
 }
 
 func (s ICBMService) ClientEventHandler(ctx context.Context, sess *state.Session, inFrame oscar.SNACFrame, inBody oscar.SNAC_0x04_0x14_ICBMClientEvent) error {
-	blocked, err := s.feedbagManager.Blocked(sess.ScreenName(), inBody.ScreenName)
+	blocked, err := s.feedbagManager.BlockedState(sess.ScreenName(), inBody.ScreenName)
 
 	switch {
 	case err != nil:
@@ -167,7 +167,7 @@ func (s ICBMService) EvilRequestHandler(ctx context.Context, sess *state.Session
 		}, nil
 	}
 
-	blocked, err := s.feedbagManager.Blocked(sess.ScreenName(), inBody.ScreenName)
+	blocked, err := s.feedbagManager.BlockedState(sess.ScreenName(), inBody.ScreenName)
 	if err != nil {
 		return oscar.SNACMessage{}, nil
 	}

+ 3 - 3
handler/icbm_test.go

@@ -228,7 +228,7 @@ func TestSendAndReceiveChannelMsgToHost(t *testing.T) {
 			//
 			feedbagManager := newMockFeedbagManager(t)
 			feedbagManager.EXPECT().
-				Blocked(tc.senderSession.ScreenName(),
+				BlockedState(tc.senderSession.ScreenName(),
 					tc.inputSNAC.Body.(oscar.SNAC_0x04_0x06_ICBMChannelMsgToHost).ScreenName).
 				Return(tc.blockedState, nil).
 				Maybe()
@@ -325,7 +325,7 @@ func TestSendAndReceiveClientEvent(t *testing.T) {
 			//
 			feedbagManager := newMockFeedbagManager(t)
 			feedbagManager.EXPECT().
-				Blocked(tc.senderScreenName, tc.inputSNAC.Body.(oscar.SNAC_0x04_0x14_ICBMClientEvent).ScreenName).
+				BlockedState(tc.senderScreenName, tc.inputSNAC.Body.(oscar.SNAC_0x04_0x14_ICBMClientEvent).ScreenName).
 				Return(tc.blockedState, nil).
 				Maybe()
 			messageRelayer := newMockMessageRelayer(t)
@@ -552,7 +552,7 @@ func TestSendAndReceiveEvilRequest(t *testing.T) {
 			//
 			feedbagManager := newMockFeedbagManager(t)
 			feedbagManager.EXPECT().
-				Blocked(tc.senderSession.ScreenName(), tc.recipientScreenName).
+				BlockedState(tc.senderSession.ScreenName(), tc.recipientScreenName).
 				Return(tc.blockedState, nil).
 				Maybe()
 			feedbagManager.EXPECT().

+ 3 - 3
handler/locate.go

@@ -52,7 +52,7 @@ func (s LocateService) RightsQueryHandler(_ context.Context, inFrame oscar.SNACF
 func (s LocateService) SetInfoHandler(ctx context.Context, sess *state.Session, inBody oscar.SNAC_0x02_0x04_LocateSetInfo) error {
 	// update profile
 	if profile, hasProfile := inBody.GetString(oscar.LocateTLVTagsInfoSigData); hasProfile {
-		if err := s.profileManager.UpsertProfile(sess.ScreenName(), profile); err != nil {
+		if err := s.profileManager.SetProfile(sess.ScreenName(), profile); err != nil {
 			return err
 		}
 	}
@@ -72,7 +72,7 @@ func (s LocateService) SetInfoHandler(ctx context.Context, sess *state.Session,
 // the profile, if requested, and/or the away message, if requested. This is a
 // v2 of the UserInfoQueryHandler handler.
 func (s LocateService) UserInfoQuery2Handler(ctx context.Context, sess *state.Session, inFrame oscar.SNACFrame, inBody oscar.SNAC_0x02_0x15_LocateUserInfoQuery2) (oscar.SNACMessage, error) {
-	blocked, err := s.feedbagManager.Blocked(sess.ScreenName(), inBody.ScreenName)
+	blocked, err := s.feedbagManager.BlockedState(sess.ScreenName(), inBody.ScreenName)
 	switch {
 	case err != nil:
 		return oscar.SNACMessage{}, err
@@ -106,7 +106,7 @@ func (s LocateService) UserInfoQuery2Handler(ctx context.Context, sess *state.Se
 	var list oscar.TLVList
 
 	if inBody.RequestProfile() {
-		profile, err := s.profileManager.RetrieveProfile(inBody.ScreenName)
+		profile, err := s.profileManager.Profile(inBody.ScreenName)
 		if err != nil {
 			return oscar.SNACMessage{}, err
 		}

+ 9 - 9
handler/locate_test.go

@@ -29,7 +29,7 @@ func TestLocateService_UserInfoQuery2Handler(t *testing.T) {
 			name: "request user info, expect user info response",
 			mockParams: mockParams{
 				feedbagManagerParams: feedbagManagerParams{
-					blockedParams: blockedParams{
+					blockedStateParams: blockedStateParams{
 						{
 							screenName1: "user_screen_name",
 							screenName2: "requested-user",
@@ -77,7 +77,7 @@ func TestLocateService_UserInfoQuery2Handler(t *testing.T) {
 			name: "request user info + profile, expect user info response + profile",
 			mockParams: mockParams{
 				feedbagManagerParams: feedbagManagerParams{
-					blockedParams: blockedParams{
+					blockedStateParams: blockedStateParams{
 						{
 							screenName1: "user_screen_name",
 							screenName2: "requested-user",
@@ -139,7 +139,7 @@ func TestLocateService_UserInfoQuery2Handler(t *testing.T) {
 			name: "request user info + profile, expect user info response + profile",
 			mockParams: mockParams{
 				feedbagManagerParams: feedbagManagerParams{
-					blockedParams: blockedParams{
+					blockedStateParams: blockedStateParams{
 						{
 							screenName1: "user_screen_name",
 							screenName2: "requested-user",
@@ -201,7 +201,7 @@ func TestLocateService_UserInfoQuery2Handler(t *testing.T) {
 			name: "request user info + away message, expect user info response + away message",
 			mockParams: mockParams{
 				feedbagManagerParams: feedbagManagerParams{
-					blockedParams: blockedParams{
+					blockedStateParams: blockedStateParams{
 						{
 							screenName1: "user_screen_name",
 							screenName2: "requested-user",
@@ -255,7 +255,7 @@ func TestLocateService_UserInfoQuery2Handler(t *testing.T) {
 			name: "request user info of user who blocked requester, expect not logged in error",
 			mockParams: mockParams{
 				feedbagManagerParams: feedbagManagerParams{
-					blockedParams: blockedParams{
+					blockedStateParams: blockedStateParams{
 						{
 							screenName1: "user_screen_name",
 							screenName2: "requested-user",
@@ -288,7 +288,7 @@ func TestLocateService_UserInfoQuery2Handler(t *testing.T) {
 			name: "request user info of user who does not exist, expect not logged in error",
 			mockParams: mockParams{
 				feedbagManagerParams: feedbagManagerParams{
-					blockedParams: blockedParams{
+					blockedStateParams: blockedStateParams{
 						{
 							screenName1: "user_screen_name",
 							screenName2: "non_existent_requested_user",
@@ -330,9 +330,9 @@ func TestLocateService_UserInfoQuery2Handler(t *testing.T) {
 	for _, tc := range cases {
 		t.Run(tc.name, func(t *testing.T) {
 			feedbagManager := newMockFeedbagManager(t)
-			for _, params := range tc.mockParams.blockedParams {
+			for _, params := range tc.mockParams.blockedStateParams {
 				feedbagManager.EXPECT().
-					Blocked(params.screenName1, params.screenName2).
+					BlockedState(params.screenName1, params.screenName2).
 					Return(params.result, nil)
 			}
 			messageRelayer := newMockMessageRelayer(t)
@@ -344,7 +344,7 @@ func TestLocateService_UserInfoQuery2Handler(t *testing.T) {
 			profileManager := newMockProfileManager(t)
 			for _, val := range tc.mockParams.retrieveProfileParams {
 				profileManager.EXPECT().
-					RetrieveProfile(val.screenName).
+					Profile(val.screenName).
 					Return(val.result, val.err)
 			}
 			svc := NewLocateService(messageRelayer, feedbagManager, profileManager)

+ 15 - 15
handler/profile_manager_mock_test.go

@@ -17,12 +17,12 @@ func (_m *mockProfileManager) EXPECT() *mockProfileManager_Expecter {
 	return &mockProfileManager_Expecter{mock: &_m.Mock}
 }
 
-// RetrieveProfile provides a mock function with given fields: screenName
-func (_m *mockProfileManager) RetrieveProfile(screenName string) (string, error) {
+// Profile provides a mock function with given fields: screenName
+func (_m *mockProfileManager) Profile(screenName string) (string, error) {
 	ret := _m.Called(screenName)
 
 	if len(ret) == 0 {
-		panic("no return value specified for RetrieveProfile")
+		panic("no return value specified for Profile")
 	}
 
 	var r0 string
@@ -45,40 +45,40 @@ func (_m *mockProfileManager) RetrieveProfile(screenName string) (string, error)
 	return r0, r1
 }
 
-// mockProfileManager_RetrieveProfile_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RetrieveProfile'
-type mockProfileManager_RetrieveProfile_Call struct {
+// mockProfileManager_Profile_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Profile'
+type mockProfileManager_Profile_Call struct {
 	*mock.Call
 }
 
-// RetrieveProfile is a helper method to define mock.On call
+// Profile is a helper method to define mock.On call
 //   - screenName string
-func (_e *mockProfileManager_Expecter) RetrieveProfile(screenName interface{}) *mockProfileManager_RetrieveProfile_Call {
-	return &mockProfileManager_RetrieveProfile_Call{Call: _e.mock.On("RetrieveProfile", screenName)}
+func (_e *mockProfileManager_Expecter) Profile(screenName interface{}) *mockProfileManager_Profile_Call {
+	return &mockProfileManager_Profile_Call{Call: _e.mock.On("Profile", screenName)}
 }
 
-func (_c *mockProfileManager_RetrieveProfile_Call) Run(run func(screenName string)) *mockProfileManager_RetrieveProfile_Call {
+func (_c *mockProfileManager_Profile_Call) Run(run func(screenName string)) *mockProfileManager_Profile_Call {
 	_c.Call.Run(func(args mock.Arguments) {
 		run(args[0].(string))
 	})
 	return _c
 }
 
-func (_c *mockProfileManager_RetrieveProfile_Call) Return(_a0 string, _a1 error) *mockProfileManager_RetrieveProfile_Call {
+func (_c *mockProfileManager_Profile_Call) Return(_a0 string, _a1 error) *mockProfileManager_Profile_Call {
 	_c.Call.Return(_a0, _a1)
 	return _c
 }
 
-func (_c *mockProfileManager_RetrieveProfile_Call) RunAndReturn(run func(string) (string, error)) *mockProfileManager_RetrieveProfile_Call {
+func (_c *mockProfileManager_Profile_Call) RunAndReturn(run func(string) (string, error)) *mockProfileManager_Profile_Call {
 	_c.Call.Return(run)
 	return _c
 }
 
 // UpsertProfile provides a mock function with given fields: screenName, body
-func (_m *mockProfileManager) UpsertProfile(screenName string, body string) error {
+func (_m *mockProfileManager) SetProfile(screenName string, body string) error {
 	ret := _m.Called(screenName, body)
 
 	if len(ret) == 0 {
-		panic("no return value specified for UpsertProfile")
+		panic("no return value specified for SetProfile")
 	}
 
 	var r0 error
@@ -91,7 +91,7 @@ func (_m *mockProfileManager) UpsertProfile(screenName string, body string) erro
 	return r0
 }
 
-// mockProfileManager_UpsertProfile_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpsertProfile'
+// mockProfileManager_UpsertProfile_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetProfile'
 type mockProfileManager_UpsertProfile_Call struct {
 	*mock.Call
 }
@@ -100,7 +100,7 @@ type mockProfileManager_UpsertProfile_Call struct {
 //   - screenName string
 //   - body string
 func (_e *mockProfileManager_Expecter) UpsertProfile(screenName interface{}, body interface{}) *mockProfileManager_UpsertProfile_Call {
-	return &mockProfileManager_UpsertProfile_Call{Call: _e.mock.On("UpsertProfile", screenName, body)}
+	return &mockProfileManager_UpsertProfile_Call{Call: _e.mock.On("SetProfile", screenName, body)}
 }
 
 func (_c *mockProfileManager_UpsertProfile_Call) Run(run func(screenName string, body string)) *mockProfileManager_UpsertProfile_Call {

+ 20 - 20
handler/test_helpers.go

@@ -82,18 +82,18 @@ type emptyParams []struct {
 // feedbagManagerParams is a helper struct that contains mock parameters for
 // FeedbagManager methods
 type feedbagManagerParams struct {
-	blockedParams
+	blockedStateParams
 	interestedUsersParams
-	upsertParams
+	feedbagUpsertParams
 	buddiesParams
-	retrieveParams
-	lastModifiedParams
-	deleteParams
+	feedbagParams
+	feedbagLastModifiedParams
+	feedbagDeleteParams
 }
 
-// blockedParams is the list of parameters passed at the mock
-// FeedbagManager.Blocked call site
-type blockedParams []struct {
+// blockedStateParams is the list of parameters passed at the mock
+// FeedbagManager.BlockedState call site
+type blockedStateParams []struct {
 	screenName1 string
 	screenName2 string
 	result      state.BlockedState
@@ -108,9 +108,9 @@ type interestedUsersParams []struct {
 	err        error
 }
 
-// upsertParams is the list of parameters passed at the mock
-// FeedbagManager.Upsert call site
-type upsertParams []struct {
+// feedbagUpsertParams is the list of parameters passed at the mock
+// FeedbagManager.FeedbagUpsert call site
+type feedbagUpsertParams []struct {
 	screenName string
 	items      []oscar.FeedbagItem
 }
@@ -122,23 +122,23 @@ type buddiesParams []struct {
 	results    []string
 }
 
-// retrieveParams is the list of parameters passed at the mock
-// FeedbagManager.Retrieve call site
-type retrieveParams []struct {
+// feedbagParams is the list of parameters passed at the mock
+// FeedbagManager.Feedbag call site
+type feedbagParams []struct {
 	screenName string
 	results    []oscar.FeedbagItem
 }
 
-// lastModifiedParams is the list of parameters passed at the mock
-// FeedbagManager.LastModified call site
-type lastModifiedParams []struct {
+// feedbagLastModifiedParams is the list of parameters passed at the mock
+// FeedbagManager.FeedbagLastModified call site
+type feedbagLastModifiedParams []struct {
 	screenName string
 	result     time.Time
 }
 
-// deleteParams is the list of parameters passed at the mock
-// FeedbagManager.Delete call site
-type deleteParams []struct {
+// feedbagDeleteParams is the list of parameters passed at the mock
+// FeedbagManager.FeedbagDelete call site
+type feedbagDeleteParams []struct {
 	screenName string
 	items      []oscar.FeedbagItem
 }

+ 10 - 10
handler/types.go

@@ -9,18 +9,18 @@ import (
 )
 
 type FeedbagManager interface {
-	Blocked(screenName1, screenName2 string) (state.BlockedState, error)
+	BlockedState(screenName1, screenName2 string) (state.BlockedState, error)
 	Buddies(screenName string) ([]string, error)
-	Delete(screenName string, items []oscar.FeedbagItem) error
-	InterestedUsers(screenName string) ([]string, error)
-	LastModified(screenName string) (time.Time, error)
-	Retrieve(screenName string) ([]oscar.FeedbagItem, error)
-	Upsert(screenName string, items []oscar.FeedbagItem) error
+	FeedbagDelete(screenName string, items []oscar.FeedbagItem) error
+	AdjacentUsers(screenName string) ([]string, error)
+	FeedbagLastModified(screenName string) (time.Time, error)
+	Feedbag(screenName string) ([]oscar.FeedbagItem, error)
+	FeedbagUpsert(screenName string, items []oscar.FeedbagItem) error
 }
 
 type UserManager interface {
-	GetUser(screenName string) (*state.User, error)
-	UpsertUser(u state.User) error
+	User(screenName string) (*state.User, error)
+	InsertUser(u state.User) error
 }
 
 type SessionManager interface {
@@ -31,8 +31,8 @@ type SessionManager interface {
 }
 
 type ProfileManager interface {
-	RetrieveProfile(screenName string) (string, error)
-	UpsertProfile(screenName string, body string) error
+	Profile(screenName string) (string, error)
+	SetProfile(screenName string, body string) error
 }
 
 type MessageRelayer interface {

+ 52 - 52
handler/user_manager_manager_mock_test.go

@@ -20,106 +20,106 @@ func (_m *mockUserManager) EXPECT() *mockUserManager_Expecter {
 	return &mockUserManager_Expecter{mock: &_m.Mock}
 }
 
-// GetUser provides a mock function with given fields: screenName
-func (_m *mockUserManager) GetUser(screenName string) (*state.User, error) {
-	ret := _m.Called(screenName)
+// InsertUser provides a mock function with given fields: u
+func (_m *mockUserManager) InsertUser(u state.User) error {
+	ret := _m.Called(u)
 
 	if len(ret) == 0 {
-		panic("no return value specified for GetUser")
-	}
-
-	var r0 *state.User
-	var r1 error
-	if rf, ok := ret.Get(0).(func(string) (*state.User, error)); ok {
-		return rf(screenName)
-	}
-	if rf, ok := ret.Get(0).(func(string) *state.User); ok {
-		r0 = rf(screenName)
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).(*state.User)
-		}
+		panic("no return value specified for InsertUser")
 	}
 
-	if rf, ok := ret.Get(1).(func(string) error); ok {
-		r1 = rf(screenName)
+	var r0 error
+	if rf, ok := ret.Get(0).(func(state.User) error); ok {
+		r0 = rf(u)
 	} else {
-		r1 = ret.Error(1)
+		r0 = ret.Error(0)
 	}
 
-	return r0, r1
+	return r0
 }
 
-// mockUserManager_GetUser_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetUser'
-type mockUserManager_GetUser_Call struct {
+// mockUserManager_InsertUser_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'InsertUser'
+type mockUserManager_InsertUser_Call struct {
 	*mock.Call
 }
 
-// GetUser is a helper method to define mock.On call
-//   - screenName string
-func (_e *mockUserManager_Expecter) GetUser(screenName interface{}) *mockUserManager_GetUser_Call {
-	return &mockUserManager_GetUser_Call{Call: _e.mock.On("GetUser", screenName)}
+// InsertUser is a helper method to define mock.On call
+//   - u state.User
+func (_e *mockUserManager_Expecter) InsertUser(u interface{}) *mockUserManager_InsertUser_Call {
+	return &mockUserManager_InsertUser_Call{Call: _e.mock.On("InsertUser", u)}
 }
 
-func (_c *mockUserManager_GetUser_Call) Run(run func(screenName string)) *mockUserManager_GetUser_Call {
+func (_c *mockUserManager_InsertUser_Call) Run(run func(u state.User)) *mockUserManager_InsertUser_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(string))
+		run(args[0].(state.User))
 	})
 	return _c
 }
 
-func (_c *mockUserManager_GetUser_Call) Return(_a0 *state.User, _a1 error) *mockUserManager_GetUser_Call {
-	_c.Call.Return(_a0, _a1)
+func (_c *mockUserManager_InsertUser_Call) Return(_a0 error) *mockUserManager_InsertUser_Call {
+	_c.Call.Return(_a0)
 	return _c
 }
 
-func (_c *mockUserManager_GetUser_Call) RunAndReturn(run func(string) (*state.User, error)) *mockUserManager_GetUser_Call {
+func (_c *mockUserManager_InsertUser_Call) RunAndReturn(run func(state.User) error) *mockUserManager_InsertUser_Call {
 	_c.Call.Return(run)
 	return _c
 }
 
-// UpsertUser provides a mock function with given fields: u
-func (_m *mockUserManager) UpsertUser(u state.User) error {
-	ret := _m.Called(u)
+// User provides a mock function with given fields: screenName
+func (_m *mockUserManager) User(screenName string) (*state.User, error) {
+	ret := _m.Called(screenName)
 
 	if len(ret) == 0 {
-		panic("no return value specified for UpsertUser")
+		panic("no return value specified for User")
 	}
 
-	var r0 error
-	if rf, ok := ret.Get(0).(func(state.User) error); ok {
-		r0 = rf(u)
+	var r0 *state.User
+	var r1 error
+	if rf, ok := ret.Get(0).(func(string) (*state.User, error)); ok {
+		return rf(screenName)
+	}
+	if rf, ok := ret.Get(0).(func(string) *state.User); ok {
+		r0 = rf(screenName)
 	} else {
-		r0 = ret.Error(0)
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*state.User)
+		}
 	}
 
-	return r0
+	if rf, ok := ret.Get(1).(func(string) error); ok {
+		r1 = rf(screenName)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
 }
 
-// mockUserManager_UpsertUser_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpsertUser'
-type mockUserManager_UpsertUser_Call struct {
+// mockUserManager_User_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'User'
+type mockUserManager_User_Call struct {
 	*mock.Call
 }
 
-// UpsertUser is a helper method to define mock.On call
-//   - u state.User
-func (_e *mockUserManager_Expecter) UpsertUser(u interface{}) *mockUserManager_UpsertUser_Call {
-	return &mockUserManager_UpsertUser_Call{Call: _e.mock.On("UpsertUser", u)}
+// User is a helper method to define mock.On call
+//   - screenName string
+func (_e *mockUserManager_Expecter) User(screenName interface{}) *mockUserManager_User_Call {
+	return &mockUserManager_User_Call{Call: _e.mock.On("User", screenName)}
 }
 
-func (_c *mockUserManager_UpsertUser_Call) Run(run func(u state.User)) *mockUserManager_UpsertUser_Call {
+func (_c *mockUserManager_User_Call) Run(run func(screenName string)) *mockUserManager_User_Call {
 	_c.Call.Run(func(args mock.Arguments) {
-		run(args[0].(state.User))
+		run(args[0].(string))
 	})
 	return _c
 }
 
-func (_c *mockUserManager_UpsertUser_Call) Return(_a0 error) *mockUserManager_UpsertUser_Call {
-	_c.Call.Return(_a0)
+func (_c *mockUserManager_User_Call) Return(_a0 *state.User, _a1 error) *mockUserManager_User_Call {
+	_c.Call.Return(_a0, _a1)
 	return _c
 }
 
-func (_c *mockUserManager_UpsertUser_Call) RunAndReturn(run func(state.User) error) *mockUserManager_UpsertUser_Call {
+func (_c *mockUserManager_User_Call) RunAndReturn(run func(string) (*state.User, error)) *mockUserManager_User_Call {
 	_c.Call.Return(run)
 	return _c
 }

+ 4 - 4
server/mgmt_api.go

@@ -12,7 +12,7 @@ import (
 	"github.com/mkaminski/goaim/state"
 )
 
-func StartManagementAPI(fs *state.SQLiteFeedbagStore, logger *slog.Logger) {
+func StartManagementAPI(fs *state.SQLiteUserStore, logger *slog.Logger) {
 	http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) {
 		switch r.Method {
 		case http.MethodGet:
@@ -38,9 +38,9 @@ func StartManagementAPI(fs *state.SQLiteFeedbagStore, logger *slog.Logger) {
 }
 
 // getUsers handles the GET /user endpoint.
-func getUsers(fs *state.SQLiteFeedbagStore, w http.ResponseWriter, r *http.Request) {
+func getUsers(fs *state.SQLiteUserStore, w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
-	users, err := fs.Users()
+	users, err := fs.AllUsers()
 	if err != nil {
 		http.Error(w, err.Error(), http.StatusInternalServerError)
 		return
@@ -57,7 +57,7 @@ type CreateUser struct {
 }
 
 // createUser handles the POST /user endpoint.
-func createUser(fs *state.SQLiteFeedbagStore, w http.ResponseWriter, r *http.Request) {
+func createUser(fs *state.SQLiteUserStore, w http.ResponseWriter, r *http.Request) {
 	var newUser CreateUser
 	if err := json.NewDecoder(r.Body).Decode(&newUser); err != nil {
 		http.Error(w, err.Error(), http.StatusBadRequest)

+ 118 - 108
state/feedbag_store.go → state/user_store.go

@@ -13,56 +13,52 @@ import (
 	"github.com/mkaminski/goaim/oscar"
 )
 
-var feedbagDDL = `
+var userStoreDDL = `
 	CREATE TABLE IF NOT EXISTS user
 	(
-		ScreenName VARCHAR(16) PRIMARY KEY,
+		screenName VARCHAR(16) PRIMARY KEY,
 		authKey    TEXT,
 		passHash   TEXT
 	);
 	CREATE TABLE IF NOT EXISTS feedbag
 	(
-		ScreenName   VARCHAR(16),
+		screenName   VARCHAR(16),
 		groupID      INTEGER,
 		itemID       INTEGER,
 		classID      INTEGER,
 		name         TEXT,
 		attributes   BLOB,
 		lastModified INTEGER,
-		UNIQUE (ScreenName, groupID, itemID)
+		UNIQUE (screenName, groupID, itemID)
 	);
 	CREATE TABLE IF NOT EXISTS profile
 	(
-		ScreenName VARCHAR(16) PRIMARY KEY,
+		screenName VARCHAR(16) PRIMARY KEY,
 		body  TEXT
 	);
 `
 
+// BlockedState represents the blocked status between two users
 type BlockedState int
 
 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
 )
 
-func NewSQLiteFeedbagStore(dbFile string) (*SQLiteFeedbagStore, error) {
-	db, err := sql.Open("sqlite3", dbFile)
-	if err != nil {
-		return nil, err
-	}
-	if _, err := db.Exec(feedbagDDL); err != nil {
-		return nil, err
-	}
-	return &SQLiteFeedbagStore{db: db}, nil
-}
-
+// User represents an instant messaging user.
 type User struct {
 	ScreenName string `json:"screen_name"`
 	AuthKey    string `json:"-"`
 	PassHash   []byte `json:"-"`
 }
 
+// HashPassword creates a password hash using the MD5 digest algorithm. The
+// hash is stored in the User.PassHash field.
 func (u *User) HashPassword(passwd string) error {
 	top := md5.New()
 	if _, err := io.WriteString(top, passwd); err != nil {
@@ -82,21 +78,39 @@ func (u *User) HashPassword(passwd string) error {
 	return nil
 }
 
-type SQLiteFeedbagStore struct {
+// SQLiteUserStore stores user feedbag (buddy list), profile, and
+// authentication credentials information in a SQLite database.
+type SQLiteUserStore struct {
 	db *sql.DB
 }
 
-func (f *SQLiteFeedbagStore) Users() ([]*User, error) {
-	q := `SELECT ScreenName FROM user`
+// NewSQLiteUserStore creates a new instance of SQLiteUserStore. If the
+// database does not already exist, a new one is created with the required
+// schema.
+func NewSQLiteUserStore(dbFilePath string) (*SQLiteUserStore, error) {
+	db, err := sql.Open("sqlite3", dbFilePath)
+	if err != nil {
+		return nil, err
+	}
+	if _, err := db.Exec(userStoreDDL); err != nil {
+		return nil, err
+	}
+	return &SQLiteUserStore{db: db}, nil
+}
+
+// AllUsers returns all stored users. It only populates the User.ScreenName field
+// populated in the returned slice.
+func (f SQLiteUserStore) AllUsers() ([]User, error) {
+	q := `SELECT screenName FROM user`
 	rows, err := f.db.Query(q)
 	if err != nil {
 		return nil, err
 	}
 	defer rows.Close()
 
-	var users []*User
+	var users []User
 	for rows.Next() {
-		u := &User{}
+		u := User{}
 		if err := rows.Scan(&u.ScreenName); err != nil {
 			return nil, err
 		}
@@ -110,35 +124,30 @@ func (f *SQLiteFeedbagStore) Users() ([]*User, error) {
 	return users, nil
 }
 
-func (f *SQLiteFeedbagStore) GetUser(screenName string) (*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) {
 	q := `
-		SELECT 
-			ScreenName, 
+		SELECT
+			screenName, 
 			authKey, 
 			passHash
 		FROM user
-		WHERE ScreenName = ?
+		WHERE screenName = ?
 	`
 	u := &User{}
 	err := f.db.QueryRow(q, screenName).Scan(&u.ScreenName, &u.AuthKey, &u.PassHash)
-	if err == sql.ErrNoRows {
+	if errors.Is(err, sql.ErrNoRows) {
 		return nil, nil
 	}
 	return u, err
 }
 
-func (f *SQLiteFeedbagStore) InsertUser(u User) error {
-	q := `
-		INSERT INTO user (ScreenName, authKey, passHash)
-		VALUES (?, ?, ?)
-	`
-	_, err := f.db.Exec(q, u.ScreenName, u.AuthKey, u.PassHash)
-	return err
-}
-
-func (f *SQLiteFeedbagStore) UpsertUser(u User) error {
+// InsertUser inserts a user to the store. It does not overwrite any fields if
+// the user already exists.
+func (f SQLiteUserStore) InsertUser(u User) error {
 	q := `
-		INSERT INTO user (ScreenName, authKey, passHash)
+		INSERT INTO user (screenName, authKey, passHash)
 		VALUES (?, ?, ?)
 		ON CONFLICT DO NOTHING
 	`
@@ -146,20 +155,8 @@ func (f *SQLiteFeedbagStore) UpsertUser(u User) error {
 	return err
 }
 
-func (f *SQLiteFeedbagStore) Delete(screenName string, items []oscar.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 {
-			return err
-		}
-	}
-
-	return nil
-}
-
-func (f *SQLiteFeedbagStore) Retrieve(screenName string) ([]oscar.FeedbagItem, error) {
+// Feedbag fetches the contents of a user's feedbag (buddy list).
+func (f SQLiteUserStore) Feedbag(screenName string) ([]oscar.FeedbagItem, error) {
 	q := `
 		SELECT 
 			groupID,
@@ -168,7 +165,7 @@ func (f *SQLiteFeedbagStore) Retrieve(screenName string) ([]oscar.FeedbagItem, e
 			name,
 			attributes
 		FROM feedbag
-		WHERE ScreenName = ?
+		WHERE screenName = ?
 	`
 
 	rows, err := f.db.Query(q, screenName)
@@ -184,8 +181,7 @@ func (f *SQLiteFeedbagStore) Retrieve(screenName string) ([]oscar.FeedbagItem, e
 		if err := rows.Scan(&item.GroupID, &item.ItemID, &item.ClassID, &item.Name, &attrs); err != nil {
 			return nil, err
 		}
-		err = oscar.Unmarshal(&item.TLVLBlock, bytes.NewBuffer(attrs))
-		if err != nil {
+		if err := oscar.Unmarshal(&item.TLVLBlock, bytes.NewBuffer(attrs)); err != nil {
 			return items, err
 		}
 		items = append(items, item)
@@ -194,19 +190,36 @@ func (f *SQLiteFeedbagStore) Retrieve(screenName string) ([]oscar.FeedbagItem, e
 	return items, nil
 }
 
-func (f *SQLiteFeedbagStore) LastModified(screenName string) (time.Time, error) {
+// FeedbagLastModified returns the last time a user's feedbag (buddy list) was
+// updated.
+func (f SQLiteUserStore) FeedbagLastModified(screenName string) (time.Time, error) {
 	var lastModified sql.NullInt64
-	q := `SELECT MAX(lastModified) FROM feedbag WHERE ScreenName = ?`
+	q := `SELECT MAX(lastModified) FROM feedbag WHERE screenName = ?`
 	err := f.db.QueryRow(q, screenName).Scan(&lastModified)
 	return time.Unix(lastModified.Int64, 0), err
 }
 
-func (f *SQLiteFeedbagStore) Upsert(screenName string, items []oscar.FeedbagItem) error {
+// FeedbagDelete deletes an entry from a user's feedbag (buddy list).
+func (f SQLiteUserStore) FeedbagDelete(screenName string, items []oscar.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 {
+			return err
+		}
+	}
+
+	return nil
+}
 
+// 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 []oscar.FeedbagItem) error {
 	q := `
-		INSERT INTO feedbag (ScreenName, groupID, itemID, classID, name, attributes, lastModified)
+		INSERT INTO feedbag (screenName, groupID, itemID, classID, name, attributes, lastModified)
 		VALUES (?, ?, ?, ?, ?, ?, UNIXEPOCH())
-		ON CONFLICT (ScreenName, groupID, itemID)
+		ON CONFLICT (screenName, groupID, itemID)
 			DO UPDATE SET classID      = excluded.classID,
 						  name         = excluded.name,
 						  attributes   = excluded.attributes,
@@ -214,7 +227,6 @@ func (f *SQLiteFeedbagStore) Upsert(screenName string, items []oscar.FeedbagItem
 	`
 
 	for _, item := range items {
-
 		buf := &bytes.Buffer{}
 		if err := oscar.Marshal(item.TLVLBlock, buf); err != nil {
 			return err
@@ -235,18 +247,18 @@ func (f *SQLiteFeedbagStore) Upsert(screenName string, items []oscar.FeedbagItem
 	return nil
 }
 
-// InterestedUsers returns all users who have screenName in their buddy list.
+// AdjacentUsers returns all users who have screenName in their buddy list.
 // Exclude users who are on screenName's block list.
-func (f *SQLiteFeedbagStore) InterestedUsers(screenName string) ([]string, error) {
+func (f SQLiteUserStore) AdjacentUsers(screenName string) ([]string, error) {
 	q := `
-		SELECT f.ScreenName
+		SELECT f.screenName
 		FROM feedbag f
 		WHERE f.name = ?
 		  AND f.classID = 0
 		-- Don't show screenName that its blocked buddy is online
-		AND NOT EXISTS(SELECT 1 FROM feedbag WHERE ScreenName = ? AND name = f.ScreenName AND classID = 3)
+		AND NOT EXISTS(SELECT 1 FROM feedbag WHERE screenName = ? AND name = f.screenName AND classID = 3)
 		-- Don't show blocked buddy that screenName is online
-		AND NOT EXISTS(SELECT 1 FROM feedbag WHERE ScreenName = f.ScreenName AND name = f.name AND classID = 3)
+		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)
@@ -257,27 +269,27 @@ func (f *SQLiteFeedbagStore) InterestedUsers(screenName string) ([]string, error
 
 	var items []string
 	for rows.Next() {
-		var screenName string
-		if err := rows.Scan(&screenName); err != nil {
+		var sn string
+		if err := rows.Scan(&sn); err != nil {
 			return nil, err
 		}
-		items = append(items, screenName)
+		items = append(items, sn)
 	}
 
 	return items, nil
 }
 
-// Buddies returns all user's buddies. Don't return a buddy if screenName
-// blocked them.
-func (f *SQLiteFeedbagStore) Buddies(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) {
 	q := `
 		SELECT f.name
 		FROM feedbag f
-		WHERE f.ScreenName = ? AND f.classID = 0
+		WHERE f.screenName = ? AND f.classID = 0
 		-- Don't include buddy if they blocked screenName
-		AND NOT EXISTS(SELECT 1 FROM feedbag WHERE ScreenName = f.name AND name = ? AND classID = 3)
+		AND NOT EXISTS(SELECT 1 FROM feedbag WHERE screenName = f.name AND name = ? AND classID = 3)
 		-- Don't include buddy if screen name blocked them
-		AND NOT EXISTS(SELECT 1 FROM feedbag WHERE ScreenName = ? AND name = f.name AND classID = 3)
+		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)
@@ -288,52 +300,49 @@ func (f *SQLiteFeedbagStore) Buddies(screenName string) ([]string, error) {
 
 	var items []string
 	for rows.Next() {
-		var screenName string
-		if err := rows.Scan(&screenName); err != nil {
+		var sn string
+		if err := rows.Scan(&sn); err != nil {
 			return nil, err
 		}
-		items = append(items, screenName)
+		items = append(items, sn)
 	}
 
 	return items, nil
 }
 
-// Blocked informs whether there is a blocking relationship between sn1 and
-// sn2. Return BlockedA if sn1 blocked sn2, BlockedB if sn2 blocked sn1, or
-// BlockedNo if neither screen name blocked the other.
-func (f *SQLiteFeedbagStore) Blocked(sn1, sn2 string) (BlockedState, error) {
+// BlockedState returns the BlockedState between two users.
+func (f SQLiteUserStore) BlockedState(screenNameA, screenNameB string) (BlockedState, error) {
 	q := `
 		SELECT EXISTS(SELECT 1
 					  FROM feedbag f
 					  WHERE f.classID = 3
-						AND f.ScreenName = ?
+						AND f.screenName = ?
 						AND f.name = ?)
 		UNION ALL
 		SELECT EXISTS(SELECT 1
 					  FROM feedbag f
 					  WHERE f.classID = 3
-						AND f.ScreenName = ?
+						AND f.screenName = ?
 						AND f.name = ?)
 	`
-	var blockedA bool
-	row, err := f.db.Query(q, sn1, sn2, sn2, sn1)
+	row, err := f.db.Query(q, screenNameA, screenNameB, screenNameB, screenNameA)
 	if err != nil {
-		// todo check to make sure there's no runtime error here...
 		return BlockedNo, err
 	}
 	defer row.Close()
 
-	row.Next()
-	err = row.Scan(&blockedA)
-	if err != nil {
-		return BlockedNo, err
+	var blockedA bool
+	if row.Next() {
+		if err := row.Scan(&blockedA); err != nil {
+			return BlockedNo, err
+		}
 	}
 
-	row.Next()
 	var blockedB bool
-	err = row.Scan(&blockedB)
-	if err != nil {
-		return BlockedNo, err
+	if row.Next() {
+		if err := row.Scan(&blockedB); err != nil {
+			return BlockedNo, err
+		}
 	}
 
 	switch {
@@ -346,13 +355,13 @@ func (f *SQLiteFeedbagStore) Blocked(sn1, sn2 string) (BlockedState, error) {
 	}
 }
 
-// RetrieveProfile fetches a user profile. Return empty string if the user
+// Profile fetches a user profile. Return empty string if the user
 // does not exist or has no profile.
-func (f *SQLiteFeedbagStore) RetrieveProfile(screenName string) (string, error) {
+func (f SQLiteUserStore) Profile(screenName string) (string, error) {
 	q := `
 		SELECT IFNULL(body, '')
 		FROM profile
-		WHERE ScreenName = ?
+		WHERE screenName = ?
 	`
 	var profile string
 	err := f.db.QueryRow(q, screenName).Scan(&profile)
@@ -362,28 +371,29 @@ func (f *SQLiteFeedbagStore) RetrieveProfile(screenName string) (string, error)
 	return profile, nil
 }
 
-func (f *SQLiteFeedbagStore) UpsertProfile(screenName string, body string) error {
+// SetProfile sets the text contents of a user's profile.
+func (f SQLiteUserStore) SetProfile(screenName string, body string) error {
 	q := `
-		INSERT INTO profile (ScreenName, body)
+		INSERT INTO profile (screenName, body)
 		VALUES (?, ?)
-		ON CONFLICT (ScreenName)
+		ON CONFLICT (screenName)
 			DO UPDATE SET body = excluded.body
 	`
 	_, err := f.db.Exec(q, screenName, body)
 	return err
 }
 
+// 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) {
-	u := User{ScreenName: screenName}
-
 	uid, err := uuid.NewRandom()
 	if err != nil {
-		return u, err
+		return User{}, err
 	}
-	u.AuthKey = uid.String()
-
-	if err := u.HashPassword("welcome1"); err != nil {
-		return u, err
+	u := User{
+		ScreenName: screenName,
+		AuthKey:    uid.String(),
 	}
-	return u, u.HashPassword("welcome1")
+	err = u.HashPassword("welcome1")
+	return u, err
 }

+ 161 - 155
state/feedbag_store_test.go → state/user_store_test.go

@@ -9,22 +9,18 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
-func TestFeedbagStore(t *testing.T) {
+const testFile string = "aim_test.db"
+
+func TestUserStore(t *testing.T) {
 
-	const testFile string = "aim_test.db"
 	const screenName = "sn2day"
 
 	defer func() {
-		err := os.Remove(testFile)
-		if err != nil {
-			t.Error("unable to clean up test file")
-		}
+		assert.NoError(t, os.Remove(testFile))
 	}()
 
-	f, err := NewSQLiteFeedbagStore(testFile)
-	if err != nil {
-		t.Fatalf("failed to create new feedbag store: %s", err.Error())
-	}
+	f, err := NewSQLiteUserStore(testFile)
+	assert.NoError(t, err)
 
 	itemsIn := []oscar.FeedbagItem{
 		{
@@ -41,11 +37,11 @@ func TestFeedbagStore(t *testing.T) {
 			Name:    "Friends",
 		},
 	}
-	if err := f.Upsert(screenName, itemsIn); err != nil {
+	if err := f.FeedbagUpsert(screenName, itemsIn); err != nil {
 		t.Fatalf("failed to upsert: %s", err.Error())
 	}
 
-	itemsOut, err := f.Retrieve(screenName)
+	itemsOut, err := f.Feedbag(screenName)
 	if err != nil {
 		t.Fatalf("failed to retrieve: %s", err.Error())
 	}
@@ -57,20 +53,14 @@ func TestFeedbagStore(t *testing.T) {
 
 func TestFeedbagDelete(t *testing.T) {
 
-	const testFile string = "aim_test.db"
 	const screenName = "sn2day"
 
 	defer func() {
-		err := os.Remove(testFile)
-		if err != nil {
-			t.Error("unable to clean up test file")
-		}
+		assert.NoError(t, os.Remove(testFile))
 	}()
 
-	f, err := NewSQLiteFeedbagStore(testFile)
-	if err != nil {
-		t.Fatalf("failed to create new feedbag store: %s", err.Error())
-	}
+	f, err := NewSQLiteUserStore(testFile)
+	assert.NoError(t, err)
 
 	itemsIn := []oscar.FeedbagItem{
 		{
@@ -98,15 +88,15 @@ func TestFeedbagDelete(t *testing.T) {
 		},
 	}
 
-	if err := f.Upsert(screenName, itemsIn); err != nil {
+	if err := f.FeedbagUpsert(screenName, itemsIn); err != nil {
 		t.Fatalf("failed to upsert: %s", err.Error())
 	}
 
-	if err := f.Delete(screenName, []oscar.FeedbagItem{itemsIn[0]}); err != nil {
+	if err := f.FeedbagDelete(screenName, []oscar.FeedbagItem{itemsIn[0]}); err != nil {
 		t.Fatalf("failed to delete: %s", err.Error())
 	}
 
-	itemsOut, err := f.Retrieve(screenName)
+	itemsOut, err := f.Feedbag(screenName)
 	if err != nil {
 		t.Fatalf("failed to retrieve: %s", err.Error())
 	}
@@ -120,22 +110,16 @@ func TestFeedbagDelete(t *testing.T) {
 
 func TestLastModifiedEmpty(t *testing.T) {
 
-	const testFile string = "aim_test.db"
 	const screenName = "sn2day"
 
 	defer func() {
-		err := os.Remove(testFile)
-		if err != nil {
-			t.Error("unable to clean up test file")
-		}
+		assert.NoError(t, os.Remove(testFile))
 	}()
 
-	f, err := NewSQLiteFeedbagStore(testFile)
-	if err != nil {
-		t.Fatalf("failed to create new feedbag store: %s", err.Error())
-	}
+	f, err := NewSQLiteUserStore(testFile)
+	assert.NoError(t, err)
 
-	_, err = f.LastModified(screenName)
+	_, err = f.FeedbagLastModified(screenName)
 
 	if err != nil {
 		t.Fatalf("get error from last modified: %s", err.Error())
@@ -144,20 +128,14 @@ func TestLastModifiedEmpty(t *testing.T) {
 
 func TestLastModifiedNotEmpty(t *testing.T) {
 
-	const testFile string = "aim_test.db"
 	const screenName = "sn2day"
 
 	defer func() {
-		err := os.Remove(testFile)
-		if err != nil {
-			t.Error("unable to clean up test file")
-		}
+		assert.NoError(t, os.Remove(testFile))
 	}()
 
-	f, err := NewSQLiteFeedbagStore(testFile)
-	if err != nil {
-		t.Fatalf("failed to create new feedbag store: %s", err.Error())
-	}
+	f, err := NewSQLiteUserStore(testFile)
+	assert.NoError(t, err)
 
 	itemsIn := []oscar.FeedbagItem{
 		{
@@ -167,11 +145,11 @@ func TestLastModifiedNotEmpty(t *testing.T) {
 			Name:    "Friends",
 		},
 	}
-	if err := f.Upsert(screenName, itemsIn); err != nil {
+	if err := f.FeedbagUpsert(screenName, itemsIn); err != nil {
 		t.Fatalf("failed to upsert: %s", err.Error())
 	}
 
-	_, err = f.LastModified(screenName)
+	_, err = f.FeedbagLastModified(screenName)
 
 	if err != nil {
 		t.Fatalf("get error from last modified: %s", err.Error())
@@ -180,29 +158,23 @@ func TestLastModifiedNotEmpty(t *testing.T) {
 
 func TestProfile(t *testing.T) {
 
-	const testFile string = "aim_test.db"
 	const screenName = "sn2day"
 
 	defer func() {
-		err := os.Remove(testFile)
-		if err != nil {
-			t.Error("unable to clean up test file")
-		}
+		assert.NoError(t, os.Remove(testFile))
 	}()
 
-	f, err := NewSQLiteFeedbagStore(testFile)
-	if err != nil {
-		t.Fatalf("failed to create new feedbag store: %s", err.Error())
-	}
+	f, err := NewSQLiteUserStore(testFile)
+	assert.NoError(t, err)
 
 	u := User{
 		ScreenName: screenName,
 	}
-	if err := f.UpsertUser(u); err != nil {
+	if err := f.InsertUser(u); err != nil {
 		t.Fatalf("failed to upsert new user: %s", err.Error())
 	}
 
-	profile, err := f.RetrieveProfile(screenName)
+	profile, err := f.Profile(screenName)
 	if err != nil {
 		t.Fatalf("failed to retrieve profile: %s", err.Error())
 	}
@@ -212,11 +184,11 @@ func TestProfile(t *testing.T) {
 	}
 
 	newProfile := "here is my profile"
-	if err := f.UpsertProfile(screenName, newProfile); err != nil {
+	if err := f.SetProfile(screenName, newProfile); err != nil {
 		t.Fatalf("failed to create new profile: %s", err.Error())
 	}
 
-	profile, err = f.RetrieveProfile(screenName)
+	profile, err = f.Profile(screenName)
 	if err != nil {
 		t.Fatalf("failed to retrieve profile: %s", err.Error())
 	}
@@ -226,11 +198,11 @@ func TestProfile(t *testing.T) {
 	}
 
 	updatedProfile := "here is my profile [updated]"
-	if err := f.UpsertProfile(screenName, updatedProfile); err != nil {
+	if err := f.SetProfile(screenName, updatedProfile); err != nil {
 		t.Fatalf("failed to create new profile: %s", err.Error())
 	}
 
-	profile, err = f.RetrieveProfile(screenName)
+	profile, err = f.Profile(screenName)
 	if err != nil {
 		t.Fatalf("failed to retrieve profile: %s", err.Error())
 	}
@@ -242,70 +214,51 @@ func TestProfile(t *testing.T) {
 
 func TestProfileNonExistent(t *testing.T) {
 
-	const testFile string = "aim_test.db"
 	const screenName = "sn2day"
 
 	defer func() {
-		err := os.Remove(testFile)
-		if err != nil {
-			t.Error("unable to clean up test file")
-		}
+		assert.NoError(t, os.Remove(testFile))
 	}()
 
-	f, err := NewSQLiteFeedbagStore(testFile)
-	if err != nil {
-		t.Fatalf("failed to create new feedbag store: %s", err.Error())
-	}
+	f, err := NewSQLiteUserStore(testFile)
+	assert.NoError(t, err)
 
-	prof, err := f.RetrieveProfile(screenName)
+	prof, err := f.Profile(screenName)
 	assert.NoError(t, err)
 	assert.Empty(t, prof)
 }
 
 func TestInterestedUsers(t *testing.T) {
-	const testFile string = "aim_test.db"
 
 	defer func() {
-		err := os.Remove(testFile)
-		if err != nil {
-			t.Error("unable to clean up test file")
-		}
+		assert.NoError(t, os.Remove(testFile))
 	}()
 
-	f, err := NewSQLiteFeedbagStore(testFile)
-	if err != nil {
-		t.Fatalf("failed to create new feedbag store: %s", err.Error())
-	}
+	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)`)
 
-	users, err := f.InterestedUsers("userA")
+	users, err := f.AdjacentUsers("userA")
 	if len(users) != 0 {
 		t.Fatalf("expected no interested users, got %v", users)
 	}
 
-	users, err = f.InterestedUsers("userB")
+	users, err = f.AdjacentUsers("userB")
 	if len(users) != 0 {
 		t.Fatalf("expected no interested users, got %v", users)
 	}
 }
 
-func TestFeedbagStoreBuddiesBlockedUser(t *testing.T) {
-	const testFile string = "aim_test.db"
-
+func TestUserStoreBuddiesBlockedUser(t *testing.T) {
 	defer func() {
-		err := os.Remove(testFile)
-		if err != nil {
-			t.Error("unable to clean up test file")
-		}
+		assert.NoError(t, os.Remove(testFile))
 	}()
 
-	f, err := NewSQLiteFeedbagStore(testFile)
-	if err != nil {
-		t.Fatalf("failed to create new feedbag store: %s", err.Error())
-	}
+	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)`)
@@ -322,20 +275,14 @@ func TestFeedbagStoreBuddiesBlockedUser(t *testing.T) {
 	}
 }
 
-func TestFeedbagStoreBlockedA(t *testing.T) {
-	const testFile string = "aim_test.db"
+func TestUserStoreBlockedA(t *testing.T) {
 
 	defer func() {
-		err := os.Remove(testFile)
-		if err != nil {
-			t.Error("unable to clean up test file")
-		}
+		assert.NoError(t, os.Remove(testFile))
 	}()
 
-	f, err := NewSQLiteFeedbagStore(testFile)
-	if err != nil {
-		t.Fatalf("failed to create new feedbag store: %s", err.Error())
-	}
+	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)`)
@@ -343,7 +290,7 @@ func TestFeedbagStoreBlockedA(t *testing.T) {
 
 	sn1 := "userA"
 	sn2 := "userB"
-	blocked, err := f.Blocked(sn1, sn2)
+	blocked, err := f.BlockedState(sn1, sn2)
 	if err != nil {
 		t.Fatalf("db err: %s", err.Error())
 	}
@@ -352,20 +299,13 @@ func TestFeedbagStoreBlockedA(t *testing.T) {
 	}
 }
 
-func TestFeedbagStoreBlockedB(t *testing.T) {
-	const testFile string = "aim_test.db"
-
+func TestUserStoreBlockedB(t *testing.T) {
 	defer func() {
-		err := os.Remove(testFile)
-		if err != nil {
-			t.Error("unable to clean up test file")
-		}
+		assert.NoError(t, os.Remove(testFile))
 	}()
 
-	f, err := NewSQLiteFeedbagStore(testFile)
-	if err != nil {
-		t.Fatalf("failed to create new feedbag store: %s", err.Error())
-	}
+	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)`)
@@ -373,7 +313,7 @@ func TestFeedbagStoreBlockedB(t *testing.T) {
 
 	sn1 := "userA"
 	sn2 := "userB"
-	blocked, err := f.Blocked(sn1, sn2)
+	blocked, err := f.BlockedState(sn1, sn2)
 	if err != nil {
 		t.Fatalf("db err: %s", err.Error())
 	}
@@ -382,27 +322,20 @@ func TestFeedbagStoreBlockedB(t *testing.T) {
 	}
 }
 
-func TestFeedbagStoreBlockedNoBlocked(t *testing.T) {
-	const testFile string = "aim_test.db"
-
+func TestUserStoreBlockedNoBlocked(t *testing.T) {
 	defer func() {
-		err := os.Remove(testFile)
-		if err != nil {
-			t.Error("unable to clean up test file")
-		}
+		assert.NoError(t, os.Remove(testFile))
 	}()
 
-	f, err := NewSQLiteFeedbagStore(testFile)
-	if err != nil {
-		t.Fatalf("failed to create new feedbag store: %s", err.Error())
-	}
+	f, err := NewSQLiteUserStore(testFile)
+	assert.NoError(t, err)
 
 	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"
-	blocked, err := f.Blocked(sn1, sn2)
+	blocked, err := f.BlockedState(sn1, sn2)
 	if err != nil {
 		t.Fatalf("db err: %s", err.Error())
 	}
@@ -412,19 +345,12 @@ func TestFeedbagStoreBlockedNoBlocked(t *testing.T) {
 }
 
 func TestGetUser(t *testing.T) {
-	const testFile string = "aim_test.db"
-
 	defer func() {
-		err := os.Remove(testFile)
-		if err != nil {
-			t.Error("unable to clean up test file")
-		}
+		assert.NoError(t, os.Remove(testFile))
 	}()
 
-	f, err := NewSQLiteFeedbagStore(testFile)
-	if err != nil {
-		t.Fatalf("failed to create new feedbag store: %s", err.Error())
-	}
+	f, err := NewSQLiteUserStore(testFile)
+	assert.NoError(t, err)
 
 	expectUser := &User{
 		ScreenName: "testscreenname",
@@ -437,7 +363,7 @@ func TestGetUser(t *testing.T) {
 		t.Fatalf("failed to insert user: %s", err.Error())
 	}
 
-	actualUser, err := f.GetUser(expectUser.ScreenName)
+	actualUser, err := f.User(expectUser.ScreenName)
 	if err != nil {
 		t.Fatalf("failed to get user: %s", err.Error())
 	}
@@ -448,21 +374,14 @@ func TestGetUser(t *testing.T) {
 }
 
 func TestGetUserNotFound(t *testing.T) {
-	const testFile string = "aim_test.db"
-
 	defer func() {
-		err := os.Remove(testFile)
-		if err != nil {
-			t.Error("unable to clean up test file")
-		}
+		assert.NoError(t, os.Remove(testFile))
 	}()
 
-	f, err := NewSQLiteFeedbagStore(testFile)
-	if err != nil {
-		t.Fatalf("failed to create new feedbag store: %s", err.Error())
-	}
+	f, err := NewSQLiteUserStore(testFile)
+	assert.NoError(t, err)
 
-	actualUser, err := f.GetUser("testscreenname")
+	actualUser, err := f.User("testscreenname")
 	if err != nil {
 		t.Fatalf("failed to get user: %s", err.Error())
 	}
@@ -472,12 +391,99 @@ func TestGetUserNotFound(t *testing.T) {
 	}
 }
 
-func TestHashPassword(t *testing.T) {
-	u := &User{
-		AuthKey: "the_auth_key",
+func TestSQLiteUserStore_Users(t *testing.T) {
+	defer func() {
+		assert.NoError(t, os.Remove(testFile))
+	}()
+
+	f, err := NewSQLiteUserStore(testFile)
+	if err != nil {
+		assert.NoError(t, err)
+	}
+
+	want := []User{
+		{ScreenName: "userA"},
+		{ScreenName: "userB"},
+		{ScreenName: "userC"},
 	}
-	if err := u.HashPassword(""); err != nil {
-		t.Fatalf("error hashing password: %s", err.Error())
+
+	for _, u := range want {
+		err := f.InsertUser(u)
+		assert.NoError(t, err)
 	}
-	t.Logf("password hash: %s", u.PassHash)
+
+	have, err := f.AllUsers()
+	assert.NoError(t, err)
+
+	assert.Equal(t, want, have)
+}
+
+func TestSQLiteUserStore_Buddies(t *testing.T) {
+	defer func() {
+		assert.NoError(t, os.Remove(testFile))
+	}()
+
+	feedbagStore, err := NewSQLiteUserStore(testFile)
+	assert.NoError(t, err)
+
+	assert.NoError(t, feedbagStore.FeedbagUpsert("userA", []oscar.FeedbagItem{
+		{Name: "userB", ItemID: 1, ClassID: oscar.FeedbagClassIdBuddy},
+		{Name: "userC", ItemID: 2, ClassID: oscar.FeedbagClassIdBuddy},
+		{Name: "userD", ItemID: 3, ClassID: oscar.FeedbagClassIdBuddy},
+	}))
+	assert.NoError(t, feedbagStore.FeedbagUpsert("userB", []oscar.FeedbagItem{
+		{Name: "userA", ItemID: 1, ClassID: oscar.FeedbagClassIdBuddy},
+		{Name: "userC", ItemID: 2, ClassID: oscar.FeedbagClassIdBuddy},
+		{Name: "userD", ItemID: 3, ClassID: oscar.FeedbagClassIdBuddy},
+	}))
+
+	want := []string{"userB", "userC", "userD"}
+	have, err := feedbagStore.Buddies("userA")
+	assert.NoError(t, err)
+
+	assert.Equal(t, want, have)
+}
+
+func TestNewStubUser(t *testing.T) {
+	have, err := NewStubUser("userA")
+	assert.NoError(t, err)
+
+	want := User{
+		ScreenName: "userA",
+		AuthKey:    have.AuthKey,
+	}
+	assert.NoError(t, want.HashPassword("welcome1"))
+
+	assert.Equal(t, want, have)
+}
+
+func TestSQLiteUserStore_InterestedUsers(t *testing.T) {
+	defer func() {
+		assert.NoError(t, os.Remove(testFile))
+	}()
+
+	feedbagStore, err := NewSQLiteUserStore(testFile)
+	assert.NoError(t, err)
+
+	assert.NoError(t, feedbagStore.FeedbagUpsert("userA", []oscar.FeedbagItem{
+		{Name: "userB", ItemID: 1, ClassID: oscar.FeedbagClassIdBuddy},
+		{Name: "userC", ItemID: 2, ClassID: oscar.FeedbagClassIdBuddy},
+		{Name: "userD", ItemID: 3, ClassID: oscar.FeedbagClassIdBuddy},
+	}))
+	assert.NoError(t, feedbagStore.FeedbagUpsert("userB", []oscar.FeedbagItem{
+		{Name: "userA", ItemID: 1, ClassID: oscar.FeedbagClassIdBuddy},
+		{Name: "userC", ItemID: 2, ClassID: oscar.FeedbagClassIdBuddy},
+		{Name: "userD", ItemID: 3, ClassID: oscar.FeedbagClassIdBuddy},
+	}))
+	assert.NoError(t, feedbagStore.FeedbagUpsert("userC", []oscar.FeedbagItem{
+		{Name: "userA", ItemID: 1, ClassID: oscar.FeedbagClassIdBuddy},
+		{Name: "userB", ItemID: 2, ClassID: oscar.FeedbagClassIdBuddy},
+		{Name: "userD", ItemID: 3, ClassID: oscar.FeedbagClassIdBuddy},
+	}))
+
+	want := []string{"userB", "userC"}
+	have, err := feedbagStore.AdjacentUsers("userA")
+	assert.NoError(t, err)
+
+	assert.Equal(t, want, have)
 }