Hackaton: Add more unit tests, take 2 (#101473)
* login/social/socialimpl: add assertions for usage stats, support bundle and oauthinfo methods * accesscontrol/acimpl: add tests for GetRoleByName * anonymous/sortopts: add tests for Sorter * cloudmigration/gmsclient: add basic test cases for all methods * shorturls/shorturlimpl: add more edge test cases * tag/tagimpl: add test to cover duplicate tag kv and nil pairs * updatechecker: add test cases for module
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package socialimpl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -85,6 +86,8 @@ func TestSocialService_ProvideService(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
env := &testEnv{
|
||||
features: featuremgmt.WithFeatures(),
|
||||
}
|
||||
@@ -92,13 +95,45 @@ func TestSocialService_ProvideService(t *testing.T) {
|
||||
tc.setup(t, env)
|
||||
}
|
||||
|
||||
socialService := ProvideService(cfg, env.features, &usagestats.UsageStatsMock{}, supportbundlestest.NewFakeBundleService(), remotecache.NewFakeStore(t), nil, ssoSettingsSvc)
|
||||
require.Equal(t, tc.expectedSocialMapLength, len(socialService.socialMap))
|
||||
usageInsights := &usagestats.UsageStatsMock{}
|
||||
supportBundle := supportbundlestest.NewFakeBundleService()
|
||||
|
||||
socialService := ProvideService(cfg, env.features, usageInsights, supportBundle, remotecache.NewFakeStore(t), nil, ssoSettingsSvc)
|
||||
require.Equal(t, tc.expectedSocialMapLength, len(socialService.GetOAuthProviders()))
|
||||
|
||||
genericOAuthInfo := socialService.GetOAuthInfoProvider("generic_oauth")
|
||||
if genericOAuthInfo != nil {
|
||||
require.Equal(t, tc.expectedGenericOAuthSkipOrgRoleSync, genericOAuthInfo.SkipOrgRoleSync)
|
||||
}
|
||||
|
||||
for name, enabled := range socialService.GetOAuthProviders() {
|
||||
client, err := socialService.GetOAuthHttpClient(name)
|
||||
if !enabled {
|
||||
require.Error(t, err)
|
||||
require.Nil(t, client)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, client)
|
||||
}
|
||||
}
|
||||
|
||||
report, err := usageInsights.GetUsageReport(ctx)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, report)
|
||||
require.Len(t, report.Metrics, tc.expectedSocialMapLength)
|
||||
|
||||
require.Len(t, supportBundle.Collectors, tc.expectedSocialMapLength)
|
||||
|
||||
createdBundles := make(map[string]struct{}, 0)
|
||||
for _, collector := range supportBundle.Collectors {
|
||||
supportItem, err := collector.Fn(ctx)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, supportItem)
|
||||
|
||||
createdBundles[supportItem.Filename] = struct{}{}
|
||||
}
|
||||
|
||||
require.Len(t, createdBundles, tc.expectedSocialMapLength)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -970,3 +970,38 @@ func TestService_DeleteExternalServiceRole(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestService_GetRoleByName(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("when the role does not exists, it returns an error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ac := setupTestEnv(t)
|
||||
ac.registrations = accesscontrol.RegistrationList{}
|
||||
|
||||
role, err := ac.GetRoleByName(ctx, 0, "not-found-role")
|
||||
require.ErrorIs(t, err, accesscontrol.ErrRoleNotFound)
|
||||
require.Nil(t, role)
|
||||
})
|
||||
|
||||
t.Run("when the role exists, it is returned", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
roleName := "fixed:test:test"
|
||||
|
||||
ac := setupTestEnv(t)
|
||||
ac.registrations = accesscontrol.RegistrationList{}
|
||||
ac.registrations.Append(accesscontrol.RoleRegistration{
|
||||
Role: accesscontrol.RoleDTO{Name: roleName},
|
||||
Grants: []string{"Admin"},
|
||||
})
|
||||
|
||||
role, err := ac.GetRoleByName(ctx, 0, roleName)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, role)
|
||||
require.Equal(t, roleName, role.Name)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
package sortopts
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSorter(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
query string
|
||||
expectedSQL []string
|
||||
expectedErr bool
|
||||
}{
|
||||
{
|
||||
name: "empty query",
|
||||
query: "",
|
||||
},
|
||||
{
|
||||
name: "single field ascending",
|
||||
query: "userAgent-asc",
|
||||
expectedSQL: []string{"user_agent ASC"},
|
||||
},
|
||||
{
|
||||
name: "single field descending",
|
||||
query: "userAgent-desc",
|
||||
expectedSQL: []string{"user_agent DESC"},
|
||||
},
|
||||
{
|
||||
name: "multiple fields",
|
||||
query: "userAgent-asc,updatedAt-desc",
|
||||
expectedSQL: []string{"user_agent ASC", "updated_at DESC"},
|
||||
},
|
||||
{
|
||||
name: "unknown option",
|
||||
query: "uzer_agent",
|
||||
expectedErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sortOptions, err := ParseSortQueryParam(tt.query)
|
||||
if tt.expectedErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
if len(tt.expectedSQL) > 0 {
|
||||
orderBy := make([]string, 0)
|
||||
for _, opt := range sortOptions {
|
||||
require.Len(t, opt.Filter, 1)
|
||||
|
||||
orderBy = append(orderBy, opt.Filter[0].OrderBy())
|
||||
}
|
||||
|
||||
assert.EqualValues(t, tt.expectedSQL, orderBy)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSorter_OrderBy(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
sorter Sorter
|
||||
expectedSQL string
|
||||
}{
|
||||
{
|
||||
name: "with table name ascending",
|
||||
sorter: Sorter{
|
||||
Field: "user_agent",
|
||||
WithTableName: true,
|
||||
},
|
||||
expectedSQL: "anon_device.user_agent ASC",
|
||||
},
|
||||
{
|
||||
name: "with table name descending",
|
||||
sorter: Sorter{
|
||||
Field: "user_agent",
|
||||
WithTableName: true,
|
||||
Descending: true,
|
||||
},
|
||||
expectedSQL: "anon_device.user_agent DESC",
|
||||
},
|
||||
{
|
||||
name: "without table name ascending",
|
||||
sorter: Sorter{
|
||||
Field: "user_agent",
|
||||
},
|
||||
expectedSQL: "user_agent ASC",
|
||||
},
|
||||
{
|
||||
name: "without table name descending",
|
||||
sorter: Sorter{
|
||||
Field: "user_agent",
|
||||
Descending: true,
|
||||
},
|
||||
expectedSQL: "user_agent DESC",
|
||||
},
|
||||
{
|
||||
name: "with table name lowercase ascending",
|
||||
sorter: Sorter{
|
||||
Field: "user_agent",
|
||||
WithTableName: true,
|
||||
LowerCase: true,
|
||||
},
|
||||
expectedSQL: "LOWER(anon_device.user_agent) ASC",
|
||||
},
|
||||
{
|
||||
name: "with table name lowercase descending",
|
||||
sorter: Sorter{
|
||||
Field: "user_agent",
|
||||
WithTableName: true,
|
||||
LowerCase: true,
|
||||
Descending: true,
|
||||
},
|
||||
expectedSQL: "LOWER(anon_device.user_agent) DESC",
|
||||
},
|
||||
{
|
||||
name: "without table name lowercase ascending",
|
||||
sorter: Sorter{
|
||||
Field: "user_agent",
|
||||
LowerCase: true,
|
||||
},
|
||||
expectedSQL: "LOWER(user_agent) ASC",
|
||||
},
|
||||
{
|
||||
name: "without table name lowercase descending",
|
||||
sorter: Sorter{
|
||||
Field: "user_agent",
|
||||
LowerCase: true,
|
||||
Descending: true,
|
||||
},
|
||||
expectedSQL: "LOWER(user_agent) DESC",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equal(t, tt.expectedSQL, tt.sorter.OrderBy())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ func NewGMSClient(cfg *setting.Cfg, httpClient *http.Client) (Client, error) {
|
||||
|
||||
type gmsClientImpl struct {
|
||||
cfg *setting.Cfg
|
||||
log *log.ConcreteLogger
|
||||
log log.Logger
|
||||
httpClient *http.Client
|
||||
|
||||
getStatusMux sync.Mutex
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
package gmsclient
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log/logtest"
|
||||
"github.com/grafana/grafana/pkg/services/cloudmigration"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
func Test_buildURL(t *testing.T) {
|
||||
@@ -129,3 +136,474 @@ func Test_handleGMSErrors(t *testing.T) {
|
||||
require.ErrorIs(t, resError, tc.expectedError)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ValidateKey(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("when the key is valid, it returns no error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
session := cloudmigration.CloudMigrationSession{
|
||||
StackID: 1234,
|
||||
AuthToken: "auth-tok",
|
||||
ClusterSlug: "cluster-slug",
|
||||
}
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
require.Equal(t, http.MethodPost, r.Method)
|
||||
|
||||
auth := r.Header.Get("Authorization")
|
||||
require.Equal(t, fmt.Sprintf("Bearer %d:%s", session.StackID, session.AuthToken), auth)
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
cfg := &setting.Cfg{
|
||||
CloudMigration: setting.CloudMigrationSettings{
|
||||
GMSDomain: server.URL,
|
||||
GMSValidateKeyTimeout: time.Hour, // arbitrary, it just can't be 0.
|
||||
},
|
||||
}
|
||||
logger := &logtest.Fake{}
|
||||
client := gmsClientImpl{cfg: cfg, log: logger, httpClient: http.DefaultClient}
|
||||
|
||||
err := client.ValidateKey(ctx, session)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("when the key invalidated for any reason, it returns a token validation failure", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
session := cloudmigration.CloudMigrationSession{
|
||||
StackID: 1234,
|
||||
AuthToken: "auth-tok",
|
||||
ClusterSlug: "cluster-slug",
|
||||
}
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
require.Equal(t, http.MethodPost, r.Method)
|
||||
|
||||
auth := r.Header.Get("Authorization")
|
||||
require.Equal(t, fmt.Sprintf("Bearer %d:%s", session.StackID, session.AuthToken), auth)
|
||||
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
_, _ = w.Write([]byte(`{"message": "instance is unreachable"}`)) // could be any other error that is unmapped.
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
cfg := &setting.Cfg{
|
||||
CloudMigration: setting.CloudMigrationSettings{
|
||||
GMSDomain: server.URL,
|
||||
GMSValidateKeyTimeout: time.Hour, // arbitrary, it just can't be 0.
|
||||
},
|
||||
}
|
||||
logger := &logtest.Fake{}
|
||||
client := gmsClientImpl{cfg: cfg, log: logger, httpClient: http.DefaultClient}
|
||||
|
||||
err := client.ValidateKey(ctx, session)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StartSnapshot(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("when the session is valid, a snapshot result is returned", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
session := cloudmigration.CloudMigrationSession{
|
||||
StackID: 1234,
|
||||
AuthToken: "auth-tok",
|
||||
ClusterSlug: "cluster-slug",
|
||||
}
|
||||
|
||||
expectedSnapshot := &cloudmigration.StartSnapshotResponse{
|
||||
SnapshotID: "uuid",
|
||||
MaxItemsPerPartition: 1024,
|
||||
Algo: "nacl",
|
||||
EncryptionKey: []uint8{0x66, 0x6f, 0x6f, 0xa}, // foo
|
||||
Metadata: []uint8{0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xa}, // metadata
|
||||
}
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
require.Equal(t, http.MethodPost, r.Method)
|
||||
|
||||
auth := r.Header.Get("Authorization")
|
||||
require.Equal(t, fmt.Sprintf("Bearer %d:%s", session.StackID, session.AuthToken), auth)
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte(`{
|
||||
"snapshotID": "uuid",
|
||||
"maxItemsPerPartition": 1024,
|
||||
"algo": "nacl",
|
||||
"encryptionKey": "Zm9vCg==",
|
||||
"metadata": "bWV0YWRhdGEK"
|
||||
}`))
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
cfg := &setting.Cfg{
|
||||
CloudMigration: setting.CloudMigrationSettings{
|
||||
GMSDomain: server.URL,
|
||||
GMSStartSnapshotTimeout: time.Hour, // arbitrary, it just can't be 0.
|
||||
},
|
||||
}
|
||||
logger := &logtest.Fake{}
|
||||
client := gmsClientImpl{cfg: cfg, log: logger, httpClient: http.DefaultClient}
|
||||
|
||||
resp, err := client.StartSnapshot(ctx, session)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, resp)
|
||||
require.EqualValues(t, expectedSnapshot, resp)
|
||||
})
|
||||
|
||||
t.Run("when there is an error in the upstream, it logs and returns the error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
session := cloudmigration.CloudMigrationSession{
|
||||
StackID: 1234,
|
||||
AuthToken: "auth-tok",
|
||||
ClusterSlug: "cluster-slug",
|
||||
}
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
require.Equal(t, http.MethodPost, r.Method)
|
||||
|
||||
auth := r.Header.Get("Authorization")
|
||||
require.Equal(t, fmt.Sprintf("Bearer %d:%s", session.StackID, session.AuthToken), auth)
|
||||
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
_, _ = w.Write([]byte(`{"message": "internal server error"}`))
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
cfg := &setting.Cfg{
|
||||
CloudMigration: setting.CloudMigrationSettings{
|
||||
GMSDomain: server.URL,
|
||||
GMSStartSnapshotTimeout: time.Hour, // arbitrary, it just can't be 0.
|
||||
},
|
||||
}
|
||||
logger := &logtest.Fake{}
|
||||
client := gmsClientImpl{cfg: cfg, log: logger, httpClient: http.DefaultClient}
|
||||
|
||||
resp, err := client.StartSnapshot(ctx, session)
|
||||
require.Error(t, err)
|
||||
require.Nil(t, resp)
|
||||
|
||||
require.Equal(t, 1, logger.ErrorLogs.Calls)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_GetSnapshotStatus(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("it queries the snapshot status and returns it", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
session := cloudmigration.CloudMigrationSession{
|
||||
StackID: 1234,
|
||||
AuthToken: "auth-tok",
|
||||
ClusterSlug: "cluster-slug",
|
||||
}
|
||||
|
||||
snapshot := cloudmigration.CloudMigrationSnapshot{
|
||||
UID: "snapshot-uuid",
|
||||
}
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
require.Equal(t, http.MethodGet, r.Method)
|
||||
|
||||
auth := r.Header.Get("Authorization")
|
||||
require.Equal(t, fmt.Sprintf("Bearer %d:%s", session.StackID, session.AuthToken), auth)
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte(`{
|
||||
"state": "PROCESSING",
|
||||
"results": []
|
||||
}`))
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
cfg := &setting.Cfg{
|
||||
CloudMigration: setting.CloudMigrationSettings{
|
||||
GMSDomain: server.URL,
|
||||
GMSGetSnapshotStatusTimeout: time.Hour, // arbitrary, it just can't be 0.
|
||||
},
|
||||
}
|
||||
logger := &logtest.Fake{}
|
||||
client := gmsClientImpl{cfg: cfg, log: logger, httpClient: http.DefaultClient}
|
||||
|
||||
g, gctx := errgroup.WithContext(ctx)
|
||||
for range runtime.NumCPU() * 2 { // run a couple of concurrent requests to check for race condition.
|
||||
g.Go(func() error {
|
||||
resp, err := client.GetSnapshotStatus(gctx, session, snapshot, 0)
|
||||
require.NotNil(t, resp)
|
||||
|
||||
return err
|
||||
})
|
||||
}
|
||||
require.NoError(t, g.Wait())
|
||||
|
||||
require.NotEmpty(t, client.getStatusLastQueried)
|
||||
})
|
||||
|
||||
t.Run("when there is an error in the upstream, it logs and returns the error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
session := cloudmigration.CloudMigrationSession{
|
||||
StackID: 1234,
|
||||
AuthToken: "auth-tok",
|
||||
ClusterSlug: "cluster-slug",
|
||||
}
|
||||
|
||||
snapshot := cloudmigration.CloudMigrationSnapshot{
|
||||
UID: "snapshot-uuid",
|
||||
}
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
require.Equal(t, http.MethodGet, r.Method)
|
||||
|
||||
auth := r.Header.Get("Authorization")
|
||||
require.Equal(t, fmt.Sprintf("Bearer %d:%s", session.StackID, session.AuthToken), auth)
|
||||
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
_, _ = w.Write([]byte(`{"message": "internal server error"}`))
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
cfg := &setting.Cfg{
|
||||
CloudMigration: setting.CloudMigrationSettings{
|
||||
GMSDomain: server.URL,
|
||||
GMSGetSnapshotStatusTimeout: time.Hour, // arbitrary, it just can't be 0.
|
||||
},
|
||||
}
|
||||
logger := &logtest.Fake{}
|
||||
client := gmsClientImpl{cfg: cfg, log: logger, httpClient: http.DefaultClient}
|
||||
|
||||
resp, err := client.GetSnapshotStatus(ctx, session, snapshot, 0)
|
||||
require.Error(t, err)
|
||||
require.Nil(t, resp)
|
||||
|
||||
require.Equal(t, 1, logger.ErrorLogs.Calls)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_CreatePresignedUploadUrl(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("when the snapshot and session are valid, it returns a presigned url string", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
session := cloudmigration.CloudMigrationSession{
|
||||
StackID: 1234,
|
||||
AuthToken: "auth-tok",
|
||||
ClusterSlug: "cluster-slug",
|
||||
}
|
||||
|
||||
snapshot := cloudmigration.CloudMigrationSnapshot{
|
||||
UID: "snapshot-uuid",
|
||||
}
|
||||
|
||||
expectedURL := "http://example.com"
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
require.Equal(t, http.MethodPost, r.Method)
|
||||
|
||||
auth := r.Header.Get("Authorization")
|
||||
require.Equal(t, fmt.Sprintf("Bearer %d:%s", session.StackID, session.AuthToken), auth)
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte(`{"uploadUrl": "` + expectedURL + `"}`))
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
cfg := &setting.Cfg{
|
||||
CloudMigration: setting.CloudMigrationSettings{
|
||||
GMSDomain: server.URL,
|
||||
GMSCreateUploadUrlTimeout: time.Hour, // arbitrary, it just can't be 0.
|
||||
},
|
||||
}
|
||||
logger := &logtest.Fake{}
|
||||
client := gmsClientImpl{cfg: cfg, log: logger, httpClient: http.DefaultClient}
|
||||
|
||||
url, err := client.CreatePresignedUploadUrl(ctx, session, snapshot)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedURL, url)
|
||||
})
|
||||
|
||||
t.Run("when there is an error in the upstream, it logs and returns the error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
session := cloudmigration.CloudMigrationSession{
|
||||
StackID: 1234,
|
||||
AuthToken: "auth-tok",
|
||||
ClusterSlug: "cluster-slug",
|
||||
}
|
||||
|
||||
snapshot := cloudmigration.CloudMigrationSnapshot{
|
||||
UID: "snapshot-uuid",
|
||||
}
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
require.Equal(t, http.MethodPost, r.Method)
|
||||
|
||||
auth := r.Header.Get("Authorization")
|
||||
require.Equal(t, fmt.Sprintf("Bearer %d:%s", session.StackID, session.AuthToken), auth)
|
||||
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
_, _ = w.Write([]byte(`{"message": "internal server error"}`))
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
cfg := &setting.Cfg{
|
||||
CloudMigration: setting.CloudMigrationSettings{
|
||||
GMSDomain: server.URL,
|
||||
GMSCreateUploadUrlTimeout: time.Hour, // arbitrary, it just can't be 0.
|
||||
},
|
||||
}
|
||||
logger := &logtest.Fake{}
|
||||
client := gmsClientImpl{cfg: cfg, log: logger, httpClient: http.DefaultClient}
|
||||
|
||||
url, err := client.CreatePresignedUploadUrl(ctx, session, snapshot)
|
||||
require.Error(t, err)
|
||||
require.Empty(t, url)
|
||||
|
||||
require.Equal(t, 1, logger.ErrorLogs.Calls)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ReportEvent(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("when the session data is valid, it does not log an error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
session := cloudmigration.CloudMigrationSession{
|
||||
StackID: 1234,
|
||||
AuthToken: "auth-tok",
|
||||
ClusterSlug: "cluster-slug",
|
||||
}
|
||||
|
||||
event := EventRequestDTO{
|
||||
LocalID: "local-id",
|
||||
Event: EventDoneUploadingSnapshot,
|
||||
}
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
require.Equal(t, http.MethodPost, r.Method)
|
||||
|
||||
auth := r.Header.Get("Authorization")
|
||||
require.Equal(t, fmt.Sprintf("Bearer %d:%s", session.StackID, session.AuthToken), auth)
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
cfg := &setting.Cfg{
|
||||
CloudMigration: setting.CloudMigrationSettings{
|
||||
GMSDomain: server.URL,
|
||||
GMSReportEventTimeout: time.Hour, // arbitrary, it just can't be 0.
|
||||
},
|
||||
}
|
||||
logger := &logtest.Fake{}
|
||||
client := gmsClientImpl{cfg: cfg, log: logger, httpClient: http.DefaultClient}
|
||||
|
||||
client.ReportEvent(ctx, session, event)
|
||||
|
||||
require.Zero(t, logger.DebugLogs.Calls)
|
||||
require.Zero(t, logger.WarnLogs.Calls)
|
||||
require.Zero(t, logger.InfoLogs.Calls)
|
||||
require.Zero(t, logger.ErrorLogs.Calls)
|
||||
})
|
||||
|
||||
t.Run("when the session is missing required data, it returns without doing anything", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
event := EventRequestDTO{
|
||||
Event: EventDoneUploadingSnapshot,
|
||||
}
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
require.True(t, false) // This will never be called, but if it does, it will cause the test to fail.
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
cfg := &setting.Cfg{
|
||||
CloudMigration: setting.CloudMigrationSettings{
|
||||
GMSDomain: server.URL,
|
||||
GMSReportEventTimeout: 0, // this won't be called.
|
||||
},
|
||||
}
|
||||
logger := &logtest.Fake{}
|
||||
client := gmsClientImpl{cfg: cfg, log: logger, httpClient: http.DefaultClient}
|
||||
|
||||
client.ReportEvent(ctx, cloudmigration.CloudMigrationSession{}, event)
|
||||
|
||||
require.Zero(t, logger.DebugLogs.Calls)
|
||||
require.Zero(t, logger.WarnLogs.Calls)
|
||||
require.Zero(t, logger.InfoLogs.Calls)
|
||||
require.Zero(t, logger.ErrorLogs.Calls)
|
||||
})
|
||||
|
||||
t.Run("when the upstream server is down, it logs the error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
session := cloudmigration.CloudMigrationSession{
|
||||
StackID: 1234,
|
||||
AuthToken: "auth-tok",
|
||||
ClusterSlug: "cluster-slug",
|
||||
}
|
||||
|
||||
event := EventRequestDTO{
|
||||
LocalID: "local-id",
|
||||
Event: EventDoneUploadingSnapshot,
|
||||
}
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
require.Equal(t, http.MethodPost, r.Method)
|
||||
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
_, _ = w.Write([]byte(`{"message": "internal server error"}`))
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
cfg := &setting.Cfg{
|
||||
CloudMigration: setting.CloudMigrationSettings{
|
||||
GMSDomain: server.URL,
|
||||
GMSReportEventTimeout: time.Hour, // arbitrary, it just can't be 0.
|
||||
},
|
||||
}
|
||||
logger := &logtest.Fake{}
|
||||
client := gmsClientImpl{cfg: cfg, log: logger, httpClient: http.DefaultClient}
|
||||
|
||||
client.ReportEvent(ctx, session, event)
|
||||
|
||||
require.Zero(t, logger.DebugLogs.Calls)
|
||||
require.Zero(t, logger.WarnLogs.Calls)
|
||||
require.Zero(t, logger.InfoLogs.Calls)
|
||||
require.Equal(t, 2, logger.ErrorLogs.Calls)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -75,6 +75,8 @@ func TestShortURLService(t *testing.T) {
|
||||
|
||||
t.Run("and no action when no stale short urls exist", func(t *testing.T) {
|
||||
cmd := shorturls.DeleteShortUrlCommand{OlderThan: time.Unix(existingShortURL.CreatedAt, 0)}
|
||||
|
||||
err = service.DeleteStaleShortURLs(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(0), cmd.NumDeleted)
|
||||
})
|
||||
@@ -89,4 +91,46 @@ func TestShortURLService(t *testing.T) {
|
||||
require.True(t, shorturls.ErrShortURLNotFound.Is(err))
|
||||
require.Nil(t, shortURL)
|
||||
})
|
||||
|
||||
t.Run("User cannot create short URLs from invalid paths", func(t *testing.T) {
|
||||
service := ShortURLService{SQLStore: &sqlStore{db: store}}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
absolutePath := "/path?test=true"
|
||||
newShortURL, err := service.CreateShortURL(ctx, user, absolutePath)
|
||||
require.ErrorIs(t, err, shorturls.ErrShortURLAbsolutePath)
|
||||
require.Nil(t, newShortURL)
|
||||
|
||||
relativePath := "path/../test?test=true"
|
||||
newShortURL, err = service.CreateShortURL(ctx, user, relativePath)
|
||||
require.ErrorIs(t, err, shorturls.ErrShortURLInvalidPath)
|
||||
require.Nil(t, newShortURL)
|
||||
|
||||
relativePath = "../path/test?test=true"
|
||||
newShortURL, err = service.CreateShortURL(ctx, user, relativePath)
|
||||
require.ErrorIs(t, err, shorturls.ErrShortURLInvalidPath)
|
||||
require.Nil(t, newShortURL)
|
||||
})
|
||||
|
||||
t.Run("The same URL will generate different entries", func(t *testing.T) {
|
||||
service := ShortURLService{SQLStore: &sqlStore{db: store}}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
const refPath = "mock/path?test=true"
|
||||
|
||||
newShortURL1, err := service.CreateShortURL(ctx, user, refPath)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, newShortURL1)
|
||||
require.NotEmpty(t, newShortURL1.Uid)
|
||||
|
||||
newShortURL2, err := service.CreateShortURL(ctx, user, refPath)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, newShortURL2)
|
||||
require.NotEmpty(t, newShortURL2.Uid)
|
||||
|
||||
require.NotEqual(t, newShortURL1.Uid, newShortURL2.Uid)
|
||||
require.Equal(t, newShortURL1.Path, newShortURL2.Path)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,10 +3,13 @@ package supportbundlestest
|
||||
import "github.com/grafana/grafana/pkg/services/supportbundles"
|
||||
|
||||
type FakeBundleService struct {
|
||||
Collectors []supportbundles.Collector
|
||||
}
|
||||
|
||||
func NewFakeBundleService() *FakeBundleService {
|
||||
return &FakeBundleService{}
|
||||
}
|
||||
|
||||
func (s *FakeBundleService) RegisterSupportItemCollector(collector supportbundles.Collector) {}
|
||||
func (s *FakeBundleService) RegisterSupportItemCollector(collector supportbundles.Collector) {
|
||||
s.Collectors = append(s.Collectors, collector)
|
||||
}
|
||||
|
||||
@@ -78,6 +78,12 @@ func TestParseTagPairs(t *testing.T) {
|
||||
assert.Equal(t, "key", tags[2].Key)
|
||||
assert.Equal(t, "val2", tags[2].Value)
|
||||
})
|
||||
|
||||
t.Run("Nil tag returns an allocated but empty pair", func(t *testing.T) {
|
||||
tags := ParseTagPairs(nil)
|
||||
require.NotNil(t, tags)
|
||||
require.Empty(t, tags)
|
||||
})
|
||||
}
|
||||
|
||||
func TestJoinTagPairs(t *testing.T) {
|
||||
|
||||
@@ -26,10 +26,12 @@ func testIntegrationSavingTags(t *testing.T, fn getStore) {
|
||||
{Key: "outage"},
|
||||
{Key: "type", Value: "outage"},
|
||||
{Key: "server", Value: "server-1"},
|
||||
{Key: "server", Value: "server-1"}, // duplicates will generate a new ID.
|
||||
{Key: "error"},
|
||||
{Key: "error"}, // duplicates will generate a new ID.
|
||||
}
|
||||
tags, err := store.EnsureTagsExist(context.Background(), tagPairs)
|
||||
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 4, len(tags))
|
||||
require.Equal(t, len(tagPairs), len(tags))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
package updatechecker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGrafanaService(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("when the Grafana version is stable", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("and the latest version is newer than the current one", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
grafanaVersion := "99.0.0"
|
||||
latestVersion := "99.0.1"
|
||||
|
||||
httpClient := &fakeHTTPClient{
|
||||
fakeResp: `{"version": "` + latestVersion + `"}`,
|
||||
}
|
||||
|
||||
service := &GrafanaService{
|
||||
enabled: true,
|
||||
grafanaVersion: grafanaVersion,
|
||||
httpClient: httpClient,
|
||||
log: log.NewNopLogger(),
|
||||
tracer: tracing.NewNoopTracerService(),
|
||||
}
|
||||
|
||||
err := service.checkForUpdates(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, service.UpdateAvailable())
|
||||
require.Equal(t, latestVersion, service.LatestVersion())
|
||||
require.False(t, service.IsDisabled())
|
||||
})
|
||||
|
||||
t.Run("and the latest version is the same as the current one", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
grafanaVersion := "99.0.0"
|
||||
latestVersion := grafanaVersion
|
||||
|
||||
httpClient := &fakeHTTPClient{
|
||||
fakeResp: `{"version": "` + latestVersion + `"}`,
|
||||
}
|
||||
|
||||
service := &GrafanaService{
|
||||
enabled: true,
|
||||
grafanaVersion: grafanaVersion,
|
||||
httpClient: httpClient,
|
||||
log: log.NewNopLogger(),
|
||||
tracer: tracing.NewNoopTracerService(),
|
||||
}
|
||||
|
||||
err := service.checkForUpdates(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.False(t, service.UpdateAvailable())
|
||||
require.Equal(t, grafanaVersion, service.LatestVersion())
|
||||
require.False(t, service.IsDisabled())
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("when the Grafana version is a development build", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
grafanaVersion := "99.0.0-222555"
|
||||
lastestVersion := "99.0.1"
|
||||
|
||||
httpClient := &fakeHTTPClient{
|
||||
fakeResp: `{"version": "` + lastestVersion + `"}`,
|
||||
}
|
||||
|
||||
service := &GrafanaService{
|
||||
enabled: true,
|
||||
grafanaVersion: grafanaVersion,
|
||||
httpClient: httpClient,
|
||||
log: log.NewNopLogger(),
|
||||
tracer: tracing.NewNoopTracerService(),
|
||||
}
|
||||
|
||||
err := service.checkForUpdates(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.False(t, service.UpdateAvailable())
|
||||
require.Empty(t, service.LatestVersion())
|
||||
require.False(t, service.IsDisabled())
|
||||
})
|
||||
}
|
||||
|
||||
func TestGrafanaService_Run(t *testing.T) {
|
||||
latestVersion := "99.0.1"
|
||||
|
||||
service := &GrafanaService{
|
||||
enabled: true,
|
||||
grafanaVersion: "99.0.0",
|
||||
httpClient: &fakeHTTPClient{
|
||||
fakeResp: `{"version": "` + latestVersion + `"}`,
|
||||
},
|
||||
log: log.NewNopLogger(),
|
||||
tracer: tracing.NewNoopTracerService(),
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
// Initially there won't be any data.
|
||||
require.False(t, service.UpdateAvailable())
|
||||
require.Empty(t, service.LatestVersion())
|
||||
|
||||
// Run in the background so we can cancel it after the first run.
|
||||
errChan := make(chan error, 1)
|
||||
go func() {
|
||||
errChan <- service.Run(ctx)
|
||||
}()
|
||||
|
||||
// It will run once immediately then schedule it for 24 hours later. This will be true because latest > current.
|
||||
require.Eventually(t, func() bool { return service.UpdateAvailable() }, 5*time.Second, 20*time.Millisecond)
|
||||
|
||||
cancel()
|
||||
|
||||
require.ErrorIs(t, <-errChan, context.Canceled)
|
||||
}
|
||||
Reference in New Issue
Block a user