Minor webhook refactor

This commit is contained in:
Fredrik Berntsson
2025-04-15 23:43:49 +02:00
parent c18e5bfea9
commit 303bef850d
15 changed files with 446 additions and 480 deletions
+10 -7
View File
@@ -1,10 +1,13 @@
with-expecter: True
dir: internal/mocks/{{.PackageName}}
mockname: "{{.InterfaceName}}"
outpkg: "{{.PackageName}}"
filename: "{{.InterfaceName}}.go"
all: True
all: false
dir: 'internal/mocks/{{.SrcPackageName}}'
filename: '{{.InterfaceName}}.go'
force-file-write: true
formatter: goimports
structname: '{{.InterfaceName}}'
pkgname: '{{.SrcPackageName}}'
template: testify
packages:
github.com/lovelaze/nebula-sync:
config:
recursive: True
all: true
recursive: true
+1 -2
View File
@@ -90,7 +90,6 @@ The following environment variables can be specified:
| `CLIENT_RETRY_DELAY_SECONDS` | 1 | 5 | Seconds to delay between connection attempts |
| `CLIENT_TIMEOUT_SECONDS` | 20 | 60 | Http client timeout in seconds |
> **Note:** The following optional settings apply only if `FULL_SYNC=false`. They allow for granular control of synchronization if a full sync is not wanted.
| Name | Default | Description |
@@ -156,7 +155,7 @@ Additionally, you can skip TLS verification for all webhooks if necessary:
|-------------------------------------------------|---------|-----------------|----------------------------------------------------|
| `SYNC_WEBHOOK_CLIENT_SKIP_TLS_VERIFICATION` | false | true | Skips TLS certificate verification |
#### Examples
#### Integration examples:
##### healthcheck.io:
+3 -3
View File
@@ -70,9 +70,9 @@ require (
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/sdk v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
golang.org/x/crypto v0.35.0 // indirect
golang.org/x/net v0.36.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/net v0.38.0 // indirect
golang.org/x/sys v0.32.0 // indirect
golang.org/x/time v0.3.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
+10 -10
View File
@@ -165,16 +165,16 @@ go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v8
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
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/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA=
golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -191,14 +191,14 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+1 -1
View File
@@ -232,7 +232,7 @@ func (cs *ConfigFilter) String() string {
return fmt.Sprintf("%+v", *cs)
}
func (wes *WebhookEventSetting) String() string {
func (wes *WebhookRequest) String() string {
return fmt.Sprintf("%+v", *wes)
}
+12 -17
View File
@@ -7,39 +7,34 @@ import (
)
type WebhookSettings struct {
Failure WebhookEventSetting `ignored:"true"`
Success WebhookEventSetting `ignored:"true"`
Client WebhookClient `ignored:"true"`
Failure WebhookRequest `ignored:"true"`
Success WebhookRequest `ignored:"true"`
Client WebhookClient `ignored:"true"`
}
type WebhookClient struct {
SkipTLSVerification bool `default:"false" envconfig:"SKIP_TLS_VERIFICATION"`
}
type WebhookEventSetting struct {
Body string `default:"" envconfig:"BODY"`
Headers map[string]string `default:"" envconfig:"HEADERS"`
type WebhookRequest struct {
Body string `envconfig:"BODY"`
Headers map[string]string `envconfig:"HEADERS"`
Method string `default:"POST" envconfig:"METHOD"`
Url string `default:"" envconfig:"URL"`
Url string `envconfig:"URL"`
}
const envPrefix = "SYNC_WEBHOOK_"
const webhookEnvPrefix = "SYNC_WEBHOOK_"
func (c *Config) loadWebhookSettings() error {
webhookSettings := WebhookSettings{
Failure: WebhookEventSetting{},
Success: WebhookEventSetting{},
Client: WebhookClient{},
}
webhookSettings := WebhookSettings{}
if err := envconfig.Process(envPrefix+"FAILURE", &webhookSettings.Failure); err != nil {
if err := envconfig.Process(webhookEnvPrefix+"FAILURE", &webhookSettings.Failure); err != nil {
return fmt.Errorf("process webhook env vars for failure: %w", err)
}
if err := envconfig.Process(envPrefix+"SUCCESS", &webhookSettings.Success); err != nil {
if err := envconfig.Process(webhookEnvPrefix+"SUCCESS", &webhookSettings.Success); err != nil {
return fmt.Errorf("process webhook env vars for success: %w", err)
}
if err := envconfig.Process(envPrefix+"CLIENT", &webhookSettings.Client); err != nil {
if err := envconfig.Process(webhookEnvPrefix+"CLIENT", &webhookSettings.Client); err != nil {
return fmt.Errorf("process webhook env vars for client: %w", err)
}
+105 -116
View File
@@ -1,12 +1,28 @@
// Code generated by mockery v2.53.3. DO NOT EDIT.
// Code generated by mockery; DO NOT EDIT.
// github.com/vektra/mockery
// template: testify
package pihole
import (
model "github.com/lovelaze/nebula-sync/internal/pihole/model"
"github.com/lovelaze/nebula-sync/internal/pihole/model"
mock "github.com/stretchr/testify/mock"
)
// NewClient creates a new instance of Client. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewClient(t interface {
mock.TestingT
Cleanup(func())
}) *Client {
mock := &Client{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
// Client is an autogenerated mock type for the Client type
type Client struct {
mock.Mock
@@ -20,21 +36,20 @@ func (_m *Client) EXPECT() *Client_Expecter {
return &Client_Expecter{mock: &_m.Mock}
}
// ApiPath provides a mock function with given fields: target
func (_m *Client) ApiPath(target string) string {
ret := _m.Called(target)
// ApiPath provides a mock function for the type Client
func (_mock *Client) ApiPath(target string) string {
ret := _mock.Called(target)
if len(ret) == 0 {
panic("no return value specified for ApiPath")
}
var r0 string
if rf, ok := ret.Get(0).(func(string) string); ok {
r0 = rf(target)
if returnFunc, ok := ret.Get(0).(func(string) string); ok {
r0 = returnFunc(target)
} else {
r0 = ret.Get(0).(string)
}
return r0
}
@@ -44,7 +59,7 @@ type Client_ApiPath_Call struct {
}
// ApiPath is a helper method to define mock.On call
// - target string
// - target
func (_e *Client_Expecter) ApiPath(target interface{}) *Client_ApiPath_Call {
return &Client_ApiPath_Call{Call: _e.mock.On("ApiPath", target)}
}
@@ -56,31 +71,30 @@ func (_c *Client_ApiPath_Call) Run(run func(target string)) *Client_ApiPath_Call
return _c
}
func (_c *Client_ApiPath_Call) Return(_a0 string) *Client_ApiPath_Call {
_c.Call.Return(_a0)
func (_c *Client_ApiPath_Call) Return(s string) *Client_ApiPath_Call {
_c.Call.Return(s)
return _c
}
func (_c *Client_ApiPath_Call) RunAndReturn(run func(string) string) *Client_ApiPath_Call {
func (_c *Client_ApiPath_Call) RunAndReturn(run func(target string) string) *Client_ApiPath_Call {
_c.Call.Return(run)
return _c
}
// DeleteSession provides a mock function with no fields
func (_m *Client) DeleteSession() error {
ret := _m.Called()
// DeleteSession provides a mock function for the type Client
func (_mock *Client) DeleteSession() error {
ret := _mock.Called()
if len(ret) == 0 {
panic("no return value specified for DeleteSession")
}
var r0 error
if rf, ok := ret.Get(0).(func() error); ok {
r0 = rf()
if returnFunc, ok := ret.Get(0).(func() error); ok {
r0 = returnFunc()
} else {
r0 = ret.Error(0)
}
return r0
}
@@ -101,8 +115,8 @@ func (_c *Client_DeleteSession_Call) Run(run func()) *Client_DeleteSession_Call
return _c
}
func (_c *Client_DeleteSession_Call) Return(_a0 error) *Client_DeleteSession_Call {
_c.Call.Return(_a0)
func (_c *Client_DeleteSession_Call) Return(err error) *Client_DeleteSession_Call {
_c.Call.Return(err)
return _c
}
@@ -111,9 +125,9 @@ func (_c *Client_DeleteSession_Call) RunAndReturn(run func() error) *Client_Dele
return _c
}
// GetConfig provides a mock function with no fields
func (_m *Client) GetConfig() (*model.ConfigResponse, error) {
ret := _m.Called()
// GetConfig provides a mock function for the type Client
func (_mock *Client) GetConfig() (*model.ConfigResponse, error) {
ret := _mock.Called()
if len(ret) == 0 {
panic("no return value specified for GetConfig")
@@ -121,23 +135,21 @@ func (_m *Client) GetConfig() (*model.ConfigResponse, error) {
var r0 *model.ConfigResponse
var r1 error
if rf, ok := ret.Get(0).(func() (*model.ConfigResponse, error)); ok {
return rf()
if returnFunc, ok := ret.Get(0).(func() (*model.ConfigResponse, error)); ok {
return returnFunc()
}
if rf, ok := ret.Get(0).(func() *model.ConfigResponse); ok {
r0 = rf()
if returnFunc, ok := ret.Get(0).(func() *model.ConfigResponse); ok {
r0 = returnFunc()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.ConfigResponse)
}
}
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
if returnFunc, ok := ret.Get(1).(func() error); ok {
r1 = returnFunc()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
@@ -168,9 +180,9 @@ func (_c *Client_GetConfig_Call) RunAndReturn(run func() (*model.ConfigResponse,
return _c
}
// GetTeleporter provides a mock function with no fields
func (_m *Client) GetTeleporter() ([]byte, error) {
ret := _m.Called()
// GetTeleporter provides a mock function for the type Client
func (_mock *Client) GetTeleporter() ([]byte, error) {
ret := _mock.Called()
if len(ret) == 0 {
panic("no return value specified for GetTeleporter")
@@ -178,23 +190,21 @@ func (_m *Client) GetTeleporter() ([]byte, error) {
var r0 []byte
var r1 error
if rf, ok := ret.Get(0).(func() ([]byte, error)); ok {
return rf()
if returnFunc, ok := ret.Get(0).(func() ([]byte, error)); ok {
return returnFunc()
}
if rf, ok := ret.Get(0).(func() []byte); ok {
r0 = rf()
if returnFunc, ok := ret.Get(0).(func() []byte); ok {
r0 = returnFunc()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]byte)
}
}
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
if returnFunc, ok := ret.Get(1).(func() error); ok {
r1 = returnFunc()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
@@ -215,8 +225,8 @@ func (_c *Client_GetTeleporter_Call) Run(run func()) *Client_GetTeleporter_Call
return _c
}
func (_c *Client_GetTeleporter_Call) Return(_a0 []byte, _a1 error) *Client_GetTeleporter_Call {
_c.Call.Return(_a0, _a1)
func (_c *Client_GetTeleporter_Call) Return(bytes []byte, err error) *Client_GetTeleporter_Call {
_c.Call.Return(bytes, err)
return _c
}
@@ -225,9 +235,9 @@ func (_c *Client_GetTeleporter_Call) RunAndReturn(run func() ([]byte, error)) *C
return _c
}
// GetVersion provides a mock function with no fields
func (_m *Client) GetVersion() (*model.VersionResponse, error) {
ret := _m.Called()
// GetVersion provides a mock function for the type Client
func (_mock *Client) GetVersion() (*model.VersionResponse, error) {
ret := _mock.Called()
if len(ret) == 0 {
panic("no return value specified for GetVersion")
@@ -235,23 +245,21 @@ func (_m *Client) GetVersion() (*model.VersionResponse, error) {
var r0 *model.VersionResponse
var r1 error
if rf, ok := ret.Get(0).(func() (*model.VersionResponse, error)); ok {
return rf()
if returnFunc, ok := ret.Get(0).(func() (*model.VersionResponse, error)); ok {
return returnFunc()
}
if rf, ok := ret.Get(0).(func() *model.VersionResponse); ok {
r0 = rf()
if returnFunc, ok := ret.Get(0).(func() *model.VersionResponse); ok {
r0 = returnFunc()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.VersionResponse)
}
}
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
if returnFunc, ok := ret.Get(1).(func() error); ok {
r1 = returnFunc()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
@@ -272,8 +280,8 @@ func (_c *Client_GetVersion_Call) Run(run func()) *Client_GetVersion_Call {
return _c
}
func (_c *Client_GetVersion_Call) Return(_a0 *model.VersionResponse, _a1 error) *Client_GetVersion_Call {
_c.Call.Return(_a0, _a1)
func (_c *Client_GetVersion_Call) Return(versionResponse *model.VersionResponse, err error) *Client_GetVersion_Call {
_c.Call.Return(versionResponse, err)
return _c
}
@@ -282,21 +290,20 @@ func (_c *Client_GetVersion_Call) RunAndReturn(run func() (*model.VersionRespons
return _c
}
// PatchConfig provides a mock function with given fields: patchRequest
func (_m *Client) PatchConfig(patchRequest *model.PatchConfigRequest) error {
ret := _m.Called(patchRequest)
// PatchConfig provides a mock function for the type Client
func (_mock *Client) PatchConfig(patchRequest *model.PatchConfigRequest) error {
ret := _mock.Called(patchRequest)
if len(ret) == 0 {
panic("no return value specified for PatchConfig")
}
var r0 error
if rf, ok := ret.Get(0).(func(*model.PatchConfigRequest) error); ok {
r0 = rf(patchRequest)
if returnFunc, ok := ret.Get(0).(func(*model.PatchConfigRequest) error); ok {
r0 = returnFunc(patchRequest)
} else {
r0 = ret.Error(0)
}
return r0
}
@@ -306,7 +313,7 @@ type Client_PatchConfig_Call struct {
}
// PatchConfig is a helper method to define mock.On call
// - patchRequest *model.PatchConfigRequest
// - patchRequest
func (_e *Client_Expecter) PatchConfig(patchRequest interface{}) *Client_PatchConfig_Call {
return &Client_PatchConfig_Call{Call: _e.mock.On("PatchConfig", patchRequest)}
}
@@ -318,31 +325,30 @@ func (_c *Client_PatchConfig_Call) Run(run func(patchRequest *model.PatchConfigR
return _c
}
func (_c *Client_PatchConfig_Call) Return(_a0 error) *Client_PatchConfig_Call {
_c.Call.Return(_a0)
func (_c *Client_PatchConfig_Call) Return(err error) *Client_PatchConfig_Call {
_c.Call.Return(err)
return _c
}
func (_c *Client_PatchConfig_Call) RunAndReturn(run func(*model.PatchConfigRequest) error) *Client_PatchConfig_Call {
func (_c *Client_PatchConfig_Call) RunAndReturn(run func(patchRequest *model.PatchConfigRequest) error) *Client_PatchConfig_Call {
_c.Call.Return(run)
return _c
}
// PostAuth provides a mock function with no fields
func (_m *Client) PostAuth() error {
ret := _m.Called()
// PostAuth provides a mock function for the type Client
func (_mock *Client) PostAuth() error {
ret := _mock.Called()
if len(ret) == 0 {
panic("no return value specified for PostAuth")
}
var r0 error
if rf, ok := ret.Get(0).(func() error); ok {
r0 = rf()
if returnFunc, ok := ret.Get(0).(func() error); ok {
r0 = returnFunc()
} else {
r0 = ret.Error(0)
}
return r0
}
@@ -363,8 +369,8 @@ func (_c *Client_PostAuth_Call) Run(run func()) *Client_PostAuth_Call {
return _c
}
func (_c *Client_PostAuth_Call) Return(_a0 error) *Client_PostAuth_Call {
_c.Call.Return(_a0)
func (_c *Client_PostAuth_Call) Return(err error) *Client_PostAuth_Call {
_c.Call.Return(err)
return _c
}
@@ -373,21 +379,20 @@ func (_c *Client_PostAuth_Call) RunAndReturn(run func() error) *Client_PostAuth_
return _c
}
// PostRunGravity provides a mock function with no fields
func (_m *Client) PostRunGravity() error {
ret := _m.Called()
// PostRunGravity provides a mock function for the type Client
func (_mock *Client) PostRunGravity() error {
ret := _mock.Called()
if len(ret) == 0 {
panic("no return value specified for PostRunGravity")
}
var r0 error
if rf, ok := ret.Get(0).(func() error); ok {
r0 = rf()
if returnFunc, ok := ret.Get(0).(func() error); ok {
r0 = returnFunc()
} else {
r0 = ret.Error(0)
}
return r0
}
@@ -408,8 +413,8 @@ func (_c *Client_PostRunGravity_Call) Run(run func()) *Client_PostRunGravity_Cal
return _c
}
func (_c *Client_PostRunGravity_Call) Return(_a0 error) *Client_PostRunGravity_Call {
_c.Call.Return(_a0)
func (_c *Client_PostRunGravity_Call) Return(err error) *Client_PostRunGravity_Call {
_c.Call.Return(err)
return _c
}
@@ -418,21 +423,20 @@ func (_c *Client_PostRunGravity_Call) RunAndReturn(run func() error) *Client_Pos
return _c
}
// PostTeleporter provides a mock function with given fields: payload, teleporterRequest
func (_m *Client) PostTeleporter(payload []byte, teleporterRequest *model.PostTeleporterRequest) error {
ret := _m.Called(payload, teleporterRequest)
// PostTeleporter provides a mock function for the type Client
func (_mock *Client) PostTeleporter(payload []byte, teleporterRequest *model.PostTeleporterRequest) error {
ret := _mock.Called(payload, teleporterRequest)
if len(ret) == 0 {
panic("no return value specified for PostTeleporter")
}
var r0 error
if rf, ok := ret.Get(0).(func([]byte, *model.PostTeleporterRequest) error); ok {
r0 = rf(payload, teleporterRequest)
if returnFunc, ok := ret.Get(0).(func([]byte, *model.PostTeleporterRequest) error); ok {
r0 = returnFunc(payload, teleporterRequest)
} else {
r0 = ret.Error(0)
}
return r0
}
@@ -442,8 +446,8 @@ type Client_PostTeleporter_Call struct {
}
// PostTeleporter is a helper method to define mock.On call
// - payload []byte
// - teleporterRequest *model.PostTeleporterRequest
// - payload
// - teleporterRequest
func (_e *Client_Expecter) PostTeleporter(payload interface{}, teleporterRequest interface{}) *Client_PostTeleporter_Call {
return &Client_PostTeleporter_Call{Call: _e.mock.On("PostTeleporter", payload, teleporterRequest)}
}
@@ -455,31 +459,30 @@ func (_c *Client_PostTeleporter_Call) Run(run func(payload []byte, teleporterReq
return _c
}
func (_c *Client_PostTeleporter_Call) Return(_a0 error) *Client_PostTeleporter_Call {
_c.Call.Return(_a0)
func (_c *Client_PostTeleporter_Call) Return(err error) *Client_PostTeleporter_Call {
_c.Call.Return(err)
return _c
}
func (_c *Client_PostTeleporter_Call) RunAndReturn(run func([]byte, *model.PostTeleporterRequest) error) *Client_PostTeleporter_Call {
func (_c *Client_PostTeleporter_Call) RunAndReturn(run func(payload []byte, teleporterRequest *model.PostTeleporterRequest) error) *Client_PostTeleporter_Call {
_c.Call.Return(run)
return _c
}
// String provides a mock function with no fields
func (_m *Client) String() string {
ret := _m.Called()
// String provides a mock function for the type Client
func (_mock *Client) String() string {
ret := _mock.Called()
if len(ret) == 0 {
panic("no return value specified for String")
}
var r0 string
if rf, ok := ret.Get(0).(func() string); ok {
r0 = rf()
if returnFunc, ok := ret.Get(0).(func() string); ok {
r0 = returnFunc()
} else {
r0 = ret.Get(0).(string)
}
return r0
}
@@ -500,8 +503,8 @@ func (_c *Client_String_Call) Run(run func()) *Client_String_Call {
return _c
}
func (_c *Client_String_Call) Return(_a0 string) *Client_String_Call {
_c.Call.Return(_a0)
func (_c *Client_String_Call) Return(s string) *Client_String_Call {
_c.Call.Return(s)
return _c
}
@@ -509,17 +512,3 @@ func (_c *Client_String_Call) RunAndReturn(run func() string) *Client_String_Cal
_c.Call.Return(run)
return _c
}
// NewClient creates a new instance of Client. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewClient(t interface {
mock.TestingT
Cleanup(func())
}) *Client {
mock := &Client{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
+103
View File
@@ -0,0 +1,103 @@
// Code generated by mockery; DO NOT EDIT.
// github.com/vektra/mockery
// template: testify
package sync
import (
mock "github.com/stretchr/testify/mock"
)
// NewCallback creates a new instance of Callback. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewCallback(t interface {
mock.TestingT
Cleanup(func())
}) *Callback {
mock := &Callback{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
// Callback is an autogenerated mock type for the Callback type
type Callback struct {
mock.Mock
}
type Callback_Expecter struct {
mock *mock.Mock
}
func (_m *Callback) EXPECT() *Callback_Expecter {
return &Callback_Expecter{mock: &_m.Mock}
}
// OnFailure provides a mock function for the type Callback
func (_mock *Callback) OnFailure(err error) {
_mock.Called(err)
return
}
// Callback_OnFailure_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'OnFailure'
type Callback_OnFailure_Call struct {
*mock.Call
}
// OnFailure is a helper method to define mock.On call
// - err
func (_e *Callback_Expecter) OnFailure(err interface{}) *Callback_OnFailure_Call {
return &Callback_OnFailure_Call{Call: _e.mock.On("OnFailure", err)}
}
func (_c *Callback_OnFailure_Call) Run(run func(err error)) *Callback_OnFailure_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(error))
})
return _c
}
func (_c *Callback_OnFailure_Call) Return() *Callback_OnFailure_Call {
_c.Call.Return()
return _c
}
func (_c *Callback_OnFailure_Call) RunAndReturn(run func(err error)) *Callback_OnFailure_Call {
_c.Run(run)
return _c
}
// OnSuccess provides a mock function for the type Callback
func (_mock *Callback) OnSuccess() {
_mock.Called()
return
}
// Callback_OnSuccess_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'OnSuccess'
type Callback_OnSuccess_Call struct {
*mock.Call
}
// OnSuccess is a helper method to define mock.On call
func (_e *Callback_Expecter) OnSuccess() *Callback_OnSuccess_Call {
return &Callback_OnSuccess_Call{Call: _e.mock.On("OnSuccess")}
}
func (_c *Callback_OnSuccess_Call) Run(run func()) *Callback_OnSuccess_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *Callback_OnSuccess_Call) Return() *Callback_OnSuccess_Call {
_c.Call.Return()
return _c
}
func (_c *Callback_OnSuccess_Call) RunAndReturn(run func()) *Callback_OnSuccess_Call {
_c.Run(run)
return _c
}
+107 -107
View File
@@ -1,117 +1,14 @@
// Code generated by mockery v2.53.3. DO NOT EDIT.
// Code generated by mockery; DO NOT EDIT.
// github.com/vektra/mockery
// template: testify
package sync
import (
config "github.com/lovelaze/nebula-sync/internal/config"
"github.com/lovelaze/nebula-sync/internal/config"
mock "github.com/stretchr/testify/mock"
)
// Target is an autogenerated mock type for the Target type
type Target struct {
mock.Mock
}
type Target_Expecter struct {
mock *mock.Mock
}
func (_m *Target) EXPECT() *Target_Expecter {
return &Target_Expecter{mock: &_m.Mock}
}
// FullSync provides a mock function with given fields: _a0
func (_m *Target) FullSync(_a0 *config.Sync) error {
ret := _m.Called(_a0)
if len(ret) == 0 {
panic("no return value specified for FullSync")
}
var r0 error
if rf, ok := ret.Get(0).(func(*config.Sync) error); ok {
r0 = rf(_a0)
} else {
r0 = ret.Error(0)
}
return r0
}
// Target_FullSync_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FullSync'
type Target_FullSync_Call struct {
*mock.Call
}
// FullSync is a helper method to define mock.On call
// - _a0 *config.Sync
func (_e *Target_Expecter) FullSync(_a0 interface{}) *Target_FullSync_Call {
return &Target_FullSync_Call{Call: _e.mock.On("FullSync", _a0)}
}
func (_c *Target_FullSync_Call) Run(run func(_a0 *config.Sync)) *Target_FullSync_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(*config.Sync))
})
return _c
}
func (_c *Target_FullSync_Call) Return(_a0 error) *Target_FullSync_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *Target_FullSync_Call) RunAndReturn(run func(*config.Sync) error) *Target_FullSync_Call {
_c.Call.Return(run)
return _c
}
// SelectiveSync provides a mock function with given fields: _a0
func (_m *Target) SelectiveSync(_a0 *config.Sync) error {
ret := _m.Called(_a0)
if len(ret) == 0 {
panic("no return value specified for SelectiveSync")
}
var r0 error
if rf, ok := ret.Get(0).(func(*config.Sync) error); ok {
r0 = rf(_a0)
} else {
r0 = ret.Error(0)
}
return r0
}
// Target_SelectiveSync_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SelectiveSync'
type Target_SelectiveSync_Call struct {
*mock.Call
}
// SelectiveSync is a helper method to define mock.On call
// - _a0 *config.Sync
func (_e *Target_Expecter) SelectiveSync(_a0 interface{}) *Target_SelectiveSync_Call {
return &Target_SelectiveSync_Call{Call: _e.mock.On("SelectiveSync", _a0)}
}
func (_c *Target_SelectiveSync_Call) Run(run func(_a0 *config.Sync)) *Target_SelectiveSync_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(*config.Sync))
})
return _c
}
func (_c *Target_SelectiveSync_Call) Return(_a0 error) *Target_SelectiveSync_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *Target_SelectiveSync_Call) RunAndReturn(run func(*config.Sync) error) *Target_SelectiveSync_Call {
_c.Call.Return(run)
return _c
}
// NewTarget creates a new instance of Target. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewTarget(t interface {
@@ -125,3 +22,106 @@ func NewTarget(t interface {
return mock
}
// Target is an autogenerated mock type for the Target type
type Target struct {
mock.Mock
}
type Target_Expecter struct {
mock *mock.Mock
}
func (_m *Target) EXPECT() *Target_Expecter {
return &Target_Expecter{mock: &_m.Mock}
}
// FullSync provides a mock function for the type Target
func (_mock *Target) FullSync(sync *config.Sync) error {
ret := _mock.Called(sync)
if len(ret) == 0 {
panic("no return value specified for FullSync")
}
var r0 error
if returnFunc, ok := ret.Get(0).(func(*config.Sync) error); ok {
r0 = returnFunc(sync)
} else {
r0 = ret.Error(0)
}
return r0
}
// Target_FullSync_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FullSync'
type Target_FullSync_Call struct {
*mock.Call
}
// FullSync is a helper method to define mock.On call
// - sync
func (_e *Target_Expecter) FullSync(sync interface{}) *Target_FullSync_Call {
return &Target_FullSync_Call{Call: _e.mock.On("FullSync", sync)}
}
func (_c *Target_FullSync_Call) Run(run func(sync *config.Sync)) *Target_FullSync_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(*config.Sync))
})
return _c
}
func (_c *Target_FullSync_Call) Return(err error) *Target_FullSync_Call {
_c.Call.Return(err)
return _c
}
func (_c *Target_FullSync_Call) RunAndReturn(run func(sync *config.Sync) error) *Target_FullSync_Call {
_c.Call.Return(run)
return _c
}
// SelectiveSync provides a mock function for the type Target
func (_mock *Target) SelectiveSync(sync *config.Sync) error {
ret := _mock.Called(sync)
if len(ret) == 0 {
panic("no return value specified for SelectiveSync")
}
var r0 error
if returnFunc, ok := ret.Get(0).(func(*config.Sync) error); ok {
r0 = returnFunc(sync)
} else {
r0 = ret.Error(0)
}
return r0
}
// Target_SelectiveSync_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SelectiveSync'
type Target_SelectiveSync_Call struct {
*mock.Call
}
// SelectiveSync is a helper method to define mock.On call
// - sync
func (_e *Target_Expecter) SelectiveSync(sync interface{}) *Target_SelectiveSync_Call {
return &Target_SelectiveSync_Call{Call: _e.mock.On("SelectiveSync", sync)}
}
func (_c *Target_SelectiveSync_Call) Run(run func(sync *config.Sync)) *Target_SelectiveSync_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(*config.Sync))
})
return _c
}
func (_c *Target_SelectiveSync_Call) Return(err error) *Target_SelectiveSync_Call {
_c.Call.Return(err)
return _c
}
func (_c *Target_SelectiveSync_Call) RunAndReturn(run func(sync *config.Sync) error) *Target_SelectiveSync_Call {
_c.Call.Return(run)
return _c
}
-122
View File
@@ -1,122 +0,0 @@
// Code generated by mockery v2.53.3. DO NOT EDIT.
package webhook
import mock "github.com/stretchr/testify/mock"
// WebhookClient is an autogenerated mock type for the WebhookClient type
type WebhookClient struct {
mock.Mock
}
type WebhookClient_Expecter struct {
mock *mock.Mock
}
func (_m *WebhookClient) EXPECT() *WebhookClient_Expecter {
return &WebhookClient_Expecter{mock: &_m.Mock}
}
// Failure provides a mock function with no fields
func (_m *WebhookClient) Failure() error {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for Failure")
}
var r0 error
if rf, ok := ret.Get(0).(func() error); ok {
r0 = rf()
} else {
r0 = ret.Error(0)
}
return r0
}
// WebhookClient_Failure_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Failure'
type WebhookClient_Failure_Call struct {
*mock.Call
}
// Failure is a helper method to define mock.On call
func (_e *WebhookClient_Expecter) Failure() *WebhookClient_Failure_Call {
return &WebhookClient_Failure_Call{Call: _e.mock.On("Failure")}
}
func (_c *WebhookClient_Failure_Call) Run(run func()) *WebhookClient_Failure_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *WebhookClient_Failure_Call) Return(_a0 error) *WebhookClient_Failure_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *WebhookClient_Failure_Call) RunAndReturn(run func() error) *WebhookClient_Failure_Call {
_c.Call.Return(run)
return _c
}
// Success provides a mock function with no fields
func (_m *WebhookClient) Success() error {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for Success")
}
var r0 error
if rf, ok := ret.Get(0).(func() error); ok {
r0 = rf()
} else {
r0 = ret.Error(0)
}
return r0
}
// WebhookClient_Success_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Success'
type WebhookClient_Success_Call struct {
*mock.Call
}
// Success is a helper method to define mock.On call
func (_e *WebhookClient_Expecter) Success() *WebhookClient_Success_Call {
return &WebhookClient_Success_Call{Call: _e.mock.On("Success")}
}
func (_c *WebhookClient_Success_Call) Run(run func()) *WebhookClient_Success_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *WebhookClient_Success_Call) Return(_a0 error) *WebhookClient_Success_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *WebhookClient_Success_Call) RunAndReturn(run func() error) *WebhookClient_Success_Call {
_c.Call.Return(run)
return _c
}
// NewWebhookClient creates a new instance of WebhookClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewWebhookClient(t interface {
mock.TestingT
Cleanup(func())
}) *WebhookClient {
mock := &WebhookClient{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
+22 -16
View File
@@ -2,22 +2,29 @@ package service
import (
"fmt"
"github.com/lovelaze/nebula-sync/internal/sync/retry"
"github.com/lovelaze/nebula-sync/internal/webhook"
"github.com/lovelaze/nebula-sync/internal/config"
"github.com/lovelaze/nebula-sync/internal/pihole"
"github.com/lovelaze/nebula-sync/internal/sync"
"github.com/lovelaze/nebula-sync/internal/webhook"
"github.com/lovelaze/nebula-sync/version"
"github.com/robfig/cron/v3"
"github.com/rs/zerolog/log"
)
type Service struct {
target sync.Target
conf config.Config
webhook webhook.WebhookClient
target sync.Target
conf config.Config
callbacks []sync.Callback
}
func NewService(target sync.Target, conf config.Config, callbacks ...sync.Callback) *Service {
return &Service{
target: target,
conf: conf,
callbacks: callbacks,
}
}
func Init() (*Service, error) {
@@ -35,11 +42,11 @@ func Init() (*Service, error) {
replicas = append(replicas, pihole.NewClient(replica, httpClient))
}
return &Service{
target: sync.NewTarget(primary, replicas),
conf: conf,
webhook: webhook.NewWebhookClient(conf.Sync.WebhookSettings),
}, nil
webhookClient := webhook.NewClient(conf.Sync.WebhookSettings)
target := sync.NewTarget(primary, replicas)
service := NewService(target, conf, webhookClient)
return service, nil
}
func (service *Service) Run() error {
@@ -69,15 +76,14 @@ func (service *Service) doSync(t sync.Target) (err error) {
}
if err != nil {
if err := service.webhook.Failure(); err != nil {
log.Error().Err(err).Msg("Failed to send failure webhook")
for _, callback := range service.callbacks {
callback.OnFailure(err)
}
} else {
log.Info().Msg("Sync completed")
if err := service.webhook.Success(); err != nil {
log.Error().Err(err).Msg("Failed to send success webhook")
for _, callback := range service.callbacks {
callback.OnSuccess()
}
log.Info().Msg("Sync completed")
}
return err
+20 -41
View File
@@ -6,7 +6,6 @@ import (
"github.com/lovelaze/nebula-sync/internal/config"
syncmock "github.com/lovelaze/nebula-sync/internal/mocks/sync"
webhookmock "github.com/lovelaze/nebula-sync/internal/mocks/webhook"
"github.com/lovelaze/nebula-sync/internal/pihole/model"
"github.com/stretchr/testify/require"
)
@@ -22,15 +21,11 @@ func TestRun_full(t *testing.T) {
}
target := syncmock.NewTarget(t)
webhook := webhookmock.NewWebhookClient(t)
callback := syncmock.NewCallback(t)
target.On("FullSync", conf.Sync).Return(nil)
webhook.On("Success").Return(nil)
callback.On("OnSuccess").Return(nil)
service := Service{
target: target,
conf: conf,
webhook: webhook,
}
service := NewService(target, conf, callback)
err := service.Run()
require.NoError(t, err)
@@ -49,15 +44,11 @@ func TestRun_selective(t *testing.T) {
}
target := syncmock.NewTarget(t)
webhook := webhookmock.NewWebhookClient(t)
callback := syncmock.NewCallback(t)
target.On("SelectiveSync", conf.Sync).Return(nil)
webhook.On("Success").Return(nil)
callback.On("OnSuccess").Return(nil)
service := Service{
target: target,
conf: conf,
webhook: webhook,
}
service := NewService(target, conf, callback)
err := service.Run()
require.NoError(t, err)
@@ -76,23 +67,19 @@ func TestRun_webhook_success(t *testing.T) {
}
target := syncmock.NewTarget(t)
webhook := webhookmock.NewWebhookClient(t)
callback := syncmock.NewCallback(t)
target.On("SelectiveSync", conf.Sync).Return(nil)
webhook.On("Success").Return(nil)
callback.On("OnSuccess").Return(nil)
service := Service{
target: target,
conf: conf,
webhook: webhook,
}
service := NewService(target, conf, callback)
err := service.Run()
require.NoError(t, err)
target.AssertCalled(t, "SelectiveSync", conf.Sync)
webhook.AssertCalled(t, "Success")
webhook.AssertNotCalled(t, "Failure")
callback.AssertCalled(t, "OnSuccess")
callback.AssertNotCalled(t, "OnFailure")
}
func TestRun_webhook_failure(t *testing.T) {
@@ -107,23 +94,19 @@ func TestRun_webhook_failure(t *testing.T) {
syncErr := errors.New("sync failed")
target := syncmock.NewTarget(t)
webhook := webhookmock.NewWebhookClient(t)
callback := syncmock.NewCallback(t)
target.On("SelectiveSync", conf.Sync).Return(syncErr)
webhook.On("Failure").Return(nil)
callback.On("OnFailure", syncErr).Return(nil)
service := Service{
target: target,
conf: conf,
webhook: webhook,
}
service := NewService(target, conf, callback)
err := service.Run()
require.ErrorIs(t, err, syncErr)
target.AssertCalled(t, "SelectiveSync", conf.Sync)
webhook.AssertCalled(t, "Failure")
webhook.AssertNotCalled(t, "Success")
callback.AssertCalled(t, "OnFailure", syncErr)
callback.AssertNotCalled(t, "OnSuccess")
}
func TestRun_webhook_error_does_not_affect_result(t *testing.T) {
@@ -137,20 +120,16 @@ func TestRun_webhook_error_does_not_affect_result(t *testing.T) {
}
target := syncmock.NewTarget(t)
webhook := webhookmock.NewWebhookClient(t)
callback := syncmock.NewCallback(t)
target.On("FullSync", conf.Sync).Return(nil)
webhook.On("Success").Return(errors.New("webhook failed"))
callback.On("OnSuccess").Return(nil)
service := Service{
target: target,
conf: conf,
webhook: webhook,
}
service := NewService(target, conf, callback)
err := service.Run()
require.NoError(t, err)
target.AssertCalled(t, "FullSync", conf.Sync)
webhook.AssertCalled(t, "Success")
callback.AssertCalled(t, "OnSuccess")
}
+6
View File
@@ -0,0 +1,6 @@
package sync
type Callback interface {
OnSuccess()
OnFailure(error)
}
+26 -19
View File
@@ -12,22 +12,17 @@ import (
"github.com/rs/zerolog/log"
)
type WebhookClient interface {
Success() error
Failure() error
type Client struct {
success config.WebhookRequest
failure config.WebhookRequest
httpClient *http.Client
}
type webhookClient struct {
successConfig config.WebhookEventSetting
failureConfig config.WebhookEventSetting
client *http.Client
}
func NewWebhookClient(c *config.WebhookSettings) WebhookClient {
return &webhookClient{
successConfig: c.Success,
failureConfig: c.Failure,
client: &http.Client{
func NewClient(c *config.WebhookSettings) *Client {
return &Client{
success: c.Success,
failure: c.Failure,
httpClient: &http.Client{
Timeout: 10 * time.Second,
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: c.Client.SkipTLSVerification},
@@ -35,15 +30,27 @@ func NewWebhookClient(c *config.WebhookSettings) WebhookClient {
}
}
func (webhookClient *webhookClient) Success() error {
return invokeWebhook(webhookClient.client, webhookClient.successConfig)
func (c *Client) OnSuccess() {
if err := c.triggerSuccess(); err != nil {
log.Warn().Err(err).Msg("Webhook trigger failed")
}
}
func (webhookClient *webhookClient) Failure() error {
return invokeWebhook(webhookClient.client, webhookClient.failureConfig)
func (c *Client) OnFailure(err error) {
if err := c.triggerFailure(); err != nil {
log.Warn().Err(err).Msg("Webhook trigger failed")
}
}
func invokeWebhook(client *http.Client, settings config.WebhookEventSetting) error {
func (c *Client) triggerSuccess() error {
return invoke(c.httpClient, c.success)
}
func (c *Client) triggerFailure() error {
return invoke(c.httpClient, c.failure)
}
func invoke(client *http.Client, settings config.WebhookRequest) error {
if settings.Url == "" {
return nil
}
+20 -19
View File
@@ -1,6 +1,7 @@
package webhook
import (
"io"
"net/http"
"net/http/httptest"
"testing"
@@ -11,23 +12,23 @@ import (
"github.com/stretchr/testify/require"
)
func TestWebhookClient(t *testing.T) {
func TestWebhook(t *testing.T) {
t.Run("success webhook uses success configuration", func(t *testing.T) {
// Setup test server to verify request
var receivedHeaders http.Header
var receivedBody string
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
receivedHeaders = r.Header
buf := make([]byte, 1024)
n, _ := r.Body.Read(buf)
receivedBody = string(buf[:n])
buf, err := io.ReadAll(r.Body)
require.NoError(t, err)
receivedBody = string(buf)
w.WriteHeader(http.StatusOK)
}))
defer ts.Close()
// Create webhook settings
settings := &config.WebhookSettings{
Success: config.WebhookEventSetting{
Success: config.WebhookRequest{
Url: ts.URL,
Method: "POST",
Body: "success-body",
@@ -36,8 +37,8 @@ func TestWebhookClient(t *testing.T) {
Client: config.WebhookClient{},
}
client := NewWebhookClient(settings)
err := client.Success()
client := NewClient(settings)
err := client.triggerSuccess()
require.NoError(t, err)
// Verify request
@@ -51,15 +52,15 @@ func TestWebhookClient(t *testing.T) {
var receivedBody string
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
receivedHeaders = r.Header
buf := make([]byte, 1024)
n, _ := r.Body.Read(buf)
receivedBody = string(buf[:n])
buf, err := io.ReadAll(r.Body)
require.NoError(t, err)
receivedBody = string(buf)
w.WriteHeader(http.StatusOK)
}))
defer ts.Close()
settings := &config.WebhookSettings{
Failure: config.WebhookEventSetting{
Failure: config.WebhookRequest{
Url: ts.URL,
Method: "PUT",
Body: "failure-body",
@@ -68,8 +69,8 @@ func TestWebhookClient(t *testing.T) {
Client: config.WebhookClient{},
}
client := NewWebhookClient(settings)
err := client.Failure()
client := NewClient(settings)
err := client.triggerFailure()
require.NoError(t, err)
assert.Equal(t, "failure-body", receivedBody)
@@ -78,13 +79,13 @@ func TestWebhookClient(t *testing.T) {
t.Run("empty url skips webhook", func(t *testing.T) {
settings := &config.WebhookSettings{
Success: config.WebhookEventSetting{
Success: config.WebhookRequest{
Url: "",
},
}
client := NewWebhookClient(settings)
err := client.Success()
client := NewClient(settings)
err := client.triggerSuccess()
require.NoError(t, err)
})
@@ -95,13 +96,13 @@ func TestWebhookClient(t *testing.T) {
defer ts.Close()
settings := &config.WebhookSettings{
Success: config.WebhookEventSetting{
Success: config.WebhookRequest{
Url: ts.URL,
},
}
client := NewWebhookClient(settings)
err := client.Success()
client := NewClient(settings)
err := client.triggerSuccess()
require.Error(t, err)
assert.Contains(t, err.Error(), "webhook returned status 400")
})