diff --git a/Makefile b/Makefile index e41d24b..07047a1 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,10 @@ lint: test: go test -covermode=count -coverprofile=count.out -v ./... +mock: + @rm -rf mocks + mockery --all + download-golang-migrate-binary: if [ ! -f ./migrate.$(PLATFORM)-amd64 ] ; \ then \ diff --git a/go.mod b/go.mod index fcbef37..1f7a7f8 100644 --- a/go.mod +++ b/go.mod @@ -11,4 +11,5 @@ require ( github.com/jpillora/backoff v1.0.0 github.com/lib/pq v1.9.0 github.com/stretchr/testify v1.7.0 + go.uber.org/zap v1.16.0 ) diff --git a/go.sum b/go.sum index ca53c0f..6728dd0 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/DATA-DOG/go-txdb v0.1.3 h1:R4v6OuOcy2O147e2zHxU0B4NDtF+INb5R9q/CV7AEMg= github.com/DATA-DOG/go-txdb v0.1.3/go.mod h1:DhAhxMXZpUJVGnT+p9IbzJoRKvlArO2pkHjnGX7o0n0= github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 h1:zV3ejI06GQ59hwDQAvmK1qxOQGB3WuVTRoY0okPTAv0= @@ -9,6 +11,7 @@ github.com/go-ozzo/ozzo-validation/v4 v4.3.0 h1:byhDUpfEwjsVQb1vBunvIjh2BHQ9ead5 github.com/go-ozzo/ozzo-validation/v4 v4.3.0/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRiTzuqKbvfrL2RxCj6Ew= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/huandu/go-assert v1.1.5 h1:fjemmA7sSfYHJD7CUqs9qTwwfdNAx7/j2/ZlHXzNB3c= @@ -21,19 +24,61 @@ github.com/jmoiron/sqlx v1.3.1 h1:aLN7YINNZ7cYOPK3QC83dbM6KT0NMqVMw961TqrejlE= github.com/jmoiron/sqlx v1.3.1/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8= github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM= +go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= diff --git a/mocks/DeliveryAttemptRepository.go b/mocks/DeliveryAttemptRepository.go new file mode 100644 index 0000000..be32214 --- /dev/null +++ b/mocks/DeliveryAttemptRepository.go @@ -0,0 +1,75 @@ +// Code generated by mockery v0.0.0-dev. DO NOT EDIT. + +package mocks + +import ( + context "context" + + postmand "github.com/allisson/postmand" + mock "github.com/stretchr/testify/mock" +) + +// DeliveryAttemptRepository is an autogenerated mock type for the DeliveryAttemptRepository type +type DeliveryAttemptRepository struct { + mock.Mock +} + +// Create provides a mock function with given fields: ctx, deliveryAttempt +func (_m *DeliveryAttemptRepository) Create(ctx context.Context, deliveryAttempt *postmand.DeliveryAttempt) error { + ret := _m.Called(ctx, deliveryAttempt) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *postmand.DeliveryAttempt) error); ok { + r0 = rf(ctx, deliveryAttempt) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Get provides a mock function with given fields: ctx, getOptions +func (_m *DeliveryAttemptRepository) Get(ctx context.Context, getOptions postmand.RepositoryGetOptions) (*postmand.DeliveryAttempt, error) { + ret := _m.Called(ctx, getOptions) + + var r0 *postmand.DeliveryAttempt + if rf, ok := ret.Get(0).(func(context.Context, postmand.RepositoryGetOptions) *postmand.DeliveryAttempt); ok { + r0 = rf(ctx, getOptions) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*postmand.DeliveryAttempt) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, postmand.RepositoryGetOptions) error); ok { + r1 = rf(ctx, getOptions) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// List provides a mock function with given fields: ctx, listOptions +func (_m *DeliveryAttemptRepository) List(ctx context.Context, listOptions postmand.RepositoryListOptions) ([]*postmand.DeliveryAttempt, error) { + ret := _m.Called(ctx, listOptions) + + var r0 []*postmand.DeliveryAttempt + if rf, ok := ret.Get(0).(func(context.Context, postmand.RepositoryListOptions) []*postmand.DeliveryAttempt); ok { + r0 = rf(ctx, listOptions) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*postmand.DeliveryAttempt) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, postmand.RepositoryListOptions) error); ok { + r1 = rf(ctx, listOptions) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/mocks/DeliveryRepository.go b/mocks/DeliveryRepository.go new file mode 100644 index 0000000..8a9dd66 --- /dev/null +++ b/mocks/DeliveryRepository.go @@ -0,0 +1,128 @@ +// Code generated by mockery v0.0.0-dev. DO NOT EDIT. + +package mocks + +import ( + context "context" + + postmand "github.com/allisson/postmand" + mock "github.com/stretchr/testify/mock" + + uuid "github.com/google/uuid" +) + +// DeliveryRepository is an autogenerated mock type for the DeliveryRepository type +type DeliveryRepository struct { + mock.Mock +} + +// Create provides a mock function with given fields: ctx, delivery +func (_m *DeliveryRepository) Create(ctx context.Context, delivery *postmand.Delivery) error { + ret := _m.Called(ctx, delivery) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *postmand.Delivery) error); ok { + r0 = rf(ctx, delivery) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Delete provides a mock function with given fields: ctx, id +func (_m *DeliveryRepository) Delete(ctx context.Context, id uuid.UUID) error { + ret := _m.Called(ctx, id) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, uuid.UUID) error); ok { + r0 = rf(ctx, id) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Dispatch provides a mock function with given fields: ctx +func (_m *DeliveryRepository) Dispatch(ctx context.Context) (*postmand.DeliveryAttempt, error) { + ret := _m.Called(ctx) + + var r0 *postmand.DeliveryAttempt + if rf, ok := ret.Get(0).(func(context.Context) *postmand.DeliveryAttempt); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*postmand.DeliveryAttempt) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Get provides a mock function with given fields: ctx, getOptions +func (_m *DeliveryRepository) Get(ctx context.Context, getOptions postmand.RepositoryGetOptions) (*postmand.Delivery, error) { + ret := _m.Called(ctx, getOptions) + + var r0 *postmand.Delivery + if rf, ok := ret.Get(0).(func(context.Context, postmand.RepositoryGetOptions) *postmand.Delivery); ok { + r0 = rf(ctx, getOptions) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*postmand.Delivery) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, postmand.RepositoryGetOptions) error); ok { + r1 = rf(ctx, getOptions) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// List provides a mock function with given fields: ctx, listOptions +func (_m *DeliveryRepository) List(ctx context.Context, listOptions postmand.RepositoryListOptions) ([]*postmand.Delivery, error) { + ret := _m.Called(ctx, listOptions) + + var r0 []*postmand.Delivery + if rf, ok := ret.Get(0).(func(context.Context, postmand.RepositoryListOptions) []*postmand.Delivery); ok { + r0 = rf(ctx, listOptions) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*postmand.Delivery) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, postmand.RepositoryListOptions) error); ok { + r1 = rf(ctx, listOptions) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Update provides a mock function with given fields: ctx, delivery +func (_m *DeliveryRepository) Update(ctx context.Context, delivery *postmand.Delivery) error { + ret := _m.Called(ctx, delivery) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *postmand.Delivery) error); ok { + r0 = rf(ctx, delivery) + } else { + r0 = ret.Error(0) + } + + return r0 +} diff --git a/mocks/WebhookRepository.go b/mocks/WebhookRepository.go new file mode 100644 index 0000000..ada0277 --- /dev/null +++ b/mocks/WebhookRepository.go @@ -0,0 +1,105 @@ +// Code generated by mockery v0.0.0-dev. DO NOT EDIT. + +package mocks + +import ( + context "context" + + postmand "github.com/allisson/postmand" + mock "github.com/stretchr/testify/mock" + + uuid "github.com/google/uuid" +) + +// WebhookRepository is an autogenerated mock type for the WebhookRepository type +type WebhookRepository struct { + mock.Mock +} + +// Create provides a mock function with given fields: ctx, webhook +func (_m *WebhookRepository) Create(ctx context.Context, webhook *postmand.Webhook) error { + ret := _m.Called(ctx, webhook) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *postmand.Webhook) error); ok { + r0 = rf(ctx, webhook) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Delete provides a mock function with given fields: ctx, id +func (_m *WebhookRepository) Delete(ctx context.Context, id uuid.UUID) error { + ret := _m.Called(ctx, id) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, uuid.UUID) error); ok { + r0 = rf(ctx, id) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Get provides a mock function with given fields: ctx, getOptions +func (_m *WebhookRepository) Get(ctx context.Context, getOptions postmand.RepositoryGetOptions) (*postmand.Webhook, error) { + ret := _m.Called(ctx, getOptions) + + var r0 *postmand.Webhook + if rf, ok := ret.Get(0).(func(context.Context, postmand.RepositoryGetOptions) *postmand.Webhook); ok { + r0 = rf(ctx, getOptions) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*postmand.Webhook) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, postmand.RepositoryGetOptions) error); ok { + r1 = rf(ctx, getOptions) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// List provides a mock function with given fields: ctx, listOptions +func (_m *WebhookRepository) List(ctx context.Context, listOptions postmand.RepositoryListOptions) ([]*postmand.Webhook, error) { + ret := _m.Called(ctx, listOptions) + + var r0 []*postmand.Webhook + if rf, ok := ret.Get(0).(func(context.Context, postmand.RepositoryListOptions) []*postmand.Webhook); ok { + r0 = rf(ctx, listOptions) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*postmand.Webhook) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, postmand.RepositoryListOptions) error); ok { + r1 = rf(ctx, listOptions) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Update provides a mock function with given fields: ctx, webhook +func (_m *WebhookRepository) Update(ctx context.Context, webhook *postmand.Webhook) error { + ret := _m.Called(ctx, webhook) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *postmand.Webhook) error); ok { + r0 = rf(ctx, webhook) + } else { + r0 = ret.Error(0) + } + + return r0 +} diff --git a/mocks/WorkerService.go b/mocks/WorkerService.go new file mode 100644 index 0000000..c39e23b --- /dev/null +++ b/mocks/WorkerService.go @@ -0,0 +1,24 @@ +// Code generated by mockery v0.0.0-dev. DO NOT EDIT. + +package mocks + +import ( + context "context" + + mock "github.com/stretchr/testify/mock" +) + +// WorkerService is an autogenerated mock type for the WorkerService type +type WorkerService struct { + mock.Mock +} + +// Run provides a mock function with given fields: ctx +func (_m *WorkerService) Run(ctx context.Context) { + _m.Called(ctx) +} + +// Shutdown provides a mock function with given fields: ctx +func (_m *WorkerService) Shutdown(ctx context.Context) { + _m.Called(ctx) +} diff --git a/repository.go b/repository.go index 8ca9808..f4f4427 100644 --- a/repository.go +++ b/repository.go @@ -32,7 +32,7 @@ type DeliveryRepository interface { Create(ctx context.Context, delivery *Delivery) error Update(ctx context.Context, delivery *Delivery) error Delete(ctx context.Context, id ID) error - Dispatch(ctx context.Context) error + Dispatch(ctx context.Context) (*DeliveryAttempt, error) } // DeliveryAttemptRepository is the interface that will be used to iterate with the DeliveryAttempt data. diff --git a/repository/delivery.go b/repository/delivery.go index 882c358..8e5c699 100644 --- a/repository/delivery.go +++ b/repository/delivery.go @@ -115,7 +115,7 @@ func (d Delivery) Delete(ctx context.Context, id postmand.ID) error { } // Dispatch fetchs a delivery and send to url destination. -func (d Delivery) Dispatch(ctx context.Context) error { +func (d Delivery) Dispatch(ctx context.Context) (*postmand.DeliveryAttempt, error) { sqlStatement := ` SELECT deliveries.* @@ -135,7 +135,7 @@ func (d Delivery) Dispatch(ctx context.Context) error { // Starts a new transaction tx, err := d.db.Beginx() if err != nil { - return err + return nil, err } // Get delivery @@ -145,10 +145,10 @@ func (d Delivery) Dispatch(ctx context.Context) error { // Skip if no result if err == sql.ErrNoRows { rollback("delivery not found", tx) - return nil + return nil, nil } rollback("get delivery", tx) - return err + return nil, err } // Get webhook @@ -158,7 +158,7 @@ func (d Delivery) Dispatch(ctx context.Context) error { err = tx.GetContext(ctx, &webhook, sql, args...) if err != nil { rollback("get webhook", tx) - return err + return nil, err } // Dispatch webhook @@ -191,7 +191,7 @@ func (d Delivery) Dispatch(ctx context.Context) error { _, err = tx.ExecContext(ctx, sql, args...) if err != nil { rollback("update delivery", tx) - return err + return nil, err } // Create delivery attempt @@ -210,18 +210,18 @@ func (d Delivery) Dispatch(ctx context.Context) error { _, err = tx.ExecContext(ctx, sql, args...) if err != nil { rollback("create delivery attempt", tx) - return err + return nil, err } if err := tx.Commit(); err != nil { rollback("unable to commit", tx) - return err + return nil, err } - return nil + return &deliveryAttempt, nil } -// NewDelivery returns postmand.Delivery with db connection. +// NewDelivery will create an implementation of postmand.DeliveryRepository. func NewDelivery(db *sqlx.DB) *Delivery { return &Delivery{db: db} } diff --git a/repository/delivery_test.go b/repository/delivery_test.go index 5d5eae3..cfee0ec 100644 --- a/repository/delivery_test.go +++ b/repository/delivery_test.go @@ -202,7 +202,7 @@ func TestDelivery(t *testing.T) { err = th.deliveryRepository.Create(ctx, &delivery) assert.Nil(t, err) - err = th.deliveryRepository.Dispatch(ctx) + _, err = th.deliveryRepository.Dispatch(ctx) assert.Nil(t, err) options := postmand.RepositoryGetOptions{Filters: map[string]interface{}{"id": delivery.ID}} @@ -239,7 +239,7 @@ func TestDelivery(t *testing.T) { err = th.deliveryRepository.Create(ctx, &delivery) assert.Nil(t, err) - err = th.deliveryRepository.Dispatch(ctx) + _, err = th.deliveryRepository.Dispatch(ctx) assert.Nil(t, err) options := postmand.RepositoryGetOptions{Filters: map[string]interface{}{"id": delivery.ID}} @@ -276,7 +276,7 @@ func TestDelivery(t *testing.T) { err = th.deliveryRepository.Create(ctx, &delivery) assert.Nil(t, err) - err = th.deliveryRepository.Dispatch(ctx) + _, err = th.deliveryRepository.Dispatch(ctx) assert.Nil(t, err) options := postmand.RepositoryGetOptions{Filters: map[string]interface{}{"id": delivery.ID}} diff --git a/repository/webhook.go b/repository/webhook.go index 55ce54a..5f309fd 100644 --- a/repository/webhook.go +++ b/repository/webhook.go @@ -51,7 +51,7 @@ func (w Webhook) Delete(ctx context.Context, id postmand.ID) error { return err } -// NewWebhook returns postmand.Webhook with db connection. +// NewWebhook will create an implementation of postmand.WebhookRepository. func NewWebhook(db *sqlx.DB) *Webhook { return &Webhook{db: db} } diff --git a/service.go b/service.go new file mode 100644 index 0000000..065132e --- /dev/null +++ b/service.go @@ -0,0 +1,9 @@ +package postmand + +import "context" + +// WorkerService is the interface that will be used on workers to dispatch webhooks. +type WorkerService interface { + Run(ctx context.Context) + Shutdown(ctx context.Context) +} diff --git a/service/worker.go b/service/worker.go new file mode 100644 index 0000000..9ac446f --- /dev/null +++ b/service/worker.go @@ -0,0 +1,68 @@ +package service + +import ( + "context" + "time" + + "github.com/allisson/postmand" + "go.uber.org/zap" +) + +// Worker implements postmand.WorkerService interface. +type Worker struct { + deliveryRepository postmand.DeliveryRepository + logger *zap.Logger + pollingInterval time.Duration + isStop bool +} + +// Run sending of webhooks until the Shutdown method is called. +func (w *Worker) Run(ctx context.Context) { + for { + // Break forloop if isStop is true. + if w.isStop { + break + } + + // Dispatch webhook. + deliveryAttempt, err := w.deliveryRepository.Dispatch(ctx) + if err != nil { + w.logger.Error("worker-dispatch-error", zap.Error(err)) + time.Sleep(w.pollingInterval) + continue + } + if deliveryAttempt == nil { + time.Sleep(w.pollingInterval) + continue + } + + // Log delivery attempt. + w.logger.Info( + "worker-delivery-attempt-created", + zap.String("id", deliveryAttempt.ID.String()), + zap.String("webhook_id", deliveryAttempt.WebhookID.String()), + zap.String("delivery_id", deliveryAttempt.DeliveryID.String()), + zap.Int("response_status_code", deliveryAttempt.ResponseStatusCode), + zap.Int("execution_duration", deliveryAttempt.ExecutionDuration), + zap.Bool("success", deliveryAttempt.Success), + ) + } + + w.logger.Info("worker-shutdown-completed") +} + +// Shutdown stops the forloop in Run method. +func (w *Worker) Shutdown(ctx context.Context) { + w.isStop = true + w.logger.Info("worker-shutdown-started") +} + +// NewWorker will create an implementation of postmand.WorkerService. +func NewWorker(deliveryRepository postmand.DeliveryRepository, logger *zap.Logger, pollingInterval time.Duration) *Worker { + return &Worker{ + deliveryRepository: deliveryRepository, + logger: logger, + pollingInterval: pollingInterval, + isStop: false, + } +} diff --git a/service/worker_test.go b/service/worker_test.go new file mode 100644 index 0000000..8fe4852 --- /dev/null +++ b/service/worker_test.go @@ -0,0 +1,63 @@ +package service + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/allisson/postmand" + "github.com/allisson/postmand/mocks" + "github.com/stretchr/testify/mock" + "go.uber.org/zap" +) + +func TestWorker(t *testing.T) { + ctx := context.Background() + pollingInterval := 10 * time.Millisecond + + t.Run("Run with dispatch error", func(t *testing.T) { + deliveryRepository := &mocks.DeliveryRepository{} + logger, _ := zap.NewDevelopment() + workerService := NewWorker(deliveryRepository, logger, pollingInterval) + + deliveryRepository.On("Dispatch", mock.Anything).Return(nil, errors.New("error")) + // Wait 15 miliseconds before call shutdown. + go func() { + workerService.Shutdown(ctx) + }() + workerService.Run(ctx) + + deliveryRepository.AssertExpectations(t) + }) + + t.Run("Run with no dispatch", func(t *testing.T) { + deliveryRepository := &mocks.DeliveryRepository{} + logger, _ := zap.NewDevelopment() + workerService := NewWorker(deliveryRepository, logger, pollingInterval) + + deliveryRepository.On("Dispatch", mock.Anything).Return(nil, nil) + // Wait 15 miliseconds before call shutdown. + go func() { + workerService.Shutdown(ctx) + }() + workerService.Run(ctx) + + deliveryRepository.AssertExpectations(t) + }) + + t.Run("Run with dispatch", func(t *testing.T) { + deliveryRepository := &mocks.DeliveryRepository{} + logger, _ := zap.NewDevelopment() + workerService := NewWorker(deliveryRepository, logger, pollingInterval) + + deliveryRepository.On("Dispatch", mock.Anything).Return(&postmand.DeliveryAttempt{}, nil) + // Wait 15 miliseconds before call shutdown. + go func() { + workerService.Shutdown(ctx) + }() + workerService.Run(ctx) + + deliveryRepository.AssertExpectations(t) + }) +}