test: improve folder integration test performance (#113518)
This commit is contained in:
@@ -68,7 +68,7 @@ jobs:
|
||||
run: |
|
||||
set -euo pipefail
|
||||
readarray -t PACKAGES <<< "$(./scripts/ci/backend-tests/pkgs-with-tests-named.sh -b TestIntegration | ./scripts/ci/backend-tests/shard.sh -N"$SHARD" -d-)"
|
||||
go test -tags=sqlite -timeout=12m -run '^TestIntegration' "${PACKAGES[@]}"
|
||||
go test -tags=sqlite -timeout=8m -run '^TestIntegration' "${PACKAGES[@]}"
|
||||
|
||||
sqlite_nocgo:
|
||||
needs: detect-changes
|
||||
@@ -109,7 +109,7 @@ jobs:
|
||||
# Build regex pattern like: pkg1$|pkg2$|pkg3$
|
||||
SKIP_PATTERN=$(echo "$SKIP_PACKAGES" | sed '/^$/d' | sed 's|.*|&$|' | paste -sd '|' -)
|
||||
readarray -t PACKAGES <<< "$(./scripts/ci/backend-tests/pkgs-with-tests-named.sh -b TestIntegration | ./scripts/ci/backend-tests/shard.sh -N "$SHARD" -d - | grep -Ev "($SKIP_PATTERN)")"
|
||||
go test -tags=sqlite -timeout=12m -run '^TestIntegration' "${PACKAGES[@]}"
|
||||
go test -tags=sqlite -timeout=8m -run '^TestIntegration' "${PACKAGES[@]}"
|
||||
- name: Run profiled tests
|
||||
id: run-profiled-tests
|
||||
if: matrix.shard == 'profiled'
|
||||
@@ -135,7 +135,7 @@ jobs:
|
||||
pkg_name=$(basename "$full_pkg" | tr '/' '_' | tr '.' '_')
|
||||
echo "📦 Running $full_pkg"
|
||||
set +e
|
||||
go test -tags=sqlite -timeout=12m -run '^TestIntegration' \
|
||||
go test -tags=sqlite -timeout=8m -run '^TestIntegration' \
|
||||
-outputdir=profiles \
|
||||
-cpuprofile="cpu_${pkg_name}.prof" \
|
||||
-memprofile="mem_${pkg_name}.prof" \
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -730,50 +731,74 @@ func TestIntegrationFolderCreatePermissions(t *testing.T) {
|
||||
|
||||
// test on all dualwriter modes
|
||||
for mode := 0; mode <= 4; mode++ {
|
||||
for _, tc := range tcs {
|
||||
t.Run(fmt.Sprintf("[Mode: %v] "+tc.description, mode), func(t *testing.T) {
|
||||
modeDw := grafanarest.DualWriterMode(mode)
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
AppModeProduction: true,
|
||||
DisableAnonymous: true,
|
||||
APIServerStorageType: "unified",
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
folders.RESOURCEGROUP: {
|
||||
DualWriterMode: modeDw,
|
||||
},
|
||||
t.Run(fmt.Sprintf("Mode_%d", mode), func(t *testing.T) {
|
||||
modeDw := grafanarest.DualWriterMode(mode)
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
AppModeProduction: true,
|
||||
DisableAnonymous: true,
|
||||
APIServerStorageType: "unified",
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
folders.RESOURCEGROUP: {
|
||||
DualWriterMode: modeDw,
|
||||
},
|
||||
})
|
||||
|
||||
user := helper.CreateUser("user", apis.Org1, org.RoleViewer, tc.permissions)
|
||||
|
||||
parentPayload := `{
|
||||
"title": "Test/parent",
|
||||
"uid": "parentuid"
|
||||
}`
|
||||
parentCreate := apis.DoRequest(helper, apis.RequestParams{
|
||||
User: helper.Org1.Admin,
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/folders",
|
||||
Body: []byte(parentPayload),
|
||||
}, &folder.Folder{})
|
||||
require.NotNil(t, parentCreate.Result)
|
||||
parentUID := parentCreate.Result.UID
|
||||
require.NotEmpty(t, parentUID)
|
||||
|
||||
resp := apis.DoRequest(helper, apis.RequestParams{
|
||||
User: user,
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/folders",
|
||||
Body: []byte(tc.input),
|
||||
}, &dtos.Folder{})
|
||||
require.Equal(t, tc.expectedCode, resp.Response.StatusCode)
|
||||
|
||||
if tc.expectedCode == http.StatusOK {
|
||||
require.Equal(t, "uid", resp.Result.UID)
|
||||
require.Equal(t, "Folder", resp.Result.Title)
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
for i, tc := range tcs {
|
||||
t.Run(fmt.Sprintf("[Mode: %v] "+tc.description, mode), func(t *testing.T) {
|
||||
username := fmt.Sprintf("user-%d", i)
|
||||
parentUID := fmt.Sprintf("parentuid-%d", i)
|
||||
childUID := fmt.Sprintf("uid-%d", i)
|
||||
|
||||
// Update permissions to use unique parent UID
|
||||
permissions := make([]resourcepermissions.SetResourcePermissionCommand, len(tc.permissions))
|
||||
for j, perm := range tc.permissions {
|
||||
permissions[j] = perm
|
||||
if perm.ResourceID == "parentuid" {
|
||||
permissions[j].ResourceID = parentUID
|
||||
}
|
||||
}
|
||||
|
||||
user := helper.CreateUser(username, apis.Org1, org.RoleViewer, permissions)
|
||||
|
||||
// Get user ID for cleanup
|
||||
userID, _ := user.Identity.GetInternalID()
|
||||
|
||||
// Register cleanup for this test case
|
||||
t.Cleanup(helper.CleanupTestResources([]string{parentUID, childUID}, []int64{userID}))
|
||||
|
||||
parentPayload := fmt.Sprintf(`{
|
||||
"title": "Test/parent",
|
||||
"uid": "%s"
|
||||
}`, parentUID)
|
||||
parentCreate := apis.DoRequest(helper, apis.RequestParams{
|
||||
User: helper.Org1.Admin,
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/folders",
|
||||
Body: []byte(parentPayload),
|
||||
}, &folder.Folder{})
|
||||
require.NotNil(t, parentCreate.Result)
|
||||
createdParentUID := parentCreate.Result.UID
|
||||
require.NotEmpty(t, createdParentUID)
|
||||
|
||||
// Update input to use unique UIDs
|
||||
input := strings.ReplaceAll(tc.input, "parentuid", parentUID)
|
||||
input = strings.ReplaceAll(input, `"uid": "uid"`, fmt.Sprintf(`"uid": "%s"`, childUID))
|
||||
|
||||
resp := apis.DoRequest(helper, apis.RequestParams{
|
||||
User: user,
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/folders",
|
||||
Body: []byte(input),
|
||||
}, &dtos.Folder{})
|
||||
require.Equal(t, tc.expectedCode, resp.Response.StatusCode)
|
||||
|
||||
if tc.expectedCode == http.StatusOK {
|
||||
require.Equal(t, childUID, resp.Result.UID)
|
||||
require.Equal(t, "Folder", resp.Result.Title)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -833,98 +858,137 @@ func TestIntegrationFolderGetPermissions(t *testing.T) {
|
||||
|
||||
// test on all dualwriter modes
|
||||
for mode := 0; mode <= 4; mode++ {
|
||||
for _, tc := range tcs {
|
||||
t.Run(fmt.Sprintf("[Mode: %v] "+tc.description, mode), func(t *testing.T) {
|
||||
modeDw := grafanarest.DualWriterMode(mode)
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
AppModeProduction: true,
|
||||
DisableAnonymous: true,
|
||||
APIServerStorageType: "unified",
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
folders.RESOURCEGROUP: {
|
||||
DualWriterMode: modeDw,
|
||||
},
|
||||
t.Run(fmt.Sprintf("Mode_%d", mode), func(t *testing.T) {
|
||||
modeDw := grafanarest.DualWriterMode(mode)
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
AppModeProduction: true,
|
||||
DisableAnonymous: true,
|
||||
APIServerStorageType: "unified",
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
folders.RESOURCEGROUP: {
|
||||
DualWriterMode: modeDw,
|
||||
},
|
||||
})
|
||||
|
||||
// Create parent folder
|
||||
parentPayload := `{
|
||||
"title": "testparent",
|
||||
"uid": "parentuid"
|
||||
}`
|
||||
parentCreate := apis.DoRequest(helper, apis.RequestParams{
|
||||
User: helper.Org1.Admin,
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/folders",
|
||||
Body: []byte(parentPayload),
|
||||
}, &folder.Folder{})
|
||||
require.NotNil(t, parentCreate.Result)
|
||||
parentUID := parentCreate.Result.UID
|
||||
require.NotEmpty(t, parentUID)
|
||||
|
||||
// Create descendant folder
|
||||
payload := "{ \"uid\": \"descuid\", \"title\": \"Folder\", \"parentUid\": \"parentuid\"}"
|
||||
resp := apis.DoRequest(helper, apis.RequestParams{
|
||||
User: helper.Org1.Admin,
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/folders",
|
||||
Body: []byte(payload),
|
||||
}, &dtos.Folder{})
|
||||
require.Equal(t, http.StatusOK, resp.Response.StatusCode)
|
||||
|
||||
user := helper.CreateUser("user", apis.Org1, org.RoleNone, tc.permissions)
|
||||
|
||||
// Get with accesscontrol disabled
|
||||
getResp := apis.DoRequest(helper, apis.RequestParams{
|
||||
User: user,
|
||||
Method: http.MethodGet,
|
||||
Path: "/api/folders/descuid",
|
||||
}, &dtos.Folder{})
|
||||
require.Equal(t, tc.expectedCode, getResp.Response.StatusCode)
|
||||
require.NotNil(t, getResp.Result)
|
||||
|
||||
require.False(t, getResp.Result.AccessControl[dashboards.ActionFoldersRead])
|
||||
require.False(t, getResp.Result.AccessControl[dashboards.ActionFoldersWrite])
|
||||
|
||||
parents := getResp.Result.Parents
|
||||
require.Equal(t, len(tc.expectedParentUIDs), len(parents))
|
||||
require.Equal(t, len(tc.expectedParentTitles), len(parents))
|
||||
for i := 0; i < len(tc.expectedParentUIDs); i++ {
|
||||
require.Equal(t, tc.expectedParentUIDs[i], parents[i].UID)
|
||||
require.Equal(t, tc.expectedParentTitles[i], parents[i].Title)
|
||||
}
|
||||
|
||||
// Get with accesscontrol enabled
|
||||
if tc.checkAccessControl {
|
||||
acPerms := []resourcepermissions.SetResourcePermissionCommand{
|
||||
{
|
||||
Actions: []string{dashboards.ActionFoldersRead},
|
||||
Resource: "folders",
|
||||
ResourceAttribute: "uid",
|
||||
ResourceID: "*",
|
||||
},
|
||||
{
|
||||
Actions: []string{dashboards.ActionFoldersWrite},
|
||||
Resource: "folders",
|
||||
ResourceAttribute: "uid",
|
||||
ResourceID: "parentuid",
|
||||
},
|
||||
}
|
||||
acUser := helper.CreateUser("acuser", apis.Org1, org.RoleNone, acPerms)
|
||||
|
||||
getWithAC := apis.DoRequest(helper, apis.RequestParams{
|
||||
User: acUser,
|
||||
Method: http.MethodGet,
|
||||
Path: "/api/folders/descuid?accesscontrol=true",
|
||||
}, &dtos.Folder{})
|
||||
require.Equal(t, tc.expectedCode, getWithAC.Response.StatusCode)
|
||||
require.NotNil(t, getWithAC.Result)
|
||||
|
||||
require.True(t, getWithAC.Result.AccessControl[dashboards.ActionFoldersRead])
|
||||
require.True(t, getWithAC.Result.AccessControl[dashboards.ActionFoldersWrite])
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Run all test cases within the same server instance
|
||||
for i, tc := range tcs {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
// Use unique UIDs per test case to avoid conflicts
|
||||
parentUID := fmt.Sprintf("parentuid-%d", i)
|
||||
descUID := fmt.Sprintf("descuid-%d", i)
|
||||
parentTitle := fmt.Sprintf("testparent-%d", i)
|
||||
userLogin := fmt.Sprintf("user-%d", i)
|
||||
acUserLogin := fmt.Sprintf("acuser-%d", i)
|
||||
|
||||
// Create parent folder
|
||||
parentPayload := fmt.Sprintf(`{
|
||||
"title": "%s",
|
||||
"uid": "%s"
|
||||
}`, parentTitle, parentUID)
|
||||
parentCreate := apis.DoRequest(helper, apis.RequestParams{
|
||||
User: helper.Org1.Admin,
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/folders",
|
||||
Body: []byte(parentPayload),
|
||||
}, &folder.Folder{})
|
||||
require.NotNil(t, parentCreate.Result)
|
||||
require.Equal(t, parentUID, parentCreate.Result.UID)
|
||||
|
||||
// Create descendant folder
|
||||
payload := fmt.Sprintf(`{ "uid": "%s", "title": "Folder-%d", "parentUid": "%s"}`, descUID, i, parentUID)
|
||||
resp := apis.DoRequest(helper, apis.RequestParams{
|
||||
User: helper.Org1.Admin,
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/folders",
|
||||
Body: []byte(payload),
|
||||
}, &dtos.Folder{})
|
||||
require.Equal(t, http.StatusOK, resp.Response.StatusCode)
|
||||
|
||||
// Update permissions to use unique descUID where needed
|
||||
permissions := tc.permissions
|
||||
for j := range permissions {
|
||||
if permissions[j].ResourceID == "descuid" {
|
||||
permissions[j].ResourceID = descUID
|
||||
}
|
||||
}
|
||||
|
||||
user := helper.CreateUser(userLogin, apis.Org1, org.RoleNone, permissions)
|
||||
|
||||
// Get user ID for cleanup
|
||||
userID, err := user.Identity.GetInternalID()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Register cleanup to delete created resources
|
||||
t.Cleanup(helper.CleanupTestResources([]string{descUID, parentUID}, []int64{userID}))
|
||||
|
||||
// Adjust expected UIDs and titles
|
||||
expectedParentUIDs := tc.expectedParentUIDs
|
||||
expectedParentTitles := tc.expectedParentTitles
|
||||
if len(expectedParentUIDs) > 0 {
|
||||
expectedParentUIDs = []string{parentUID}
|
||||
expectedParentTitles = []string{parentTitle}
|
||||
}
|
||||
|
||||
// Get with accesscontrol disabled
|
||||
getResp := apis.DoRequest(helper, apis.RequestParams{
|
||||
User: user,
|
||||
Method: http.MethodGet,
|
||||
Path: "/api/folders/" + descUID,
|
||||
}, &dtos.Folder{})
|
||||
require.Equal(t, tc.expectedCode, getResp.Response.StatusCode)
|
||||
|
||||
if tc.expectedCode == http.StatusOK {
|
||||
require.NotNil(t, getResp.Result)
|
||||
require.False(t, getResp.Result.AccessControl[dashboards.ActionFoldersRead])
|
||||
require.False(t, getResp.Result.AccessControl[dashboards.ActionFoldersWrite])
|
||||
|
||||
parents := getResp.Result.Parents
|
||||
require.Equal(t, len(expectedParentUIDs), len(parents))
|
||||
require.Equal(t, len(expectedParentTitles), len(parents))
|
||||
for j := 0; j < len(expectedParentUIDs); j++ {
|
||||
require.Equal(t, expectedParentUIDs[j], parents[j].UID)
|
||||
require.Equal(t, expectedParentTitles[j], parents[j].Title)
|
||||
}
|
||||
|
||||
// Get with accesscontrol enabled
|
||||
if tc.checkAccessControl {
|
||||
acPerms := []resourcepermissions.SetResourcePermissionCommand{
|
||||
{
|
||||
Actions: []string{dashboards.ActionFoldersRead},
|
||||
Resource: "folders",
|
||||
ResourceAttribute: "uid",
|
||||
ResourceID: "*",
|
||||
},
|
||||
{
|
||||
Actions: []string{dashboards.ActionFoldersWrite},
|
||||
Resource: "folders",
|
||||
ResourceAttribute: "uid",
|
||||
ResourceID: parentUID,
|
||||
},
|
||||
}
|
||||
acUser := helper.CreateUser(acUserLogin, apis.Org1, org.RoleNone, acPerms)
|
||||
|
||||
// Get user ID for cleanup
|
||||
acUserID, err := acUser.Identity.GetInternalID()
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(helper.CleanupTestResources([]string{}, []int64{acUserID}))
|
||||
|
||||
getWithAC := apis.DoRequest(helper, apis.RequestParams{
|
||||
User: acUser,
|
||||
Method: http.MethodGet,
|
||||
Path: "/api/folders/" + descUID + "?accesscontrol=true",
|
||||
}, &dtos.Folder{})
|
||||
require.Equal(t, tc.expectedCode, getWithAC.Response.StatusCode)
|
||||
require.NotNil(t, getWithAC.Result)
|
||||
|
||||
require.True(t, getWithAC.Result.AccessControl[dashboards.ActionFoldersRead])
|
||||
require.True(t, getWithAC.Result.AccessControl[dashboards.ActionFoldersWrite])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1012,21 +1076,26 @@ func TestIntegrationFoldersCreateAPIEndpointK8S(t *testing.T) {
|
||||
|
||||
// test on all dualwriter modes
|
||||
for mode := 0; mode <= 4; mode++ {
|
||||
for _, tc := range tcs {
|
||||
modeDw := grafanarest.DualWriterMode(mode)
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
AppModeProduction: true,
|
||||
DisableAnonymous: true,
|
||||
APIServerStorageType: "unified",
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
folders.RESOURCEGROUP: {
|
||||
DualWriterMode: modeDw,
|
||||
},
|
||||
},
|
||||
})
|
||||
for i, tc := range tcs {
|
||||
t.Run(fmt.Sprintf("[Mode: %v] "+testDescription(tc.description, tc.expectedFolderSvcError), mode), func(t *testing.T) {
|
||||
modeDw := grafanarest.DualWriterMode(mode)
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
AppModeProduction: true,
|
||||
DisableAnonymous: true,
|
||||
APIServerStorageType: "unified",
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
folders.RESOURCEGROUP: {
|
||||
DualWriterMode: modeDw,
|
||||
},
|
||||
},
|
||||
})
|
||||
username := fmt.Sprintf("user-%d", i)
|
||||
folderUID := fmt.Sprintf("uid-%d", i)
|
||||
|
||||
userTest := helper.CreateUser("user", apis.Org1, org.RoleViewer, tc.permissions)
|
||||
// Update input to use unique UIDs
|
||||
input := strings.ReplaceAll(tc.input, `"uid": "uid"`, fmt.Sprintf(`"uid": "%s"`, folderUID))
|
||||
|
||||
userTest := helper.CreateUser(username, apis.Org1, org.RoleViewer, tc.permissions)
|
||||
|
||||
if tc.createSecondRecord {
|
||||
client := helper.GetResourceClient(apis.ResourceClientArgs{
|
||||
@@ -1037,7 +1106,7 @@ func TestIntegrationFoldersCreateAPIEndpointK8S(t *testing.T) {
|
||||
User: client.Args.User,
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/folders",
|
||||
Body: []byte(tc.input),
|
||||
Body: []byte(input),
|
||||
}, &folder.Folder{})
|
||||
require.NotEmpty(t, create2.Response)
|
||||
require.Equal(t, http.StatusOK, create2.Response.StatusCode)
|
||||
@@ -1045,13 +1114,13 @@ func TestIntegrationFoldersCreateAPIEndpointK8S(t *testing.T) {
|
||||
|
||||
addr := helper.GetEnv().Server.HTTPServer.Listener.Addr()
|
||||
login := userTest.Identity.GetLogin()
|
||||
baseUrl := fmt.Sprintf("http://%s:%s@%s", login, user.Password("user"), addr)
|
||||
baseUrl := fmt.Sprintf("http://%s:%s@%s", login, user.Password(username), addr)
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, fmt.Sprintf(
|
||||
"%s%s",
|
||||
baseUrl,
|
||||
"/api/folders",
|
||||
), bytes.NewBuffer([]byte(tc.input)))
|
||||
), bytes.NewBuffer([]byte(input)))
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
@@ -1071,7 +1140,7 @@ func TestIntegrationFoldersCreateAPIEndpointK8S(t *testing.T) {
|
||||
require.NoError(t, resp.Body.Close())
|
||||
|
||||
if tc.expectedCode == http.StatusOK {
|
||||
require.Equal(t, "uid", folder.UID)
|
||||
require.Equal(t, folderUID, folder.UID)
|
||||
require.Equal(t, "Folder", folder.Title)
|
||||
}
|
||||
|
||||
@@ -1179,78 +1248,109 @@ func TestIntegrationFoldersGetAPIEndpointK8S(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
// test on all dualwriter modes
|
||||
for mode := 0; mode <= 4; mode++ {
|
||||
for _, tc := range tcs {
|
||||
t.Run(fmt.Sprintf("Mode: %d, %s", mode, tc.description), func(t *testing.T) {
|
||||
modeDw := grafanarest.DualWriterMode(mode)
|
||||
t.Run(fmt.Sprintf("Mode_%d", mode), func(t *testing.T) {
|
||||
modeDw := grafanarest.DualWriterMode(mode)
|
||||
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
AppModeProduction: true,
|
||||
DisableAnonymous: true,
|
||||
APIServerStorageType: "unified",
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
folders.RESOURCEGROUP: {
|
||||
DualWriterMode: modeDw,
|
||||
},
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
AppModeProduction: true,
|
||||
DisableAnonymous: true,
|
||||
APIServerStorageType: "unified",
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
folders.RESOURCEGROUP: {
|
||||
DualWriterMode: modeDw,
|
||||
},
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagUnifiedStorageSearch,
|
||||
},
|
||||
})
|
||||
},
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagUnifiedStorageSearch,
|
||||
},
|
||||
})
|
||||
|
||||
userTest := helper.CreateUser("user", apis.Org1, org.RoleNone, tc.permissions)
|
||||
// Run all test cases within the same server instance
|
||||
for i, tc := range tcs {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
// Use unique UIDs for folders to avoid conflicts between test cases
|
||||
userTest := helper.CreateUser(fmt.Sprintf("user-%d", i), apis.Org1, org.RoleNone, tc.permissions)
|
||||
|
||||
for _, f := range tc.createFolders {
|
||||
client := helper.GetResourceClient(apis.ResourceClientArgs{
|
||||
User: userTest,
|
||||
GVR: gvr,
|
||||
})
|
||||
create2 := apis.DoRequest(helper, apis.RequestParams{
|
||||
User: client.Args.User,
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/folders",
|
||||
Body: []byte(f),
|
||||
}, &folder.Folder{})
|
||||
require.NotEmpty(t, create2.Response)
|
||||
require.Equal(t, http.StatusOK, create2.Response.StatusCode)
|
||||
}
|
||||
// Create folders with unique UIDs per test case
|
||||
for _, f := range tc.createFolders {
|
||||
// Replace hardcoded UIDs with unique ones
|
||||
uniqueFolder := f
|
||||
uniqueFolder = strings.Replace(uniqueFolder, `"foo"`, fmt.Sprintf(`"foo-%d"`, i), 1)
|
||||
uniqueFolder = strings.Replace(uniqueFolder, `"bar"`, fmt.Sprintf(`"bar-%d"`, i), 1)
|
||||
uniqueFolder = strings.Replace(uniqueFolder, `"qux"`, fmt.Sprintf(`"qux-%d"`, i), 1)
|
||||
uniqueFolder = strings.Replace(uniqueFolder, `"parentUid": "foo"`, fmt.Sprintf(`"parentUid": "foo-%d"`, i), 1)
|
||||
|
||||
addr := helper.GetEnv().Server.HTTPServer.Listener.Addr()
|
||||
login := userTest.Identity.GetLogin()
|
||||
baseUrl := fmt.Sprintf("http://%s:%s@%s", login, user.Password("user"), addr)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf(
|
||||
"%s%s",
|
||||
baseUrl,
|
||||
fmt.Sprintf("/api/folders%s", tc.params),
|
||||
), nil)
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
if tc.requestToAnotherOrg {
|
||||
req.Header.Set("x-grafana-org-id", "2")
|
||||
}
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, resp)
|
||||
require.Equal(t, tc.expectedCode, resp.StatusCode)
|
||||
|
||||
if tc.expectedCode == http.StatusOK {
|
||||
list := []dtos.FolderSearchHit{}
|
||||
err = json.NewDecoder(resp.Body).Decode(&list)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, resp.Body.Close())
|
||||
|
||||
// ignore IDs
|
||||
for i := 0; i < len(list); i++ {
|
||||
list[i].ID = 0
|
||||
client := helper.GetResourceClient(apis.ResourceClientArgs{
|
||||
User: userTest,
|
||||
GVR: gvr,
|
||||
})
|
||||
create2 := apis.DoRequest(helper, apis.RequestParams{
|
||||
User: client.Args.User,
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/folders",
|
||||
Body: []byte(uniqueFolder),
|
||||
}, &folder.Folder{})
|
||||
require.NotEmpty(t, create2.Response)
|
||||
require.Equal(t, http.StatusOK, create2.Response.StatusCode)
|
||||
}
|
||||
|
||||
require.ElementsMatch(t, tc.expectedOutput, list)
|
||||
}
|
||||
})
|
||||
}
|
||||
addr := helper.GetEnv().Server.HTTPServer.Listener.Addr()
|
||||
login := userTest.Identity.GetLogin()
|
||||
baseUrl := fmt.Sprintf("http://%s:%s@%s", login, user.Password(fmt.Sprintf("user-%d", i)), addr)
|
||||
|
||||
// Adjust params with unique UIDs
|
||||
params := tc.params
|
||||
params = strings.ReplaceAll(params, "foo", fmt.Sprintf("foo-%d", i))
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf(
|
||||
"%s%s",
|
||||
baseUrl,
|
||||
fmt.Sprintf("/api/folders%s", params),
|
||||
), nil)
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
if tc.requestToAnotherOrg {
|
||||
req.Header.Set("x-grafana-org-id", "2")
|
||||
}
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, resp)
|
||||
require.Equal(t, tc.expectedCode, resp.StatusCode)
|
||||
|
||||
if tc.expectedCode == http.StatusOK {
|
||||
list := []dtos.FolderSearchHit{}
|
||||
err = json.NewDecoder(resp.Body).Decode(&list)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, resp.Body.Close())
|
||||
|
||||
// Adjust expected output with unique UIDs
|
||||
expectedOutput := make([]dtos.FolderSearchHit, len(tc.expectedOutput))
|
||||
for j, output := range tc.expectedOutput {
|
||||
expectedOutput[j] = output
|
||||
expectedOutput[j].ID = 0 // ignore IDs
|
||||
switch output.UID {
|
||||
case "foo":
|
||||
expectedOutput[j].UID = fmt.Sprintf("foo-%d", i)
|
||||
case "bar":
|
||||
expectedOutput[j].UID = fmt.Sprintf("bar-%d", i)
|
||||
expectedOutput[j].ParentUID = fmt.Sprintf("foo-%d", i)
|
||||
case "qux":
|
||||
expectedOutput[j].UID = fmt.Sprintf("qux-%d", i)
|
||||
}
|
||||
}
|
||||
|
||||
// ignore IDs in actual list
|
||||
for j := 0; j < len(list); j++ {
|
||||
list[j].ID = 0
|
||||
}
|
||||
|
||||
require.ElementsMatch(t, expectedOutput, list)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,24 @@ const (
|
||||
Org2 = "OrgB"
|
||||
)
|
||||
|
||||
var (
|
||||
sharedHTTPClient = &http.Client{
|
||||
Timeout: 30 * time.Second,
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
},
|
||||
Transport: &http.Transport{
|
||||
MaxIdleConns: 1000,
|
||||
MaxIdleConnsPerHost: 500,
|
||||
MaxConnsPerHost: 500,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
DisableKeepAlives: false,
|
||||
DisableCompression: true,
|
||||
ForceAttemptHTTP2: false,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type K8sTestHelper struct {
|
||||
t *testing.T
|
||||
listenerAddress string
|
||||
@@ -498,12 +516,8 @@ func DoRequest[T any](c *K8sTestHelper, params RequestParams, result *T) K8sResp
|
||||
if params.Accept != "" {
|
||||
req.Header.Set("Accept", params.Accept)
|
||||
}
|
||||
client := &http.Client{
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
},
|
||||
}
|
||||
rsp, err := client.Do(req)
|
||||
|
||||
rsp, err := sharedHTTPClient.Do(req)
|
||||
require.NoError(c.t, err)
|
||||
|
||||
r := K8sResponse[T]{
|
||||
@@ -943,6 +957,50 @@ func (c *K8sTestHelper) DeleteServiceAccount(user User, orgID int64, saID int64)
|
||||
require.Equal(c.t, http.StatusOK, resp.Response.StatusCode, "failed to delete service account, body: %s", string(resp.Body))
|
||||
}
|
||||
|
||||
func (c *K8sTestHelper) DeleteFolder(user User, folderUID string) error {
|
||||
c.t.Helper()
|
||||
|
||||
resp := DoRequest(c, RequestParams{
|
||||
User: user,
|
||||
Method: http.MethodDelete,
|
||||
Path: fmt.Sprintf("/api/folders/%s", folderUID),
|
||||
}, &struct{}{})
|
||||
|
||||
if resp.Response.StatusCode != http.StatusOK && resp.Response.StatusCode != http.StatusNotFound {
|
||||
return fmt.Errorf("failed to delete folder %s: status %d, body: %s", folderUID, resp.Response.StatusCode, string(resp.Body))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *K8sTestHelper) DeleteUser(adminUser User, userID int64) error {
|
||||
c.t.Helper()
|
||||
|
||||
resp := DoRequest(c, RequestParams{
|
||||
User: adminUser,
|
||||
Method: http.MethodDelete,
|
||||
Path: fmt.Sprintf("/api/admin/users/%d", userID),
|
||||
}, &struct{}{})
|
||||
|
||||
if resp.Response.StatusCode != http.StatusOK && resp.Response.StatusCode != http.StatusNotFound {
|
||||
return fmt.Errorf("failed to delete user %d: status %d, body: %s", userID, resp.Response.StatusCode, string(resp.Body))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *K8sTestHelper) CleanupTestResources(folderUIDs []string, userIDs []int64) func() {
|
||||
return func() {
|
||||
c.t.Helper()
|
||||
// Delete folders first (they may have dependencies)
|
||||
for _, uid := range folderUIDs {
|
||||
_ = c.DeleteFolder(c.Org1.Admin, uid)
|
||||
}
|
||||
// Then delete users
|
||||
for _, id := range userIDs {
|
||||
_ = c.DeleteUser(c.Org1.Admin, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensures that the passed error is an APIStatus error and fails the test if it is not.
|
||||
func (c *K8sTestHelper) RequireApiErrorStatus(err error, reason metav1.StatusReason, httpCode int) metav1.Status {
|
||||
require.Error(c.t, err)
|
||||
|
||||
Reference in New Issue
Block a user