217e7abab3
The query statements now use camelCase instead of undercase. Added more documentation. Renamed a few config variables. The length counters in the MemoryUserStore and MemoryTopicStore are now atomic. Added the Flush method to the UserCache and TopicCache. Made more visual improvements to Tempra Conflux. The avatar block vanished from the opening posts for topics for some reason, I added it back. Added more tests. And many more changes...
469 lines
11 KiB
Go
469 lines
11 KiB
Go
package main
|
|
|
|
import "strconv"
|
|
import "testing"
|
|
|
|
// TODO: Generate a test database to work with rather than a live one
|
|
// TODO: We might need to refactor TestUserStore soon, as it's getting fairly complex
|
|
func TestUserStore(t *testing.T) {
|
|
if !gloinited {
|
|
err := gloinit()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if !pluginsInited {
|
|
initPlugins()
|
|
}
|
|
|
|
users = NewMemoryUserStore(config.UserCacheCapacity)
|
|
users.(UserCache).Flush()
|
|
userStoreTest(t)
|
|
users = NewSQLUserStore()
|
|
userStoreTest(t)
|
|
}
|
|
func userStoreTest(t *testing.T) {
|
|
var user *User
|
|
var err error
|
|
var length int
|
|
|
|
ucache, hasCache := users.(UserCache)
|
|
if hasCache && ucache.GetLength() != 0 {
|
|
t.Error("Initial ucache length isn't zero")
|
|
}
|
|
|
|
_, err = users.Get(-1)
|
|
if err == nil {
|
|
t.Error("UID #-1 shouldn't exist")
|
|
} else if err != ErrNoRows {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if hasCache && ucache.GetLength() != 0 {
|
|
t.Error("There shouldn't be anything in the user cache")
|
|
}
|
|
|
|
_, err = users.Get(0)
|
|
if err == nil {
|
|
t.Error("UID #0 shouldn't exist")
|
|
} else if err != ErrNoRows {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if hasCache && ucache.GetLength() != 0 {
|
|
t.Error("There shouldn't be anything in the user cache")
|
|
}
|
|
|
|
user, err = users.Get(1)
|
|
if err == ErrNoRows {
|
|
t.Error("Couldn't find UID #1")
|
|
} else if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if user.ID != 1 {
|
|
t.Error("user.ID does not match the requested UID. Got '" + strconv.Itoa(user.ID) + "' instead.")
|
|
}
|
|
|
|
if hasCache {
|
|
length = ucache.GetLength()
|
|
if length != 1 {
|
|
t.Error("User cache length should be 1, not " + strconv.Itoa(length))
|
|
}
|
|
|
|
user, err = ucache.CacheGet(1)
|
|
if err == ErrNoRows {
|
|
t.Error("Couldn't find UID #1 in the cache")
|
|
} else if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if user.ID != 1 {
|
|
t.Error("user.ID does not match the requested UID. Got '" + strconv.Itoa(user.ID) + "' instead.")
|
|
}
|
|
|
|
ucache.Flush()
|
|
length = ucache.GetLength()
|
|
if length != 0 {
|
|
t.Error("User cache length should be 0, not " + strconv.Itoa(length))
|
|
}
|
|
}
|
|
|
|
// TODO: Lock onto the specific error type. Is this even possible without sacrificing the detailed information in the error message?
|
|
var userList map[int]*User
|
|
userList, _ = users.BulkGetMap([]int{-1})
|
|
if len(userList) > 0 {
|
|
t.Error("There shouldn't be any results for UID #-1")
|
|
}
|
|
|
|
if hasCache {
|
|
length = ucache.GetLength()
|
|
if length != 0 {
|
|
t.Error("User cache length should be 0, not " + strconv.Itoa(length))
|
|
}
|
|
}
|
|
|
|
userList, _ = users.BulkGetMap([]int{0})
|
|
if len(userList) > 0 {
|
|
t.Error("There shouldn't be any results for UID #0")
|
|
}
|
|
|
|
if hasCache {
|
|
length = ucache.GetLength()
|
|
if length != 0 {
|
|
t.Error("User cache length should be 0, not " + strconv.Itoa(length))
|
|
}
|
|
}
|
|
|
|
userList, _ = users.BulkGetMap([]int{1})
|
|
if len(userList) == 0 {
|
|
t.Error("The returned map is empty for UID #1")
|
|
} else if len(userList) > 1 {
|
|
t.Error("Too many results were returned for UID #1")
|
|
}
|
|
|
|
user, ok := userList[1]
|
|
if !ok {
|
|
t.Error("We couldn't find UID #1 in the returned map")
|
|
t.Error("userList", userList)
|
|
}
|
|
|
|
if user.ID != 1 {
|
|
t.Error("user.ID does not match the requested UID. Got '" + strconv.Itoa(user.ID) + "' instead.")
|
|
}
|
|
|
|
if hasCache {
|
|
length = ucache.GetLength()
|
|
if length != 1 {
|
|
t.Error("User cache length should be 1, not " + strconv.Itoa(length))
|
|
}
|
|
|
|
user, err = ucache.CacheGet(1)
|
|
if err == ErrNoRows {
|
|
t.Error("Couldn't find UID #1 in the cache")
|
|
} else if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if user.ID != 1 {
|
|
t.Error("user.ID does not match the requested UID. Got '" + strconv.Itoa(user.ID) + "' instead.")
|
|
}
|
|
|
|
ucache.Flush()
|
|
}
|
|
|
|
ok = users.Exists(-1)
|
|
if ok {
|
|
t.Error("UID #-1 shouldn't exist")
|
|
}
|
|
|
|
ok = users.Exists(0)
|
|
if ok {
|
|
t.Error("UID #0 shouldn't exist")
|
|
}
|
|
|
|
ok = users.Exists(1)
|
|
if !ok {
|
|
t.Error("UID #1 should exist")
|
|
}
|
|
|
|
if hasCache {
|
|
length = ucache.GetLength()
|
|
if length != 0 {
|
|
t.Error("User cache length should be 0, not " + strconv.Itoa(length))
|
|
}
|
|
}
|
|
|
|
count := users.GetGlobalCount()
|
|
if count <= 0 {
|
|
t.Error("The number of users should be bigger than zero")
|
|
t.Error("count", count)
|
|
}
|
|
}
|
|
|
|
func TestTopicStore(t *testing.T) {
|
|
if !gloinited {
|
|
err := gloinit()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if !pluginsInited {
|
|
initPlugins()
|
|
}
|
|
|
|
topics = NewMemoryTopicStore(config.TopicCacheCapacity)
|
|
topicStoreTest(t)
|
|
topics = NewSQLTopicStore()
|
|
topicStoreTest(t)
|
|
}
|
|
func topicStoreTest(t *testing.T) {
|
|
var topic *Topic
|
|
var err error
|
|
|
|
_, err = topics.Get(-1)
|
|
if err == nil {
|
|
t.Error("TID #-1 shouldn't exist")
|
|
} else if err != ErrNoRows {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
_, err = topics.Get(0)
|
|
if err == nil {
|
|
t.Error("TID #0 shouldn't exist")
|
|
} else if err != ErrNoRows {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
topic, err = topics.Get(1)
|
|
if err == ErrNoRows {
|
|
t.Error("Couldn't find TID #1")
|
|
} else if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if topic.ID != 1 {
|
|
t.Error("topic.ID does not match the requested TID. Got '" + strconv.Itoa(topic.ID) + "' instead.")
|
|
}
|
|
|
|
// TODO: Add BulkGetMap() to the TopicStore
|
|
|
|
ok := topics.Exists(-1)
|
|
if ok {
|
|
t.Error("TID #-1 shouldn't exist")
|
|
}
|
|
|
|
ok = topics.Exists(0)
|
|
if ok {
|
|
t.Error("TID #0 shouldn't exist")
|
|
}
|
|
|
|
ok = topics.Exists(1)
|
|
if !ok {
|
|
t.Error("TID #1 should exist")
|
|
}
|
|
|
|
count := topics.GetGlobalCount()
|
|
if count <= 0 {
|
|
t.Error("The number of topics should be bigger than zero")
|
|
t.Error("count", count)
|
|
}
|
|
}
|
|
|
|
func TestForumStore(t *testing.T) {
|
|
if !gloinited {
|
|
gloinit()
|
|
}
|
|
if !pluginsInited {
|
|
initPlugins()
|
|
}
|
|
|
|
var forum *Forum
|
|
var err error
|
|
|
|
_, err = fstore.Get(-1)
|
|
if err == nil {
|
|
t.Error("FID #-1 shouldn't exist")
|
|
} else if err != ErrNoRows {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
forum, err = fstore.Get(0)
|
|
if err == ErrNoRows {
|
|
t.Error("Couldn't find FID #0")
|
|
} else if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if forum.ID != 0 {
|
|
t.Error("forum.ID doesn't not match the requested UID. Got '" + strconv.Itoa(forum.ID) + "' instead.")
|
|
}
|
|
if forum.Name != "Uncategorised" {
|
|
t.Error("FID #0 is named '" + forum.Name + "' and not 'Uncategorised'")
|
|
}
|
|
|
|
forum, err = fstore.Get(1)
|
|
if err == ErrNoRows {
|
|
t.Error("Couldn't find FID #1")
|
|
} else if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if forum.ID != 1 {
|
|
t.Error("forum.ID doesn't not match the requested FID. Got '" + strconv.Itoa(forum.ID) + "' instead.'")
|
|
}
|
|
if forum.Name != "Reports" {
|
|
t.Error("FID #0 is named '" + forum.Name + "' and not 'Reports'")
|
|
}
|
|
|
|
forum, err = fstore.Get(2)
|
|
if err == ErrNoRows {
|
|
t.Error("Couldn't find FID #2")
|
|
} else if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
_ = forum
|
|
|
|
ok := fstore.Exists(-1)
|
|
if ok {
|
|
t.Error("FID #-1 shouldn't exist")
|
|
}
|
|
|
|
ok = fstore.Exists(0)
|
|
if !ok {
|
|
t.Error("FID #0 should exist")
|
|
}
|
|
|
|
ok = fstore.Exists(1)
|
|
if !ok {
|
|
t.Error("FID #1 should exist")
|
|
}
|
|
}
|
|
|
|
func TestGroupStore(t *testing.T) {
|
|
if !gloinited {
|
|
gloinit()
|
|
}
|
|
if !pluginsInited {
|
|
initPlugins()
|
|
}
|
|
|
|
var group *Group
|
|
var err error
|
|
|
|
_, err = gstore.Get(-1)
|
|
if err == nil {
|
|
t.Error("GID #-1 shouldn't exist")
|
|
} else if err != ErrNoRows {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
group, err = gstore.Get(0)
|
|
if err == ErrNoRows {
|
|
t.Error("Couldn't find GID #0")
|
|
} else if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if group.ID != 0 {
|
|
t.Error("group.ID doesn't not match the requested GID. Got '" + strconv.Itoa(group.ID) + "' instead.")
|
|
}
|
|
if group.Name != "Unknown" {
|
|
t.Error("GID #0 is named '" + group.Name + "' and not 'Unknown'")
|
|
}
|
|
|
|
// ? - What if they delete this group? x.x
|
|
// ? - Maybe, pick a random group ID? That would take an extra query, and I'm not sure if I want to be rewriting custom test queries. Possibly, a Random() method on the GroupStore? Seems useless for normal use, it might have some merit for the TopicStore though
|
|
group, err = gstore.Get(1)
|
|
if err == ErrNoRows {
|
|
t.Error("Couldn't find GID #1")
|
|
} else if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if group.ID != 1 {
|
|
t.Error("group.ID doesn't not match the requested GID. Got '" + strconv.Itoa(group.ID) + "' instead.'")
|
|
}
|
|
|
|
_ = group
|
|
|
|
ok := gstore.Exists(-1)
|
|
if ok {
|
|
t.Error("GID #-1 shouldn't exist")
|
|
}
|
|
|
|
ok = gstore.Exists(0)
|
|
if !ok {
|
|
t.Error("GID #0 should exist")
|
|
}
|
|
|
|
ok = gstore.Exists(1)
|
|
if !ok {
|
|
t.Error("GID #1 should exist")
|
|
}
|
|
}
|
|
|
|
func TestSlugs(t *testing.T) {
|
|
var res string
|
|
var msgList []MEPair
|
|
|
|
msgList = addMEPair(msgList, "Unknown", "unknown")
|
|
msgList = addMEPair(msgList, "Unknown2", "unknown2")
|
|
msgList = addMEPair(msgList, "Unknown ", "unknown")
|
|
msgList = addMEPair(msgList, "Unknown 2", "unknown-2")
|
|
msgList = addMEPair(msgList, "Unknown 2", "unknown-2")
|
|
msgList = addMEPair(msgList, "Admin Alice", "admin-alice")
|
|
msgList = addMEPair(msgList, "Admin_Alice", "adminalice")
|
|
msgList = addMEPair(msgList, "Admin_Alice-", "adminalice")
|
|
msgList = addMEPair(msgList, "-Admin_Alice-", "adminalice")
|
|
msgList = addMEPair(msgList, "-Admin@Alice-", "adminalice")
|
|
msgList = addMEPair(msgList, "-Admin😀Alice-", "adminalice")
|
|
msgList = addMEPair(msgList, "u", "u")
|
|
msgList = addMEPair(msgList, "", "untitled")
|
|
msgList = addMEPair(msgList, " ", "untitled")
|
|
msgList = addMEPair(msgList, "-", "untitled")
|
|
msgList = addMEPair(msgList, "--", "untitled")
|
|
msgList = addMEPair(msgList, "é", "é")
|
|
msgList = addMEPair(msgList, "-é-", "é")
|
|
|
|
for _, item := range msgList {
|
|
t.Log("Testing string '" + item.Msg + "'")
|
|
res = nameToSlug(item.Msg)
|
|
if res != item.Expects {
|
|
t.Error("Bad output:", "'"+res+"'")
|
|
t.Error("Expected:", item.Expects)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAuth(t *testing.T) {
|
|
// bcrypt likes doing stupid things, so this test will probably fail
|
|
var realPassword string
|
|
var hashedPassword string
|
|
var password string
|
|
var salt string
|
|
var err error
|
|
|
|
/* No extra salt tests, we might not need this extra salt, as bcrypt has it's own? */
|
|
realPassword = "Madame Cassandra's Mystic Orb"
|
|
t.Log("Set real_password to '" + realPassword + "'")
|
|
t.Log("Hashing the real password")
|
|
hashedPassword, err = BcryptGeneratePasswordNoSalt(realPassword)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
password = realPassword
|
|
t.Log("Testing password '" + password + "'")
|
|
t.Log("Testing salt '" + salt + "'")
|
|
err = CheckPassword(hashedPassword, password, salt)
|
|
if err == ErrMismatchedHashAndPassword {
|
|
t.Error("The two don't match")
|
|
} else if err == ErrPasswordTooLong {
|
|
t.Error("CheckPassword thinks the password is too long")
|
|
} else if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
password = "hahaha"
|
|
t.Log("Testing password '" + password + "'")
|
|
t.Log("Testing salt '" + salt + "'")
|
|
err = CheckPassword(hashedPassword, password, salt)
|
|
if err == ErrPasswordTooLong {
|
|
t.Error("CheckPassword thinks the password is too long")
|
|
} else if err == nil {
|
|
t.Error("The two shouldn't match!")
|
|
}
|
|
|
|
password = "Madame Cassandra's Mystic"
|
|
t.Log("Testing password '" + password + "'")
|
|
t.Log("Testing salt '" + salt + "'")
|
|
err = CheckPassword(hashedPassword, password, salt)
|
|
if err == ErrPasswordTooLong {
|
|
t.Error("CheckPassword thinks the password is too long")
|
|
} else if err == nil {
|
|
t.Error("The two shouldn't match!")
|
|
}
|
|
}
|