Path: blob/main/pkg/integrations/v2/controller_httpintegration_test.go
5287 views
package integrations12import (3"fmt"4"io"5"net/http"6"net/http/httptest"7"strings"8"testing"910"github.com/go-kit/log"11"github.com/gorilla/mux"12"github.com/grafana/agent/pkg/util"13"github.com/stretchr/testify/require"14)1516//17// Tests for controller's utilization of the HTTPIntegration interface.18//1920func Test_controller_Handler_Sync(t *testing.T) {21httpConfigFromID := func(t *testing.T, name, identifier string) Config {22t.Helper()2324cfg := mockConfigNameTuple(t, name, identifier)25cfg.NewIntegrationFunc = func(log.Logger, Globals) (Integration, error) {26i := mockHTTPIntegration{27Integration: NoOpIntegration,28HandlerFunc: func(prefix string) (http.Handler, error) {29return http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {30// We should never reach here since we don't run the integrations.31rw.WriteHeader(http.StatusBadRequest)32}), nil33},34}35return i, nil36}3738return cfg39}4041cfg := controllerConfig{httpConfigFromID(t, "foo", "bar")}42ctrl, err := newController(util.TestLogger(t), cfg, Globals{})43require.NoError(t, err)4445handler, err := ctrl.Handler("/integrations/")46require.NoError(t, err)4748srv := httptest.NewServer(handler)4950resp, err := srv.Client().Get(srv.URL + "/integrations/foo/bar")51require.NoError(t, err)52require.Equal(t, http.StatusServiceUnavailable, resp.StatusCode)53}5455// Test_controller_HTTPIntegration_Prefixes ensures that the controller will assign56// appropriate prefixes to HTTPIntegrations.57func Test_controller_HTTPIntegration_Prefixes(t *testing.T) {58httpConfigFromID := func(t *testing.T, prefixes *[]string, name, identifier string) Config {59t.Helper()6061cfg := mockConfigNameTuple(t, name, identifier)62cfg.NewIntegrationFunc = func(log.Logger, Globals) (Integration, error) {63i := mockHTTPIntegration{64Integration: NoOpIntegration,65HandlerFunc: func(prefix string) (http.Handler, error) {66*prefixes = append(*prefixes, prefix)67return http.NotFoundHandler(), nil68},69}70return i, nil71}7273return cfg74}7576t.Run("fully unique", func(t *testing.T) {77var prefixes []string7879ctrl, err := newController(80util.TestLogger(t),81controllerConfig{82httpConfigFromID(t, &prefixes, "foo", "bar"),83httpConfigFromID(t, &prefixes, "fizz", "buzz"),84httpConfigFromID(t, &prefixes, "hello", "world"),85},86Globals{},87)88require.NoError(t, err)89_ = newSyncController(t, ctrl)9091_, err = ctrl.Handler("/integrations/")92require.NoError(t, err)9394expect := []string{95"/integrations/foo/",96"/integrations/fizz/",97"/integrations/hello/",98}99require.ElementsMatch(t, prefixes, expect)100})101102t.Run("multiple instances", func(t *testing.T) {103var prefixes []string104105ctrl, err := newController(106util.TestLogger(t),107controllerConfig{108httpConfigFromID(t, &prefixes, "foo", "bar"),109httpConfigFromID(t, &prefixes, "foo", "buzz"),110httpConfigFromID(t, &prefixes, "hello", "world"),111},112Globals{},113)114require.NoError(t, err)115_ = newSyncController(t, ctrl)116117_, err = ctrl.Handler("/integrations/")118require.NoError(t, err)119120expect := []string{121"/integrations/foo/bar/",122"/integrations/foo/buzz/",123"/integrations/hello/",124}125require.ElementsMatch(t, prefixes, expect)126})127}128129// Test_controller_HTTPIntegration_Routing ensures that the controller will route130// requests to the appropriate integration.131func Test_controller_HTTPIntegration_Routing(t *testing.T) {132httpConfigFromID := func(t *testing.T, name, identifier string) Config {133t.Helper()134135cfg := mockConfigNameTuple(t, name, identifier)136cfg.NewIntegrationFunc = func(log.Logger, Globals) (Integration, error) {137i := mockHTTPIntegration{138Integration: NoOpIntegration,139HandlerFunc: func(prefix string) (http.Handler, error) {140return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {141fmt.Fprintf(rw, "prefix=%s, path=%s", prefix, r.URL.Path)142}), nil143},144}145return i, nil146}147148return cfg149}150151ctrl, err := newController(152util.TestLogger(t),153controllerConfig{154httpConfigFromID(t, "foo", "bar"),155httpConfigFromID(t, "foo", "buzz"),156httpConfigFromID(t, "hello", "world"),157},158Globals{},159)160require.NoError(t, err)161_ = newSyncController(t, ctrl)162163handler, err := ctrl.Handler("/integrations/")164require.NoError(t, err)165166srv := httptest.NewServer(handler)167168getResponse := func(t *testing.T, path string) string {169t.Helper()170resp, err := srv.Client().Get(srv.URL + path)171require.NoError(t, err)172defer resp.Body.Close()173174var sb strings.Builder175_, err = io.Copy(&sb, resp.Body)176require.NoError(t, err)177return sb.String()178}179180tt := []struct {181path, expect string182}{183{"/integrations/foo/bar", "prefix=/integrations/foo/bar/, path=/integrations/foo/bar"},184{"/integrations/foo/bar/", "prefix=/integrations/foo/bar/, path=/integrations/foo/bar/"},185{"/integrations/foo/bar/extra", "prefix=/integrations/foo/bar/, path=/integrations/foo/bar/extra"},186}187188for _, tc := range tt {189require.Equal(t, tc.expect, getResponse(t, tc.path))190}191}192193// Test_controller_HTTPIntegration_NestedRouting ensures that the controller194// will work with nested routers.195func Test_controller_HTTPIntegration_NestedRouting(t *testing.T) {196cfg := mockConfigNameTuple(t, "test", "test")197cfg.NewIntegrationFunc = func(log.Logger, Globals) (Integration, error) {198i := mockHTTPIntegration{199Integration: NoOpIntegration,200HandlerFunc: func(prefix string) (http.Handler, error) {201r := mux.NewRouter()202r.StrictSlash(true)203204r.HandleFunc(prefix, func(rw http.ResponseWriter, r *http.Request) {205fmt.Fprintf(rw, "prefix=%s, path=%s", prefix, r.URL.Path)206})207208r.HandleFunc(prefix+"greet", func(rw http.ResponseWriter, _ *http.Request) {209fmt.Fprintf(rw, "Hello, world!")210})211return r, nil212},213}214return i, nil215}216217ctrl, err := newController(util.TestLogger(t), controllerConfig{cfg}, Globals{})218require.NoError(t, err)219_ = newSyncController(t, ctrl)220221handler, err := ctrl.Handler("/integrations/")222require.NoError(t, err)223224srv := httptest.NewServer(handler)225226getResponse := func(t *testing.T, path string) string {227t.Helper()228resp, err := srv.Client().Get(srv.URL + path)229require.NoError(t, err)230defer resp.Body.Close()231232var sb strings.Builder233_, err = io.Copy(&sb, resp.Body)234require.NoError(t, err)235return sb.String()236}237238tt := []struct {239path, expect string240}{241{"/integrations/test", "prefix=/integrations/test/, path=/integrations/test/"},242{"/integrations/test/", "prefix=/integrations/test/, path=/integrations/test/"},243{"/integrations/test/greet", "Hello, world!"},244}245246for _, tc := range tt {247require.Equal(t, tc.expect, getResponse(t, tc.path))248}249}250251type mockHTTPIntegration struct {252Integration253HandlerFunc func(prefix string) (http.Handler, error)254}255256func (m mockHTTPIntegration) Handler(prefix string) (http.Handler, error) {257return m.HandlerFunc(prefix)258}259260261