summaryrefslogtreecommitdiff
path: root/internal/cache/timeline/status_test.go
diff options
context:
space:
mode:
authorLibravatar kim <grufwub@gmail.com>2025-05-17 11:52:49 +0000
committerLibravatar kim <gruf@noreply.codeberg.org>2025-05-17 11:52:49 +0000
commit3a29a59e55dc8aa8a9bf31395f0f942eea700919 (patch)
tree706769c356185140ef0c09f6840fcf2e810e58fa /internal/cache/timeline/status_test.go
parent[chore] various federatingdb tweaks (#4178) (diff)
downloadgotosocial-3a29a59e55dc8aa8a9bf31395f0f942eea700919.tar.xz
[bugfix] fix case of failed timeline preload causing lockups (#4182)
- moves preloader Done() function calling to be handled entirely by the preloader, not the caller - adds tests for multiple preload success / failure / clear states Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4182 Co-authored-by: kim <grufwub@gmail.com> Co-committed-by: kim <grufwub@gmail.com>
Diffstat (limited to 'internal/cache/timeline/status_test.go')
-rw-r--r--internal/cache/timeline/status_test.go88
1 files changed, 86 insertions, 2 deletions
diff --git a/internal/cache/timeline/status_test.go b/internal/cache/timeline/status_test.go
index fc7e43da8..6d513032a 100644
--- a/internal/cache/timeline/status_test.go
+++ b/internal/cache/timeline/status_test.go
@@ -19,9 +19,12 @@ package timeline
import (
"context"
+ "errors"
"fmt"
"slices"
+ "sync/atomic"
"testing"
+ "time"
apimodel "code.superseriousbusiness.org/gotosocial/internal/api/model"
"code.superseriousbusiness.org/gotosocial/internal/gtsmodel"
@@ -65,6 +68,87 @@ var testStatusMeta = []*StatusMeta{
},
}
+func TestStatusTimelinePreloader(t *testing.T) {
+ ctx, cncl := context.WithCancel(context.Background())
+ defer cncl()
+
+ var tt StatusTimeline
+ tt.Init(1000)
+
+ // Start goroutine to add some
+ // concurrent usage to preloader.
+ var started atomic.Int32
+ go func() {
+ for {
+ select {
+ case <-ctx.Done():
+ return
+ default:
+ }
+ tt.preloader.Check()
+ started.Add(1)
+ }
+ }()
+
+ // Wait until goroutine running.
+ for started.Load() == 0 {
+ time.Sleep(time.Millisecond)
+ }
+
+ // Variable to check whether
+ // our hook funcs are called.
+ var called bool
+ reset := func() { called = false }
+
+ // "no error" preloader hook.
+ preloadNoErr := func() error {
+ called = true
+ return nil
+ }
+
+ // "error return" preloader hook.
+ preloadErr := func() error {
+ called = true
+ return errors.New("oh no")
+ }
+
+ // Check that on fail does not mark as preloaded.
+ err := tt.preloader.CheckPreload(preloadErr)
+ assert.Error(t, err)
+ assert.False(t, tt.preloader.Check())
+ assert.True(t, called)
+ reset()
+
+ // Check that on success marks itself as preloaded.
+ err = tt.preloader.CheckPreload(preloadNoErr)
+ assert.NoError(t, err)
+ assert.True(t, tt.preloader.Check())
+ assert.True(t, called)
+ reset()
+
+ // Check that preload func not called again
+ // if it's already in the 'preloaded' state.
+ err = tt.preloader.CheckPreload(preloadErr)
+ assert.NoError(t, err)
+ assert.True(t, tt.preloader.Check())
+ assert.False(t, called)
+ reset()
+
+ // Ensure that a clear operation
+ // successfully unsets preloader.
+ tt.preloader.Clear()
+ assert.False(t, tt.preloader.Check())
+ assert.False(t, called)
+ reset()
+
+ // Ensure that it can be marked as preloaded again.
+ err = tt.preloader.CheckPreload(preloadNoErr)
+ assert.NoError(t, err)
+ assert.True(t, tt.preloader.Check())
+ assert.True(t, called)
+ reset()
+}
+
func TestStatusTimelineLoadLimit(t *testing.T) {
var tt StatusTimeline
tt.Init(1000)
@@ -80,7 +164,7 @@ func TestStatusTimelineLoadLimit(t *testing.T) {
_ = tt.cache.Insert(data...)
// Manually mark timeline as 'preloaded'.
- tt.preloader.CheckPreload(tt.preloader.Done)
+ tt.preloader.CheckPreload(func() error { return nil })
// Craft a new page for selection,
// setting placeholder min / max values
@@ -251,7 +335,7 @@ func TestStatusTimelineInserts(t *testing.T) {
assert.Equal(t, maxID, maxStatus(&tt).ID)
// Manually mark timeline as 'preloaded'.
- tt.preloader.CheckPreload(tt.preloader.Done)
+ tt.preloader.CheckPreload(func() error { return nil })
// Specifically craft a boost of latest (i.e. max) status in timeline.
boost := &gtsmodel.Status{ID: "06B1A00PQWDZZH9WK9P5VND35C", BoostOfID: maxID}