1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
package test

import (
	"fmt"
	"rss_parrot/dal"
	"rss_parrot/logic"
	"rss_parrot/shared"
	"sort"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

type tootExtract struct {
	postTime     time.Time
	postGuidHash int64
}

// setupFeedFollowerTest returns a feed follower on a fresh database, together
// with that database. Its check loop runs, but finds nothing due: the only
// account is the birb, which is never checked.
func setupFeedFollowerTest(t *testing.T) (dal.IRepo, logic.IFeedFollower) {
	cfg := &shared.Config{
		Host: "kakapo.example.com",
		Birb: &shared.UserInfo{User: "birb", Published: time.Now()},
	}
	repo := newTestRepo(t, cfg)
	ff := logic.NewFeedFollower(cfg, newTestLogger(), nopUserAgent{}, repo,
		nil, nil, &fakeTexts{}, nil, nopMetrics{})
	return repo, ff
}

func test_Feed_Follower_Purge_Old_Posts(t *testing.T,
	postExtracts []tootExtract, fromBefore *time.Time, minCount int) {

	repo, ff := setupFeedFollowerTest(t)

	const handle = "some.site.com.feed"
	if _, err := repo.AddAccountIfNotExist(&dal.Account{
		CreatedAt: time.Now(), Handle: handle, UserUrl: "https://kakapo.example.com/u/" + handle,
	}, ""); err != nil {
		t.Fatal(err)
	}
	acct, err := repo.GetAccount(handle)
	if err != nil {
		t.Fatal(err)
	}
	for _, e := range postExtracts {
		if err := repo.AddToot(acct.Id, &dal.Toot{
			PostGuidHash: e.postGuidHash,
			TootedAt:     e.postTime,
			StatusId:     fmt.Sprintf("https://kakapo.example.com/u/%s/status/%d", handle, e.postGuidHash),
		}); err != nil {
			t.Fatal(err)
		}
	}

	// Purge items beyond minCount that are older than 2 days
	err = ff.PurgeOldPosts(acct, minCount, 2)
	assert.Nil(t, err)

	// Exactly the toots tooted after fromBefore must be left (all of them, if
	// nothing was to be purged).
	var want []int64
	for _, e := range postExtracts {
		if fromBefore == nil || e.postTime.After(*fromBefore) {
			want = append(want, e.postGuidHash)
		}
	}
	left, err := repo.GetTootExtracts(acct.Id)
	if err != nil {
		t.Fatal(err)
	}
	var got []int64
	for _, toot := range left {
		got = append(got, toot.PostGuidHash)
	}
	sort.Slice(want, func(i, j int) bool { return want[i] < want[j] })
	sort.Slice(got, func(i, j int) bool { return got[i] < got[j] })
	assert.Equal(t, want, got)
}

func Test_Feed_Follower_Purge_Old_Posts_Scenarios(t *testing.T) {
	now := time.Now().UTC()
	tootExtracts := []tootExtract{
		{now.Add(-3 * time.Hour), int64(getNextId())},
		{now.Add(-52 * time.Hour), int64(getNextId())},
		{now.Add(-1 * time.Hour), int64(getNextId())},
		{now.Add(-49 * time.Hour), int64(getNextId())},
		{now.Add(-2 * time.Hour), int64(getNextId())},
	}
	test_Feed_Follower_Purge_Old_Posts(t, tootExtracts, nil, 5)
	test_Feed_Follower_Purge_Old_Posts(t, tootExtracts, &tootExtracts[1].postTime, 4)
	test_Feed_Follower_Purge_Old_Posts(t, tootExtracts, &tootExtracts[3].postTime, 3)
	test_Feed_Follower_Purge_Old_Posts(t, tootExtracts, &tootExtracts[3].postTime, 2)

	// A toot just after the cutoff must survive. Without it every other toot
	// is hours away from the cutoff, so a purge that deleted slightly too much
	// would go unnoticed.
	justAfter := append(tootExtracts[:len(tootExtracts):len(tootExtracts)],
		tootExtract{tootExtracts[1].postTime.Add(time.Minute), int64(getNextId())})
	test_Feed_Follower_Purge_Old_Posts(t, justAfter, &tootExtracts[1].postTime, 5)
}