more attachment tests

Add UpdateLinked method to AttachmentStore.
This commit is contained in:
Azareal 2020-02-19 20:32:26 +10:00
parent 8d38926863
commit 1766d5decd
7 changed files with 241 additions and 168 deletions

View File

@ -3,6 +3,8 @@ package common
import ( import (
"database/sql" "database/sql"
"errors" "errors"
//"fmt"
"os" "os"
"strings" "strings"
@ -34,6 +36,8 @@ type AttachmentStore interface {
CountIn(originTable string, oid int) int CountIn(originTable string, oid int) int
CountInPath(path string) int CountInPath(path string) int
Delete(id int) error Delete(id int) error
UpdateLinked(otable string, oid int) (err error)
} }
type DefaultAttachmentStore struct { type DefaultAttachmentStore struct {
@ -46,20 +50,27 @@ type DefaultAttachmentStore struct {
move *sql.Stmt move *sql.Stmt
moveByExtra *sql.Stmt moveByExtra *sql.Stmt
delete *sql.Stmt delete *sql.Stmt
replyUpdateAttachs *sql.Stmt
topicUpdateAttachs *sql.Stmt
} }
func NewDefaultAttachmentStore(acc *qgen.Accumulator) (*DefaultAttachmentStore, error) { func NewDefaultAttachmentStore(acc *qgen.Accumulator) (*DefaultAttachmentStore, error) {
a := "attachments" a := "attachments"
return &DefaultAttachmentStore{ return &DefaultAttachmentStore{
get: acc.Select(a).Columns("originID, sectionID, uploadedBy, path, extra").Where("attachID=?").Prepare(), get: acc.Select(a).Columns("originID, sectionID, uploadedBy, path, extra").Where("attachID=?").Prepare(),
getByObj: acc.Select(a).Columns("attachID, sectionID, uploadedBy, path, extra").Where("originTable = ? AND originID = ?").Prepare(), getByObj: acc.Select(a).Columns("attachID, sectionID, uploadedBy, path, extra").Where("originTable=? AND originID=?").Prepare(),
add: acc.Insert(a).Columns("sectionID, sectionTable, originID, originTable, uploadedBy, path, extra").Fields("?,?,?,?,?,?,?").Prepare(), add: acc.Insert(a).Columns("sectionID, sectionTable, originID, originTable, uploadedBy, path, extra").Fields("?,?,?,?,?,?,?").Prepare(),
count: acc.Count(a).Prepare(), count: acc.Count(a).Prepare(),
countIn: acc.Count(a).Where("originTable=? and originID=?").Prepare(), countIn: acc.Count(a).Where("originTable=? and originID=?").Prepare(),
countInPath: acc.Count(a).Where("path = ?").Prepare(), countInPath: acc.Count(a).Where("path=?").Prepare(),
move: acc.Update(a).Set("sectionID=?").Where("originID=? AND originTable=?").Prepare(), move: acc.Update(a).Set("sectionID=?").Where("originID=? AND originTable=?").Prepare(),
moveByExtra: acc.Update(a).Set("sectionID=?").Where("originTable=? AND extra=?").Prepare(), moveByExtra: acc.Update(a).Set("sectionID=?").Where("originTable=? AND extra=?").Prepare(),
delete: acc.Delete(a).Where("attachID=?").Prepare(), delete: acc.Delete(a).Where("attachID=?").Prepare(),
// TODO: Less race-y attachment count updates
replyUpdateAttachs: acc.Update("replies").Set("attachCount=?").Where("rid=?").Prepare(),
topicUpdateAttachs: acc.Update("topics").Set("attachCount=?").Where("tid=?").Prepare(),
}, acc.FirstError() }, acc.FirstError()
} }
@ -80,7 +91,14 @@ func (s *DefaultAttachmentStore) MiniGetList(originTable string, originID int) (
a.Image = ImageFileExts.Contains(a.Ext) a.Image = ImageFileExts.Contains(a.Ext)
alist = append(alist, a) alist = append(alist, a)
} }
return alist, rows.Err() err = rows.Err()
if err != nil {
return nil, err
}
if len(alist) == 0 {
err = sql.ErrNoRows
}
return alist, err
} }
func (s *DefaultAttachmentStore) BulkMiniGetList(originTable string, ids []int) (amap map[int][]*MiniAttachment, err error) { func (s *DefaultAttachmentStore) BulkMiniGetList(originTable string, ids []int) (amap map[int][]*MiniAttachment, err error) {
@ -190,14 +208,34 @@ func (s *DefaultAttachmentStore) Delete(id int) error {
return err return err
} }
// TODO: Split this out of this store
func (s *DefaultAttachmentStore) UpdateLinked(otable string, oid int) (err error) {
switch otable {
case "topics":
_, err = s.topicUpdateAttachs.Exec(s.CountIn(otable, oid), oid)
if err != nil {
return err
}
err = Topics.Reload(oid)
case "replies":
_, err = s.replyUpdateAttachs.Exec(s.CountIn(otable, oid), oid)
}
if err != nil {
return err
}
return nil
}
// TODO: Add a table for the files and lock the file row when performing tasks related to the file // TODO: Add a table for the files and lock the file row when performing tasks related to the file
func DeleteAttachment(aid int) error { func DeleteAttachment(aid int) error {
attach, err := Attachments.Get(aid) attach, err := Attachments.Get(aid)
if err != nil { if err != nil {
//fmt.Println("o1")
return err return err
} }
err = Attachments.Delete(aid) err = Attachments.Delete(aid)
if err != nil { if err != nil {
//fmt.Println("o2")
return err return err
} }
@ -205,9 +243,11 @@ func DeleteAttachment(aid int) error {
if count == 0 { if count == 0 {
err := os.Remove("./attachs/" + attach.Path) err := os.Remove("./attachs/" + attach.Path)
if err != nil { if err != nil {
//fmt.Println("o3")
return err return err
} }
} }
//fmt.Println("o4")
return nil return nil
} }

View File

@ -90,10 +90,11 @@ type DefaultPageStore struct {
func NewDefaultPageStore(acc *qgen.Accumulator) (*DefaultPageStore, error) { func NewDefaultPageStore(acc *qgen.Accumulator) (*DefaultPageStore, error) {
pa := "pages" pa := "pages"
allCols := "pid, name, title, body, allowedGroups, menuID"
return &DefaultPageStore{ return &DefaultPageStore{
get: acc.Select(pa).Columns("name, title, body, allowedGroups, menuID").Where("pid=?").Prepare(), get: acc.Select(pa).Columns("name, title, body, allowedGroups, menuID").Where("pid=?").Prepare(),
getByName: acc.Select(pa).Columns("pid, name, title, body, allowedGroups, menuID").Where("name=?").Prepare(), getByName: acc.Select(pa).Columns(allCols).Where("name=?").Prepare(),
getOffset: acc.Select(pa).Columns("pid, name, title, body, allowedGroups, menuID").Orderby("pid DESC").Limit("?,?").Prepare(), getOffset: acc.Select(pa).Columns(allCols).Orderby("pid DESC").Limit("?,?").Prepare(),
count: acc.Count(pa).Prepare(), count: acc.Count(pa).Prepare(),
delete: acc.Delete(pa).Where("pid=?").Prepare(), delete: acc.Delete(pa).Where("pid=?").Prepare(),
}, acc.FirstError() }, acc.FirstError()

View File

@ -88,7 +88,7 @@ LastIPCutoff - The number of months which need to pass before the last IP stored
PostIPCutoff - The number of days which need to pass before the IP data for a post is automatically deleted. 0 defaults to whatever the current default is, currently 120 and -1 disables this feature. PostIPCutoff - The number of days which need to pass before the IP data for a post is automatically deleted. 0 defaults to whatever the current default is, currently 120 and -1 disables this feature.
PollIPCutoff - The number of days which need to pass before the IP data for a poll is automatically deleted. 0 defaults to whatever the current default is, currently 365 and -1 disables this feature. PollIPCutoff - The number of days which need to pass before the IP data for a poll is automatically deleted. 0 defaults to whatever the current default is, currently 90 and -1 disables this feature.
DisableLastIP - Disable storing last IPs for users and purge any existing user last IP data. Default: false DisableLastIP - Disable storing last IPs for users and purge any existing user last IP data. Default: false

View File

@ -561,92 +561,6 @@ func topicStoreTest(t *testing.T, newID int, ip string) {
// TODO: Test topic creation and retrieving that created topic plus reload and inspecting the cache // TODO: Test topic creation and retrieving that created topic plus reload and inspecting the cache
} }
// TODO: Implement this
func TestAttachments(t *testing.T) {
miscinit(t)
if !c.PluginsInited {
c.InitPlugins()
}
filename := "n0-48.png"
srcFile := "./test_data/" + filename
destFile := "./attachs/" + filename
expect(t, c.Attachments.Count() == 0, "the number of attachments should be 0")
expect(t, c.Attachments.CountIn("topics", 1) == 0, "the number of attachments in topic 1 should be 0")
expect(t, c.Attachments.CountInPath(filename) == 0, fmt.Sprintf("the number of attachments with path '%s' should be 0", filename))
_, err := c.Attachments.Get(1)
if err != nil && err != sql.ErrNoRows {
t.Error(err)
}
expect(t, err == sql.ErrNoRows, ".Get should have no results")
// Sim an upload, try a proper upload through the proper pathway later on
_, err = os.Stat(destFile)
if err != nil && !os.IsNotExist(err) {
expectNilErr(t, err)
} else if err == nil {
err := os.Remove(destFile)
expectNilErr(t, err)
}
input, err := ioutil.ReadFile(srcFile)
expectNilErr(t, err)
err = ioutil.WriteFile(destFile, input, 0644)
expectNilErr(t, err)
tid, err := c.Topics.Create(2, "Attach Test", "Fillter Body", 1, "")
expectNilErr(t, err)
aid, err := c.Attachments.Add(2, "forums", tid, "topics", 1, filename, "")
expectNilErr(t, err)
expect(t, c.Attachments.Count() == 1, "the number of attachments should be 1")
expect(t, c.Attachments.CountIn("topics", tid) == 1, fmt.Sprintf("the number of attachments in topic %d should be 1", tid))
expect(t, c.Attachments.CountInPath(filename) == 1, fmt.Sprintf("the number of attachments with path '%s' should be 1", filename))
a, err := c.Attachments.Get(aid)
expectNilErr(t, err)
expect(t, a.ID == aid, fmt.Sprintf("a.ID should be %d not %d", aid, a.ID))
expect(t, a.SectionID == 2, fmt.Sprintf("a.SectionID should be %d not %d", 2, a.SectionID))
expect(t, a.OriginID == tid, fmt.Sprintf("a.OriginID should be %d not %d", tid, a.OriginID))
expect(t, a.UploadedBy == 1, fmt.Sprintf("a.UploadedBy should be %d not %d", 1, a.UploadedBy))
expect(t, a.Path == filename, fmt.Sprintf("a.Path should be %s not %s", filename, a.Path))
expect(t, a.Extra == "", fmt.Sprintf("a.Extra should be blank not %s", a.Extra))
expect(t, a.Image, "a.Image should be true")
expect(t, a.Ext == "png", fmt.Sprintf("a.Ext should be png not %s", a.Ext))
alist, err := c.Attachments.MiniGetList("topics", tid)
expectNilErr(t, err)
expect(t, len(alist) == 1, fmt.Sprintf("len(alist) should be 1 not %d", len(alist)))
a = alist[0]
expect(t, a.ID == aid, fmt.Sprintf("a.ID should be %d not %d", aid, a.ID))
expect(t, a.SectionID == 2, fmt.Sprintf("a.SectionID should be %d not %d", 2, a.SectionID))
expect(t, a.OriginID == tid, fmt.Sprintf("a.OriginID should be %d not %d", tid, a.OriginID))
expect(t, a.UploadedBy == 1, fmt.Sprintf("a.UploadedBy should be %d not %d", 1, a.UploadedBy))
expect(t, a.Path == filename, fmt.Sprintf("a.Path should be %s not %s", filename, a.Path))
expect(t, a.Extra == "", fmt.Sprintf("a.Extra should be blank not %s", a.Extra))
expect(t, a.Image, "a.Image should be true")
expect(t, a.Ext == "png", fmt.Sprintf("a.Ext should be png not %s", a.Ext))
// TODO: Cover the other bits of creation / deletion not covered in the AttachmentStore like updating the reply / topic attachCount
// TODO: Get attachment tests
// TODO: Move attachment tests
expectNilErr(t, c.Attachments.Delete(aid))
expect(t, c.Attachments.Count() == 0, "the number of attachments should be 0")
expect(t, c.Attachments.CountIn("topics", tid) == 0, fmt.Sprintf("the number of attachments in topic %d should be 0", tid))
expect(t, c.Attachments.CountInPath(filename) == 0, fmt.Sprintf("the number of attachments with path '%s' should be 0", filename))
_, err = c.Attachments.Get(aid)
if err != nil && err != sql.ErrNoRows {
t.Error(err)
}
expect(t, err == sql.ErrNoRows, ".Get should have no results")
// TODO: Reply attachment test
// TODO: Delete reply attachment
// TODO: Path overlap tests
}
func TestForumStore(t *testing.T) { func TestForumStore(t *testing.T) {
miscinit(t) miscinit(t)
if !c.PluginsInited { if !c.PluginsInited {
@ -656,7 +570,6 @@ func TestForumStore(t *testing.T) {
fcache, ok := c.Forums.(c.ForumCache) fcache, ok := c.Forums.(c.ForumCache)
expect(t, ok, "Unable to cast ForumStore to ForumCache") expect(t, ok, "Unable to cast ForumStore to ForumCache")
expect(t, c.Forums.Count() == 2, "The forumstore global count should be 2") expect(t, c.Forums.Count() == 2, "The forumstore global count should be 2")
expect(t, fcache.Length() == 2, "The forum cache length should be 2") expect(t, fcache.Length() == 2, "The forum cache length should be 2")
@ -861,16 +774,16 @@ func TestGroupStore(t *testing.T) {
recordMustNotExist(t, err, "GID #-1 shouldn't exist") recordMustNotExist(t, err, "GID #-1 shouldn't exist")
// TODO: Refactor the group store to remove GID #0 // TODO: Refactor the group store to remove GID #0
group, err := c.Groups.Get(0) g, err := c.Groups.Get(0)
recordMustExist(t, err, "Couldn't find GID #0") recordMustExist(t, err, "Couldn't find GID #0")
expect(t, group.ID == 0, fmt.Sprintf("group.ID doesn't not match the requested GID. Got '%d' instead.", group.ID)) expect(t, g.ID == 0, fmt.Sprintf("g.ID doesn't not match the requested GID. Got '%d' instead.", g.ID))
expect(t, group.Name == "Unknown", fmt.Sprintf("GID #0 is named '%s' and not 'Unknown'", group.Name)) expect(t, g.Name == "Unknown", fmt.Sprintf("GID #0 is named '%s' and not 'Unknown'", g.Name))
group, err = c.Groups.Get(1) g, err = c.Groups.Get(1)
recordMustExist(t, err, "Couldn't find GID #1") recordMustExist(t, err, "Couldn't find GID #1")
expect(t, group.ID == 1, fmt.Sprintf("group.ID doesn't not match the requested GID. Got '%d' instead.'", group.ID)) expect(t, g.ID == 1, fmt.Sprintf("g.ID doesn't not match the requested GID. Got '%d' instead.'", g.ID))
expect(t, len(group.CanSee) > 0, "group.CanSee should not be zero") expect(t, len(g.CanSee) > 0, "g.CanSee should not be zero")
expect(t, !c.Groups.Exists(-1), "GID #-1 shouldn't exist") expect(t, !c.Groups.Exists(-1), "GID #-1 shouldn't exist")
// 0 aka Unknown, for system posts and other oddities // 0 aka Unknown, for system posts and other oddities
@ -884,13 +797,13 @@ func TestGroupStore(t *testing.T) {
expectNilErr(t, err) expectNilErr(t, err)
expect(t, c.Groups.Exists(gid), "The group we just made doesn't exist") expect(t, c.Groups.Exists(gid), "The group we just made doesn't exist")
group, err = c.Groups.Get(gid) g, err = c.Groups.Get(gid)
expectNilErr(t, err) expectNilErr(t, err)
expect(t, group.ID == gid, "The group ID should match the requested ID") expect(t, g.ID == gid, "The group ID should match the requested ID")
expect(t, group.IsAdmin, "This should be an admin group") expect(t, g.IsAdmin, "This should be an admin group")
expect(t, group.IsMod, "This should be a mod group") expect(t, g.IsMod, "This should be a mod group")
expect(t, !group.IsBanned, "This shouldn't be a ban group") expect(t, !g.IsBanned, "This shouldn't be a ban group")
expect(t, len(group.CanSee) == 0, "group.CanSee should be empty") expect(t, len(g.CanSee) == 0, "g.CanSee should be empty")
isAdmin = false isAdmin = false
isMod = true isMod = true
@ -899,36 +812,36 @@ func TestGroupStore(t *testing.T) {
expectNilErr(t, err) expectNilErr(t, err)
expect(t, c.Groups.Exists(gid), "The group we just made doesn't exist") expect(t, c.Groups.Exists(gid), "The group we just made doesn't exist")
group, err = c.Groups.Get(gid) g, err = c.Groups.Get(gid)
expectNilErr(t, err) expectNilErr(t, err)
expect(t, group.ID == gid, "The group ID should match the requested ID") expect(t, g.ID == gid, "The group ID should match the requested ID")
expect(t, !group.IsAdmin, "This should not be an admin group") expect(t, !g.IsAdmin, "This should not be an admin group")
expect(t, group.IsMod, "This should be a mod group") expect(t, g.IsMod, "This should be a mod group")
expect(t, !group.IsBanned, "This shouldn't be a ban group") expect(t, !g.IsBanned, "This shouldn't be a ban group")
// TODO: Make sure this pointer doesn't change once we refactor the group store to stop updating the pointer // TODO: Make sure this pointer doesn't change once we refactor the group store to stop updating the pointer
err = group.ChangeRank(false, false, true) err = g.ChangeRank(false, false, true)
expectNilErr(t, err) expectNilErr(t, err)
group, err = c.Groups.Get(gid) g, err = c.Groups.Get(gid)
expectNilErr(t, err) expectNilErr(t, err)
expect(t, group.ID == gid, "The group ID should match the requested ID") expect(t, g.ID == gid, "The group ID should match the requested ID")
expect(t, !group.IsAdmin, "This shouldn't be an admin group") expect(t, !g.IsAdmin, "This shouldn't be an admin group")
expect(t, !group.IsMod, "This shouldn't be a mod group") expect(t, !g.IsMod, "This shouldn't be a mod group")
expect(t, group.IsBanned, "This should be a ban group") expect(t, g.IsBanned, "This should be a ban group")
err = group.ChangeRank(true, true, true) err = g.ChangeRank(true, true, true)
expectNilErr(t, err) expectNilErr(t, err)
group, err = c.Groups.Get(gid) g, err = c.Groups.Get(gid)
expectNilErr(t, err) expectNilErr(t, err)
expect(t, group.ID == gid, "The group ID should match the requested ID") expect(t, g.ID == gid, "The group ID should match the requested ID")
expect(t, group.IsAdmin, "This should be an admin group") expect(t, g.IsAdmin, "This should be an admin group")
expect(t, group.IsMod, "This should be a mod group") expect(t, g.IsMod, "This should be a mod group")
expect(t, !group.IsBanned, "This shouldn't be a ban group") expect(t, !g.IsBanned, "This shouldn't be a ban group")
expect(t, len(group.CanSee) == 0, "len(group.CanSee) should be 0") expect(t, len(g.CanSee) == 0, "len(g.CanSee) should be 0")
err = group.ChangeRank(false, true, true) err = g.ChangeRank(false, true, true)
expectNilErr(t, err) expectNilErr(t, err)
forum, err := c.Forums.Get(2) forum, err := c.Forums.Get(2)
@ -944,26 +857,26 @@ func TestGroupStore(t *testing.T) {
err = forum.SetPerms(&forumPerms, "custom", gid) err = forum.SetPerms(&forumPerms, "custom", gid)
expectNilErr(t, err) expectNilErr(t, err)
group, err = c.Groups.Get(gid) g, err = c.Groups.Get(gid)
expectNilErr(t, err) expectNilErr(t, err)
expect(t, group.ID == gid, "The group ID should match the requested ID") expect(t, g.ID == gid, "The group ID should match the requested ID")
expect(t, !group.IsAdmin, "This shouldn't be an admin group") expect(t, !g.IsAdmin, "This shouldn't be an admin group")
expect(t, group.IsMod, "This should be a mod group") expect(t, g.IsMod, "This should be a mod group")
expect(t, !group.IsBanned, "This shouldn't be a ban group") expect(t, !g.IsBanned, "This shouldn't be a ban group")
expect(t, group.CanSee != nil, "group.CanSee must not be nil") expect(t, g.CanSee != nil, "g.CanSee must not be nil")
expect(t, len(group.CanSee) == 1, "len(group.CanSee) should not be one") expect(t, len(g.CanSee) == 1, "len(g.CanSee) should not be one")
expect(t, group.CanSee[0] == 2, "group.CanSee[0] should be 2") expect(t, g.CanSee[0] == 2, "g.CanSee[0] should be 2")
canSee := group.CanSee canSee := g.CanSee
// Make sure the data is static // Make sure the data is static
c.Groups.Reload(gid) c.Groups.Reload(gid)
group, err = c.Groups.Get(gid) g, err = c.Groups.Get(gid)
expectNilErr(t, err) expectNilErr(t, err)
expect(t, group.ID == gid, "The group ID should match the requested ID") expect(t, g.ID == gid, "The group ID should match the requested ID")
expect(t, !group.IsAdmin, "This shouldn't be an admin group") expect(t, !g.IsAdmin, "This shouldn't be an admin group")
expect(t, group.IsMod, "This should be a mod group") expect(t, g.IsMod, "This should be a mod group")
expect(t, !group.IsBanned, "This shouldn't be a ban group") expect(t, !g.IsBanned, "This shouldn't be a ban group")
// TODO: Don't enforce a specific order here // TODO: Don't enforce a specific order here
canSeeTest := func(a, b []int) bool { canSeeTest := func(a, b []int) bool {
@ -981,7 +894,7 @@ func TestGroupStore(t *testing.T) {
return true return true
} }
expect(t, canSeeTest(group.CanSee, canSee), "group.CanSee is not being reused") expect(t, canSeeTest(g.CanSee, canSee), "g.CanSee is not being reused")
// TODO: Test group deletion // TODO: Test group deletion
// TODO: Test group reload // TODO: Test group reload
@ -1051,7 +964,6 @@ func TestReplyStore(t *testing.T) {
if !c.PluginsInited { if !c.PluginsInited {
c.InitPlugins() c.InitPlugins()
} }
_, err := c.Rstore.Get(-1) _, err := c.Rstore.Get(-1)
recordMustNotExist(t, err, "RID #-1 shouldn't exist") recordMustNotExist(t, err, "RID #-1 shouldn't exist")
_, err = c.Rstore.Get(0) _, err = c.Rstore.Get(0)
@ -1074,10 +986,10 @@ func testReplyStore(t *testing.T, newID, newPostCount int, ip string) {
} }
replyTest := func(rid, parentID, createdBy int, content, ip string) { replyTest := func(rid, parentID, createdBy int, content, ip string) {
reply, err := c.Rstore.Get(rid) r, err := c.Rstore.Get(rid)
replyTest2(reply, err, rid, parentID, createdBy, content, ip) replyTest2(r, err, rid, parentID, createdBy, content, ip)
reply, err = c.Rstore.GetCache().Get(rid) r, err = c.Rstore.GetCache().Get(rid)
replyTest2(reply, err, rid, parentID, createdBy, content, ip) replyTest2(r, err, rid, parentID, createdBy, content, ip)
} }
replyTest(1, 1, 1, "A reply!", "::1") replyTest(1, 1, 1, "A reply!", "::1")
@ -1145,6 +1057,143 @@ func testReplyStore(t *testing.T, newID, newPostCount int, ip string) {
// TODO: Add tests for ReplyCache // TODO: Add tests for ReplyCache
} }
// TODO: Implement this
func TestAttachments(t *testing.T) {
miscinit(t)
if !c.PluginsInited {
c.InitPlugins()
}
filename := "n0-48.png"
srcFile := "./test_data/" + filename
destFile := "./attachs/" + filename
expect(t, c.Attachments.Count() == 0, "the number of attachments should be 0")
expect(t, c.Attachments.CountIn("topics", 1) == 0, "the number of attachments in topic 1 should be 0")
expect(t, c.Attachments.CountInPath(filename) == 0, fmt.Sprintf("the number of attachments with path '%s' should be 0", filename))
_, err := c.Attachments.Get(1)
if err != nil && err != sql.ErrNoRows {
t.Error(err)
}
expect(t, err == sql.ErrNoRows, ".Get should have no results")
_, err = c.Attachments.MiniGetList("topics", 1)
if err != nil && err != sql.ErrNoRows {
t.Error(err)
}
expect(t, err == sql.ErrNoRows, ".MiniGetList should have no results")
_, err = c.Attachments.BulkMiniGetList("topics", []int{1})
if err != nil && err != sql.ErrNoRows {
t.Error(err)
}
expect(t, err == sql.ErrNoRows, ".BulkMiniGetList should have no results")
// Sim an upload, try a proper upload through the proper pathway later on
_, err = os.Stat(destFile)
if err != nil && !os.IsNotExist(err) {
expectNilErr(t, err)
} else if err == nil {
err := os.Remove(destFile)
expectNilErr(t, err)
}
input, err := ioutil.ReadFile(srcFile)
expectNilErr(t, err)
err = ioutil.WriteFile(destFile, input, 0644)
expectNilErr(t, err)
tid, err := c.Topics.Create(2, "Attach Test", "Fillter Body", 1, "")
expectNilErr(t, err)
aid, err := c.Attachments.Add(2, "forums", tid, "topics", 1, filename, "")
expectNilErr(t, err)
expectNilErr(t, c.Attachments.UpdateLinked("topics", tid))
expect(t, c.Attachments.Count() == 1, "the number of attachments should be 1")
expect(t, c.Attachments.CountIn("topics", tid) == 1, fmt.Sprintf("the number of attachments in topic %d should be 1", tid))
expect(t, c.Attachments.CountInPath(filename) == 1, fmt.Sprintf("the number of attachments with path '%s' should be 1", filename))
var a *c.MiniAttachment
f := func(aid, sid, oid, uploadedBy int, path, extra, ext string) {
expect(t, a.ID == aid, fmt.Sprintf("ID should be %d not %d", aid, a.ID))
expect(t, a.SectionID == sid, fmt.Sprintf("SectionID should be %d not %d", sid, a.SectionID))
expect(t, a.OriginID == oid, fmt.Sprintf("OriginID should be %d not %d", oid, a.OriginID))
expect(t, a.UploadedBy == uploadedBy, fmt.Sprintf("UploadedBy should be %d not %d", uploadedBy, a.UploadedBy))
expect(t, a.Path == path, fmt.Sprintf("Path should be %s not %s", path, a.Path))
expect(t, a.Extra == extra, fmt.Sprintf("Extra should be %s not %s", extra, a.Extra))
expect(t, a.Image, "Image should be true")
expect(t, a.Ext == ext, fmt.Sprintf("Ext should be %s not %s", ext, a.Ext))
}
f2 := func(aid, oid int, extra string, topic bool) {
var tbl string
if topic {
tbl = "topics"
} else {
tbl = "replies"
}
a, err = c.Attachments.Get(aid)
expectNilErr(t, err)
f(aid, 2, oid, 1, filename, extra, "png")
alist, err := c.Attachments.MiniGetList(tbl, oid)
expectNilErr(t, err)
expect(t, len(alist) == 1, fmt.Sprintf("len(alist) should be 1 not %d", len(alist)))
a = alist[0]
f(aid, 2, oid, 1, filename, extra, "png")
amap, err := c.Attachments.BulkMiniGetList(tbl, []int{oid})
expectNilErr(t, err)
expect(t, len(amap) == 1, fmt.Sprintf("len(amap) should be 1 not %d", len(amap)))
alist, ok := amap[oid]
if !ok {
t.Logf("key %d not found in amap", oid)
}
expect(t, len(alist) == 1, fmt.Sprintf("len(alist) should be 1 not %d", len(alist)))
a = alist[0]
f(aid, 2, oid, 1, filename, extra, "png")
}
topic, err := c.Topics.Get(tid)
expectNilErr(t, err)
expect(t, topic.AttachCount == 1, fmt.Sprintf("topic.AttachCount should be 1 not %d", topic.AttachCount))
f2(aid, tid, "", true)
// TODO: Cover the other bits of creation / deletion not covered in the AttachmentStore like updating the reply / topic attachCount
// TODO: Move attachment tests
expectNilErr(t, c.Attachments.Delete(aid))
expect(t, c.Attachments.Count() == 0, "the number of attachments should be 0")
expect(t, c.Attachments.CountIn("topics", tid) == 0, fmt.Sprintf("the number of attachments in topic %d should be 0", tid))
expect(t, c.Attachments.CountInPath(filename) == 0, fmt.Sprintf("the number of attachments with path '%s' should be 0", filename))
_, err = c.Attachments.Get(aid)
if err != nil && err != sql.ErrNoRows {
t.Error(err)
}
expect(t, err == sql.ErrNoRows, ".Get should have no results")
_, err = c.Attachments.MiniGetList("topics", tid)
if err != nil && err != sql.ErrNoRows {
t.Error(err)
}
expect(t, err == sql.ErrNoRows, ".MiniGetList should have no results")
_, err = c.Attachments.BulkMiniGetList("topics", []int{tid})
if err != nil && err != sql.ErrNoRows {
t.Error(err)
}
expect(t, err == sql.ErrNoRows, ".BulkMiniGetList should have no results")
rid, err := c.Rstore.Create(topic, "Reply Filler", "", 1)
expectNilErr(t, err)
aid, err = c.Attachments.Add(2, "forums", rid, "replies", 1, filename, strconv.Itoa(topic.ID))
expectNilErr(t, err)
expectNilErr(t, c.Attachments.UpdateLinked("replies", rid))
r, err := c.Rstore.Get(rid)
expectNilErr(t, err)
expect(t, r.AttachCount == 1, fmt.Sprintf("r.AttachCount should be 1 not %d", r.AttachCount))
f2(aid, rid, strconv.Itoa(topic.ID), false)
// TODO: Delete reply attachment
// TODO: Path overlap tests
}
func TestProfileReplyStore(t *testing.T) { func TestProfileReplyStore(t *testing.T) {
miscinit(t) miscinit(t)
if !c.PluginsInited { if !c.PluginsInited {

View File

@ -101,7 +101,7 @@ func deleteAttachment(w http.ResponseWriter, r *http.Request, user c.User, aid i
// TODO: Stop duplicating this code // TODO: Stop duplicating this code
// TODO: Use a transaction here // TODO: Use a transaction here
// TODO: Move this function to neutral ground // TODO: Move this function to neutral ground
func uploadAttachment(w http.ResponseWriter, r *http.Request, user c.User, sid int, sectionTable string, oid int, originTable, extra string) (pathMap map[string]string, rerr c.RouteError) { func uploadAttachment(w http.ResponseWriter, r *http.Request, user c.User, sid int, stable string, oid int, otable, extra string) (pathMap map[string]string, rerr c.RouteError) {
pathMap = make(map[string]string) pathMap = make(map[string]string)
files, rerr := uploadFilesWithHash(w, r, user, "./attachs/") files, rerr := uploadFilesWithHash(w, r, user, "./attachs/")
if rerr != nil { if rerr != nil {
@ -109,7 +109,7 @@ func uploadAttachment(w http.ResponseWriter, r *http.Request, user c.User, sid i
} }
for _, filename := range files { for _, filename := range files {
aid, err := c.Attachments.Add(sid, sectionTable, oid, originTable, user.ID, filename, extra) aid, err := c.Attachments.Add(sid, stable, oid, otable, user.ID, filename, extra)
if err != nil { if err != nil {
return nil, c.InternalError(err, w, r) return nil, c.InternalError(err, w, r)
} }
@ -121,22 +121,10 @@ func uploadAttachment(w http.ResponseWriter, r *http.Request, user c.User, sid i
pathMap[filename] = strconv.Itoa(aid) pathMap[filename] = strconv.Itoa(aid)
} }
switch originTable { err = c.Attachments.UpdateLinked(otable, oid)
case "topics":
_, err = topicStmts.updateAttachs.Exec(c.Attachments.CountIn(originTable, oid), oid)
if err != nil { if err != nil {
return nil, c.InternalError(err, w, r) return nil, c.InternalError(err, w, r)
} }
err = c.Topics.Reload(oid)
if err != nil {
return nil, c.InternalError(err, w, r)
}
case "replies":
_, err = replyStmts.updateAttachs.Exec(c.Attachments.CountIn(originTable, oid), oid)
if err != nil {
return nil, c.InternalError(err, w, r)
}
}
} }
return pathMap, nil return pathMap, nil

View File

@ -15,7 +15,6 @@ import (
) )
type ReplyStmts struct { type ReplyStmts struct {
updateAttachs *sql.Stmt
createReplyPaging *sql.Stmt createReplyPaging *sql.Stmt
} }
@ -25,8 +24,6 @@ var replyStmts ReplyStmts
func init() { func init() {
c.DbInits.Add(func(acc *qgen.Accumulator) error { c.DbInits.Add(func(acc *qgen.Accumulator) error {
replyStmts = ReplyStmts{ replyStmts = ReplyStmts{
// TODO: Less race-y attachment count updates
updateAttachs: acc.Update("replies").Set("attachCount=?").Where("rid=?").Prepare(),
createReplyPaging: acc.Select("replies").Cols("rid").Where("rid >= ? - 1 AND tid=?").Orderby("rid ASC").Prepare(), createReplyPaging: acc.Select("replies").Cols("rid").Where("rid >= ? - 1 AND tid=?").Orderby("rid ASC").Prepare(),
} }
return acc.FirstError() return acc.FirstError()

View File

@ -30,7 +30,6 @@ import (
type TopicStmts struct { type TopicStmts struct {
getLikedTopic *sql.Stmt getLikedTopic *sql.Stmt
updateAttachs *sql.Stmt
} }
var topicStmts TopicStmts var topicStmts TopicStmts
@ -40,8 +39,6 @@ func init() {
c.DbInits.Add(func(acc *qgen.Accumulator) error { c.DbInits.Add(func(acc *qgen.Accumulator) error {
topicStmts = TopicStmts{ topicStmts = TopicStmts{
getLikedTopic: acc.Select("likes").Columns("targetItem").Where("sentBy=? && targetItem=? && targetType='topics'").Prepare(), getLikedTopic: acc.Select("likes").Columns("targetItem").Where("sentBy=? && targetItem=? && targetType='topics'").Prepare(),
// TODO: Less race-y attachment count updates
updateAttachs: acc.Update("topics").Set("attachCount=?").Where("tid=?").Prepare(),
} }
return acc.FirstError() return acc.FirstError()
}) })
@ -134,7 +131,7 @@ func ViewTopic(w http.ResponseWriter, r *http.Request, user c.User, header *c.He
if topic.AttachCount > 0 { if topic.AttachCount > 0 {
attachs, err := c.Attachments.MiniGetList("topics", topic.ID) attachs, err := c.Attachments.MiniGetList("topics", topic.ID)
if err != nil { if err != nil && err != sql.ErrNoRows {
// TODO: We might want to be a little permissive here in-case of a desync? // TODO: We might want to be a little permissive here in-case of a desync?
return c.InternalError(err, w, r) return c.InternalError(err, w, r)
} }
@ -452,6 +449,7 @@ func CreateTopicSubmit(w http.ResponseWriter, r *http.Request, user c.User) c.Ro
return nil return nil
} }
// TODO: Move this function
func uploadFilesWithHash(w http.ResponseWriter, r *http.Request, user c.User, dir string) (filenames []string, rerr c.RouteError) { func uploadFilesWithHash(w http.ResponseWriter, r *http.Request, user c.User, dir string) (filenames []string, rerr c.RouteError) {
files, ok := r.MultipartForm.File["upload_files"] files, ok := r.MultipartForm.File["upload_files"]
if !ok { if !ok {