diff --git a/channels/nickname.go b/channels/nickname.go index a7dfb45215111396617944ca19fc233aa7246c0e..71523d87e0df4933c41289ab3d306ed109fb8725 100644 --- a/channels/nickname.go +++ b/channels/nickname.go @@ -35,12 +35,13 @@ func loadOrNewNicknameManager(kv *versioned.KV) *nicknameManager { jww.FATAL.Panicf("Failed to load nicknameManager: %+v", err) } - return nm, nil + return nm } // GetNickname returns the nickname for the given channel if it exists -func (nm *nicknameManager) GetNickname(ch *id.ID) (nickname string, exists bool) { +func (nm *nicknameManager) GetNickname(ch *id.ID) ( + nickname string, exists bool) { nm.mux.RLock() defer nm.mux.RUnlock() @@ -59,21 +60,39 @@ func (nm *nicknameManager) SetNickname(newNick string, ch *id.ID) error { } nm.byChannel[*ch] = newNick - return nil + return nm.save() } // DeleteNickname removes the nickname for a given channel, using the codename // for that channel instead -func (nm *nicknameManager) DeleteNickname(ch *id.ID) { +func (nm *nicknameManager) DeleteNickname(ch *id.ID) error { nm.mux.Lock() defer nm.mux.Unlock() delete(nm.byChannel, *ch) + + return nm.save() +} + +// channelIDToNickname is a serialization structure. This is used by the save +// and load functions to serialize the nicknameManager's byChannel map. +type channelIDToNickname struct { + channelId id.ID + nickname string } -// save stores the nickname manager to disk. It must occur under the mux. +// save stores the nickname manager to disk. The caller of this must +// hold the mux. func (nm *nicknameManager) save() error { - data, err := json.Marshal(&nm.byChannel) + list := make([]channelIDToNickname, 0) + for chId, nickname := range nm.byChannel { + list = append(list, channelIDToNickname{ + channelId: chId, + nickname: nickname, + }) + } + + data, err := json.Marshal(list) if err != nil { return err } @@ -92,7 +111,19 @@ func (nm *nicknameManager) load() error { if err != nil { return err } - return json.Unmarshal(obj.Data, &nm.byChannel) + + list := make([]channelIDToNickname, 0) + err = json.Unmarshal(obj.Data, &list) + if err != nil { + return err + } + + for i := range list { + current := list[i] + nm.byChannel[current.channelId] = current.nickname + } + + return nil } // IsNicknameValid checks if a nickname is valid @@ -104,4 +135,6 @@ func IsNicknameValid(nm string) error { if len([]rune(nm)) > 24 { return errors.New("nicknames must be 24 characters in length or less") } + + return nil } diff --git a/channels/nickname_test.go b/channels/nickname_test.go new file mode 100644 index 0000000000000000000000000000000000000000..dc0cad8f05fd5a24d2e033160f308a23bab68e08 --- /dev/null +++ b/channels/nickname_test.go @@ -0,0 +1,79 @@ +package channels + +import ( + "gitlab.com/elixxir/client/storage/versioned" + "gitlab.com/elixxir/ekv" + "gitlab.com/xx_network/primitives/id" + "strconv" + "testing" +) + +// Unit test. Tests that once you set a nickname with SetNickname, you can +// retrieve the nickname using GetNickname. +func TestNicknameManager_SetGetNickname(t *testing.T) { + kv := versioned.NewKV(ekv.MakeMemstore()) + nm := loadOrNewNicknameManager(kv) + + for i := 0; i < numTests; i++ { + chId := id.NewIdFromUInt(uint64(i), id.User, t) + nickname := "nickname#" + strconv.Itoa(i) + err := nm.SetNickname(nickname, chId) + if err != nil { + t.Fatalf("SetNickname error when setting %s: %+v", nickname, err) + } + + received, _ := nm.GetNickname(chId) + if received != nickname { + t.Fatalf("GetNickname did not return expected values."+ + "\nExpected: %s"+ + "\nReceived: %s", nickname, received) + } + } + +} + +// Error case: Tests that nicknameManager.GetNickname returns a false boolean +// if no nickname has been set with the channel ID. +func TestNicknameManager_GetNickname_Error(t *testing.T) { + kv := versioned.NewKV(ekv.MakeMemstore()) + nm := loadOrNewNicknameManager(kv) + + for i := 0; i < numTests; i++ { + chId := id.NewIdFromUInt(uint64(i), id.User, t) + _, exists := nm.GetNickname(chId) + if exists { + t.Fatalf("GetNickname expected error case: " + + "This should not retrieve nicknames for channel IDs " + + "that are not set.") + } + } +} + +// Unit test. Check that once you SetNickname and DeleteNickname, +// GetNickname returns a false boolean. +func TestNicknameManager_DeleteNickname(t *testing.T) { + kv := versioned.NewKV(ekv.MakeMemstore()) + nm := loadOrNewNicknameManager(kv) + + for i := 0; i < numTests; i++ { + chId := id.NewIdFromUInt(uint64(i), id.User, t) + nickname := "nickname#" + strconv.Itoa(i) + err := nm.SetNickname(nickname, chId) + if err != nil { + t.Fatalf("SetNickname error when setting %s: %+v", nickname, err) + } + + err = nm.DeleteNickname(chId) + if err != nil { + t.Fatalf("DeleteNickname error: %+v", err) + } + + _, exists := nm.GetNickname(chId) + if exists { + t.Fatalf("GetNickname expected error case: " + + "This should not retrieve nicknames for channel IDs " + + "that are not set.") + } + } + +}