mirror of
https://github.com/grafana/grafana.git
synced 2026-02-03 20:49:50 -05:00
Hackaton: Add more unit tests, take 3 (#101525)
* serviceaccounts/secretscan: test Service more thoroughly * middleware/cookies: add tests for CookieOptions * anonymous/anonimpl: cover a couple more methods * components/imguploader: Implement WebDAV integration tests * components/apikeygen: also check IsValid method * bus: cover invalid callback signature cases * cloudmigration/objectstorage: add basic unit tests * login/social/connectors: add test case for GitHub OAuth fetch emails+orgs * expr/classic: cover more evaluator types in tests
This commit is contained in:
parent
dc2defd84f
commit
3539764008
9 changed files with 522 additions and 25 deletions
|
|
@ -2,6 +2,7 @@ package bus
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
|
@ -91,3 +92,61 @@ func TestEventCtxPublish(t *testing.T) {
|
|||
|
||||
require.True(t, invoked)
|
||||
}
|
||||
|
||||
func TestEventListenerError(t *testing.T) {
|
||||
bus := ProvideBus(tracing.InitializeTracerForTest())
|
||||
|
||||
mockErr := errors.New("error")
|
||||
|
||||
invocations := 0
|
||||
|
||||
// Will be called in order of declaration.
|
||||
bus.AddEventListener(func(ctx context.Context, query *testQuery) error {
|
||||
invocations++
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddEventListener(func(ctx context.Context, query *testQuery) error {
|
||||
invocations++
|
||||
return mockErr
|
||||
})
|
||||
|
||||
bus.AddEventListener(func(ctx context.Context, query *testQuery) {
|
||||
invocations++
|
||||
})
|
||||
|
||||
err := bus.Publish(context.Background(), &testQuery{})
|
||||
require.ErrorIs(t, err, mockErr)
|
||||
require.Equal(t, 2, invocations)
|
||||
}
|
||||
|
||||
func TestEventListenerInvalidCallbackType(t *testing.T) {
|
||||
bus := ProvideBus(tracing.InitializeTracerForTest())
|
||||
|
||||
invoked := false
|
||||
|
||||
bus.AddEventListener(func(ctx context.Context, query *testQuery) bool {
|
||||
invoked = true
|
||||
return invoked
|
||||
})
|
||||
|
||||
err := bus.Publish(context.Background(), &testQuery{})
|
||||
require.Error(t, err)
|
||||
require.True(t, invoked)
|
||||
}
|
||||
|
||||
func TestEventListenerInvalidCallback(t *testing.T) {
|
||||
bus := ProvideBus(tracing.InitializeTracerForTest())
|
||||
|
||||
invoked := false
|
||||
|
||||
bus.AddEventListener(func(ctx context.Context, query *testQuery) {
|
||||
invoked = true
|
||||
})
|
||||
|
||||
require.Panics(t, func() {
|
||||
err := bus.Publish(context.Background(), &testQuery{})
|
||||
require.NoError(t, err) // unreachable
|
||||
})
|
||||
require.True(t, invoked)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,4 +22,8 @@ func TestApiKeyGen(t *testing.T) {
|
|||
keyHashed, err := util.EncodePassword(keyInfo.Key, keyInfo.Name)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, result.HashedKey, keyHashed)
|
||||
|
||||
valid, err := IsValid(keyInfo, keyHashed)
|
||||
require.NoError(t, err)
|
||||
require.True(t, valid)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,43 +2,83 @@ package imguploader
|
|||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/net/webdav"
|
||||
)
|
||||
|
||||
func TestUploadToWebdav(t *testing.T) {
|
||||
// Can be tested with this docker container: https://hub.docker.com/r/morrisjobke/webdav/
|
||||
t.Run("[Integration test] for external_image_store.webdav", func(t *testing.T) {
|
||||
t.Skip("Skip test [Integration test] for external_image_store.webdav")
|
||||
webdavUploader, _ := NewWebdavImageUploader("http://localhost:8888/webdav/", "test", "test", "")
|
||||
path, err := webdavUploader.Upload(context.Background(), "../../../public/img/logo_transparent_400x.png")
|
||||
t.Parallel()
|
||||
|
||||
t.Run("[Integration test] for external_image_store.webdav", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
handler := &webdav.Handler{
|
||||
FileSystem: webdav.Dir(t.TempDir()),
|
||||
LockSystem: webdav.NewMemLS(),
|
||||
Logger: func(r *http.Request, err error) {
|
||||
require.Equal(t, http.MethodPut, r.Method)
|
||||
require.NoError(t, err)
|
||||
},
|
||||
}
|
||||
|
||||
server := httptest.NewServer(handler)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
webdavUploader, err := NewWebdavImageUploader(server.URL, "test", "test", "")
|
||||
require.NoError(t, err)
|
||||
require.True(t, strings.HasPrefix(path, "http://localhost:8888/webdav/"))
|
||||
require.NotNil(t, webdavUploader)
|
||||
|
||||
path, err := webdavUploader.Upload(context.Background(), "../../../public/img/logo_transparent_400x.png")
|
||||
require.NoError(t, err)
|
||||
require.True(t, strings.HasPrefix(path, server.URL))
|
||||
})
|
||||
|
||||
t.Run("[Integration test] for external_image_store.webdav with public url", func(t *testing.T) {
|
||||
t.Skip("Skip test [Integration test] for external_image_store.webdav with public url")
|
||||
webdavUploader, _ := NewWebdavImageUploader("http://localhost:8888/webdav/", "test", "test", "http://publicurl:8888/webdav")
|
||||
path, err := webdavUploader.Upload(context.Background(), "../../../public/img/logo_transparent_400x.png")
|
||||
t.Parallel()
|
||||
|
||||
handler := &webdav.Handler{
|
||||
FileSystem: webdav.Dir(t.TempDir()),
|
||||
LockSystem: webdav.NewMemLS(),
|
||||
Logger: func(r *http.Request, err error) {
|
||||
require.Equal(t, http.MethodPut, r.Method)
|
||||
require.NoError(t, err)
|
||||
},
|
||||
}
|
||||
|
||||
server := httptest.NewServer(handler)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
webdavUploader, err := NewWebdavImageUploader(server.URL, "test", "test", "http://publicurl:8888/webdav")
|
||||
require.NoError(t, err)
|
||||
require.True(t, strings.HasPrefix(path, "http://publicurl:8888/webdav/"))
|
||||
require.NotNil(t, webdavUploader)
|
||||
|
||||
path, err := webdavUploader.Upload(context.Background(), "../../../public/img/logo_transparent_400x.png")
|
||||
require.NoError(t, err)
|
||||
require.True(t, strings.HasPrefix(path, "http://publicurl:8888/webdav/"))
|
||||
})
|
||||
}
|
||||
|
||||
func TestPublicURL(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("Given a public URL with parameters, and no template", func(t *testing.T) {
|
||||
webdavUploader, _ := NewWebdavImageUploader("http://localhost:8888/webdav/", "test", "test", "http://cloudycloud.me/s/DOIFDOMV/download?files=")
|
||||
assert.Equal(t, "http://cloudycloud.me/s/DOIFDOMV/download/fileyfile.png?files=", webdavUploader.PublicURL("fileyfile.png"))
|
||||
t.Parallel()
|
||||
|
||||
webdavUploader, err := NewWebdavImageUploader("http://localhost:8888/webdav/", "test", "test", "http://cloudycloud.me/s/DOIFDOMV/download?files=")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "http://cloudycloud.me/s/DOIFDOMV/download/fileyfile.png?files=", webdavUploader.PublicURL("fileyfile.png"))
|
||||
})
|
||||
|
||||
t.Run("Given a public URL with parameters, and a template", func(t *testing.T) {
|
||||
webdavUploader, _ := NewWebdavImageUploader("http://localhost:8888/webdav/", "test", "test", "http://cloudycloud.me/s/DOIFDOMV/download?files={{file}}")
|
||||
assert.Equal(t, "http://cloudycloud.me/s/DOIFDOMV/download?files=fileyfile.png", webdavUploader.PublicURL("fileyfile.png"))
|
||||
t.Parallel()
|
||||
|
||||
webdavUploader, err := NewWebdavImageUploader("http://localhost:8888/webdav/", "test", "test", "http://cloudycloud.me/s/DOIFDOMV/download?files={{file}}")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "http://cloudycloud.me/s/DOIFDOMV/download?files=fileyfile.png", webdavUploader.PublicURL("fileyfile.png"))
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -148,6 +148,54 @@ func TestRangedEvaluator(t *testing.T) {
|
|||
inputNumber: newNumber(util.Pointer(50.0)),
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "value 100 is outside range 1, 100: false",
|
||||
evaluator: &rangedEvaluator{"outside_range", 1, 100},
|
||||
inputNumber: newNumber(util.Pointer(100.)),
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "value 1 is outside range 1, 100: false",
|
||||
evaluator: &rangedEvaluator{"outside_range", 1, 100},
|
||||
inputNumber: newNumber(util.Pointer(1.)),
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "value 100 is within range included 1, 100: true",
|
||||
evaluator: &rangedEvaluator{"within_range_included", 1, 100},
|
||||
inputNumber: newNumber(util.Pointer(100.)),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "value 1 is within range included 1, 100: true",
|
||||
evaluator: &rangedEvaluator{"within_range_included", 1, 100},
|
||||
inputNumber: newNumber(util.Pointer(1.)),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "value 100 is outside range included 1, 100: true",
|
||||
evaluator: &rangedEvaluator{"outside_range_included", 1, 100},
|
||||
inputNumber: newNumber(util.Pointer(100.)),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "value 1 is outside range included 1, 100: true",
|
||||
evaluator: &rangedEvaluator{"outside_range_included", 1, 100},
|
||||
inputNumber: newNumber(util.Pointer(1.)),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "unknown evaluator type returns false",
|
||||
evaluator: &rangedEvaluator{"", 1, 100},
|
||||
inputNumber: newNumber(util.Pointer(1.)),
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "nil number conversion returns false",
|
||||
evaluator: &rangedEvaluator{"", 1, 100},
|
||||
inputNumber: newNumber(nil),
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package connectors
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
|
|
@ -86,7 +87,12 @@ const testGHUserTeamsJSON = `[
|
|||
}
|
||||
]`
|
||||
|
||||
const testGHUserJSON = `{
|
||||
var (
|
||||
testGHUserJSON = fmt.Sprintf(testGHUserJSONTemplate, "octocat@github.com")
|
||||
testGHUserEmptyEmailJSON = fmt.Sprintf(testGHUserJSONTemplate, "")
|
||||
)
|
||||
|
||||
const testGHUserJSONTemplate = `{
|
||||
"login": "octocat",
|
||||
"id": 1,
|
||||
"node_id": "MDQ6VXNlcjE=",
|
||||
|
|
@ -109,7 +115,7 @@ const testGHUserJSON = `{
|
|||
"company": "GitHub",
|
||||
"blog": "https://github.com/blog",
|
||||
"location": "San Francisco",
|
||||
"email": "octocat@github.com",
|
||||
"email": "%s",
|
||||
"hireable": false,
|
||||
"bio": "There once was...",
|
||||
"twitter_username": "monatheoctocat",
|
||||
|
|
@ -133,6 +139,16 @@ const testGHUserJSON = `{
|
|||
}
|
||||
}`
|
||||
|
||||
const testGHUserEmailJSON = `[{
|
||||
"email": "octocat@github.com",
|
||||
"primary": true,
|
||||
"verified": true
|
||||
}]`
|
||||
|
||||
const testGHOrgsJSON = `[{
|
||||
"login": "github"
|
||||
}]`
|
||||
|
||||
func TestSocialGitHub_UserInfo(t *testing.T) {
|
||||
var boolPointer *bool
|
||||
tests := []struct {
|
||||
|
|
@ -310,20 +326,45 @@ func TestSocialGitHub_UserInfo(t *testing.T) {
|
|||
userTeamsRawJSON: testGHUserTeamsJSON,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "should fetch email and allowed orgs",
|
||||
userRawJSON: testGHUserEmptyEmailJSON,
|
||||
userTeamsRawJSON: testGHUserTeamsJSON,
|
||||
oAuthExtraInfo: map[string]string{
|
||||
"allowed_organizations": "github",
|
||||
},
|
||||
want: &social.BasicUserInfo{
|
||||
Id: "1",
|
||||
Name: "monalisa octocat",
|
||||
Email: "octocat@github.com",
|
||||
Login: "octocat",
|
||||
OrgRoles: map[int64]org.RoleType{1: org.RoleViewer},
|
||||
Groups: []string{"https://github.com/orgs/github/teams/justice-league", "@github/justice-league"},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
reqURL := request.URL.String()
|
||||
|
||||
// return JSON if matches user endpoint
|
||||
if strings.HasSuffix(request.URL.String(), "/user") {
|
||||
if strings.HasSuffix(reqURL, "/user") {
|
||||
writer.Header().Set("Content-Type", "application/json")
|
||||
_, err := writer.Write([]byte(tt.userRawJSON))
|
||||
require.NoError(t, err)
|
||||
} else if strings.HasSuffix(request.URL.String(), "/user/teams?per_page=100") {
|
||||
} else if strings.HasSuffix(reqURL, "/user/teams?per_page=100") {
|
||||
writer.Header().Set("Content-Type", "application/json")
|
||||
_, err := writer.Write([]byte(tt.userTeamsRawJSON))
|
||||
require.NoError(t, err)
|
||||
} else if strings.HasSuffix(reqURL, "/emails") { // only called if email is empty
|
||||
writer.Header().Set("Content-Type", "application/json")
|
||||
_, err := writer.Write([]byte(testGHUserEmailJSON))
|
||||
require.NoError(t, err)
|
||||
} else if strings.HasSuffix(reqURL, "/orgs?per_page=100") {
|
||||
writer.Header().Set("Content-Type", "application/json")
|
||||
_, err := writer.Write([]byte(testGHOrgsJSON))
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
|
|
|
|||
37
pkg/middleware/cookies/cookies_test.go
Normal file
37
pkg/middleware/cookies/cookies_test.go
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
package cookies
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCookieOptions(t *testing.T) {
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
expectedName := "cookie-name"
|
||||
expectedValue := "cookie-value"
|
||||
|
||||
WriteCookie(rr, expectedName, expectedValue, 100, nil)
|
||||
|
||||
cookie, err := http.ParseSetCookie(rr.Header().Get("Set-Cookie"))
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, cookie)
|
||||
|
||||
require.Equal(t, expectedName, cookie.Name)
|
||||
require.Equal(t, expectedValue, cookie.Value)
|
||||
require.GreaterOrEqual(t, cookie.MaxAge, 0)
|
||||
|
||||
// Does not override but appends to the `Set-Cookie` header.
|
||||
DeleteCookie(rr, expectedName, nil)
|
||||
|
||||
cookieHeader := rr.Header().Values("Set-Cookie")
|
||||
require.Len(t, cookieHeader, 2)
|
||||
|
||||
cookie, err = http.ParseSetCookie(cookieHeader[1])
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, cookie)
|
||||
require.NoError(t, cookie.Valid())
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ import (
|
|||
"github.com/grafana/grafana/pkg/services/anonymous"
|
||||
"github.com/grafana/grafana/pkg/services/anonymous/anonimpl/anonstore"
|
||||
"github.com/grafana/grafana/pkg/services/anonymous/validator"
|
||||
"github.com/grafana/grafana/pkg/services/authn"
|
||||
"github.com/grafana/grafana/pkg/services/authn/authntest"
|
||||
"github.com/grafana/grafana/pkg/services/org/orgtest"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
|
|
@ -41,6 +42,7 @@ func TestIntegrationDeviceService_tag(t *testing.T) {
|
|||
expectedAnonUICount int64
|
||||
expectedKey string
|
||||
expectedDevice *anonstore.Device
|
||||
disableService bool
|
||||
}{
|
||||
{
|
||||
name: "no requests",
|
||||
|
|
@ -118,20 +120,49 @@ func TestIntegrationDeviceService_tag(t *testing.T) {
|
|||
},
|
||||
expectedAnonUICount: 2,
|
||||
},
|
||||
{
|
||||
name: "when the service is disabled, read operations return empty",
|
||||
req: []tagReq{
|
||||
{
|
||||
httpReq: &http.Request{
|
||||
Header: http.Header{
|
||||
"User-Agent": []string{"test"},
|
||||
"X-Forwarded-For": []string{"10.30.30.1"},
|
||||
http.CanonicalHeaderKey(deviceIDHeader): []string{"32mdo31deeqwes"},
|
||||
},
|
||||
},
|
||||
kind: anonymous.AnonDeviceUI,
|
||||
},
|
||||
},
|
||||
disableService: true,
|
||||
expectedAnonUICount: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
store := db.InitTestDB(t)
|
||||
anonService := ProvideAnonymousDeviceService(&usagestats.UsageStatsMock{},
|
||||
&authntest.FakeService{}, store, setting.NewCfg(), orgtest.NewOrgServiceFake(), nil, actest.FakeAccessControl{}, &routing.RouteRegisterImpl{}, validator.FakeAnonUserLimitValidator{})
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
cfg.Anonymous.Enabled = !tc.disableService
|
||||
|
||||
anonService := ProvideAnonymousDeviceService(
|
||||
&usagestats.UsageStatsMock{}, &authntest.FakeService{}, store, cfg, orgtest.NewOrgServiceFake(),
|
||||
nil, actest.FakeAccessControl{}, &routing.RouteRegisterImpl{}, validator.FakeAnonUserLimitValidator{},
|
||||
)
|
||||
|
||||
for _, req := range tc.req {
|
||||
err := anonService.TagDevice(context.Background(), req.httpReq, req.kind)
|
||||
err := anonService.TagDevice(ctx, req.httpReq, req.kind)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Cleanup(func() {
|
||||
anonService.untagDevice(ctx, nil, &authn.Request{HTTPRequest: req.httpReq}, nil)
|
||||
})
|
||||
}
|
||||
|
||||
devices, err := anonService.anonStore.ListDevices(context.Background(), nil, nil)
|
||||
devices, err := anonService.ListDevices(ctx, nil, nil)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, devices, int(tc.expectedAnonUICount))
|
||||
if tc.expectedDevice != nil {
|
||||
|
|
@ -147,10 +178,29 @@ func TestIntegrationDeviceService_tag(t *testing.T) {
|
|||
assert.Equal(t, tc.expectedDevice, devices[0])
|
||||
}
|
||||
|
||||
to := time.Now()
|
||||
from := to.AddDate(0, 0, -1)
|
||||
|
||||
devicesCount, err := anonService.CountDevices(ctx, from, to)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.expectedAnonUICount, devicesCount)
|
||||
|
||||
devicesFound, err := anonService.SearchDevices(ctx, &anonstore.SearchDeviceQuery{
|
||||
From: from,
|
||||
To: to,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
if tc.expectedAnonUICount > 0 {
|
||||
require.NotNil(t, devicesFound)
|
||||
require.Equal(t, tc.expectedAnonUICount, devicesFound.TotalCount)
|
||||
}
|
||||
|
||||
stats, err := anonService.usageStatFn(context.Background())
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tc.expectedAnonUICount, stats["stats.anonymous.device.ui.count"].(int64), stats)
|
||||
if !tc.disableService {
|
||||
assert.Equal(t, tc.expectedAnonUICount, stats["stats.anonymous.device.ui.count"].(int64), stats)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
103
pkg/services/cloudmigration/objectstorage/s3_test.go
Normal file
103
pkg/services/cloudmigration/objectstorage/s3_test.go
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
package objectstorage
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"math"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPresignedURLUpload(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("successfully send data to the server", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
key := "snapshot/uuid/key"
|
||||
data := "sending-some-data"
|
||||
|
||||
reader := bytes.NewBufferString(data)
|
||||
|
||||
qs, err := url.ParseQuery("one=a&two=b")
|
||||
require.NoError(t, err)
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
contentType := r.Header.Get("Content-Type")
|
||||
_, boundary, found := strings.Cut(contentType, "boundary=")
|
||||
require.True(t, found)
|
||||
|
||||
mpr := multipart.NewReader(r.Body, boundary)
|
||||
|
||||
form, err := mpr.ReadForm(math.MaxInt64)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, form)
|
||||
require.NotNil(t, form.Value)
|
||||
|
||||
require.Equal(t, key, form.Value["key"][0])
|
||||
require.Equal(t, qs.Get("one"), form.Value["one"][0])
|
||||
require.Equal(t, qs.Get("two"), form.Value["two"][0])
|
||||
|
||||
require.Len(t, form.File, 1)
|
||||
require.Len(t, form.File["file"], 1)
|
||||
|
||||
fileHeader := form.File["file"][0]
|
||||
require.Equal(t, "file", fileHeader.Filename)
|
||||
|
||||
file, err := fileHeader.Open()
|
||||
require.NoError(t, err)
|
||||
|
||||
contents, err := io.ReadAll(file)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, data, string(contents))
|
||||
|
||||
require.NoError(t, file.Close())
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
s3 := NewS3(http.DefaultClient, tracing.NewNoopTracerService())
|
||||
|
||||
presignedURL, err := url.Parse(server.URL + "?" + qs.Encode())
|
||||
require.NoError(t, err)
|
||||
|
||||
err = s3.PresignedURLUpload(ctx, presignedURL.String(), key, reader)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("when the request to the server returns an error, it is propagated", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
key := "snapshot/uuid/key"
|
||||
data := "sending-some-data"
|
||||
|
||||
reader := bytes.NewBufferString(data)
|
||||
|
||||
body := "test error"
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
_, _ = w.Write([]byte(`{"message": "` + body + `}`))
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
s3 := NewS3(http.DefaultClient, tracing.NewNoopTracerService())
|
||||
|
||||
presignedURL, err := url.Parse(server.URL)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = s3.PresignedURLUpload(ctx, presignedURL.String(), key, reader)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), body)
|
||||
})
|
||||
}
|
||||
|
|
@ -2,13 +2,20 @@ package secretscan
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/apikey"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
func TestService_CheckTokens(t *testing.T) {
|
||||
|
|
@ -170,3 +177,111 @@ func TestService_CheckTokens(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestService(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
t.Cleanup(cancel)
|
||||
|
||||
// Fake Secret Scanner + Webhook.
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if strings.Contains(r.RequestURI, "/tokens") {
|
||||
_, err := io.Copy(io.Discard, r.Body)
|
||||
require.NoError(t, err)
|
||||
|
||||
defer func() {
|
||||
_ = r.Body.Close()
|
||||
}()
|
||||
|
||||
_, _ = w.Write([]byte(`[
|
||||
{"type": "token_type", "hash": "test-hash-1", "url": "http://example.com", "reported_at": "2006-01-20T01:02:03Z" }
|
||||
]`))
|
||||
}
|
||||
|
||||
if strings.Contains(r.RequestURI, "/oncall") {
|
||||
var webhookReq struct {
|
||||
State string `json:"state"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
err := json.NewDecoder(r.Body).Decode(&webhookReq)
|
||||
require.NoError(t, err)
|
||||
|
||||
defer func() {
|
||||
_ = r.Body.Close()
|
||||
}()
|
||||
|
||||
require.Equal(t, "alerting", webhookReq.State)
|
||||
require.Contains(t, webhookReq.Message, "test-1")
|
||||
}
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
unixZero := time.Unix(0, 0).Unix()
|
||||
revoked := true
|
||||
|
||||
tokenRetriever := &MockTokenRetriever{keys: []apikey.APIKey{
|
||||
// Valid
|
||||
{
|
||||
ID: 1,
|
||||
OrgID: 1,
|
||||
Name: "test-1",
|
||||
Key: "test-hash-1",
|
||||
Role: "Viewer",
|
||||
Expires: nil,
|
||||
ServiceAccountId: new(int64),
|
||||
IsRevoked: new(bool),
|
||||
},
|
||||
// Expired
|
||||
{
|
||||
ID: 2,
|
||||
OrgID: 1,
|
||||
Name: "test-2",
|
||||
Key: "test-hash-2",
|
||||
Role: "Viewer",
|
||||
Expires: &unixZero,
|
||||
ServiceAccountId: new(int64),
|
||||
IsRevoked: new(bool),
|
||||
},
|
||||
// Revoked
|
||||
{
|
||||
ID: 3,
|
||||
OrgID: 1,
|
||||
Name: "test-3",
|
||||
Key: "test-hash-3",
|
||||
Role: "Viewer",
|
||||
Expires: nil,
|
||||
ServiceAccountId: new(int64),
|
||||
IsRevoked: &revoked,
|
||||
},
|
||||
// Revoked + Expired
|
||||
{
|
||||
ID: 4,
|
||||
OrgID: 1,
|
||||
Name: "test-4",
|
||||
Key: "test-hash-4",
|
||||
Role: "Viewer",
|
||||
Expires: &unixZero,
|
||||
ServiceAccountId: new(int64),
|
||||
IsRevoked: &revoked,
|
||||
},
|
||||
}}
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
section := cfg.Raw.Section("secretscan")
|
||||
|
||||
baseURL := section.Key("base_url")
|
||||
baseURL.SetValue(server.URL)
|
||||
|
||||
oncallURL := section.Key("oncall_url")
|
||||
oncallURL.SetValue(server.URL + "/oncall")
|
||||
|
||||
revoke := section.Key("revoke")
|
||||
revoke.SetValue("true")
|
||||
|
||||
service, err := NewService(tokenRetriever, cfg)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, service)
|
||||
|
||||
err = service.CheckTokens(ctx)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue