1
0
mirror of https://github.com/mxpv/podsync.git synced 2024-05-11 05:55:04 +00:00

Add feed caching

This commit is contained in:
Maksym Pavlenko
2019-03-11 21:23:36 -07:00
committed by Maksym Pavlenko
parent f288c22b53
commit b7a0f10a5d
9 changed files with 251 additions and 15 deletions

View File

@ -8,6 +8,8 @@ import (
"os/signal"
"syscall"
"github.com/mxpv/podsync/pkg/cache"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
@ -57,6 +59,13 @@ func main() {
patreon := support.NewPatreon(database)
// Cache
redisCache, err := cache.NewRedisCache(cfg.RedisURL)
if err != nil {
log.WithError(err).Fatal("failed to initialize Redis cache")
}
// Builders
youtube, err := builders.NewYouTubeBuilder(cfg.YouTubeAPIKey)
@ -81,7 +90,7 @@ func main() {
srv := http.Server{
Addr: fmt.Sprintf(":%d", 5001),
Handler: handler.New(feed, patreon, cfg),
Handler: handler.New(feed, patreon, redisCache, cfg),
}
go func() {
@ -99,6 +108,10 @@ func main() {
log.WithError(err).Error("server shutdown failed")
}
if err := redisCache.Close(); err != nil {
log.WithError(err).Error("failed to close redis cache")
}
if err := database.Close(); err != nil {
log.WithError(err).Error("failed to close database")
}

7
go.mod
View File

@ -15,7 +15,8 @@ require (
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7 // indirect
github.com/gin-gonic/gin v0.0.0-20170702092826-d459835d2b07
github.com/go-pg/pg v6.14.2+incompatible
github.com/golang/mock v1.1.1
github.com/go-redis/redis v6.15.2+incompatible
github.com/golang/mock v1.2.0
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 // indirect
github.com/gorilla/sessions v1.1.1 // indirect
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce // indirect
@ -42,9 +43,11 @@ require (
github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf // indirect
github.com/ugorji/go v1.1.1 // indirect
github.com/ventu-io/go-shortid v0.0.0-20171029131806-771a37caa5cf
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd
golang.org/x/net v0.0.0-20190311183353-d8887717615a
golang.org/x/oauth2 v0.0.0-20180620175406-ef147856a6dd
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f // indirect
golang.org/x/sys v0.0.0-20190311152110-c8c8c57fd1e1 // indirect
golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5 // indirect
google.golang.org/api v0.0.0-20180718221112-efcb5f25ac56
google.golang.org/appengine v1.1.0 // indirect
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect

15
go.sum
View File

@ -30,8 +30,12 @@ github.com/gin-gonic/gin v0.0.0-20170702092826-d459835d2b07 h1:cZPJWzd2oNeoS0oJM
github.com/gin-gonic/gin v0.0.0-20170702092826-d459835d2b07/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y=
github.com/go-pg/pg v6.14.2+incompatible h1:FrOgsHDUhC3V3wkBGAIN5LVj4nJczFPyy1YNFnetfIQ=
github.com/go-pg/pg v6.14.2+incompatible/go.mod h1:a2oXow+aFOrvwcKs3eIA0lNFmMilrxK2sOkB5NWe0vA=
github.com/go-redis/redis v6.15.2+incompatible h1:9SpNVG76gr6InJGxoZ6IuuxaCOQwDAhzyXg+Bs+0Sb4=
github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 h1:zLTLjkaOFEFIOxY5BWLFLwh+cL8vOBW4XJ2aqLE/Tf0=
@ -62,8 +66,6 @@ github.com/memcachier/mc v2.0.1+incompatible h1:s8EDz0xrJLP8goitwZOoq1vA/sm0fPS4
github.com/memcachier/mc v2.0.1+incompatible/go.mod h1:7bkvFE61leUBvXz+yxsOnGBQSZpBSPIMUQSmmSHvuXc=
github.com/mitchellh/mapstructure v0.0.0-20180715050151-f15292f7a699 h1:KXZJFdun9knAVAR8tg/aHJEr5DgtcbqyvzacK+CDCaI=
github.com/mitchellh/mapstructure v0.0.0-20180715050151-f15292f7a699/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mxpv/patreon-go v0.0.0-20171031001022-1d2f253ac700 h1:39PjdU78pNilVLU9tjWVDGt/rziIKKkKHuzWbH1kBbw=
github.com/mxpv/patreon-go v0.0.0-20171031001022-1d2f253ac700/go.mod h1:ksYjm2GAbGlgIP7jO9Q5/AdyE4MwwEbgQ+lFMx3hyiM=
github.com/mxpv/patreon-go v0.0.0-20180807002359-67dbab1ad14c h1:glRDvmNgmckYjQHMQ17XFlci6RehtmGmRQV+Cq6FusI=
github.com/mxpv/patreon-go v0.0.0-20180807002359-67dbab1ad14c/go.mod h1:ksYjm2GAbGlgIP7jO9Q5/AdyE4MwwEbgQ+lFMx3hyiM=
github.com/mxpv/podcast v0.0.0-20170823220358-fe328ad87d18 h1:YYsu49Y42JA+CSs9+z2MGBdGxb5jklpagLp5QPJ6BwQ=
@ -104,8 +106,12 @@ github.com/ventu-io/go-shortid v0.0.0-20171029131806-771a37caa5cf h1:cgAKVljim9R
github.com/ventu-io/go-shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:6rZqAOk/eYX5FJyjQJ6Z3RBSN389IXX2ijwW4FcggaM=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/oauth2 v0.0.0-20180620175406-ef147856a6dd h1:QQhib242ErYDSMitlBm8V7wYCm/1a25hV8qMadIKLPA=
golang.org/x/oauth2 v0.0.0-20180620175406-ef147856a6dd/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -114,8 +120,13 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190311152110-c8c8c57fd1e1 h1:FQNj2xvjQ1lgFyzbSybGZr792Y8Dy95D7uuqnZAzNaA=
golang.org/x/sys v0.0.0-20190311152110-c8c8c57fd1e1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5 h1:ZcPpqKMdoZeNQ/4GHlyY4COf8n8SmpPv6mcqF1+VPSM=
golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
google.golang.org/api v0.0.0-20180718221112-efcb5f25ac56 h1:iDRbkenn0VZEo05mHiCtN6/EfbZj7x1Rg+tPjB5HiQc=
google.golang.org/api v0.0.0-20180718221112-efcb5f25ac56/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs=

46
pkg/cache/redis.go vendored Normal file
View File

@ -0,0 +1,46 @@
package cache
import (
"time"
"github.com/go-redis/redis"
"github.com/pkg/errors"
)
var ErrNotFound = errors.New("not found")
// RedisCache implements caching layer for feeds using Redis
type RedisCache struct {
client *redis.Client
}
func NewRedisCache(redisURL string) (RedisCache, error) {
opts, err := redis.ParseURL(redisURL)
if err != nil {
return RedisCache{}, err
}
client := redis.NewClient(opts)
if err := client.Ping().Err(); err != nil {
return RedisCache{}, err
}
return RedisCache{client: client}, nil
}
func (c RedisCache) Set(key, value string, ttl time.Duration) error {
return c.client.Set(key, value, ttl).Err()
}
func (c RedisCache) Get(key string) (string, error) {
val, err := c.client.Get(key).Result()
if err == redis.Nil {
return "", ErrNotFound
} else {
return val, err
}
}
func (c RedisCache) Close() error {
return c.client.Close()
}

67
pkg/cache/redis_test.go vendored Normal file
View File

@ -0,0 +1,67 @@
package cache
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestRedisCache_Get(t *testing.T) {
s := createRedisClient(t)
defer s.Close()
err := s.Set("1", "value", 1*time.Minute)
assert.NoError(t, err)
val, err := s.Get("1")
assert.NoError(t, err)
assert.Equal(t, "value", val)
}
func TestRedisCache_GetInvalidKey(t *testing.T) {
s := createRedisClient(t)
defer s.Close()
val, err := s.Get("1")
assert.Equal(t, ErrNotFound, err)
assert.Empty(t, val)
}
func TestNewRedisCache_TTL(t *testing.T) {
s := createRedisClient(t)
defer s.Close()
err := s.Set("1", "value", 500*time.Millisecond)
assert.NoError(t, err)
val, err := s.Get("1")
assert.NoError(t, err)
assert.Equal(t, "value", val)
time.Sleep(501 * time.Millisecond)
_, err = s.Get("1")
assert.Equal(t, ErrNotFound, err)
}
// docker run -it --rm -p 6379:6379 redis
func createRedisClient(t *testing.T) RedisCache {
if testing.Short() {
t.Skip("run redis tests manually")
}
client, err := NewRedisCache("redis://localhost")
require.NoError(t, err)
keys, err := client.client.Keys("*").Result()
assert.NoError(t, err)
if len(keys) > 0 {
err = client.client.Del(keys...).Err()
assert.NoError(t, err)
}
return client
}

View File

@ -21,6 +21,7 @@ type AppConfig struct {
AWSAccessSecret string `yaml:"awsAccessSecret"`
DynamoFeedsTableName string `yaml:"dynamoFeedsTableName"`
DynamoPledgesTableName string `yaml:"dynamoPledgesTableName"`
RedisURL string `yaml:"redisUrl"`
}
func ReadConfiguration() (*AppConfig, error) {

View File

@ -5,6 +5,7 @@ import (
"io/ioutil"
"net/http"
"strings"
"time"
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
@ -36,14 +37,20 @@ type patreonService interface {
GetFeatureLevelFromAmount(amount int) int
}
type cacheService interface {
Set(key, value string, ttl time.Duration) error
Get(key string) (string, error)
}
type handler struct {
feed feedService
cfg *config.AppConfig
oauth2 oauth2.Config
patreon patreonService
cache cacheService
}
func New(feed feedService, support patreonService, cfg *config.AppConfig) http.Handler {
func New(feed feedService, support patreonService, cache cacheService, cfg *config.AppConfig) http.Handler {
r := gin.New()
r.Use(gin.Recovery())
@ -53,6 +60,7 @@ func New(feed feedService, support patreonService, cfg *config.AppConfig) http.H
h := handler{
feed: feed,
patreon: support,
cache: cache,
cfg: cfg,
}
@ -214,6 +222,14 @@ func (h handler) getFeed(c *gin.Context) {
hashID = strings.TrimSuffix(hashID, ".xml")
}
const feedContentType = "application/rss+xml; charset=UTF-8"
cached, err := h.cache.Get(hashID)
if err == nil {
c.Data(http.StatusOK, feedContentType, []byte(cached))
return
}
podcast, err := h.feed.BuildFeed(hashID)
if err != nil {
code := http.StatusInternalServerError
@ -232,7 +248,13 @@ func (h handler) getFeed(c *gin.Context) {
return
}
c.Data(http.StatusOK, "application/rss+xml; charset=UTF-8", podcast.Bytes())
data := podcast.String()
if err := h.cache.Set(hashID, data, 10*time.Minute); err != nil {
log.WithError(err).Warnf("failed to cache feed %q", hashID)
}
c.Data(http.StatusOK, feedContentType, []byte(data))
}
func (h handler) metadata(c *gin.Context) {

View File

@ -10,6 +10,7 @@ import (
podcast "github.com/mxpv/podcast"
api "github.com/mxpv/podsync/pkg/api"
reflect "reflect"
time "time"
)
// MockfeedService is a mock of feedService interface
@ -37,6 +38,7 @@ func (m *MockfeedService) EXPECT() *MockfeedServiceMockRecorder {
// CreateFeed mocks base method
func (m *MockfeedService) CreateFeed(req *api.CreateFeedRequest, identity *api.Identity) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateFeed", req, identity)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
@ -45,11 +47,13 @@ func (m *MockfeedService) CreateFeed(req *api.CreateFeedRequest, identity *api.I
// CreateFeed indicates an expected call of CreateFeed
func (mr *MockfeedServiceMockRecorder) CreateFeed(req, identity interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFeed", reflect.TypeOf((*MockfeedService)(nil).CreateFeed), req, identity)
}
// BuildFeed mocks base method
func (m *MockfeedService) BuildFeed(hashID string) (*podcast.Podcast, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "BuildFeed", hashID)
ret0, _ := ret[0].(*podcast.Podcast)
ret1, _ := ret[1].(error)
@ -58,24 +62,28 @@ func (m *MockfeedService) BuildFeed(hashID string) (*podcast.Podcast, error) {
// BuildFeed indicates an expected call of BuildFeed
func (mr *MockfeedServiceMockRecorder) BuildFeed(hashID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BuildFeed", reflect.TypeOf((*MockfeedService)(nil).BuildFeed), hashID)
}
// GetMetadata mocks base method
func (m *MockfeedService) GetMetadata(hashId string) (*api.Metadata, error) {
ret := m.ctrl.Call(m, "GetMetadata", hashId)
func (m *MockfeedService) GetMetadata(hashID string) (*api.Metadata, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetMetadata", hashID)
ret0, _ := ret[0].(*api.Metadata)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetMetadata indicates an expected call of GetMetadata
func (mr *MockfeedServiceMockRecorder) GetMetadata(hashId interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMetadata", reflect.TypeOf((*MockfeedService)(nil).GetMetadata), hashId)
func (mr *MockfeedServiceMockRecorder) GetMetadata(hashID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMetadata", reflect.TypeOf((*MockfeedService)(nil).GetMetadata), hashID)
}
// Downgrade mocks base method
func (m *MockfeedService) Downgrade(patronID string, featureLevel int) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Downgrade", patronID, featureLevel)
ret0, _ := ret[0].(error)
return ret0
@ -83,6 +91,7 @@ func (m *MockfeedService) Downgrade(patronID string, featureLevel int) error {
// Downgrade indicates an expected call of Downgrade
func (mr *MockfeedServiceMockRecorder) Downgrade(patronID, featureLevel interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Downgrade", reflect.TypeOf((*MockfeedService)(nil).Downgrade), patronID, featureLevel)
}
@ -111,6 +120,7 @@ func (m *MockpatreonService) EXPECT() *MockpatreonServiceMockRecorder {
// Hook mocks base method
func (m *MockpatreonService) Hook(pledge *patreon_go.Pledge, event string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Hook", pledge, event)
ret0, _ := ret[0].(error)
return ret0
@ -118,11 +128,13 @@ func (m *MockpatreonService) Hook(pledge *patreon_go.Pledge, event string) error
// Hook indicates an expected call of Hook
func (mr *MockpatreonServiceMockRecorder) Hook(pledge, event interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Hook", reflect.TypeOf((*MockpatreonService)(nil).Hook), pledge, event)
}
// GetFeatureLevelByID mocks base method
func (m *MockpatreonService) GetFeatureLevelByID(patronID string) int {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetFeatureLevelByID", patronID)
ret0, _ := ret[0].(int)
return ret0
@ -130,11 +142,13 @@ func (m *MockpatreonService) GetFeatureLevelByID(patronID string) int {
// GetFeatureLevelByID indicates an expected call of GetFeatureLevelByID
func (mr *MockpatreonServiceMockRecorder) GetFeatureLevelByID(patronID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFeatureLevelByID", reflect.TypeOf((*MockpatreonService)(nil).GetFeatureLevelByID), patronID)
}
// GetFeatureLevelFromAmount mocks base method
func (m *MockpatreonService) GetFeatureLevelFromAmount(amount int) int {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetFeatureLevelFromAmount", amount)
ret0, _ := ret[0].(int)
return ret0
@ -142,5 +156,58 @@ func (m *MockpatreonService) GetFeatureLevelFromAmount(amount int) int {
// GetFeatureLevelFromAmount indicates an expected call of GetFeatureLevelFromAmount
func (mr *MockpatreonServiceMockRecorder) GetFeatureLevelFromAmount(amount interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFeatureLevelFromAmount", reflect.TypeOf((*MockpatreonService)(nil).GetFeatureLevelFromAmount), amount)
}
// MockcacheService is a mock of cacheService interface
type MockcacheService struct {
ctrl *gomock.Controller
recorder *MockcacheServiceMockRecorder
}
// MockcacheServiceMockRecorder is the mock recorder for MockcacheService
type MockcacheServiceMockRecorder struct {
mock *MockcacheService
}
// NewMockcacheService creates a new mock instance
func NewMockcacheService(ctrl *gomock.Controller) *MockcacheService {
mock := &MockcacheService{ctrl: ctrl}
mock.recorder = &MockcacheServiceMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockcacheService) EXPECT() *MockcacheServiceMockRecorder {
return m.recorder
}
// Set mocks base method
func (m *MockcacheService) Set(key, value string, ttl time.Duration) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Set", key, value, ttl)
ret0, _ := ret[0].(error)
return ret0
}
// Set indicates an expected call of Set
func (mr *MockcacheServiceMockRecorder) Set(key, value, ttl interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Set", reflect.TypeOf((*MockcacheService)(nil).Set), key, value, ttl)
}
// Get mocks base method
func (m *MockcacheService) Get(key string) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Get", key)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Get indicates an expected call of Get
func (mr *MockcacheServiceMockRecorder) Get(key interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockcacheService)(nil).Get), key)
}

View File

@ -9,6 +9,8 @@ import (
"strings"
"testing"
"github.com/pkg/errors"
"github.com/golang/mock/gomock"
itunes "github.com/mxpv/podcast"
"github.com/stretchr/testify/require"
@ -36,7 +38,7 @@ func TestCreateFeed(t *testing.T) {
patreon := NewMockpatreonService(ctrl)
patreon.EXPECT().GetFeatureLevelByID(gomock.Any()).Return(api.DefaultFeatures)
srv := httptest.NewServer(New(feed, patreon, cfg))
srv := httptest.NewServer(New(feed, patreon, nil, cfg))
defer srv.Close()
query := `{"url": "https://youtube.com/channel/123", "page_size": 55, "quality": "low", "format": "audio"}`
@ -51,7 +53,7 @@ func TestCreateInvalidFeed(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
srv := httptest.NewServer(New(NewMockfeedService(ctrl), nil, cfg))
srv := httptest.NewServer(New(NewMockfeedService(ctrl), nil, nil, cfg))
defer srv.Close()
query := `{}`
@ -104,7 +106,11 @@ func TestGetFeed(t *testing.T) {
feed := NewMockfeedService(ctrl)
feed.EXPECT().BuildFeed("123").Return(&podcast, nil)
srv := httptest.NewServer(New(feed, nil, cfg))
cache := NewMockcacheService(ctrl)
cache.EXPECT().Get("123").Times(1).Return("", errors.New("not found"))
cache.EXPECT().Set("123", podcast.String(), gomock.Any()).Return(nil).Times(1)
srv := httptest.NewServer(New(feed, nil, cache, cfg))
defer srv.Close()
resp, err := http.Get(srv.URL + "/123")
@ -119,7 +125,7 @@ func TestGetMetadata(t *testing.T) {
feed := NewMockfeedService(ctrl)
feed.EXPECT().GetMetadata("123").Times(1).Return(&api.Metadata{}, nil)
srv := httptest.NewServer(New(feed, nil, cfg))
srv := httptest.NewServer(New(feed, nil, nil, cfg))
defer srv.Close()
resp, err := http.Get(srv.URL + "/api/metadata/123")