diff --git a/backend/_example/memory_store/accessor/data_test.go b/backend/_example/memory_store/accessor/data_test.go
index 95e03e36..a76d0d23 100644
--- a/backend/_example/memory_store/accessor/data_test.go
+++ b/backend/_example/memory_store/accessor/data_test.go
@@ -10,6 +10,7 @@ import (
"fmt"
"sort"
"testing"
+ "testing/synctest"
"time"
"github.com/stretchr/testify/assert"
@@ -521,51 +522,52 @@ func TestMemData_FlagListVerified(t *testing.T) {
}
func TestMemData_FlagListBlocked(t *testing.T) {
-
- b := prepMem(t)
- setBlocked := func(site, user string, status engine.FlagStatus, ttl time.Duration) error {
- req := engine.FlagRequest{Flag: engine.Blocked, Locator: store.Locator{SiteID: site}, UserID: user, Update: status,
- TTL: ttl}
- _, err := b.Flag(req)
- return err
- }
-
- toBlocked := func(inp []any) (res []store.BlockedUser) {
- res = make([]store.BlockedUser, len(inp))
- for i, v := range inp {
- vv, ok := v.(store.BlockedUser)
- require.True(t, ok)
- res[i] = vv
+ synctest.Test(t, func(t *testing.T) {
+ b := prepMem(t)
+ setBlocked := func(site, user string, status engine.FlagStatus, ttl time.Duration) error {
+ req := engine.FlagRequest{Flag: engine.Blocked, Locator: store.Locator{SiteID: site}, UserID: user, Update: status,
+ TTL: ttl}
+ _, err := b.Flag(req)
+ return err
}
- return res
- }
- assert.NoError(t, setBlocked("radio-t", "user1", engine.FlagTrue, 0))
- assert.NoError(t, setBlocked("radio-t", "user2", engine.FlagTrue, 50*time.Millisecond))
- assert.NoError(t, setBlocked("radio-t", "user3", engine.FlagFalse, 0))
- vv, err := b.ListFlags(engine.FlagRequest{Flag: engine.Blocked, Locator: store.Locator{SiteID: "radio-t"}})
- assert.NoError(t, err)
+ toBlocked := func(inp []any) (res []store.BlockedUser) {
+ res = make([]store.BlockedUser, len(inp))
+ for i, v := range inp {
+ vv, ok := v.(store.BlockedUser)
+ require.True(t, ok)
+ res[i] = vv
+ }
+ return res
+ }
+ assert.NoError(t, setBlocked("radio-t", "user1", engine.FlagTrue, 0))
+ assert.NoError(t, setBlocked("radio-t", "user2", engine.FlagTrue, 50*time.Millisecond))
+ assert.NoError(t, setBlocked("radio-t", "user3", engine.FlagFalse, 0))
- blockedList := toBlocked(vv)
- var blockedIDs = make([]string, len(blockedList))
- for i, x := range blockedList {
- blockedIDs[i] = x.ID
- }
- require.Equal(t, 2, len(blockedList), b.metaUsers)
- assert.ElementsMatch(t, []string{"user1", "user2"}, blockedIDs)
- t.Logf("%+v", blockedList)
+ vv, err := b.ListFlags(engine.FlagRequest{Flag: engine.Blocked, Locator: store.Locator{SiteID: "radio-t"}})
+ assert.NoError(t, err)
- // check block expiration
- time.Sleep(50 * time.Millisecond)
- vv, err = b.ListFlags(engine.FlagRequest{Flag: engine.Blocked, Locator: store.Locator{SiteID: "radio-t"}})
- assert.NoError(t, err)
- blockedList = toBlocked(vv)
- require.Equal(t, 1, len(blockedList))
- assert.Equal(t, "user1", blockedList[0].ID)
+ blockedList := toBlocked(vv)
+ var blockedIDs = make([]string, len(blockedList))
+ for i, x := range blockedList {
+ blockedIDs[i] = x.ID
+ }
+ require.Equal(t, 2, len(blockedList), b.metaUsers)
+ assert.ElementsMatch(t, []string{"user1", "user2"}, blockedIDs)
+ t.Logf("%+v", blockedList)
- vv, err = b.ListFlags(engine.FlagRequest{Flag: engine.Blocked, Locator: store.Locator{SiteID: "bad"}})
- assert.NoError(t, err)
- assert.Equal(t, 0, len(vv))
+ // check block expiration
+ time.Sleep(50 * time.Millisecond)
+ vv, err = b.ListFlags(engine.FlagRequest{Flag: engine.Blocked, Locator: store.Locator{SiteID: "radio-t"}})
+ assert.NoError(t, err)
+ blockedList = toBlocked(vv)
+ require.Equal(t, 1, len(blockedList))
+ assert.Equal(t, "user1", blockedList[0].ID)
+
+ vv, err = b.ListFlags(engine.FlagRequest{Flag: engine.Blocked, Locator: store.Locator{SiteID: "bad"}})
+ assert.NoError(t, err)
+ assert.Equal(t, 0, len(vv))
+ })
}
func TestMemData_DeleteComment(t *testing.T) {
diff --git a/backend/app/migrator/backup_test.go b/backend/app/migrator/backup_test.go
index 7917db41..c9c86879 100644
--- a/backend/app/migrator/backup_test.go
+++ b/backend/app/migrator/backup_test.go
@@ -6,6 +6,7 @@ import (
"io"
"os"
"testing"
+ "testing/synctest"
"time"
"github.com/stretchr/testify/assert"
@@ -59,19 +60,21 @@ func TestBackup_Do(t *testing.T) {
defer os.RemoveAll(loc)
assert.NoError(t, os.MkdirAll(loc, 0o700))
- ctx, cancel := context.WithCancel(context.Background())
- go func() {
- time.Sleep(time.Second)
- cancel()
- }()
+ synctest.Test(t, func(t *testing.T) {
+ ctx, cancel := context.WithCancel(context.Background())
+ go func() {
+ time.Sleep(time.Second)
+ cancel()
+ }()
- bk := AutoBackup{BackupLocation: loc, SiteID: "site1", KeepMax: 3, Exporter: &mockExporter{}, Duration: 600 * time.Millisecond}
- bk.Do(ctx)
+ bk := AutoBackup{BackupLocation: loc, SiteID: "site1", KeepMax: 3, Exporter: &mockExporter{}, Duration: 600 * time.Millisecond}
+ bk.Do(ctx)
- expFile := fmt.Sprintf("/tmp/remark-backups.test/backup-site1-%s.gz", time.Now().Format("20060102"))
- fi, err := os.Lstat(expFile)
- assert.NoError(t, err)
- assert.Equal(t, int64(52), fi.Size())
+ expFile := fmt.Sprintf("/tmp/remark-backups.test/backup-site1-%s.gz", time.Now().Format("20060102"))
+ fi, err := os.Lstat(expFile)
+ assert.NoError(t, err)
+ assert.Equal(t, int64(52), fi.Size())
+ })
}
type mockExporter struct{}
diff --git a/backend/app/notify/notify_mock.go b/backend/app/notify/notify_mock.go
index 9244f6c1..dc58c7f6 100644
--- a/backend/app/notify/notify_mock.go
+++ b/backend/app/notify/notify_mock.go
@@ -4,7 +4,6 @@ import (
"context"
"fmt"
"sync"
- "time"
log "github.com/go-pkgz/lgr"
)
@@ -22,14 +21,13 @@ type MockDest struct {
func (m *MockDest) Send(ctx context.Context, r Request) error {
m.lock.Lock()
defer m.lock.Unlock()
- select {
- case <-time.After(10 * time.Millisecond):
- m.data = append(m.data, r)
- log.Printf("sent %s -> %d", r.Comment.ID, m.id)
- case <-ctx.Done():
+ if err := ctx.Err(); err != nil {
log.Printf("ctx closed %d", m.id)
m.closed = true
+ return nil
}
+ m.data = append(m.data, r)
+ log.Printf("sent %s -> %d", r.Comment.ID, m.id)
return nil
}
@@ -37,14 +35,13 @@ func (m *MockDest) Send(ctx context.Context, r Request) error {
func (m *MockDest) SendVerification(ctx context.Context, v VerificationRequest) error {
m.lock.Lock()
defer m.lock.Unlock()
- select {
- case <-time.After(10 * time.Millisecond):
- m.verificationData = append(m.verificationData, v)
- log.Printf("sent verification %s -> %d", v.User, m.id)
- case <-ctx.Done():
+ if err := ctx.Err(); err != nil {
log.Printf("verification ctx closed %d", m.id)
m.closed = true
+ return nil
}
+ m.verificationData = append(m.verificationData, v)
+ log.Printf("sent verification %s -> %d", v.User, m.id)
return nil
}
diff --git a/backend/app/notify/notify_test.go b/backend/app/notify/notify_test.go
index 394e2eb1..db280a41 100644
--- a/backend/app/notify/notify_test.go
+++ b/backend/app/notify/notify_test.go
@@ -2,10 +2,9 @@ package notify
import (
"fmt"
- "math/rand"
"sync/atomic"
"testing"
- "time"
+ "testing/synctest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -26,243 +25,256 @@ func TestService_NoDestinations(t *testing.T) {
}
func TestService_WithDestinations(t *testing.T) {
- d1, d2 := &MockDest{id: 1}, &MockDest{id: 2}
- s := NewService(nil, 1, d1, d2)
- assert.NotNil(t, s)
+ synctest.Test(t, func(t *testing.T) {
+ d1, d2 := &MockDest{id: 1}, &MockDest{id: 2}
+ s := NewService(nil, 1, d1, d2)
+ assert.NotNil(t, s)
- s.Submit(Request{Comment: store.Comment{ID: "100"}})
- time.Sleep(time.Millisecond * 110)
- s.Submit(Request{Comment: store.Comment{ID: "101"}})
- time.Sleep(time.Millisecond * 110)
- s.Submit(Request{Comment: store.Comment{ID: "102"}})
- time.Sleep(time.Millisecond * 110)
- s.Close()
+ s.Submit(Request{Comment: store.Comment{ID: "100"}})
+ synctest.Wait()
+ s.Submit(Request{Comment: store.Comment{ID: "101"}})
+ synctest.Wait()
+ s.Submit(Request{Comment: store.Comment{ID: "102"}})
+ synctest.Wait()
+ s.Close()
- require.Equal(t, 3, len(d1.Get()), "got all comments to d1")
- require.Equal(t, 3, len(d2.Get()), "got all comments to d2")
+ require.Equal(t, 3, len(d1.Get()), "got all comments to d1")
+ require.Equal(t, 3, len(d2.Get()), "got all comments to d2")
- assert.Equal(t, "100", d1.Get()[0].Comment.ID)
- assert.Equal(t, "101", d1.Get()[1].Comment.ID)
- assert.Equal(t, "102", d1.Get()[2].Comment.ID)
+ assert.Equal(t, "100", d1.Get()[0].Comment.ID)
+ assert.Equal(t, "101", d1.Get()[1].Comment.ID)
+ assert.Equal(t, "102", d1.Get()[2].Comment.ID)
+ })
}
func TestService_WithDrops(t *testing.T) {
- d1, d2 := &MockDest{id: 1}, &MockDest{id: 2}
- s := NewService(nil, 1, d1, d2)
- assert.NotNil(t, s)
+ synctest.Test(t, func(t *testing.T) {
+ d1, d2 := &MockDest{id: 1}, &MockDest{id: 2}
+ s := NewService(nil, 1, d1, d2)
+ assert.NotNil(t, s)
- s.Submit(Request{Comment: store.Comment{ID: "100"}})
- s.Submit(Request{Comment: store.Comment{ID: "101"}})
- s.Submit(Request{Comment: store.Comment{ID: "102"}})
- time.Sleep(time.Millisecond * 21)
- s.Close()
+ s.Submit(Request{Comment: store.Comment{ID: "100"}})
+ s.Submit(Request{Comment: store.Comment{ID: "101"}})
+ s.Submit(Request{Comment: store.Comment{ID: "102"}})
+ synctest.Wait()
+ s.Close()
- s.Submit(Request{Comment: store.Comment{ID: "111"}}) // safe to send after close
+ s.Submit(Request{Comment: store.Comment{ID: "111"}}) // safe to send after close
- assert.LessOrEqual(t, len(d1.Get()), 2, "at least one comment from three dropped from d1, got: %v", d1.Get())
- assert.LessOrEqual(t, len(d2.Get()), 2, "at least one comment from three dropped from d2, got: %v", d2.Get())
+ assert.LessOrEqual(t, len(d1.Get()), 2, "at least one comment from three dropped from d1, got: %v", d1.Get())
+ assert.LessOrEqual(t, len(d2.Get()), 2, "at least one comment from three dropped from d2, got: %v", d2.Get())
+ })
}
func TestService_SubmitVerificationWithDrops(t *testing.T) {
- d1, d2 := &MockDest{id: 1}, &MockDest{id: 2}
- s := NewService(nil, 1, d1, d2)
- assert.NotNil(t, s)
+ synctest.Test(t, func(t *testing.T) {
+ d1, d2 := &MockDest{id: 1}, &MockDest{id: 2}
+ s := NewService(nil, 1, d1, d2)
+ assert.NotNil(t, s)
- s.SubmitVerification(VerificationRequest{
- SiteID: "remark",
- User: "testUser",
- Email: "test@example.org",
- Token: "testToken",
+ s.SubmitVerification(VerificationRequest{
+ SiteID: "remark",
+ User: "testUser",
+ Email: "test@example.org",
+ Token: "testToken",
+ })
+ s.SubmitVerification(VerificationRequest{})
+ s.SubmitVerification(VerificationRequest{})
+ synctest.Wait()
+ s.Close()
+
+ s.SubmitVerification(VerificationRequest{}) // safe to send after close
+
+ assert.LessOrEqual(t, len(d2.GetVerify()), 2, "one request from three dropped from d2, got: %v", d2.GetVerify())
+
+ verifyDest := d1.GetVerify()
+ require.LessOrEqual(t, len(verifyDest), 2, "one request from three dropped from d1, got: %v", verifyDest)
+ assert.Equal(t, "remark", verifyDest[0].SiteID)
+ assert.Equal(t, "testUser", verifyDest[0].User)
+ assert.Equal(t, "test@example.org", verifyDest[0].Email)
+ assert.Equal(t, "testToken", verifyDest[0].Token)
})
- s.SubmitVerification(VerificationRequest{})
- s.SubmitVerification(VerificationRequest{})
- time.Sleep(time.Millisecond * 21)
- s.Close()
-
- s.SubmitVerification(VerificationRequest{}) // safe to send after close
-
- assert.LessOrEqual(t, len(d2.GetVerify()), 2, "one request from three dropped from d2, got: %v", d2.GetVerify())
-
- verifyDest := d1.GetVerify()
- require.LessOrEqual(t, len(verifyDest), 2, "one request from three dropped from d1, got: %v", verifyDest)
- assert.Equal(t, "remark", verifyDest[0].SiteID)
- assert.Equal(t, "testUser", verifyDest[0].User)
- assert.Equal(t, "test@example.org", verifyDest[0].Email)
- assert.Equal(t, "testToken", verifyDest[0].Token)
}
func TestService_Many(t *testing.T) {
- d1, d2 := &MockDest{id: 1}, &MockDest{id: 2}
- s := NewService(nil, 5, d1, d2)
- assert.NotNil(t, s)
+ synctest.Test(t, func(t *testing.T) {
+ d1, d2 := &MockDest{id: 1}, &MockDest{id: 2}
+ s := NewService(nil, 5, d1, d2)
+ assert.NotNil(t, s)
- for i := range 10 {
- s.Submit(Request{Comment: store.Comment{ID: fmt.Sprintf("%d", 100+i)}})
- s.SubmitVerification(VerificationRequest{User: fmt.Sprintf("%d", 100+i)})
- time.Sleep(time.Millisecond * time.Duration(rand.Int31n(20)))
- }
- s.Close()
+ for i := range 10 {
+ s.Submit(Request{Comment: store.Comment{ID: fmt.Sprintf("%d", 100+i)}})
+ s.SubmitVerification(VerificationRequest{User: fmt.Sprintf("%d", 100+i)})
+ }
+ s.Close()
- assert.NotEqual(t, 10, len(d1.Get()), "some comments dropped from d1")
- assert.NotEqual(t, 10, len(d1.GetVerify()), "some verifications dropped from d1")
- assert.NotEqual(t, 10, len(d2.Get()), "some comments dropped from d2")
- assert.NotEqual(t, 10, len(d2.GetVerify()), "some verifications dropped from d2")
+ assert.NotEqual(t, 10, len(d1.Get()), "some comments dropped from d1")
+ assert.NotEqual(t, 10, len(d1.GetVerify()), "some verifications dropped from d1")
+ assert.NotEqual(t, 10, len(d2.Get()), "some comments dropped from d2")
+ assert.NotEqual(t, 10, len(d2.GetVerify()), "some verifications dropped from d2")
+ })
}
func TestService_WithParent(t *testing.T) {
- dest := &MockDest{id: 1}
- dataStore := &mockStore{data: map[string]store.Comment{}}
+ synctest.Test(t, func(t *testing.T) {
+ dest := &MockDest{id: 1}
+ dataStore := &mockStore{data: map[string]store.Comment{}}
- dataStore.data["p1"] = store.Comment{ID: "p1"}
- dataStore.data["p2"] = store.Comment{ID: "p2"}
+ dataStore.data["p1"] = store.Comment{ID: "p1"}
+ dataStore.data["p2"] = store.Comment{ID: "p2"}
- s := NewService(dataStore, 1, dest)
- assert.NotNil(t, s)
+ s := NewService(dataStore, 1, dest)
+ assert.NotNil(t, s)
- s.Submit(Request{Comment: store.Comment{ID: "c1", ParentID: "p1"}})
- time.Sleep(time.Millisecond * 110)
- s.Submit(Request{Comment: store.Comment{ID: "c11", ParentID: "p11"}})
- time.Sleep(time.Millisecond * 110)
- s.Close()
+ s.Submit(Request{Comment: store.Comment{ID: "c1", ParentID: "p1"}})
+ synctest.Wait()
+ s.Submit(Request{Comment: store.Comment{ID: "c11", ParentID: "p11"}})
+ synctest.Wait()
+ s.Close()
- destRes := dest.Get()
- require.Equal(t, 2, len(destRes), "two comment notified")
- assert.Equal(t, "p1", destRes[0].Comment.ParentID)
- assert.Equal(t, "p1", destRes[0].parent.ID)
- assert.Equal(t, "p11", destRes[1].Comment.ParentID)
- assert.Equal(t, "", destRes[1].parent.ID)
+ destRes := dest.Get()
+ require.Equal(t, 2, len(destRes), "two comment notified")
+ assert.Equal(t, "p1", destRes[0].Comment.ParentID)
+ assert.Equal(t, "p1", destRes[0].parent.ID)
+ assert.Equal(t, "p11", destRes[1].Comment.ParentID)
+ assert.Equal(t, "", destRes[1].parent.ID)
+ })
}
func TestService_EmailRetrieval(t *testing.T) {
- dest := &MockDest{id: 1}
- dataStore := &mockStore{data: map[string]store.Comment{}, userDetails: map[string]string{}}
+ synctest.Test(t, func(t *testing.T) {
+ dest := &MockDest{id: 1}
+ dataStore := &mockStore{data: map[string]store.Comment{}, userDetails: map[string]string{}}
- dataStore.data["p1"] = store.Comment{ID: "p1", User: store.User{ID: "u1"}}
- dataStore.data["p2"] = store.Comment{ID: "p2", ParentID: "p1", User: store.User{ID: "u1"}}
- dataStore.data["p3"] = store.Comment{ID: "p3", ParentID: "p1", User: store.User{ID: "u2"}}
- dataStore.data["p4"] = store.Comment{ID: "p4", ParentID: "p3", User: store.User{ID: "u1"}}
- dataStore.userDetails["u1"] = "u1@example.com"
+ dataStore.data["p1"] = store.Comment{ID: "p1", User: store.User{ID: "u1"}}
+ dataStore.data["p2"] = store.Comment{ID: "p2", ParentID: "p1", User: store.User{ID: "u1"}}
+ dataStore.data["p3"] = store.Comment{ID: "p3", ParentID: "p1", User: store.User{ID: "u2"}}
+ dataStore.data["p4"] = store.Comment{ID: "p4", ParentID: "p3", User: store.User{ID: "u1"}}
+ dataStore.userDetails["u1"] = "u1@example.com"
- s := NewService(dataStore, 1, dest)
- assert.NotNil(t, s)
+ s := NewService(dataStore, 1, dest)
+ assert.NotNil(t, s)
- // one comment, one notification
- s.Submit(Request{Comment: dataStore.data["p1"]})
- time.Sleep(time.Millisecond * 110)
+ // one comment, one notification
+ s.Submit(Request{Comment: dataStore.data["p1"]})
+ synctest.Wait()
- destRes := dest.Get()
- require.Equal(t, 1, len(destRes), "one comment notified")
- assert.Equal(t, "p1", destRes[0].Comment.ID)
- assert.Empty(t, destRes[0].parent)
- assert.Empty(t, destRes[0].Emails)
+ destRes := dest.Get()
+ require.Equal(t, 1, len(destRes), "one comment notified")
+ assert.Equal(t, "p1", destRes[0].Comment.ID)
+ assert.Empty(t, destRes[0].parent)
+ assert.Empty(t, destRes[0].Emails)
- // reply to the first comment, same comment as one in original comment
- s.Submit(Request{Comment: dataStore.data["p2"]})
- time.Sleep(time.Millisecond * 110)
+ // reply to the first comment, same comment as one in original comment
+ s.Submit(Request{Comment: dataStore.data["p2"]})
+ synctest.Wait()
- destRes = dest.Get()
- require.Equal(t, 2, len(destRes), "two comment notified")
- assert.Equal(t, "p2", destRes[1].Comment.ID)
- assert.Equal(t, "p1", destRes[1].parent.ID)
- assert.Equal(t, "u1", destRes[1].parent.User.ID)
- assert.Empty(t, destRes[1].Emails, "u1 is not notified they are the one who left the comment")
+ destRes = dest.Get()
+ require.Equal(t, 2, len(destRes), "two comment notified")
+ assert.Equal(t, "p2", destRes[1].Comment.ID)
+ assert.Equal(t, "p1", destRes[1].parent.ID)
+ assert.Equal(t, "u1", destRes[1].parent.User.ID)
+ assert.Empty(t, destRes[1].Emails, "u1 is not notified they are the one who left the comment")
- // another reply to the first comment, another user
- s.Submit(Request{Comment: dataStore.data["p3"]})
- time.Sleep(time.Millisecond * 110)
+ // another reply to the first comment, another user
+ s.Submit(Request{Comment: dataStore.data["p3"]})
+ synctest.Wait()
- destRes = dest.Get()
- require.Equal(t, 3, len(destRes), "three comment notified")
- assert.Equal(t, "p3", destRes[2].Comment.ID)
- assert.Equal(t, "p1", destRes[2].parent.ID)
- assert.Equal(t, "u1", destRes[2].parent.User.ID)
- assert.ElementsMatch(t, []string{"u1@example.com"}, destRes[2].Emails)
+ destRes = dest.Get()
+ require.Equal(t, 3, len(destRes), "three comment notified")
+ assert.Equal(t, "p3", destRes[2].Comment.ID)
+ assert.Equal(t, "p1", destRes[2].parent.ID)
+ assert.Equal(t, "u1", destRes[2].parent.User.ID)
+ assert.ElementsMatch(t, []string{"u1@example.com"}, destRes[2].Emails)
- // reply to the last comment by another user, should trigger email retrieval error
- s.Submit(Request{Comment: dataStore.data["p4"]})
- time.Sleep(time.Millisecond * 110)
+ // reply to the last comment by another user, should trigger email retrieval error
+ s.Submit(Request{Comment: dataStore.data["p4"]})
+ synctest.Wait()
- destRes = dest.Get()
- require.Equal(t, 4, len(destRes), "four comment notified")
- assert.Equal(t, "p4", destRes[3].Comment.ID)
- assert.Equal(t, "p3", destRes[3].parent.ID)
- assert.Equal(t, "u2", destRes[3].parent.User.ID)
- assert.Empty(t, destRes[3].Emails, "no email can be retrieved for u2")
+ destRes = dest.Get()
+ require.Equal(t, 4, len(destRes), "four comment notified")
+ assert.Equal(t, "p4", destRes[3].Comment.ID)
+ assert.Equal(t, "p3", destRes[3].parent.ID)
+ assert.Equal(t, "u2", destRes[3].parent.User.ID)
+ assert.Empty(t, destRes[3].Emails, "no email can be retrieved for u2")
- s.Close()
+ s.Close()
+ })
}
func TestService_Recursive(t *testing.T) {
- dest := &MockDest{id: 1}
- dataStore := &mockStore{data: map[string]store.Comment{}, userDetails: map[string]string{}}
+ synctest.Test(t, func(t *testing.T) {
+ dest := &MockDest{id: 1}
+ dataStore := &mockStore{data: map[string]store.Comment{}, userDetails: map[string]string{}}
- dataStore.data["p1"] = store.Comment{ID: "p1", User: store.User{ID: "u1"}}
- dataStore.data["p2"] = store.Comment{ID: "p2", ParentID: "p1", User: store.User{ID: "u2"}}
- dataStore.data["p3"] = store.Comment{ID: "p3", ParentID: "p2", User: store.User{ID: "u3"}}
- dataStore.data["p4"] = store.Comment{ID: "p4", ParentID: "p3", User: store.User{ID: "u1"}}
- dataStore.data["p5"] = store.Comment{ID: "p5", ParentID: "p4", User: store.User{ID: "u4"}}
- dataStore.userDetails["u1"] = "u1@example.com"
- // second comment goes without email address for notification
- dataStore.userDetails["u3"] = "u3@example.com"
+ dataStore.data["p1"] = store.Comment{ID: "p1", User: store.User{ID: "u1"}}
+ dataStore.data["p2"] = store.Comment{ID: "p2", ParentID: "p1", User: store.User{ID: "u2"}}
+ dataStore.data["p3"] = store.Comment{ID: "p3", ParentID: "p2", User: store.User{ID: "u3"}}
+ dataStore.data["p4"] = store.Comment{ID: "p4", ParentID: "p3", User: store.User{ID: "u1"}}
+ dataStore.data["p5"] = store.Comment{ID: "p5", ParentID: "p4", User: store.User{ID: "u4"}}
+ dataStore.userDetails["u1"] = "u1@example.com"
+ // second comment goes without email address for notification
+ dataStore.userDetails["u3"] = "u3@example.com"
- s := NewService(dataStore, 1, dest)
- assert.NotNil(t, s)
+ s := NewService(dataStore, 1, dest)
+ assert.NotNil(t, s)
- // one comment from u1 with email set
- s.Submit(Request{Comment: dataStore.data["p1"]})
- time.Sleep(time.Millisecond * 110)
+ // one comment from u1 with email set
+ s.Submit(Request{Comment: dataStore.data["p1"]})
+ synctest.Wait()
- destRes := dest.Get()
- require.Equal(t, 1, len(destRes), "one comment notified")
- assert.Equal(t, "p1", destRes[0].Comment.ID)
- assert.Empty(t, destRes[0].parent)
- assert.Empty(t, destRes[0].Emails)
+ destRes := dest.Get()
+ require.Equal(t, 1, len(destRes), "one comment notified")
+ assert.Equal(t, "p1", destRes[0].Comment.ID)
+ assert.Empty(t, destRes[0].parent)
+ assert.Empty(t, destRes[0].Emails)
- // reply to the first comment from u2 without email set
- s.Submit(Request{Comment: dataStore.data["p2"]})
- time.Sleep(time.Millisecond * 110)
+ // reply to the first comment from u2 without email set
+ s.Submit(Request{Comment: dataStore.data["p2"]})
+ synctest.Wait()
- destRes = dest.Get()
- require.Equal(t, 2, len(destRes), "two comment notified")
- assert.Equal(t, "p2", destRes[1].Comment.ID)
- assert.Equal(t, "p1", destRes[1].parent.ID)
- assert.Equal(t, "u1", destRes[1].parent.User.ID)
- assert.ElementsMatch(t, []string{"u1@example.com"}, destRes[1].Emails)
+ destRes = dest.Get()
+ require.Equal(t, 2, len(destRes), "two comment notified")
+ assert.Equal(t, "p2", destRes[1].Comment.ID)
+ assert.Equal(t, "p1", destRes[1].parent.ID)
+ assert.Equal(t, "u1", destRes[1].parent.User.ID)
+ assert.ElementsMatch(t, []string{"u1@example.com"}, destRes[1].Emails)
- // reply to the second comment from u3 with email set
- s.Submit(Request{Comment: dataStore.data["p3"]})
- time.Sleep(time.Millisecond * 110)
+ // reply to the second comment from u3 with email set
+ s.Submit(Request{Comment: dataStore.data["p3"]})
+ synctest.Wait()
- destRes = dest.Get()
- require.Equal(t, 3, len(destRes), "three comment notified")
- assert.Equal(t, "p3", destRes[2].Comment.ID)
- assert.Equal(t, "p2", destRes[2].parent.ID)
- assert.Equal(t, "u2", destRes[2].parent.User.ID)
- assert.ElementsMatch(t, []string{"u1@example.com"}, destRes[2].Emails)
+ destRes = dest.Get()
+ require.Equal(t, 3, len(destRes), "three comment notified")
+ assert.Equal(t, "p3", destRes[2].Comment.ID)
+ assert.Equal(t, "p2", destRes[2].parent.ID)
+ assert.Equal(t, "u2", destRes[2].parent.User.ID)
+ assert.ElementsMatch(t, []string{"u1@example.com"}, destRes[2].Emails)
- // reply to the third comment from u1 (author of the first comment), only u3 should be notified
- s.Submit(Request{Comment: dataStore.data["p4"]})
- time.Sleep(time.Millisecond * 110)
+ // reply to the third comment from u1 (author of the first comment), only u3 should be notified
+ s.Submit(Request{Comment: dataStore.data["p4"]})
+ synctest.Wait()
- destRes = dest.Get()
- require.Equal(t, 4, len(destRes), "four comment notified once each")
- assert.Equal(t, "p4", destRes[3].Comment.ID)
- assert.Equal(t, "p3", destRes[3].parent.ID)
- assert.Equal(t, "u3", destRes[3].parent.User.ID)
- assert.ElementsMatch(t, []string{"u3@example.com"}, destRes[3].Emails, "u1 is not notified they are the one who left the comment")
+ destRes = dest.Get()
+ require.Equal(t, 4, len(destRes), "four comment notified once each")
+ assert.Equal(t, "p4", destRes[3].Comment.ID)
+ assert.Equal(t, "p3", destRes[3].parent.ID)
+ assert.Equal(t, "u3", destRes[3].parent.User.ID)
+ assert.ElementsMatch(t, []string{"u3@example.com"}, destRes[3].Emails, "u1 is not notified they are the one who left the comment")
- // reply to the fourth comment from u4, u1 and u3 should be notified once as a result
- s.Submit(Request{Comment: dataStore.data["p5"]})
- time.Sleep(time.Millisecond * 110)
+ // reply to the fourth comment from u4, u1 and u3 should be notified once as a result
+ s.Submit(Request{Comment: dataStore.data["p5"]})
+ synctest.Wait()
- destRes = dest.Get()
- require.Equal(t, 5, len(destRes), "four comment notified once each")
- assert.Equal(t, "p5", destRes[4].Comment.ID)
- assert.Equal(t, "p4", destRes[4].parent.ID)
- assert.Equal(t, "u1", destRes[4].parent.User.ID)
- assert.ElementsMatch(t, []string{"u1@example.com", "u3@example.com"}, destRes[4].Emails, "u3 and u1 notified once")
+ destRes = dest.Get()
+ require.Equal(t, 5, len(destRes), "four comment notified once each")
+ assert.Equal(t, "p5", destRes[4].Comment.ID)
+ assert.Equal(t, "p4", destRes[4].parent.ID)
+ assert.Equal(t, "u1", destRes[4].parent.User.ID)
+ assert.ElementsMatch(t, []string{"u1@example.com", "u3@example.com"}, destRes[4].Emails, "u3 and u1 notified once")
- s.Close()
+ s.Close()
+ })
}
func TestService_Nop(t *testing.T) {
diff --git a/backend/app/providers/telegram_test.go b/backend/app/providers/telegram_test.go
index 7edc23c4..d59d9405 100644
--- a/backend/app/providers/telegram_test.go
+++ b/backend/app/providers/telegram_test.go
@@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"testing"
+ "testing/synctest"
"time"
ntf "github.com/go-pkgz/notify"
@@ -12,12 +13,14 @@ import (
)
func TestDispatchTelegramUpdates(t *testing.T) {
- ctx, cancel := context.WithCancel(context.Background())
- poolPeriod := time.Millisecond * 100
- go DispatchTelegramUpdates(ctx, &mockTGRequester{t: t}, []TGUpdatesReceiver{&mockTGUpdatesReceiver{t: t}}, poolPeriod)
- time.Sleep(poolPeriod * 3)
- cancel()
- time.Sleep(poolPeriod)
+ synctest.Test(t, func(t *testing.T) {
+ ctx, cancel := context.WithCancel(context.Background())
+ poolPeriod := time.Millisecond * 100
+ go DispatchTelegramUpdates(ctx, &mockTGRequester{t: t}, []TGUpdatesReceiver{&mockTGUpdatesReceiver{t: t}}, poolPeriod)
+ time.Sleep(poolPeriod * 3)
+ cancel()
+ synctest.Wait()
+ })
}
const getUpdatesResp = `{
diff --git a/backend/app/store/engine/bolt_test.go b/backend/app/store/engine/bolt_test.go
index 73e81783..63e7b78e 100644
--- a/backend/app/store/engine/bolt_test.go
+++ b/backend/app/store/engine/bolt_test.go
@@ -4,6 +4,7 @@ import (
"fmt"
"os"
"testing"
+ "testing/synctest"
"time"
"github.com/stretchr/testify/assert"
@@ -571,47 +572,49 @@ func TestBolt_FlagListVerified(t *testing.T) {
}
func TestBolt_FlagListBlocked(t *testing.T) {
- b, teardown := prep(t)
- defer teardown()
+ synctest.Test(t, func(t *testing.T) {
+ b, teardown := prep(t)
+ defer teardown()
- setBlocked := func(site, user string, status FlagStatus, ttl time.Duration) error {
- req := FlagRequest{Flag: Blocked, Locator: store.Locator{SiteID: site}, UserID: user, Update: status, TTL: ttl}
- _, err := b.Flag(req)
- return err
- }
-
- toBlocked := func(inp []any) (res []store.BlockedUser) {
- res = make([]store.BlockedUser, len(inp))
- for i, v := range inp {
- vv, ok := v.(store.BlockedUser)
- require.True(t, ok)
- res[i] = vv
+ setBlocked := func(site, user string, status FlagStatus, ttl time.Duration) error {
+ req := FlagRequest{Flag: Blocked, Locator: store.Locator{SiteID: site}, UserID: user, Update: status, TTL: ttl}
+ _, err := b.Flag(req)
+ return err
}
- return res
- }
- assert.NoError(t, setBlocked("radio-t", "user1", FlagTrue, 0))
- assert.NoError(t, setBlocked("radio-t", "user2", FlagTrue, 150*time.Millisecond))
- assert.NoError(t, setBlocked("radio-t", "user3", FlagFalse, 0))
- vv, err := b.ListFlags(FlagRequest{Flag: Blocked, Locator: store.Locator{SiteID: "radio-t"}})
- assert.NoError(t, err)
+ toBlocked := func(inp []any) (res []store.BlockedUser) {
+ res = make([]store.BlockedUser, len(inp))
+ for i, v := range inp {
+ vv, ok := v.(store.BlockedUser)
+ require.True(t, ok)
+ res[i] = vv
+ }
+ return res
+ }
+ assert.NoError(t, setBlocked("radio-t", "user1", FlagTrue, 0))
+ assert.NoError(t, setBlocked("radio-t", "user2", FlagTrue, 150*time.Millisecond))
+ assert.NoError(t, setBlocked("radio-t", "user3", FlagFalse, 0))
- blockedList := toBlocked(vv)
- require.Equal(t, 2, len(blockedList))
- assert.Equal(t, "user1", blockedList[0].ID)
- assert.Equal(t, "user2", blockedList[1].ID)
- t.Logf("%+v", blockedList)
+ vv, err := b.ListFlags(FlagRequest{Flag: Blocked, Locator: store.Locator{SiteID: "radio-t"}})
+ assert.NoError(t, err)
- // check block expiration
- time.Sleep(150 * time.Millisecond)
- vv, err = b.ListFlags(FlagRequest{Flag: Blocked, Locator: store.Locator{SiteID: "radio-t"}})
- assert.NoError(t, err)
- blockedList = toBlocked(vv)
- require.Equal(t, 1, len(blockedList))
- assert.Equal(t, "user1", blockedList[0].ID)
+ blockedList := toBlocked(vv)
+ require.Equal(t, 2, len(blockedList))
+ assert.Equal(t, "user1", blockedList[0].ID)
+ assert.Equal(t, "user2", blockedList[1].ID)
+ t.Logf("%+v", blockedList)
- _, err = b.ListFlags(FlagRequest{Flag: Blocked, Locator: store.Locator{SiteID: "bad"}})
- assert.EqualError(t, err, `site "bad" not found`)
+ // check block expiration
+ time.Sleep(150 * time.Millisecond)
+ vv, err = b.ListFlags(FlagRequest{Flag: Blocked, Locator: store.Locator{SiteID: "radio-t"}})
+ assert.NoError(t, err)
+ blockedList = toBlocked(vv)
+ require.Equal(t, 1, len(blockedList))
+ assert.Equal(t, "user1", blockedList[0].ID)
+
+ _, err = b.ListFlags(FlagRequest{Flag: Blocked, Locator: store.Locator{SiteID: "bad"}})
+ assert.EqualError(t, err, `site "bad" not found`)
+ })
}
func TestBoltDB_UserDetail(t *testing.T) {
diff --git a/backend/app/store/image/bolt_store_test.go b/backend/app/store/image/bolt_store_test.go
index 232db45d..7fa54563 100644
--- a/backend/app/store/image/bolt_store_test.go
+++ b/backend/app/store/image/bolt_store_test.go
@@ -5,6 +5,7 @@ import (
"os"
"path"
"testing"
+ "testing/synctest"
"time"
"github.com/stretchr/testify/assert"
@@ -89,50 +90,53 @@ func TestBoltStore_LoadAfterDelete(t *testing.T) {
}
func TestBoltStore_Cleanup(t *testing.T) {
- svc, teardown := prepareBoltImageStorageTest(t)
- defer teardown()
+ synctest.Test(t, func(t *testing.T) {
+ svc, teardown := prepareBoltImageStorageTest(t)
+ defer teardown()
- save := func(file string) (id string) {
- err := svc.Save(file, gopherPNGBytes())
+ save := func(file string) (id string) {
+ err := svc.Save(file, gopherPNGBytes())
+ require.NoError(t, err)
+
+ checkBoltImgData(t, svc.db, imagesStagedBktName, file, func(data []byte) error {
+ require.NotNil(t, data)
+ assert.Equal(t, 1462, len(data))
+ return nil
+ })
+ return file
+ }
+
+ // save 3 images to staging
+ img1 := save("blah_ff1.png")
+ img1ts := time.Now()
+ time.Sleep(100 * time.Millisecond)
+ img2 := save("blah_ff2.png")
+ time.Sleep(100 * time.Millisecond)
+ img3 := save("blah_ff3.png")
+
+ // Cleanup check is `age > ttl` (strict), so pick a ttl strictly less than img1's age
+ err := svc.Cleanup(context.Background(), time.Since(img1ts)-time.Millisecond)
+ assert.NoError(t, err)
+
+ assertBoltImgNil(t, svc.db, imagesStagedBktName, img1)
+ assertBoltImgNil(t, svc.db, imagesBktName, img1)
+ assertBoltImgNotNil(t, svc.db, imagesStagedBktName, img2)
+ assertBoltImgNotNil(t, svc.db, imagesStagedBktName, img3)
+
+ err = svc.Commit(img3)
require.NoError(t, err)
- checkBoltImgData(t, svc.db, imagesStagedBktName, file, func(data []byte) error {
- require.NotNil(t, data)
- assert.Equal(t, 1462, len(data))
- return nil
- })
- return file
- }
+ // reset the time to cleanup
+ err = svc.ResetCleanupTimer(img2)
+ require.NoError(t, err)
+ err = svc.Cleanup(context.Background(), time.Millisecond*100)
+ assert.NoError(t, err)
- // save 3 images to staging
- img1 := save("blah_ff1.png")
- img1ts := time.Now()
- time.Sleep(100 * time.Millisecond)
- img2 := save("blah_ff2.png")
- time.Sleep(100 * time.Millisecond)
- img3 := save("blah_ff3.png")
-
- err := svc.Cleanup(context.Background(), time.Since(img1ts)) // clean first images
- assert.NoError(t, err)
-
- assertBoltImgNil(t, svc.db, imagesStagedBktName, img1)
- assertBoltImgNil(t, svc.db, imagesBktName, img1)
- assertBoltImgNotNil(t, svc.db, imagesStagedBktName, img2)
- assertBoltImgNotNil(t, svc.db, imagesStagedBktName, img3)
-
- err = svc.Commit(img3)
- require.NoError(t, err)
-
- // reset the time to cleanup
- err = svc.ResetCleanupTimer(img2)
- require.NoError(t, err)
- err = svc.Cleanup(context.Background(), time.Millisecond*100)
- assert.NoError(t, err)
-
- assertBoltImgNotNil(t, svc.db, imagesStagedBktName, img2)
- assertBoltImgNil(t, svc.db, imagesBktName, img2)
- assertBoltImgNotNil(t, svc.db, imagesBktName, img3)
- assert.NoError(t, err)
+ assertBoltImgNotNil(t, svc.db, imagesStagedBktName, img2)
+ assertBoltImgNil(t, svc.db, imagesBktName, img2)
+ assertBoltImgNotNil(t, svc.db, imagesBktName, img3)
+ assert.NoError(t, err)
+ })
}
func TestBolt_Info(t *testing.T) {
diff --git a/backend/app/store/image/image_test.go b/backend/app/store/image/image_test.go
index 30b995d8..6b0171f6 100644
--- a/backend/app/store/image/image_test.go
+++ b/backend/app/store/image/image_test.go
@@ -11,6 +11,7 @@ import (
"strconv"
"strings"
"testing"
+ "testing/synctest"
"time"
"github.com/stretchr/testify/assert"
@@ -125,37 +126,41 @@ func TestService_ExtractPictures(t *testing.T) {
}
func TestService_Cleanup(t *testing.T) {
- store := StoreMock{
- CleanupFunc: func(context.Context, time.Duration) error {
- return nil
- },
- }
+ synctest.Test(t, func(t *testing.T) {
+ store := StoreMock{
+ CleanupFunc: func(context.Context, time.Duration) error {
+ return nil
+ },
+ }
- svc := NewService(&store, ServiceParams{EditDuration: 20 * time.Millisecond})
- // cancel context after 2.1 cleanup TTLs
- ctx, cancel := context.WithTimeout(context.Background(), svc.EditDuration/100*15*21)
- defer cancel()
- svc.Cleanup(ctx)
- assert.Equal(t, 2, len(store.CleanupCalls()))
+ svc := NewService(&store, ServiceParams{EditDuration: 20 * time.Millisecond})
+ // cancel context after 2.1 cleanup TTLs
+ ctx, cancel := context.WithTimeout(context.Background(), svc.EditDuration/100*15*21)
+ defer cancel()
+ svc.Cleanup(ctx)
+ assert.Equal(t, 2, len(store.CleanupCalls()))
+ })
}
func TestService_Submit(t *testing.T) {
- store := StoreMock{
- CommitFunc: func(string) error { return nil },
- ResetCleanupTimerFunc: func(string) error { return nil },
- }
- svc := NewService(&store, ServiceParams{ImageAPI: "/blah/", EditDuration: time.Millisecond * 100})
- svc.Submit(func() []string { return []string{"id1", "id2", "id3"} })
- assert.Equal(t, 3, len(store.ResetCleanupTimerCalls()))
- err := svc.Commit(func() []string { return []string{"id4", "id5"} })
- assert.NoError(t, err)
- svc.Submit(func() []string { return []string{"id6", "id7"} })
- assert.Equal(t, 5, len(store.ResetCleanupTimerCalls()))
- svc.Submit(nil)
- assert.Equal(t, 2, len(store.CommitCalls()))
- time.Sleep(time.Millisecond * 175)
- assert.Equal(t, 7, len(store.CommitCalls()))
- svc.Close(context.TODO())
+ synctest.Test(t, func(t *testing.T) {
+ store := StoreMock{
+ CommitFunc: func(string) error { return nil },
+ ResetCleanupTimerFunc: func(string) error { return nil },
+ }
+ svc := NewService(&store, ServiceParams{ImageAPI: "/blah/", EditDuration: time.Millisecond * 100})
+ svc.Submit(func() []string { return []string{"id1", "id2", "id3"} })
+ assert.Equal(t, 3, len(store.ResetCleanupTimerCalls()))
+ err := svc.Commit(func() []string { return []string{"id4", "id5"} })
+ assert.NoError(t, err)
+ svc.Submit(func() []string { return []string{"id6", "id7"} })
+ assert.Equal(t, 5, len(store.ResetCleanupTimerCalls()))
+ svc.Submit(nil)
+ assert.Equal(t, 2, len(store.CommitCalls()))
+ time.Sleep(time.Millisecond * 175)
+ assert.Equal(t, 7, len(store.CommitCalls()))
+ svc.Close(context.TODO())
+ })
}
func TestService_Close(t *testing.T) {
@@ -173,21 +178,23 @@ func TestService_Close(t *testing.T) {
}
func TestService_SubmitDelay(t *testing.T) {
- store := StoreMock{
- CommitFunc: func(string) error { return nil },
- ResetCleanupTimerFunc: func(string) error {
- return nil
- },
- }
- svc := NewService(&store, ServiceParams{EditDuration: 20 * time.Millisecond})
- svc.Submit(func() []string { return []string{"id1", "id2", "id3"} })
- time.Sleep(150 * time.Millisecond) // let first batch to pass TTL
- svc.Submit(func() []string { return []string{"id4", "id5"} })
- svc.Submit(nil)
- assert.Equal(t, 5, len(store.ResetCleanupTimerCalls()))
- assert.Equal(t, 3, len(store.CommitCalls()))
- svc.Close(context.TODO())
- assert.Equal(t, 5, len(store.CommitCalls()))
+ synctest.Test(t, func(t *testing.T) {
+ store := StoreMock{
+ CommitFunc: func(string) error { return nil },
+ ResetCleanupTimerFunc: func(string) error {
+ return nil
+ },
+ }
+ svc := NewService(&store, ServiceParams{EditDuration: 20 * time.Millisecond})
+ svc.Submit(func() []string { return []string{"id1", "id2", "id3"} })
+ time.Sleep(150 * time.Millisecond) // let first batch to pass TTL
+ svc.Submit(func() []string { return []string{"id4", "id5"} })
+ svc.Submit(nil)
+ assert.Equal(t, 5, len(store.ResetCleanupTimerCalls()))
+ assert.Equal(t, 3, len(store.CommitCalls()))
+ svc.Close(context.TODO())
+ assert.Equal(t, 5, len(store.CommitCalls()))
+ })
}
func TestService_Info(t *testing.T) {
diff --git a/backend/app/store/service/service_test.go b/backend/app/store/service/service_test.go
index 7c0f3f8a..1e4f410a 100644
--- a/backend/app/store/service/service_test.go
+++ b/backend/app/store/service/service_test.go
@@ -14,6 +14,7 @@ import (
"sync"
"sync/atomic"
"testing"
+ "testing/synctest"
"time"
"github.com/go-pkgz/lgr"
@@ -716,34 +717,36 @@ func TestService_VoteSameIP(t *testing.T) {
}
func TestService_VoteSameIPWithDuration(t *testing.T) {
- eng, teardown := prepStoreEngine(t)
- defer teardown()
- b := DataStore{Engine: eng, AdminStore: admin.NewStaticKeyStore("secret 123"),
- MaxVotes: -1}
- b.RestrictSameIPVotes.Enabled = true
- b.RestrictSameIPVotes.Duration = 500 * time.Millisecond
+ synctest.Test(t, func(t *testing.T) {
+ eng, teardown := prepStoreEngine(t)
+ defer teardown()
+ b := DataStore{Engine: eng, AdminStore: admin.NewStaticKeyStore("secret 123"),
+ MaxVotes: -1}
+ b.RestrictSameIPVotes.Enabled = true
+ b.RestrictSameIPVotes.Duration = 500 * time.Millisecond
- c, err := b.Vote(VoteReq{Locator: store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, CommentID: "id-2",
- UserID: "user2", UserIP: "123", Val: true})
- assert.NoError(t, err)
- assert.Equal(t, 1, c.Score, "should have 1 score")
+ c, err := b.Vote(VoteReq{Locator: store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, CommentID: "id-2",
+ UserID: "user2", UserIP: "123", Val: true})
+ assert.NoError(t, err)
+ assert.Equal(t, 1, c.Score, "should have 1 score")
- c, err = b.Vote(VoteReq{Locator: store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, CommentID: "id-2",
- UserID: "user3", UserIP: "123", Val: true})
- assert.EqualError(t, err, "the same ip cce61be6e0a692420ae0de31dceca179123c3b8a already voted for id-2")
- assert.Equal(t, 1, c.Score, "still have 1 score")
+ c, err = b.Vote(VoteReq{Locator: store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, CommentID: "id-2",
+ UserID: "user3", UserIP: "123", Val: true})
+ assert.EqualError(t, err, "the same ip cce61be6e0a692420ae0de31dceca179123c3b8a already voted for id-2")
+ assert.Equal(t, 1, c.Score, "still have 1 score")
- c, err = b.Vote(VoteReq{Locator: store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, CommentID: "id-2",
- UserID: "user4", UserIP: "12345", Val: true})
- assert.NoError(t, err)
- assert.Equal(t, 2, c.Score, "have 2 score")
+ c, err = b.Vote(VoteReq{Locator: store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, CommentID: "id-2",
+ UserID: "user4", UserIP: "12345", Val: true})
+ assert.NoError(t, err)
+ assert.Equal(t, 2, c.Score, "have 2 score")
- time.Sleep(501 * time.Millisecond)
+ time.Sleep(501 * time.Millisecond)
- c, err = b.Vote(VoteReq{Locator: store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, CommentID: "id-2",
- UserID: "user3", UserIP: "123", Val: true})
- assert.NoError(t, err)
- assert.Equal(t, 3, c.Score, "have 3 score")
+ c, err = b.Vote(VoteReq{Locator: store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, CommentID: "id-2",
+ UserID: "user3", UserIP: "123", Val: true})
+ assert.NoError(t, err)
+ assert.Equal(t, 3, c.Score, "have 3 score")
+ })
}
func TestService_Controversy(t *testing.T) {
@@ -857,8 +860,6 @@ func TestService_EditCommentDurationFailed(t *testing.T) {
require.Equal(t, 2, len(res))
assert.Nil(t, res[0].Edit)
- time.Sleep(time.Second)
-
_, err = b.EditComment(store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID,
EditRequest{Orig: "yyy", Text: "xxx", Summary: "my edit"})
assert.Error(t, err)
@@ -904,8 +905,6 @@ func TestService_EditCommentAdmin(t *testing.T) {
require.Equal(t, 2, len(res))
assert.Nil(t, res[0].Edit)
- time.Sleep(time.Second)
-
_, err = b.EditComment(store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID,
EditRequest{Orig: "yyy", Text: "xxx", Summary: "my edit", Admin: true})
assert.NoError(t, err)
@@ -1181,87 +1180,89 @@ func TestService_HasReplies(t *testing.T) {
}
func TestService_UserReplies(t *testing.T) {
- // two comments for https://radio-t.com, no reply
- eng, teardown := prepStoreEngine(t)
- defer teardown()
- b := DataStore{Engine: eng,
- AdminStore: admin.NewStaticStore("secret 123", nil, []string{"user2"}, "user@email.com")}
+ synctest.Test(t, func(t *testing.T) {
+ // two comments for https://radio-t.com, no reply
+ eng, teardown := prepStoreEngine(t)
+ defer teardown()
+ b := DataStore{Engine: eng,
+ AdminStore: admin.NewStaticStore("secret 123", nil, []string{"user2"}, "user@email.com")}
- c1 := store.Comment{
- ID: "comment-id-1",
- Text: "test 123",
- Locator: store.Locator{URL: "https://radio-t.com/blah10", SiteID: "radio-t"},
- User: store.User{ID: "u1", Name: "developer one u1"},
- }
- c2 := store.Comment{
- ID: "comment-id-2",
- ParentID: "comment-id-1",
- Text: "xyz test",
- Locator: store.Locator{URL: "https://radio-t.com/blah10", SiteID: "radio-t"},
- User: store.User{ID: "u2", Name: "developer one u2"},
- }
- c3 := store.Comment{
- ID: "comment-id-3",
- ParentID: "comment-id-1",
- Text: "xyz test",
- Locator: store.Locator{URL: "https://radio-t.com/blah10", SiteID: "radio-t"},
- User: store.User{ID: "u2", Name: "developer one u3"},
- }
- c4 := store.Comment{
- ID: "comment-id-4",
- ParentID: "",
- Text: "xyz test",
- Locator: store.Locator{URL: "https://radio-t.com/blah10", SiteID: "radio-t"},
- User: store.User{ID: "u4", Name: "developer one u4"},
- }
- c5 := store.Comment{
- ID: "comment-id-5",
- ParentID: "comment-id-1",
- Text: "xyz test",
- Locator: store.Locator{URL: "https://radio-t.com/blah10", SiteID: "radio-t"},
- User: store.User{ID: "u2", Name: "developer one u2"},
- }
+ c1 := store.Comment{
+ ID: "comment-id-1",
+ Text: "test 123",
+ Locator: store.Locator{URL: "https://radio-t.com/blah10", SiteID: "radio-t"},
+ User: store.User{ID: "u1", Name: "developer one u1"},
+ }
+ c2 := store.Comment{
+ ID: "comment-id-2",
+ ParentID: "comment-id-1",
+ Text: "xyz test",
+ Locator: store.Locator{URL: "https://radio-t.com/blah10", SiteID: "radio-t"},
+ User: store.User{ID: "u2", Name: "developer one u2"},
+ }
+ c3 := store.Comment{
+ ID: "comment-id-3",
+ ParentID: "comment-id-1",
+ Text: "xyz test",
+ Locator: store.Locator{URL: "https://radio-t.com/blah10", SiteID: "radio-t"},
+ User: store.User{ID: "u2", Name: "developer one u3"},
+ }
+ c4 := store.Comment{
+ ID: "comment-id-4",
+ ParentID: "",
+ Text: "xyz test",
+ Locator: store.Locator{URL: "https://radio-t.com/blah10", SiteID: "radio-t"},
+ User: store.User{ID: "u4", Name: "developer one u4"},
+ }
+ c5 := store.Comment{
+ ID: "comment-id-5",
+ ParentID: "comment-id-1",
+ Text: "xyz test",
+ Locator: store.Locator{URL: "https://radio-t.com/blah10", SiteID: "radio-t"},
+ User: store.User{ID: "u2", Name: "developer one u2"},
+ }
- _, err := b.Create(c1)
- require.NoError(t, err)
- _, err = b.Create(c2)
- require.NoError(t, err)
- _, err = b.Create(c3)
- require.NoError(t, err)
- _, err = b.Create(c4)
- require.NoError(t, err)
+ // small sleeps give each Create a unique nanosecond timestamp under synctest's fake clock,
+ // since Bolt keys the "last" bucket by comment timestamp
+ _, err := b.Create(c1)
+ require.NoError(t, err)
+ time.Sleep(time.Nanosecond)
+ _, err = b.Create(c2)
+ require.NoError(t, err)
+ time.Sleep(time.Nanosecond)
+ _, err = b.Create(c3)
+ require.NoError(t, err)
+ time.Sleep(time.Nanosecond)
+ _, err = b.Create(c4)
+ require.NoError(t, err)
- time.Sleep(100 * time.Millisecond)
- _, err = b.Create(c5)
- require.NoError(t, err)
+ time.Sleep(100 * time.Millisecond)
+ _, err = b.Create(c5)
+ require.NoError(t, err)
- cc, u, err := b.UserReplies("radio-t", "u1", 10, time.Hour)
- assert.NoError(t, err)
- require.Equal(t, 3, len(cc), "3 replies to u1")
- assert.Equal(t, "developer one u1", u)
+ cc, u, err := b.UserReplies("radio-t", "u1", 10, time.Hour)
+ assert.NoError(t, err)
+ require.Equal(t, 3, len(cc), "3 replies to u1")
+ assert.Equal(t, "developer one u1", u)
- // mutex to prevent multiple b.UserReplies calls resulting in data race
- l := sync.Mutex{}
- assert.Eventually(t, func() bool {
- l.Lock()
- defer l.Unlock()
+ // advance fake clock so c2 and c3 (created 100ms before c5) age past the 299ms window,
+ // leaving only c5 as a recent reply
+ time.Sleep(200 * time.Millisecond)
cc, u, err = b.UserReplies("radio-t", "u1", 10, time.Millisecond*299)
require.NoError(t, err)
require.Equal(t, "developer one u1", u)
- return len(cc) == 1
- }, 300*time.Millisecond, 30*time.Millisecond, "1 reply to u1 in the last 300ms")
+ require.Equal(t, 1, len(cc), "1 reply to u1 in the last 299ms")
- l.Lock()
- defer l.Unlock()
- cc, u, err = b.UserReplies("radio-t", "u2", 10, time.Hour)
- assert.NoError(t, err)
- assert.Equal(t, 0, len(cc), "0 replies to u2")
- assert.Equal(t, "developer one u2", u)
+ cc, u, err = b.UserReplies("radio-t", "u2", 10, time.Hour)
+ assert.NoError(t, err)
+ assert.Equal(t, 0, len(cc), "0 replies to u2")
+ assert.Equal(t, "developer one u2", u)
- cc, u, err = b.UserReplies("radio-t", "uxxx", 10, time.Hour)
- assert.NoError(t, err)
- assert.Equal(t, 0, len(cc), "0 replies to uxxx")
- assert.Equal(t, "", u)
+ cc, u, err = b.UserReplies("radio-t", "uxxx", 10, time.Hour)
+ assert.NoError(t, err)
+ assert.Equal(t, 0, len(cc), "0 replies to uxxx")
+ assert.Equal(t, "", u)
+ })
}
func TestService_Find(t *testing.T) {
@@ -1358,7 +1359,6 @@ func TestService_Info(t *testing.T) {
assert.True(t, info.LastTS.After(info.FirstTS))
firstTS := info.FirstTS
- time.Sleep(1 * time.Second) // make post RO in 1sec
info, err = b.Info(store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, 1)
require.NoError(t, err)
assert.Equal(t, "https://radio-t.com", info.URL)
@@ -1401,75 +1401,77 @@ func TestService_Delete(t *testing.T) {
func TestService_deleteImagesOnCommentDelete(t *testing.T) {
lgr.Setup(lgr.Debug, lgr.CallerFile, lgr.CallerFunc)
- mockStore := image.StoreMock{
- DeleteFunc: func(string) error { return nil },
- CommitFunc: func(string) error { return nil },
- ResetCleanupTimerFunc: func(string) error { return nil },
- }
- imgSvc := image.NewService(&mockStore,
- image.ServiceParams{
- EditDuration: 50 * time.Millisecond,
- ImageAPI: "/images/dev/",
- ProxyAPI: "/non_existent",
- })
- defer imgSvc.Close(context.TODO())
+ synctest.Test(t, func(t *testing.T) {
+ mockStore := image.StoreMock{
+ DeleteFunc: func(string) error { return nil },
+ CommitFunc: func(string) error { return nil },
+ ResetCleanupTimerFunc: func(string) error { return nil },
+ }
+ imgSvc := image.NewService(&mockStore,
+ image.ServiceParams{
+ EditDuration: 50 * time.Millisecond,
+ ImageAPI: "/images/dev/",
+ ProxyAPI: "/non_existent",
+ })
+ defer imgSvc.Close(context.TODO())
- // two comments for https://radio-t.com
- eng, teardown := prepStoreEngine(t)
- defer teardown()
- b := DataStore{Engine: eng, EditDuration: 50 * time.Millisecond,
- AdminStore: admin.NewStaticKeyStore("secret 123"), ImageService: imgSvc}
+ // two comments for https://radio-t.com
+ eng, teardown := prepStoreEngine(t)
+ defer teardown()
+ b := DataStore{Engine: eng, EditDuration: 50 * time.Millisecond,
+ AdminStore: admin.NewStaticKeyStore("secret 123"), ImageService: imgSvc}
- c := store.Comment{
- ID: "id-22",
- Text: `some text
xx
`,
- Timestamp: time.Date(2017, 12, 20, 15, 18, 22, 0, time.UTC),
- Locator: store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"},
- User: store.User{ID: "user1", Name: "user name"},
- }
- _, err := b.Engine.Create(c) // create directly with engine, doesn't call submitImages
- assert.NoError(t, err)
- b.submitImages(c)
- // reply to the first comment with one new image and one existing one
- c = store.Comment{
- ID: "id-23",
- ParentID: "id-22",
- Text: `some text
xx
`,
- Locator: store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"},
- User: store.User{ID: "user1", Name: "user name"},
- }
- _, err = b.Engine.Create(c) // create directly with engine, doesn't call submitImages
- assert.NoError(t, err)
- b.submitImages(c)
+ c := store.Comment{
+ ID: "id-22",
+ Text: `some text
xx
`,
+ Timestamp: time.Date(2017, 12, 20, 15, 18, 22, 0, time.UTC),
+ Locator: store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"},
+ User: store.User{ID: "user1", Name: "user name"},
+ }
+ _, err := b.Engine.Create(c) // create directly with engine, doesn't call submitImages
+ assert.NoError(t, err)
+ b.submitImages(c)
+ // reply to the first comment with one new image and one existing one
+ c = store.Comment{
+ ID: "id-23",
+ ParentID: "id-22",
+ Text: `some text
xx
`,
+ Locator: store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"},
+ User: store.User{ID: "user1", Name: "user name"},
+ }
+ _, err = b.Engine.Create(c) // create directly with engine, doesn't call submitImages
+ assert.NoError(t, err)
+ b.submitImages(c)
- // verify that images are in staging store
- assert.Equal(t, 4, len(mockStore.ResetCleanupTimerCalls()))
- assert.Equal(t, "dev/pic1.png", mockStore.ResetCleanupTimerCalls()[0].ID)
- assert.Equal(t, "dev/pic2.png", mockStore.ResetCleanupTimerCalls()[1].ID)
- assert.Equal(t, "dev/pic2.png", mockStore.ResetCleanupTimerCalls()[2].ID)
- assert.Equal(t, "dev/pic3.png", mockStore.ResetCleanupTimerCalls()[3].ID)
- time.Sleep(b.EditDuration + 100*time.Millisecond)
- // verify that they got into the main store
- assert.Equal(t, 4, len(mockStore.CommitCalls()))
- assert.Equal(t, "dev/pic1.png", mockStore.CommitCalls()[0].ID)
- assert.Equal(t, "dev/pic2.png", mockStore.CommitCalls()[1].ID)
- assert.Equal(t, "dev/pic2.png", mockStore.CommitCalls()[2].ID)
- assert.Equal(t, "dev/pic3.png", mockStore.CommitCalls()[3].ID)
+ // verify that images are in staging store
+ assert.Equal(t, 4, len(mockStore.ResetCleanupTimerCalls()))
+ assert.Equal(t, "dev/pic1.png", mockStore.ResetCleanupTimerCalls()[0].ID)
+ assert.Equal(t, "dev/pic2.png", mockStore.ResetCleanupTimerCalls()[1].ID)
+ assert.Equal(t, "dev/pic2.png", mockStore.ResetCleanupTimerCalls()[2].ID)
+ assert.Equal(t, "dev/pic3.png", mockStore.ResetCleanupTimerCalls()[3].ID)
+ time.Sleep(b.EditDuration + 100*time.Millisecond)
+ // verify that they got into the main store
+ assert.Equal(t, 4, len(mockStore.CommitCalls()))
+ assert.Equal(t, "dev/pic1.png", mockStore.CommitCalls()[0].ID)
+ assert.Equal(t, "dev/pic2.png", mockStore.CommitCalls()[1].ID)
+ assert.Equal(t, "dev/pic2.png", mockStore.CommitCalls()[2].ID)
+ assert.Equal(t, "dev/pic3.png", mockStore.CommitCalls()[3].ID)
- // delete the first comment
- err = b.Delete(store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, "id-22", store.SoftDelete)
- assert.NoError(t, err)
- // verify that images are deleted from the main store
- assert.Equal(t, 1, len(mockStore.DeleteCalls()))
- assert.Equal(t, "dev/pic1.png", mockStore.DeleteCalls()[0].ID)
+ // delete the first comment
+ err = b.Delete(store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, "id-22", store.SoftDelete)
+ assert.NoError(t, err)
+ // verify that images are deleted from the main store
+ assert.Equal(t, 1, len(mockStore.DeleteCalls()))
+ assert.Equal(t, "dev/pic1.png", mockStore.DeleteCalls()[0].ID)
- // delete the second comment
- err = b.Delete(store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, "id-23", store.SoftDelete)
- assert.NoError(t, err)
- // verify that images are deleted from the main store
- assert.Equal(t, 3, len(mockStore.DeleteCalls()))
- assert.Equal(t, "dev/pic2.png", mockStore.DeleteCalls()[1].ID)
- assert.Equal(t, "dev/pic3.png", mockStore.DeleteCalls()[2].ID)
+ // delete the second comment
+ err = b.Delete(store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, "id-23", store.SoftDelete)
+ assert.NoError(t, err)
+ // verify that images are deleted from the main store
+ assert.Equal(t, 3, len(mockStore.DeleteCalls()))
+ assert.Equal(t, "dev/pic2.png", mockStore.DeleteCalls()[1].ID)
+ assert.Equal(t, "dev/pic3.png", mockStore.DeleteCalls()[2].ID)
+ })
}
// DeleteUser removes all comments from user
@@ -1651,129 +1653,133 @@ func TestService_DeleteAll(t *testing.T) {
func TestService_submitImages(t *testing.T) {
lgr.Setup(lgr.Debug, lgr.CallerFile, lgr.CallerFunc)
- mockStore := image.StoreMock{
- CommitFunc: func(string) error { return nil },
- ResetCleanupTimerFunc: func(string) error { return nil },
- }
- imgSvc := image.NewService(&mockStore,
- image.ServiceParams{
- EditDuration: 50 * time.Millisecond,
- ImageAPI: "/images/dev/",
- ProxyAPI: "/non_existent",
- })
- defer imgSvc.Close(context.TODO())
+ synctest.Test(t, func(t *testing.T) {
+ mockStore := image.StoreMock{
+ CommitFunc: func(string) error { return nil },
+ ResetCleanupTimerFunc: func(string) error { return nil },
+ }
+ imgSvc := image.NewService(&mockStore,
+ image.ServiceParams{
+ EditDuration: 50 * time.Millisecond,
+ ImageAPI: "/images/dev/",
+ ProxyAPI: "/non_existent",
+ })
+ defer imgSvc.Close(context.TODO())
- // two comments for https://radio-t.com
- eng, teardown := prepStoreEngine(t)
- defer teardown()
- b := DataStore{Engine: eng, EditDuration: 50 * time.Millisecond,
- AdminStore: admin.NewStaticKeyStore("secret 123"), ImageService: imgSvc}
+ // two comments for https://radio-t.com
+ eng, teardown := prepStoreEngine(t)
+ defer teardown()
+ b := DataStore{Engine: eng, EditDuration: 50 * time.Millisecond,
+ AdminStore: admin.NewStaticKeyStore("secret 123"), ImageService: imgSvc}
- c := store.Comment{
- ID: "id-22",
- Text: `some text
xx
`,
- Timestamp: time.Date(2017, 12, 20, 15, 18, 22, 0, time.UTC),
- Locator: store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"},
- User: store.User{ID: "user1", Name: "user name"},
- }
- _, err := b.Engine.Create(c) // create directly with engine, doesn't call submitImages
- assert.NoError(t, err)
+ c := store.Comment{
+ ID: "id-22",
+ Text: `some text
xx
`,
+ Timestamp: time.Date(2017, 12, 20, 15, 18, 22, 0, time.UTC),
+ Locator: store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"},
+ User: store.User{ID: "user1", Name: "user name"},
+ }
+ _, err := b.Engine.Create(c) // create directly with engine, doesn't call submitImages
+ assert.NoError(t, err)
- b.submitImages(c)
- assert.Equal(t, 2, len(mockStore.ResetCleanupTimerCalls()))
- assert.Equal(t, "dev/pic1.png", mockStore.ResetCleanupTimerCalls()[0].ID)
- assert.Equal(t, "dev/pic2.png", mockStore.ResetCleanupTimerCalls()[1].ID)
- time.Sleep(b.EditDuration + 100*time.Millisecond)
- assert.Equal(t, 2, len(mockStore.CommitCalls()))
- assert.Equal(t, "dev/pic1.png", mockStore.CommitCalls()[0].ID)
- assert.Equal(t, "dev/pic2.png", mockStore.CommitCalls()[1].ID)
+ b.submitImages(c)
+ assert.Equal(t, 2, len(mockStore.ResetCleanupTimerCalls()))
+ assert.Equal(t, "dev/pic1.png", mockStore.ResetCleanupTimerCalls()[0].ID)
+ assert.Equal(t, "dev/pic2.png", mockStore.ResetCleanupTimerCalls()[1].ID)
+ time.Sleep(b.EditDuration + 100*time.Millisecond)
+ assert.Equal(t, 2, len(mockStore.CommitCalls()))
+ assert.Equal(t, "dev/pic1.png", mockStore.CommitCalls()[0].ID)
+ assert.Equal(t, "dev/pic2.png", mockStore.CommitCalls()[1].ID)
+ })
}
func TestService_ResubmitStagingImages(t *testing.T) {
- mockStore := image.StoreMock{
- InfoFunc: func() (image.StoreInfo, error) {
- return image.StoreInfo{FirstStagingImageTS: time.Time{}.Add(time.Second)}, nil
- },
- CommitFunc: func(string) error {
- return nil
- },
- ResetCleanupTimerFunc: func(string) error { return nil },
- }
- imgSvc := image.NewService(&mockStore,
- image.ServiceParams{
- EditDuration: 10 * time.Millisecond,
- ImageAPI: "http://127.0.0.1:8080/api/v1/picture/",
- ProxyAPI: "http://127.0.0.1:8080/api/v1/img",
- })
- defer imgSvc.Close(context.TODO())
+ synctest.Test(t, func(t *testing.T) {
+ mockStore := image.StoreMock{
+ InfoFunc: func() (image.StoreInfo, error) {
+ return image.StoreInfo{FirstStagingImageTS: time.Time{}.Add(time.Second)}, nil
+ },
+ CommitFunc: func(string) error {
+ return nil
+ },
+ ResetCleanupTimerFunc: func(string) error { return nil },
+ }
+ imgSvc := image.NewService(&mockStore,
+ image.ServiceParams{
+ EditDuration: 10 * time.Millisecond,
+ ImageAPI: "http://127.0.0.1:8080/api/v1/picture/",
+ ProxyAPI: "http://127.0.0.1:8080/api/v1/img",
+ })
+ defer imgSvc.Close(context.TODO())
- eng, teardown := prepStoreEngine(t)
- defer teardown()
- b := DataStore{Engine: eng, EditDuration: 10 * time.Millisecond, ImageService: imgSvc}
+ eng, teardown := prepStoreEngine(t)
+ defer teardown()
+ b := DataStore{Engine: eng, EditDuration: 10 * time.Millisecond, ImageService: imgSvc}
- // create comment with three images without preparing it properly
- comment := store.Comment{
- ID: "id-0",
- Text: `
+ // create comment with three images without preparing it properly
+ comment := store.Comment{
+ ID: "id-0",
+ Text: `


`,
- Timestamp: time.Date(2017, 12, 20, 15, 18, 22, 0, time.UTC),
- Locator: store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"},
- User: store.User{ID: "user1", Name: "user name"},
- }
- _, err := b.Engine.Create(comment)
- require.NoError(t, err)
+ Timestamp: time.Date(2017, 12, 20, 15, 18, 22, 0, time.UTC),
+ Locator: store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"},
+ User: store.User{ID: "user1", Name: "user name"},
+ }
+ _, err := b.Engine.Create(comment)
+ require.NoError(t, err)
- // resubmit single comment with three images, of which two are in staging storage
- err = b.ResubmitStagingImages([]string{"radio-t"})
- assert.NoError(t, err)
+ // resubmit single comment with three images, of which two are in staging storage
+ err = b.ResubmitStagingImages([]string{"radio-t"})
+ assert.NoError(t, err)
- // wait for Submit goroutine to commit image
- time.Sleep(b.EditDuration + time.Millisecond*100)
+ // wait for Submit goroutine to commit image
+ time.Sleep(b.EditDuration + time.Millisecond*100)
- assert.Equal(t, 1, len(mockStore.InfoCalls()))
- assert.Equal(t, 3, len(mockStore.CommitCalls()))
+ assert.Equal(t, 1, len(mockStore.InfoCalls()))
+ assert.Equal(t, 3, len(mockStore.CommitCalls()))
- // empty answer
- mockStoreEmpty := image.StoreMock{InfoFunc: func() (image.StoreInfo, error) {
- return image.StoreInfo{FirstStagingImageTS: time.Time{}}, nil
- }}
- imgSvcEmpty := image.NewService(&mockStoreEmpty,
- image.ServiceParams{
- EditDuration: 10 * time.Millisecond,
- ImageAPI: "http://127.0.0.1:8080/api/v1/picture/",
- })
- defer imgSvcEmpty.Close(context.TODO())
- bEmpty := DataStore{Engine: eng, EditDuration: 10 * time.Millisecond, ImageService: imgSvcEmpty}
+ // empty answer
+ mockStoreEmpty := image.StoreMock{InfoFunc: func() (image.StoreInfo, error) {
+ return image.StoreInfo{FirstStagingImageTS: time.Time{}}, nil
+ }}
+ imgSvcEmpty := image.NewService(&mockStoreEmpty,
+ image.ServiceParams{
+ EditDuration: 10 * time.Millisecond,
+ ImageAPI: "http://127.0.0.1:8080/api/v1/picture/",
+ })
+ defer imgSvcEmpty.Close(context.TODO())
+ bEmpty := DataStore{Engine: eng, EditDuration: 10 * time.Millisecond, ImageService: imgSvcEmpty}
- // resubmit receive empty timestamp and should do nothing )
- err = bEmpty.ResubmitStagingImages([]string{"radio-t", "non_existent"})
- assert.NoError(t, err)
+ // resubmit receive empty timestamp and should do nothing )
+ err = bEmpty.ResubmitStagingImages([]string{"radio-t", "non_existent"})
+ assert.NoError(t, err)
- assert.Equal(t, 1, len(mockStore.InfoCalls()))
+ assert.Equal(t, 1, len(mockStore.InfoCalls()))
- // error from image storage
- mockStoreError := image.StoreMock{InfoFunc: func() (image.StoreInfo, error) {
- return image.StoreInfo{}, fmt.Errorf("mock_err")
- }}
- imgSvcError := image.NewService(&mockStoreError,
- image.ServiceParams{
- EditDuration: 10 * time.Millisecond,
- ImageAPI: "http://127.0.0.1:8080/api/v1/picture/",
- })
- defer imgSvcError.Close(context.TODO())
- bError := DataStore{Engine: eng, EditDuration: 10 * time.Millisecond, ImageService: imgSvcError}
+ // error from image storage
+ mockStoreError := image.StoreMock{InfoFunc: func() (image.StoreInfo, error) {
+ return image.StoreInfo{}, fmt.Errorf("mock_err")
+ }}
+ imgSvcError := image.NewService(&mockStoreError,
+ image.ServiceParams{
+ EditDuration: 10 * time.Millisecond,
+ ImageAPI: "http://127.0.0.1:8080/api/v1/picture/",
+ })
+ defer imgSvcError.Close(context.TODO())
+ bError := DataStore{Engine: eng, EditDuration: 10 * time.Millisecond, ImageService: imgSvcError}
- // resubmit will receive error from image storage and should return it
- err = bError.ResubmitStagingImages([]string{"radio-t"})
- assert.EqualError(t, err, "mock_err")
+ // resubmit will receive error from image storage and should return it
+ err = bError.ResubmitStagingImages([]string{"radio-t"})
+ assert.EqualError(t, err, "mock_err")
- assert.Equal(t, 1, len(mockStore.InfoCalls()))
- assert.Equal(t, 3, len(mockStore.ResetCleanupTimerCalls()))
- assert.Equal(t, "dev_user/bqf122eq9r8ad657n3ng", mockStore.ResetCleanupTimerCalls()[0].ID)
- assert.Equal(t, "dev_user/bqf321eq9r8ad657n3ng", mockStore.ResetCleanupTimerCalls()[1].ID)
- assert.Equal(t, "cached_images/12318fbd4c55e9d177b8b5ae197bc89c5afd8e07-a41fcb00643f28d700504256ec81cbf2e1aac53e", mockStore.ResetCleanupTimerCalls()[2].ID)
+ assert.Equal(t, 1, len(mockStore.InfoCalls()))
+ assert.Equal(t, 3, len(mockStore.ResetCleanupTimerCalls()))
+ assert.Equal(t, "dev_user/bqf122eq9r8ad657n3ng", mockStore.ResetCleanupTimerCalls()[0].ID)
+ assert.Equal(t, "dev_user/bqf321eq9r8ad657n3ng", mockStore.ResetCleanupTimerCalls()[1].ID)
+ assert.Equal(t, "cached_images/12318fbd4c55e9d177b8b5ae197bc89c5afd8e07-a41fcb00643f28d700504256ec81cbf2e1aac53e", mockStore.ResetCleanupTimerCalls()[2].ID)
+ })
}
func TestService_ResubmitStagingImages_EngineError(t *testing.T) {