This repository has been archived on 2022-05-25. You can view files and clone it, but cannot push or open issues or pull requests.
elon-eats-my-tweets/daemon/tweet_persister_test.go

177 lines
5.7 KiB
Go

package daemon_test
import (
"context"
"errors"
"testing"
"time"
"git.netflux.io/rob/elon-eats-my-tweets/daemon"
"git.netflux.io/rob/elon-eats-my-tweets/generated/mocks"
"git.netflux.io/rob/elon-eats-my-tweets/generated/store"
"git.netflux.io/rob/elon-eats-my-tweets/twitterapi"
"github.com/jackc/pgx/v4"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"go.uber.org/zap"
)
func TestTweetUpdaterUpdateTweetsInitialTweet(t *testing.T) {
utcLoc, _ := time.LoadLocation("UTC")
createdAt := time.Date(2022, 1, 1, 12, 12, 23, 0, utcLoc)
testCases := []struct {
name string
lastKnownTweetErr error
fetchedTweet *twitterapi.Tweet
fetchedTweetErr error
insertTweetErr error
wantCount int
wantErr string
}{
{
name: "error fetching last known tweet",
lastKnownTweetErr: errors.New("database error"),
wantCount: 0,
wantErr: "error fetching last Elon tweet: database error",
},
{
name: "no tweet available from the API",
lastKnownTweetErr: pgx.ErrNoRows,
fetchedTweetErr: twitterapi.ErrNoTweets,
wantCount: 0,
wantErr: "",
},
{
name: "error fetching tweet from API",
lastKnownTweetErr: pgx.ErrNoRows,
fetchedTweetErr: errors.New("API error"),
wantCount: 0,
wantErr: "error fetching initial tweet: API error",
},
{
name: "error inserting tweet",
lastKnownTweetErr: pgx.ErrNoRows,
fetchedTweet: &twitterapi.Tweet{ID: "101", Text: "bar", CreatedAt: createdAt},
insertTweetErr: errors.New("boom"),
wantCount: 0,
wantErr: "error inserting initial tweet: error upserting tweet: boom",
},
{
name: "success",
lastKnownTweetErr: pgx.ErrNoRows,
fetchedTweet: &twitterapi.Tweet{ID: "101", Text: "bar", CreatedAt: createdAt},
wantCount: 1,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
var mockStore mocks.Store
mockStore.On("GetLastElonTweet", mock.Anything).Return(store.ElonTweet{}, tc.lastKnownTweetErr)
mockStore.
On("UpsertElonTweet", mock.Anything, mock.MatchedBy(func(params store.UpsertElonTweetParams) bool {
return params.TwitterID == 101 && params.Text == "bar" && params.PostedAt.Equal(createdAt) && params.ProcessedAt.Valid
})).
Return(store.ElonTweet{}, tc.insertTweetErr)
var mockAPIClient mocks.TwitterAPIClient
mockAPIClient.On("GetLastTweet", "44196397").Return(tc.fetchedTweet, tc.fetchedTweetErr)
if tc.wantErr == "" {
defer mockAPIClient.AssertExpectations(t)
}
updater := daemon.NewTweetPersister(&mockStore, &mockAPIClient, zap.NewNop().Sugar())
n, err := updater.PersistTweets(context.Background())
assert.Equal(t, tc.wantCount, n)
if tc.wantErr == "" {
assert.NoError(t, err)
} else {
assert.EqualError(t, err, tc.wantErr)
}
})
}
}
func TestTweetUpdaterUpdateTweets(t *testing.T) {
utcLoc, _ := time.LoadLocation("UTC")
createdAt := time.Date(2022, 1, 1, 12, 12, 23, 0, utcLoc)
lastKnownTweet := store.ElonTweet{TwitterID: 100, Text: "foo", PostedAt: createdAt, CreatedAt: createdAt}
testCases := []struct {
name string
lastKnownTweetErr error
fetchedTweets []*twitterapi.Tweet
fetchedTweetsErr error
insertTweetErr error
wantCount int
wantErr string
}{
{
name: "error fetching tweets",
fetchedTweetsErr: errors.New("whale"),
wantCount: 0,
wantErr: "error fetching latest Elon tweets: whale",
},
{
name: "error inserting tweet",
fetchedTweets: []*twitterapi.Tweet{{ID: "101", Text: "bar", CreatedAt: createdAt}},
insertTweetErr: errors.New("database error"),
wantCount: 0,
wantErr: "error inserting tweet: error upserting tweet: database error",
},
{
name: "success",
fetchedTweets: []*twitterapi.Tweet{
{ID: "101", Text: "bar", CreatedAt: createdAt},
{ID: "102", Text: "baz", CreatedAt: createdAt},
{ID: "103", Text: "qux", CreatedAt: createdAt},
},
wantCount: 3,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
var mockStore mocks.Store
mockStore.On("GetLastElonTweet", mock.Anything).Return(lastKnownTweet, nil)
mockStore.
On("UpsertElonTweet", mock.Anything, mock.MatchedBy(func(params store.UpsertElonTweetParams) bool {
return params.TwitterID == 101 && params.Text == "bar" && params.PostedAt.Equal(createdAt) && !params.ProcessedAt.Valid
})).
Return(store.ElonTweet{}, tc.insertTweetErr)
mockStore.
On("UpsertElonTweet", mock.Anything, mock.MatchedBy(func(params store.UpsertElonTweetParams) bool {
return params.TwitterID == 102 && params.Text == "baz" && params.PostedAt.Equal(createdAt) && !params.ProcessedAt.Valid
})).
Return(store.ElonTweet{}, tc.insertTweetErr)
mockStore.
On("UpsertElonTweet", mock.Anything, mock.MatchedBy(func(params store.UpsertElonTweetParams) bool {
return params.TwitterID == 103 && params.Text == "qux" && params.PostedAt.Equal(createdAt) && !params.ProcessedAt.Valid
})).
Return(store.ElonTweet{}, tc.insertTweetErr)
if tc.wantErr == "" {
defer mockStore.AssertExpectations(t)
}
var mockAPIClient mocks.TwitterAPIClient
mockAPIClient.On("GetTweets", "44196397", "100").Return(tc.fetchedTweets, tc.fetchedTweetsErr)
if tc.wantErr == "" {
defer mockAPIClient.AssertExpectations(t)
}
updater := daemon.NewTweetPersister(&mockStore, &mockAPIClient, zap.NewNop().Sugar())
n, err := updater.PersistTweets(context.Background())
assert.Equal(t, tc.wantCount, n)
if tc.wantErr == "" {
assert.NoError(t, err)
} else {
assert.EqualError(t, err, tc.wantErr)
}
})
}
}