diff --git a/pkg/bus/bus_test.go b/pkg/bus/bus_test.go index 170297797bd..f7af56e6fa2 100644 --- a/pkg/bus/bus_test.go +++ b/pkg/bus/bus_test.go @@ -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) +} diff --git a/pkg/components/apikeygen/apikeygen_test.go b/pkg/components/apikeygen/apikeygen_test.go index 80418c04a80..82ac531c959 100644 --- a/pkg/components/apikeygen/apikeygen_test.go +++ b/pkg/components/apikeygen/apikeygen_test.go @@ -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) } diff --git a/pkg/components/imguploader/webdavuploader_test.go b/pkg/components/imguploader/webdavuploader_test.go index acea35a67a5..a3d1d7b4a18 100644 --- a/pkg/components/imguploader/webdavuploader_test.go +++ b/pkg/components/imguploader/webdavuploader_test.go @@ -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")) }) } diff --git a/pkg/expr/classic/evaluator_test.go b/pkg/expr/classic/evaluator_test.go index 4a82b1c920c..ba38be57480 100644 --- a/pkg/expr/classic/evaluator_test.go +++ b/pkg/expr/classic/evaluator_test.go @@ -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) { diff --git a/pkg/login/social/connectors/github_oauth_test.go b/pkg/login/social/connectors/github_oauth_test.go index e54d617c217..90b648f5f84 100644 --- a/pkg/login/social/connectors/github_oauth_test.go +++ b/pkg/login/social/connectors/github_oauth_test.go @@ -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) } diff --git a/pkg/middleware/cookies/cookies_test.go b/pkg/middleware/cookies/cookies_test.go new file mode 100644 index 00000000000..6e2c822ce3d --- /dev/null +++ b/pkg/middleware/cookies/cookies_test.go @@ -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()) +} diff --git a/pkg/services/anonymous/anonimpl/impl_test.go b/pkg/services/anonymous/anonimpl/impl_test.go index 0ca7cb2cf78..8a81c2035c9 100644 --- a/pkg/services/anonymous/anonimpl/impl_test.go +++ b/pkg/services/anonymous/anonimpl/impl_test.go @@ -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) + } }) } } diff --git a/pkg/services/cloudmigration/objectstorage/s3_test.go b/pkg/services/cloudmigration/objectstorage/s3_test.go new file mode 100644 index 00000000000..f4fe4614ebd --- /dev/null +++ b/pkg/services/cloudmigration/objectstorage/s3_test.go @@ -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) + }) +} diff --git a/pkg/services/serviceaccounts/secretscan/service_test.go b/pkg/services/serviceaccounts/secretscan/service_test.go index f1260acb575..eee781f4ea4 100644 --- a/pkg/services/serviceaccounts/secretscan/service_test.go +++ b/pkg/services/serviceaccounts/secretscan/service_test.go @@ -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) +}