From 378d55af653cd2fc412521a572cdf125d8383c4b Mon Sep 17 00:00:00 2001 From: Daniel Oaks Date: Sat, 7 Oct 2017 22:19:37 +1000 Subject: [PATCH] Add tests to subpackages --- irc/caps/constants.go | 10 +++++ irc/caps/set_test.go | 46 ++++++++++++++++++++ irc/caps/version.go | 14 ------ irc/isupport/list_test.go | 28 ++++++++++++ irc/passwd/salted.go | 6 +-- irc/passwd/salted_test.go | 86 +++++++++++++++++++++++++++++++++++++ irc/passwd/unsalted_test.go | 54 +++++++++++++++++++++++ 7 files changed, 227 insertions(+), 17 deletions(-) create mode 100644 irc/caps/set_test.go delete mode 100644 irc/caps/version.go create mode 100644 irc/passwd/salted_test.go create mode 100644 irc/passwd/unsalted_test.go diff --git a/irc/caps/constants.go b/irc/caps/constants.go index 8be3b9d4..9b6516ed 100644 --- a/irc/caps/constants.go +++ b/irc/caps/constants.go @@ -49,3 +49,13 @@ const ( func (capability Capability) Name() string { return string(capability) } + +// Version is used to select which max version of CAP the client supports. +type Version uint + +const ( + // Cap301 refers to the base CAP spec. + Cap301 Version = 301 + // Cap302 refers to the IRCv3.2 CAP spec. + Cap302 Version = 302 +) diff --git a/irc/caps/set_test.go b/irc/caps/set_test.go new file mode 100644 index 00000000..6cf0d1b8 --- /dev/null +++ b/irc/caps/set_test.go @@ -0,0 +1,46 @@ +// Copyright (c) 2017 Daniel Oaks +// released under the MIT license + +package caps + +import "testing" +import "reflect" + +func TestSets(t *testing.T) { + s1 := NewSet() + + s1.Enable(AccountTag, EchoMessage, UserhostInNames) + + if !s1.Has(AccountTag, EchoMessage, UserhostInNames) { + t.Error("Did not have the tags we expected") + } + + if s1.Has(AccountTag, EchoMessage, STS, UserhostInNames) { + t.Error("Has() returned true when we don't have all the given capabilities") + } + + s1.Disable(AccountTag) + + if s1.Has(AccountTag) { + t.Error("Disable() did not correctly disable the given capability") + } + + enabledCaps := make(map[Capability]bool) + for _, capab := range s1.List() { + enabledCaps[capab] = true + } + expectedCaps := map[Capability]bool{ + EchoMessage: true, + UserhostInNames: true, + } + if !reflect.DeepEqual(enabledCaps, expectedCaps) { + t.Errorf("Enabled and expected capability lists do not match: %v, %v", enabledCaps, expectedCaps) + } + + // make sure re-enabling doesn't add to the count or something weird like that + s1.Enable(EchoMessage) + + if s1.Count() != 2 { + t.Error("Count() did not match expected capability count") + } +} diff --git a/irc/caps/version.go b/irc/caps/version.go deleted file mode 100644 index 96c610f1..00000000 --- a/irc/caps/version.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) 2017 Daniel Oaks -// released under the MIT license - -package caps - -// Version is used to select which max version of CAP the client supports. -type Version uint - -const ( - // Cap301 refers to the base CAP spec. - Cap301 Version = 301 - // Cap302 refers to the IRCv3.2 CAP spec. - Cap302 Version = 302 -) diff --git a/irc/isupport/list_test.go b/irc/isupport/list_test.go index 4c92313e..055b8f5d 100644 --- a/irc/isupport/list_test.go +++ b/irc/isupport/list_test.go @@ -9,6 +9,34 @@ import ( ) func TestISUPPORT(t *testing.T) { + // test multiple output replies + tListLong := NewList() + tListLong.AddNoValue("1") + tListLong.AddNoValue("2") + tListLong.AddNoValue("3") + tListLong.AddNoValue("4") + tListLong.AddNoValue("5") + tListLong.AddNoValue("6") + tListLong.AddNoValue("7") + tListLong.AddNoValue("8") + tListLong.AddNoValue("9") + tListLong.AddNoValue("A") + tListLong.AddNoValue("B") + tListLong.AddNoValue("C") + tListLong.AddNoValue("D") + tListLong.AddNoValue("E") + tListLong.AddNoValue("F") + tListLong.RegenerateCachedReply() + + longReplies := [][]string{ + {"1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "are supported by this server"}, + {"E", "F", "are supported by this server"}, + } + + if !reflect.DeepEqual(tListLong.CachedReply, longReplies) { + t.Errorf("Multiple output replies did not match, got [%v]", longReplies) + } + // create first list tList1 := NewList() tList1.Add("SASL", "yes") diff --git a/irc/passwd/salted.go b/irc/passwd/salted.go index 299cf318..decf9190 100644 --- a/irc/passwd/salted.go +++ b/irc/passwd/salted.go @@ -36,9 +36,9 @@ type SaltedManager struct { // NewSaltedManager returns a new SaltedManager with the given salt. func NewSaltedManager(salt []byte) SaltedManager { - var sm SaltedManager - sm.salt = salt - return sm + return SaltedManager{ + salt: salt, + } } // assemblePassword returns an assembled slice of bytes for the given password details. diff --git a/irc/passwd/salted_test.go b/irc/passwd/salted_test.go new file mode 100644 index 00000000..68ddc796 --- /dev/null +++ b/irc/passwd/salted_test.go @@ -0,0 +1,86 @@ +// Copyright (c) 2016 Daniel Oaks +// released under the MIT license + +package passwd + +import ( + "encoding/base64" + "testing" +) + +type SaltedPasswordTest struct { + ManagerSalt string + Salt string + Hash string + Password string +} + +var SaltedPasswords = []SaltedPasswordTest{ + { + ManagerSalt: "3TPITDVf/NGb4OlCyV1uZNW1H7zy3BFos+Dsu7dj", + Salt: "b6oVqshJUfcm1zWEtqwKqUVylqLONAZfqt17ns+Y", + Hash: "JDJhJDE0JFYuT28xOFFNZldaaTI1UWpzNENMeHVKdm5vS1lkL2tFL1lFVkQ2a0loUEY2Vzk3UTZSVDVP", + Password: "test", + }, + { + ManagerSalt: "iNGeNEfuPihM8kYDZ/C6qAJ0JERKeKkUYp6wYDU0", + Salt: "U7TA6k6VLSLHfdjSsQH0vc3Jqq6cUezJNyd0DC9c", + Hash: "JDJhJDE0JEguY2Rva3VOTVRrNm1VeGdXWjAwamViMGNvV0xYZFdHcTZjenFCRWE3Ymt2N1JiSFJDZlYy", + Password: "test2", + }, + { + ManagerSalt: "ghKJaaSNTjuFmgLRqrgY4FGfx8wXEGOBE02PZvbv", + Salt: "NO/mtrMhGjX1FGDGdpGrDJIi4jxsb0aFa7ybId7r", + Hash: "JDJhJDE0JEI0M055Z2NDcjNUanB5ZEJ5MzUybi5FT3o4Y1MyNXp2c1NDVS9hS0hOcUxSRDZTWmUxTnN5", + Password: "supermono", + }, +} + +func TestSaltedPassword(t *testing.T) { + // check newly-generated password + managerSalt, err := NewSalt() + if err != nil { + t.Error("Could not generate manager salt") + } + + salt, err := NewSalt() + if err != nil { + t.Error("Could not generate salt") + } + + manager := NewSaltedManager(managerSalt) + + passHash, err := manager.GenerateFromPassword(salt, "this is a test password") + if err != nil { + t.Error("Could not generate from password") + } + + if manager.CompareHashAndPassword(passHash, salt, "this is a test password") != nil { + t.Error("Generated password does not match") + } + + // check our stored passwords + for i, info := range SaltedPasswords { + // decode strings to bytes + managerSalt, err = base64.StdEncoding.DecodeString(info.ManagerSalt) + if err != nil { + t.Errorf("Could not decode manager salt for test %d", i) + } + + salt, err := base64.StdEncoding.DecodeString(info.Salt) + if err != nil { + t.Errorf("Could not decode salt for test %d", i) + } + + hash, err := base64.StdEncoding.DecodeString(info.Hash) + if err != nil { + t.Errorf("Could not decode hash for test %d", i) + } + + // make sure our test values are still correct + manager := NewSaltedManager(managerSalt) + if manager.CompareHashAndPassword(hash, salt, info.Password) != nil { + t.Errorf("Password does not match for [%s]", info.Password) + } + } +} diff --git a/irc/passwd/unsalted_test.go b/irc/passwd/unsalted_test.go new file mode 100644 index 00000000..80d69c66 --- /dev/null +++ b/irc/passwd/unsalted_test.go @@ -0,0 +1,54 @@ +// Copyright (c) 2016 Daniel Oaks +// released under the MIT license + +package passwd + +import ( + "testing" +) + +var UnsaltedPasswords = map[string]string{ + "test1": "JDJhJDA0JFFwZ1V0RWZTMFVaMkFrdlRrTG9FZk9FNEZWbWkvVEhsdGFnSXlIUC5wVmpYTkNERFJPNlcu", + "test2": "JDJhJDA0JHpQTGNqczlIanc3V2NFQ3JEOVlTM09aNkRTbGRsQzRyNmt3Q01aSUs2Y2xyWURVODZ1V0px", + "supernomo": "JDJhJDA0JHdJekhnQmk1VXQ4WUphL0pIL0tXQWVKVXJ6dXcvRDJ3WFljWW9XOGhzNllIbW1DRlFkL1VL", +} + +func TestUnsaltedPassword(t *testing.T) { + for password, hash := range UnsaltedPasswords { + generatedHash, err := GenerateEncodedPassword(password) + if err != nil { + t.Errorf("Could not hash password for [%s]: %s", password, err.Error()) + } + + hashBytes, err := DecodePasswordHash(hash) + if err != nil { + t.Errorf("Could not decode hash for [%s]: %s", password, err.Error()) + } + + generatedHashBytes, err := DecodePasswordHash(generatedHash) + if err != nil { + t.Errorf("Could not decode generated hash for [%s]: %s", password, err.Error()) + } + + passwordBytes := []byte(password) + + if ComparePassword(hashBytes, passwordBytes) != nil { + t.Errorf("Stored hash for [%s] did not match", password) + } + if ComparePassword(generatedHashBytes, passwordBytes) != nil { + t.Errorf("Generated hash for [%s] did not match", password) + } + } +} + +func TestUnsaltedPasswordFailures(t *testing.T) { + _, err := GenerateEncodedPassword("") + if err != ErrEmptyPassword { + t.Error("Generating empty password did not fail as expected!") + } + + _, err = DecodePasswordHash("") + if err != ErrEmptyPassword { + t.Error("Decoding empty password hash did not fail as expected!") + } +}