Adding upstream version 0.8.9.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
3b2c48b5e4
commit
c0c4addb85
285 changed files with 25880 additions and 0 deletions
422
pkg/services/opsgenie/opsgenie_test.go
Normal file
422
pkg/services/opsgenie/opsgenie_test.go
Normal file
|
@ -0,0 +1,422 @@
|
|||
package opsgenie
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/onsi/ginkgo/v2"
|
||||
"github.com/onsi/gomega"
|
||||
|
||||
"github.com/nicholas-fedor/shoutrrr/pkg/types"
|
||||
)
|
||||
|
||||
const (
|
||||
mockAPIKey = "eb243592-faa2-4ba2-a551q-1afdf565c889"
|
||||
mockHost = "api.opsgenie.com"
|
||||
)
|
||||
|
||||
func TestOpsGenie(t *testing.T) {
|
||||
gomega.RegisterFailHandler(ginkgo.Fail)
|
||||
ginkgo.RunSpecs(t, "Shoutrrr OpsGenie Suite")
|
||||
}
|
||||
|
||||
var _ = ginkgo.Describe("the OpsGenie service", func() {
|
||||
var (
|
||||
// a simulated http server to mock out OpsGenie itself
|
||||
mockServer *httptest.Server
|
||||
// the host of our mock server
|
||||
mockHost string
|
||||
// function to check if the http request received by the mock server is as expected
|
||||
checkRequest func(body string, header http.Header)
|
||||
// the shoutrrr OpsGenie service
|
||||
service *Service
|
||||
// just a mock logger
|
||||
mockLogger *log.Logger
|
||||
)
|
||||
|
||||
ginkgo.BeforeEach(func() {
|
||||
// Initialize a mock http server
|
||||
httpHandler := func(_ http.ResponseWriter, r *http.Request) {
|
||||
body, err := io.ReadAll(r.Body)
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
defer r.Body.Close()
|
||||
|
||||
checkRequest(string(body), r.Header)
|
||||
}
|
||||
mockServer = httptest.NewTLSServer(http.HandlerFunc(httpHandler))
|
||||
|
||||
// Our mock server doesn't have a valid cert
|
||||
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
|
||||
// Determine the host of our mock http server
|
||||
mockServerURL, err := url.Parse(mockServer.URL)
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
mockHost = mockServerURL.Host
|
||||
|
||||
// Initialize a mock logger
|
||||
var buf bytes.Buffer
|
||||
mockLogger = log.New(&buf, "", 0)
|
||||
})
|
||||
|
||||
ginkgo.AfterEach(func() {
|
||||
mockServer.Close()
|
||||
})
|
||||
|
||||
ginkgo.Context("without query parameters", func() {
|
||||
ginkgo.BeforeEach(func() {
|
||||
// Initialize service
|
||||
serviceURL, err := url.Parse(fmt.Sprintf("opsgenie://%s/%s", mockHost, mockAPIKey))
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
|
||||
service = &Service{}
|
||||
err = service.Initialize(serviceURL, mockLogger)
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
})
|
||||
|
||||
ginkgo.When("sending a simple alert", func() {
|
||||
ginkgo.It("should send a request to our mock OpsGenie server", func() {
|
||||
checkRequest = func(body string, header http.Header) {
|
||||
gomega.Expect(header["Authorization"][0]).
|
||||
To(gomega.Equal("GenieKey " + mockAPIKey))
|
||||
gomega.Expect(header["Content-Type"][0]).To(gomega.Equal("application/json"))
|
||||
gomega.Expect(body).To(gomega.Equal(`{"message":"hello world"}`))
|
||||
}
|
||||
|
||||
err := service.Send("hello world", &types.Params{})
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.When("sending an alert with runtime parameters", func() {
|
||||
ginkgo.It(
|
||||
"should send a request to our mock OpsGenie server with all fields populated from runtime parameters",
|
||||
func() {
|
||||
checkRequest = func(body string, header http.Header) {
|
||||
gomega.Expect(header["Authorization"][0]).
|
||||
To(gomega.Equal("GenieKey " + mockAPIKey))
|
||||
gomega.Expect(header["Content-Type"][0]).
|
||||
To(gomega.Equal("application/json"))
|
||||
gomega.Expect(body).To(gomega.Equal(`{"` +
|
||||
`message":"An example alert message",` +
|
||||
`"alias":"Life is too short for no alias",` +
|
||||
`"description":"Every alert needs a description",` +
|
||||
`"responders":[{"type":"team","id":"4513b7ea-3b91-438f-b7e4-e3e54af9147c"},{"type":"team","name":"NOC"},{"type":"user","username":"Donald"},{"type":"user","id":"696f0759-3b0f-4a15-b8c8-19d3dfca33f2"}],` +
|
||||
`"visibleTo":[{"type":"team","name":"rocket"}],` +
|
||||
`"actions":["action1","action2"],` +
|
||||
`"tags":["tag1","tag2"],` +
|
||||
`"details":{"key1":"value1","key2":"value2"},` +
|
||||
`"entity":"An example entity",` +
|
||||
`"source":"The source",` +
|
||||
`"priority":"P1",` +
|
||||
`"user":"Dracula",` +
|
||||
`"note":"Here is a note"` +
|
||||
`}`))
|
||||
}
|
||||
|
||||
err := service.Send("An example alert message", &types.Params{
|
||||
"alias": "Life is too short for no alias",
|
||||
"description": "Every alert needs a description",
|
||||
"responders": "team:4513b7ea-3b91-438f-b7e4-e3e54af9147c,team:NOC,user:Donald,user:696f0759-3b0f-4a15-b8c8-19d3dfca33f2",
|
||||
"visibleTo": "team:rocket",
|
||||
"actions": "action1,action2",
|
||||
"tags": "tag1,tag2",
|
||||
"details": "key1:value1,key2:value2",
|
||||
"entity": "An example entity",
|
||||
"source": "The source",
|
||||
"priority": "P1",
|
||||
"user": "Dracula",
|
||||
"note": "Here is a note",
|
||||
})
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
},
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.Context("with query parameters", func() {
|
||||
ginkgo.BeforeEach(func() {
|
||||
// Initialize service
|
||||
serviceURL, err := url.Parse(
|
||||
fmt.Sprintf(
|
||||
`opsgenie://%s/%s?alias=query-alias&description=query-description&responders=team:query_team&visibleTo=user:query_user&actions=queryAction1,queryAction2&tags=queryTag1,queryTag2&details=queryKey1:queryValue1,queryKey2:queryValue2&entity=query-entity&source=query-source&priority=P2&user=query-user¬e=query-note`,
|
||||
mockHost,
|
||||
mockAPIKey,
|
||||
),
|
||||
)
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
|
||||
service = &Service{}
|
||||
err = service.Initialize(serviceURL, mockLogger)
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
})
|
||||
|
||||
ginkgo.When("sending a simple alert", func() {
|
||||
ginkgo.It(
|
||||
"should send a request to our mock OpsGenie server with all fields populated from query parameters",
|
||||
func() {
|
||||
checkRequest = func(body string, header http.Header) {
|
||||
gomega.Expect(header["Authorization"][0]).
|
||||
To(gomega.Equal("GenieKey " + mockAPIKey))
|
||||
gomega.Expect(header["Content-Type"][0]).
|
||||
To(gomega.Equal("application/json"))
|
||||
gomega.Expect(body).To(gomega.Equal(`{` +
|
||||
`"message":"An example alert message",` +
|
||||
`"alias":"query-alias",` +
|
||||
`"description":"query-description",` +
|
||||
`"responders":[{"type":"team","name":"query_team"}],` +
|
||||
`"visibleTo":[{"type":"user","username":"query_user"}],` +
|
||||
`"actions":["queryAction1","queryAction2"],` +
|
||||
`"tags":["queryTag1","queryTag2"],` +
|
||||
`"details":{"queryKey1":"queryValue1","queryKey2":"queryValue2"},` +
|
||||
`"entity":"query-entity",` +
|
||||
`"source":"query-source",` +
|
||||
`"priority":"P2",` +
|
||||
`"user":"query-user",` +
|
||||
`"note":"query-note"` +
|
||||
`}`))
|
||||
}
|
||||
|
||||
err := service.Send("An example alert message", &types.Params{})
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
ginkgo.When("sending two alerts", func() {
|
||||
ginkgo.It("should not mix-up the runtime parameters and the query parameters", func() {
|
||||
// Internally the opsgenie service copies runtime parameters into the config struct
|
||||
// before generating the alert payload. This test ensures that none of the parameters
|
||||
// from alert 1 remain in the config struct when sending alert 2
|
||||
// In short: This tests if we clone the config struct
|
||||
|
||||
checkRequest = func(body string, header http.Header) {
|
||||
gomega.Expect(header["Authorization"][0]).
|
||||
To(gomega.Equal("GenieKey " + mockAPIKey))
|
||||
gomega.Expect(header["Content-Type"][0]).To(gomega.Equal("application/json"))
|
||||
gomega.Expect(body).To(gomega.Equal(`{"` +
|
||||
`message":"1",` +
|
||||
`"alias":"1",` +
|
||||
`"description":"1",` +
|
||||
`"responders":[{"type":"team","name":"1"}],` +
|
||||
`"visibleTo":[{"type":"team","name":"1"}],` +
|
||||
`"actions":["action1","action2"],` +
|
||||
`"tags":["tag1","tag2"],` +
|
||||
`"details":{"key1":"value1","key2":"value2"},` +
|
||||
`"entity":"1",` +
|
||||
`"source":"1",` +
|
||||
`"priority":"P1",` +
|
||||
`"user":"1",` +
|
||||
`"note":"1"` +
|
||||
`}`))
|
||||
}
|
||||
|
||||
err := service.Send("1", &types.Params{
|
||||
"alias": "1",
|
||||
"description": "1",
|
||||
"responders": "team:1",
|
||||
"visibleTo": "team:1",
|
||||
"actions": "action1,action2",
|
||||
"tags": "tag1,tag2",
|
||||
"details": "key1:value1,key2:value2",
|
||||
"entity": "1",
|
||||
"source": "1",
|
||||
"priority": "P1",
|
||||
"user": "1",
|
||||
"note": "1",
|
||||
})
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
|
||||
checkRequest = func(body string, header http.Header) {
|
||||
gomega.Expect(header["Authorization"][0]).
|
||||
To(gomega.Equal("GenieKey " + mockAPIKey))
|
||||
gomega.Expect(header["Content-Type"][0]).To(gomega.Equal("application/json"))
|
||||
gomega.Expect(body).To(gomega.Equal(`{` +
|
||||
`"message":"2",` +
|
||||
`"alias":"query-alias",` +
|
||||
`"description":"query-description",` +
|
||||
`"responders":[{"type":"team","name":"query_team"}],` +
|
||||
`"visibleTo":[{"type":"user","username":"query_user"}],` +
|
||||
`"actions":["queryAction1","queryAction2"],` +
|
||||
`"tags":["queryTag1","queryTag2"],` +
|
||||
`"details":{"queryKey1":"queryValue1","queryKey2":"queryValue2"},` +
|
||||
`"entity":"query-entity",` +
|
||||
`"source":"query-source",` +
|
||||
`"priority":"P2",` +
|
||||
`"user":"query-user",` +
|
||||
`"note":"query-note"` +
|
||||
`}`))
|
||||
}
|
||||
|
||||
err = service.Send("2", nil)
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.It("should return the correct service ID", func() {
|
||||
service := &Service{}
|
||||
gomega.Expect(service.GetID()).To(gomega.Equal("opsgenie"))
|
||||
})
|
||||
})
|
||||
|
||||
var _ = ginkgo.Describe("the OpsGenie Config struct", func() {
|
||||
ginkgo.When("generating a config from a simple URL", func() {
|
||||
ginkgo.It("should populate the config with host and apikey", func() {
|
||||
url, err := url.Parse(fmt.Sprintf("opsgenie://%s/%s", mockHost, mockAPIKey))
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
|
||||
config := Config{}
|
||||
err = config.SetURL(url)
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
|
||||
gomega.Expect(config.APIKey).To(gomega.Equal(mockAPIKey))
|
||||
gomega.Expect(config.Host).To(gomega.Equal(mockHost))
|
||||
gomega.Expect(config.Port).To(gomega.Equal(uint16(443)))
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.When("generating a config from a url with port", func() {
|
||||
ginkgo.It("should populate the port field", func() {
|
||||
url, err := url.Parse(
|
||||
fmt.Sprintf("opsgenie://%s/%s", net.JoinHostPort(mockHost, "12345"), mockAPIKey),
|
||||
)
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
|
||||
config := Config{}
|
||||
err = config.SetURL(url)
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
|
||||
gomega.Expect(config.Port).To(gomega.Equal(uint16(12345)))
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.When("generating a config from a url with query parameters", func() {
|
||||
ginkgo.It("should populate the config fields with the query parameter values", func() {
|
||||
queryParams := `alias=Life+is+too+short+for+no+alias&description=Every+alert+needs+a+description&actions=An+action&tags=tag1,tag2&details=key:value,key2:value2&entity=An+example+entity&source=The+source&priority=P1&user=Dracula¬e=Here+is+a+note&responders=user:Test,team:NOC&visibleTo=user:A+User`
|
||||
url, err := url.Parse(
|
||||
fmt.Sprintf(
|
||||
"opsgenie://%s/%s?%s",
|
||||
net.JoinHostPort(mockHost, "12345"),
|
||||
mockAPIKey,
|
||||
queryParams,
|
||||
),
|
||||
)
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
|
||||
config := Config{}
|
||||
err = config.SetURL(url)
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
|
||||
gomega.Expect(config.Alias).To(gomega.Equal("Life is too short for no alias"))
|
||||
gomega.Expect(config.Description).To(gomega.Equal("Every alert needs a description"))
|
||||
gomega.Expect(config.Responders).To(gomega.Equal([]Entity{
|
||||
{Type: "user", Username: "Test"},
|
||||
{Type: "team", Name: "NOC"},
|
||||
}))
|
||||
gomega.Expect(config.VisibleTo).To(gomega.Equal([]Entity{
|
||||
{Type: "user", Username: "A User"},
|
||||
}))
|
||||
gomega.Expect(config.Actions).To(gomega.Equal([]string{"An action"}))
|
||||
gomega.Expect(config.Tags).To(gomega.Equal([]string{"tag1", "tag2"}))
|
||||
gomega.Expect(config.Details).
|
||||
To(gomega.Equal(map[string]string{"key": "value", "key2": "value2"}))
|
||||
gomega.Expect(config.Entity).To(gomega.Equal("An example entity"))
|
||||
gomega.Expect(config.Source).To(gomega.Equal("The source"))
|
||||
gomega.Expect(config.Priority).To(gomega.Equal("P1"))
|
||||
gomega.Expect(config.User).To(gomega.Equal("Dracula"))
|
||||
gomega.Expect(config.Note).To(gomega.Equal("Here is a note"))
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.When("generating a config from a url with differently escaped spaces", func() {
|
||||
ginkgo.It("should parse the escaped spaces correctly", func() {
|
||||
// Use: '%20', '+' and a normal space
|
||||
queryParams := `alias=Life is+too%20short+for+no+alias`
|
||||
url, err := url.Parse(
|
||||
fmt.Sprintf(
|
||||
"opsgenie://%s/%s?%s",
|
||||
net.JoinHostPort(mockHost, "12345"),
|
||||
mockAPIKey,
|
||||
queryParams,
|
||||
),
|
||||
)
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
|
||||
config := Config{}
|
||||
err = config.SetURL(url)
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
|
||||
gomega.Expect(config.Alias).To(gomega.Equal("Life is too short for no alias"))
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.When("generating a url from a simple config", func() {
|
||||
ginkgo.It("should generate a url", func() {
|
||||
config := Config{
|
||||
Host: "api.opsgenie.com",
|
||||
APIKey: "eb243592-faa2-4ba2-a551q-1afdf565c889",
|
||||
}
|
||||
|
||||
url := config.GetURL()
|
||||
|
||||
gomega.Expect(url.String()).
|
||||
To(gomega.Equal("opsgenie://api.opsgenie.com/eb243592-faa2-4ba2-a551q-1afdf565c889"))
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.When("generating a url from a config with a port", func() {
|
||||
ginkgo.It("should generate a url with port", func() {
|
||||
config := Config{
|
||||
Host: "api.opsgenie.com",
|
||||
APIKey: "eb243592-faa2-4ba2-a551q-1afdf565c889",
|
||||
Port: 12345,
|
||||
}
|
||||
|
||||
url := config.GetURL()
|
||||
|
||||
gomega.Expect(url.String()).
|
||||
To(gomega.Equal("opsgenie://api.opsgenie.com:12345/eb243592-faa2-4ba2-a551q-1afdf565c889"))
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.When("generating a url from a config with all optional config fields", func() {
|
||||
ginkgo.It("should generate a url with query parameters", func() {
|
||||
config := Config{
|
||||
Host: "api.opsgenie.com",
|
||||
APIKey: "eb243592-faa2-4ba2-a551q-1afdf565c889",
|
||||
Alias: "Life is too short for no alias",
|
||||
Description: "Every alert needs a description",
|
||||
Responders: []Entity{
|
||||
{Type: "user", Username: "Test"},
|
||||
{Type: "team", Name: "NOC"},
|
||||
{Type: "team", ID: "4513b7ea-3b91-438f-b7e4-e3e54af9147c"},
|
||||
},
|
||||
VisibleTo: []Entity{
|
||||
{Type: "user", Username: "A User"},
|
||||
},
|
||||
Actions: []string{"action1", "action2"},
|
||||
Tags: []string{"tag1", "tag2"},
|
||||
Details: map[string]string{"key": "value"},
|
||||
Entity: "An example entity",
|
||||
Source: "The source",
|
||||
Priority: "P1",
|
||||
User: "Dracula",
|
||||
Note: "Here is a note",
|
||||
}
|
||||
|
||||
url := config.GetURL()
|
||||
gomega.Expect(url.String()).
|
||||
To(gomega.Equal(`opsgenie://api.opsgenie.com/eb243592-faa2-4ba2-a551q-1afdf565c889?actions=action1%2Caction2&alias=Life+is+too+short+for+no+alias&description=Every+alert+needs+a+description&details=key%3Avalue&entity=An+example+entity¬e=Here+is+a+note&priority=P1&responders=user%3ATest%2Cteam%3ANOC%2Cteam%3A4513b7ea-3b91-438f-b7e4-e3e54af9147c&source=The+source&tags=tag1%2Ctag2&user=Dracula&visibleto=user%3AA+User`))
|
||||
})
|
||||
})
|
||||
})
|
Loading…
Add table
Add a link
Reference in a new issue