177 lines
5.7 KiB
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)
|
|
}
|
|
})
|
|
}
|
|
}
|