fix: enable dashboard api k8s list tests (#106508)
* fix: separate dashboard api tests * fix: typo * refactor: revert file rename * fix: compilable * fix: trial * fix: remove unused func * Pass the right value for kubernetesDashboardsEnabled flag Signed-off-by: Maicon Costa <maiconscosta@gmail.com> * Enable mode 3 for remaining tests Signed-off-by: Maicon Costa <maiconscosta@gmail.com> * Disable integration tests for MySQL and Postgres Signed-off-by: Maicon Costa <maiconscosta@gmail.com> * Run go work use Signed-off-by: Maicon Costa <maiconscosta@gmail.com> * Update go.mod Signed-off-by: Maicon Costa <maiconscosta@gmail.com> * Remove TODO comment Signed-off-by: Maicon Costa <maiconscosta@gmail.com> * Revert to go 1.24.5 Signed-off-by: Maicon Costa <maiconscosta@gmail.com> * go.mod Signed-off-by: Maicon Costa <maiconscosta@gmail.com> * Remove duplicated block Signed-off-by: Maicon Costa <maiconscosta@gmail.com> * Experiment without reusing client Signed-off-by: Maicon Costa <maiconscosta@gmail.com> * Reenable integration tests on Postgres and MySQL Signed-off-by: Maicon Costa <maiconscosta@gmail.com> * Revert "Experiment without reusing client" This reverts commit0126e321a0. * Reapply "Experiment without reusing client" This reverts commit5368b4c531. * Refactor how we get an adminClient Signed-off-by: Maicon Costa <maiconscosta@gmail.com> * Add TODO comments Signed-off-by: Maicon Costa <maiconscosta@gmail.com> --------- Signed-off-by: Maicon Costa <maiconscosta@gmail.com> Co-authored-by: Maicon Costa <maiconscosta@gmail.com>
This commit is contained in:
co-authored by
Maicon Costa
parent
16339b0788
commit
761150fcb8
@@ -56,8 +56,8 @@ type TestContext struct {
|
||||
OrgID int64
|
||||
}
|
||||
|
||||
// TestIntegrationValidation tests the dashboard K8s API
|
||||
func TestIntegrationValidation(t *testing.T) {
|
||||
// TestIntegrationDashboardAPIValidation tests the dashboard K8s API with validation checks
|
||||
func TestIntegrationDashboardAPIValidation(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
@@ -79,7 +79,19 @@ func TestIntegrationValidation(t *testing.T) {
|
||||
},
|
||||
}})
|
||||
|
||||
testIntegrationValidationForServer(t, helper, dualWriterMode)
|
||||
t.Cleanup(func() {
|
||||
helper.Shutdown()
|
||||
})
|
||||
|
||||
org1Ctx := createTestContext(t, helper, helper.Org1, dualWriterMode)
|
||||
|
||||
t.Run("Dashboard validation tests", func(t *testing.T) {
|
||||
runDashboardValidationTests(t, org1Ctx)
|
||||
})
|
||||
|
||||
t.Run("Dashboard quota tests", func(t *testing.T) {
|
||||
runQuotaTests(t, org1Ctx)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -114,45 +126,110 @@ func TestIntegrationValidation(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func testIntegrationValidationForServer(t *testing.T, helper *apis.K8sTestHelper, dualWriterMode rest.DualWriterMode) {
|
||||
t.Cleanup(func() {
|
||||
helper.Shutdown()
|
||||
})
|
||||
// TestIntegrationDashboardAPIAuthorization tests the dashboard K8s API with authorization checks
|
||||
func TestIntegrationDashboardAPIAuthorization(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
|
||||
// Create test contexts organization
|
||||
org1Ctx := createTestContext(t, helper, helper.Org1, dualWriterMode)
|
||||
org2Ctx := createTestContext(t, helper, helper.OrgB, dualWriterMode)
|
||||
dualWriterModes := []rest.DualWriterMode{rest.Mode0, rest.Mode1, rest.Mode2, rest.Mode3, rest.Mode4, rest.Mode5}
|
||||
for _, dualWriterMode := range dualWriterModes {
|
||||
t.Run(fmt.Sprintf("DualWriterMode %d", dualWriterMode), func(t *testing.T) {
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
DisableAnonymous: true,
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders, // Enable dashboard feature
|
||||
featuremgmt.FlagUnifiedStorageSearch,
|
||||
},
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
"dashboards.dashboard.grafana.app": {
|
||||
DualWriterMode: dualWriterMode,
|
||||
},
|
||||
"folders.folder.grafana.app": {
|
||||
DualWriterMode: dualWriterMode,
|
||||
},
|
||||
}})
|
||||
|
||||
t.Run("Organization 1 tests", func(t *testing.T) {
|
||||
t.Run("Dashboard validation tests", func(t *testing.T) {
|
||||
runDashboardValidationTests(t, org1Ctx)
|
||||
t.Cleanup(func() {
|
||||
helper.Shutdown()
|
||||
})
|
||||
|
||||
org1Ctx := createTestContext(t, helper, helper.Org1, dualWriterMode)
|
||||
org2Ctx := createTestContext(t, helper, helper.OrgB, dualWriterMode)
|
||||
|
||||
t.Run("Authorization tests for all identity types", func(t *testing.T) {
|
||||
runAuthorizationTests(t, org1Ctx)
|
||||
})
|
||||
|
||||
t.Run("Dashboard permission tests", func(t *testing.T) {
|
||||
runDashboardPermissionTests(t, org1Ctx, false)
|
||||
})
|
||||
|
||||
t.Run("Cross-organization tests", func(t *testing.T) {
|
||||
runCrossOrgTests(t, org1Ctx, org2Ctx)
|
||||
})
|
||||
|
||||
t.Run("Dashboard HTTP API test", func(t *testing.T) {
|
||||
runDashboardHttpTest(t, org1Ctx, org2Ctx)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("Dashboard quota tests", func(t *testing.T) {
|
||||
runQuotaTests(t, org1Ctx)
|
||||
// TestIntegrationDashboardAPI tests the dashboard K8s API
|
||||
func TestIntegrationDashboardAPI(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
|
||||
dualWriterModes := []rest.DualWriterMode{rest.Mode0, rest.Mode1, rest.Mode2, rest.Mode3, rest.Mode4, rest.Mode5}
|
||||
for _, dualWriterMode := range dualWriterModes {
|
||||
t.Run(fmt.Sprintf("DualWriterMode %d", dualWriterMode), func(t *testing.T) {
|
||||
// Create a K8sTestHelper which will set up a real API server
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
DisableAnonymous: true,
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders, // Enable dashboard feature
|
||||
featuremgmt.FlagUnifiedStorageSearch,
|
||||
featuremgmt.FlagKubernetesDashboards,
|
||||
},
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
"dashboards.dashboard.grafana.app": {
|
||||
DualWriterMode: dualWriterMode,
|
||||
},
|
||||
"folders.folder.grafana.app": {
|
||||
DualWriterMode: dualWriterMode,
|
||||
},
|
||||
}})
|
||||
|
||||
t.Cleanup(func() {
|
||||
helper.Shutdown()
|
||||
})
|
||||
|
||||
org1Ctx := createTestContext(t, helper, helper.Org1, dualWriterMode)
|
||||
org2Ctx := createTestContext(t, helper, helper.OrgB, dualWriterMode)
|
||||
|
||||
t.Run("Dashboard LIST API test", func(t *testing.T) {
|
||||
runDashboardListTests(t, org1Ctx)
|
||||
})
|
||||
|
||||
t.Run("Authorization tests for all identity types", func(t *testing.T) {
|
||||
runAuthorizationTests(t, org1Ctx)
|
||||
})
|
||||
|
||||
t.Run("Dashboard permission tests", func(t *testing.T) {
|
||||
runDashboardPermissionTests(t, org1Ctx, true)
|
||||
})
|
||||
|
||||
t.Run("Dashboard HTTP API test", func(t *testing.T) {
|
||||
runDashboardHttpTest(t, org1Ctx, org2Ctx)
|
||||
})
|
||||
|
||||
t.Run("Cross-organization tests", func(t *testing.T) {
|
||||
runCrossOrgTests(t, org1Ctx, org2Ctx)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Authorization tests for all identity types", func(t *testing.T) {
|
||||
runAuthorizationTests(t, org1Ctx)
|
||||
})
|
||||
|
||||
t.Run("Dashboard permission tests", func(t *testing.T) {
|
||||
runDashboardPermissionTests(t, org1Ctx, true)
|
||||
})
|
||||
|
||||
t.Run("Dashboard LIST API test", func(t *testing.T) {
|
||||
t.Skip("Skip LIST")
|
||||
runDashboardListTest(t, org1Ctx)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Dashboard HTTP API test", func(t *testing.T) {
|
||||
runDashboardHttpTest(t, org1Ctx, org2Ctx)
|
||||
})
|
||||
|
||||
t.Run("Cross-organization tests", func(t *testing.T) {
|
||||
runCrossOrgTests(t, org1Ctx, org2Ctx)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Auth identity types (user or token) with resource client
|
||||
@@ -169,7 +246,11 @@ type Identity struct {
|
||||
func runDashboardValidationTests(t *testing.T, ctx TestContext) {
|
||||
t.Helper()
|
||||
|
||||
adminClient := getResourceClient(t, ctx.Helper, ctx.AdminUser, getDashboardGVR())
|
||||
// Get a new resource client for admin user
|
||||
// TODO: we need to figure out why reusing the same client results in slower tests
|
||||
adminClient := func() *apis.K8sResourceClient {
|
||||
return getResourceClient(t, ctx.Helper, ctx.AdminUser, getDashboardGVR())
|
||||
}
|
||||
editorClient := getResourceClient(t, ctx.Helper, ctx.EditorUser, getDashboardGVR())
|
||||
|
||||
t.Run("Dashboard UID validations", func(t *testing.T) {
|
||||
@@ -177,15 +258,15 @@ func runDashboardValidationTests(t *testing.T, ctx TestContext) {
|
||||
t.Run("reject dashboard with existing UID", func(t *testing.T) {
|
||||
// Create a dashboard with a specific UID
|
||||
specificUID := "existing-uid-dash"
|
||||
createdDash, err := createDashboard(t, adminClient, "Dashboard with Specific UID", nil, &specificUID)
|
||||
createdDash, err := createDashboard(t, adminClient(), "Dashboard with Specific UID", nil, &specificUID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Try to create another dashboard with the same UID
|
||||
_, err = createDashboard(t, adminClient, "Another Dashboard with Same UID", nil, &specificUID)
|
||||
_, err = createDashboard(t, adminClient(), "Another Dashboard with Same UID", nil, &specificUID)
|
||||
require.Error(t, err)
|
||||
|
||||
// Clean up
|
||||
err = adminClient.Resource.Delete(context.Background(), createdDash.GetName(), v1.DeleteOptions{})
|
||||
err = adminClient().Resource.Delete(context.Background(), createdDash.GetName(), v1.DeleteOptions{})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
@@ -193,14 +274,14 @@ func runDashboardValidationTests(t *testing.T, ctx TestContext) {
|
||||
t.Run("reject dashboard with too long UID", func(t *testing.T) {
|
||||
// Create a dashboard with a long UID (over 40 chars)
|
||||
longUID := "this-uid-is-way-too-long-for-a-dashboard-uid-12345678901234567890"
|
||||
_, err := createDashboard(t, adminClient, "Dashboard with Long UID", nil, &longUID)
|
||||
_, err := createDashboard(t, adminClient(), "Dashboard with Long UID", nil, &longUID)
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
// Test creating dashboard with invalid UID characters
|
||||
t.Run("reject dashboard with invalid UID characters", func(t *testing.T) {
|
||||
invalidUID := "invalid/uid/with/slashes"
|
||||
_, err := createDashboard(t, adminClient, "Dashboard with Invalid UID", nil, &invalidUID)
|
||||
_, err := createDashboard(t, adminClient(), "Dashboard with Invalid UID", nil, &invalidUID)
|
||||
require.Error(t, err)
|
||||
})
|
||||
})
|
||||
@@ -209,47 +290,47 @@ func runDashboardValidationTests(t *testing.T, ctx TestContext) {
|
||||
t.Run("Dashboard title validations", func(t *testing.T) {
|
||||
// Test empty title
|
||||
t.Run("reject dashboard with empty title", func(t *testing.T) {
|
||||
_, err := createDashboard(t, adminClient, "", nil, nil)
|
||||
_, err := createDashboard(t, adminClient(), "", nil, nil)
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
// Test long title
|
||||
t.Run("reject dashboard with excessively long title", func(t *testing.T) {
|
||||
veryLongTitle := strings.Repeat("a", 10000)
|
||||
_, err := createDashboard(t, adminClient, veryLongTitle, nil, nil)
|
||||
_, err := createDashboard(t, adminClient(), veryLongTitle, nil, nil)
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
// Test updating dashboard with empty title
|
||||
t.Run("reject dashboard update with empty title", func(t *testing.T) {
|
||||
// First create a valid dashboard
|
||||
dash, err := createDashboard(t, adminClient, "Valid Dashboard Title", nil, nil)
|
||||
dash, err := createDashboard(t, adminClient(), "Valid Dashboard Title", nil, nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, dash)
|
||||
|
||||
// Try to update with empty title
|
||||
_, err = updateDashboard(t, adminClient, dash, "", nil)
|
||||
_, err = updateDashboard(t, adminClient(), dash, "", nil)
|
||||
require.Error(t, err)
|
||||
|
||||
// Clean up
|
||||
err = adminClient.Resource.Delete(context.Background(), dash.GetName(), v1.DeleteOptions{})
|
||||
err = adminClient().Resource.Delete(context.Background(), dash.GetName(), v1.DeleteOptions{})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
// Test updating dashboard with excessively long title
|
||||
t.Run("reject dashboard update with excessively long title", func(t *testing.T) {
|
||||
// First create a valid dashboard
|
||||
dash, err := createDashboard(t, adminClient, "Valid Dashboard Title", nil, nil)
|
||||
dash, err := createDashboard(t, adminClient(), "Valid Dashboard Title", nil, nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, dash)
|
||||
|
||||
// Try to update with excessively long title
|
||||
veryLongTitle := strings.Repeat("a", 10000)
|
||||
_, err = updateDashboard(t, adminClient, dash, veryLongTitle, nil)
|
||||
_, err = updateDashboard(t, adminClient(), dash, veryLongTitle, nil)
|
||||
require.Error(t, err)
|
||||
|
||||
// Clean up
|
||||
err = adminClient.Resource.Delete(context.Background(), dash.GetName(), v1.DeleteOptions{})
|
||||
err = adminClient().Resource.Delete(context.Background(), dash.GetName(), v1.DeleteOptions{})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
})
|
||||
@@ -257,15 +338,15 @@ func runDashboardValidationTests(t *testing.T, ctx TestContext) {
|
||||
t.Run("Dashboard message validations", func(t *testing.T) {
|
||||
// Test long message
|
||||
t.Run("reject dashboard with excessively long update message", func(t *testing.T) {
|
||||
dash, err := createDashboard(t, adminClient, "Regular dashboard", nil, nil)
|
||||
dash, err := createDashboard(t, adminClient(), "Regular dashboard", nil, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
veryLongMessage := strings.Repeat("a", 600)
|
||||
_, err = updateDashboard(t, adminClient, dash, "Dashboard updated with a long message", &veryLongMessage)
|
||||
_, err = updateDashboard(t, adminClient(), dash, "Dashboard updated with a long message", &veryLongMessage)
|
||||
require.Error(t, err)
|
||||
|
||||
// Clean up
|
||||
err = adminClient.Resource.Delete(context.Background(), dash.GetName(), v1.DeleteOptions{})
|
||||
err = adminClient().Resource.Delete(context.Background(), dash.GetName(), v1.DeleteOptions{})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
})
|
||||
@@ -274,21 +355,21 @@ func runDashboardValidationTests(t *testing.T, ctx TestContext) {
|
||||
// Test non-existent folder UID
|
||||
t.Run("reject dashboard with non-existent folder UID", func(t *testing.T) {
|
||||
nonExistentFolderUID := "non-existent-folder-uid"
|
||||
_, err := createDashboard(t, adminClient, "Dashboard in Non-existent Folder", &nonExistentFolderUID, nil)
|
||||
_, err := createDashboard(t, adminClient(), "Dashboard in Non-existent Folder", &nonExistentFolderUID, nil)
|
||||
ctx.Helper.EnsureStatusError(err, http.StatusNotFound, "folders.folder.grafana.app \"non-existent-folder-uid\" not found")
|
||||
})
|
||||
|
||||
t.Run("allow moving folder to general folder", func(t *testing.T) {
|
||||
folder1 := createFolderObject(t, "folder1", "default", "")
|
||||
folder1UID := folder1.GetName()
|
||||
dash, err := createDashboard(t, adminClient, "Dashboard in a Folder", &folder1UID, nil)
|
||||
dash, err := createDashboard(t, adminClient(), "Dashboard in a Folder", &folder1UID, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
generalFolderUID := ""
|
||||
_, err = updateDashboard(t, adminClient, dash, "Move dashboard into the General Folder", &generalFolderUID)
|
||||
_, err = updateDashboard(t, adminClient(), dash, "Move dashboard into the General Folder", &generalFolderUID)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = adminClient.Resource.Delete(context.Background(), dash.GetName(), v1.DeleteOptions{})
|
||||
err = adminClient().Resource.Delete(context.Background(), dash.GetName(), v1.DeleteOptions{})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
})
|
||||
@@ -403,7 +484,7 @@ func runDashboardValidationTests(t *testing.T, ctx TestContext) {
|
||||
// Test version increment on update
|
||||
t.Run("version increments on dashboard update", func(t *testing.T) {
|
||||
// Create a dashboard with admin
|
||||
dash, err := createDashboard(t, adminClient, "Dashboard for Version Test", nil, nil)
|
||||
dash, err := createDashboard(t, adminClient(), "Dashboard for Version Test", nil, nil)
|
||||
require.NoError(t, err, "Failed to create dashboard for version test")
|
||||
dashUID := dash.GetName()
|
||||
|
||||
@@ -413,7 +494,7 @@ func runDashboardValidationTests(t *testing.T, ctx TestContext) {
|
||||
initialRV := meta.GetResourceVersion()
|
||||
|
||||
// Update the dashboard
|
||||
updatedDash, err := updateDashboard(t, adminClient, dash, "Updated Dashboard for Version Test", nil)
|
||||
updatedDash, err := updateDashboard(t, adminClient(), dash, "Updated Dashboard for Version Test", nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, updatedDash)
|
||||
|
||||
@@ -423,25 +504,25 @@ func runDashboardValidationTests(t *testing.T, ctx TestContext) {
|
||||
require.NotEqual(t, meta.GetResourceVersion(), initialRV, "Resource version should be changed after update")
|
||||
|
||||
// Clean up
|
||||
err = adminClient.Resource.Delete(context.Background(), dashUID, v1.DeleteOptions{})
|
||||
err = adminClient().Resource.Delete(context.Background(), dashUID, v1.DeleteOptions{})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
// Test generation conflict when updating concurrently
|
||||
t.Run("reject update with version conflict", func(t *testing.T) {
|
||||
// Create a dashboard with admin
|
||||
dash, err := createDashboard(t, adminClient, "Dashboard for Version Conflict Test", nil, nil)
|
||||
dash, err := createDashboard(t, adminClient(), "Dashboard for Version Conflict Test", nil, nil)
|
||||
require.NoError(t, err, "Failed to create dashboard for version conflict test")
|
||||
dashUID := dash.GetName()
|
||||
|
||||
// Get the dashboard twice (simulating two users getting it)
|
||||
dash1, err := adminClient.Resource.Get(context.Background(), dashUID, v1.GetOptions{})
|
||||
dash1, err := adminClient().Resource.Get(context.Background(), dashUID, v1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
dash2, err := editorClient.Resource.Get(context.Background(), dashUID, v1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Update with the first copy
|
||||
updatedDash1, err := updateDashboard(t, adminClient, dash1, "Updated by first user", nil)
|
||||
updatedDash1, err := updateDashboard(t, adminClient(), dash1, "Updated by first user", nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, updatedDash1)
|
||||
|
||||
@@ -451,7 +532,7 @@ func runDashboardValidationTests(t *testing.T, ctx TestContext) {
|
||||
require.Contains(t, err.Error(), "the object has been modified", "Should fail with version conflict error")
|
||||
|
||||
// Clean up
|
||||
err = adminClient.Resource.Delete(context.Background(), dashUID, v1.DeleteOptions{})
|
||||
err = adminClient().Resource.Delete(context.Background(), dashUID, v1.DeleteOptions{})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
@@ -464,12 +545,12 @@ func runDashboardValidationTests(t *testing.T, ctx TestContext) {
|
||||
meta.SetGeneration(5)
|
||||
|
||||
// Create the dashboard
|
||||
createdDash, err := adminClient.Resource.Create(context.Background(), dashObj, v1.CreateOptions{})
|
||||
createdDash, err := adminClient().Resource.Create(context.Background(), dashObj, v1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
dashUID := createdDash.GetName()
|
||||
|
||||
// Fetch the created dashboard
|
||||
fetchedDash, err := adminClient.Resource.Get(context.Background(), dashUID, v1.GetOptions{})
|
||||
fetchedDash, err := adminClient().Resource.Get(context.Background(), dashUID, v1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify the generation was handled properly
|
||||
@@ -477,22 +558,22 @@ func runDashboardValidationTests(t *testing.T, ctx TestContext) {
|
||||
require.Equal(t, 5, meta.GetGeneration(), "Generation should be 5")
|
||||
|
||||
// Clean up
|
||||
err = adminClient.Resource.Delete(context.Background(), dashUID, v1.DeleteOptions{})
|
||||
err = adminClient().Resource.Delete(context.Background(), dashUID, v1.DeleteOptions{})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("dashboard version history available, even for UIDs ending in hyphen", func(t *testing.T) {
|
||||
dashboardUID := "test-dashboard-"
|
||||
dash, err := createDashboard(t, adminClient, "Dashboard with uid ending in hyphen", nil, &dashboardUID)
|
||||
dash, err := createDashboard(t, adminClient(), "Dashboard with uid ending in hyphen", nil, &dashboardUID)
|
||||
require.NoError(t, err)
|
||||
|
||||
updatedDash, err := updateDashboard(t, adminClient, dash, "Updated dashboard with uid ending in hyphen", nil)
|
||||
updatedDash, err := updateDashboard(t, adminClient(), dash, "Updated dashboard with uid ending in hyphen", nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, updatedDash)
|
||||
|
||||
labelSelector := utils.LabelKeyGetHistory + "=true"
|
||||
fieldSelector := "metadata.name=" + dashboardUID
|
||||
versions, err := adminClient.Resource.List(context.Background(), v1.ListOptions{
|
||||
versions, err := adminClient().Resource.List(context.Background(), v1.ListOptions{
|
||||
LabelSelector: labelSelector,
|
||||
FieldSelector: fieldSelector,
|
||||
Limit: 10,
|
||||
@@ -502,7 +583,7 @@ func runDashboardValidationTests(t *testing.T, ctx TestContext) {
|
||||
// one from initial save, one from update
|
||||
require.Equal(t, len(versions.Items), 2)
|
||||
|
||||
err = adminClient.Resource.Delete(context.Background(), dashboardUID, v1.DeleteOptions{})
|
||||
err = adminClient().Resource.Delete(context.Background(), dashboardUID, v1.DeleteOptions{})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
})
|
||||
@@ -530,12 +611,12 @@ func runDashboardValidationTests(t *testing.T, ctx TestContext) {
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Create a dashboard with admin
|
||||
dash, err := createDashboard(t, adminClient, "Dashboard for Provisioning Test", nil, nil)
|
||||
dash, err := createDashboard(t, adminClient(), "Dashboard for Provisioning Test", nil, nil)
|
||||
require.NoError(t, err, "Failed to create dashboard for provisioning test")
|
||||
dashUID := dash.GetName()
|
||||
|
||||
// Fetch the created dashboard
|
||||
fetchedDash, err := adminClient.Resource.Get(context.Background(), dashUID, v1.GetOptions{})
|
||||
fetchedDash, err := adminClient().Resource.Get(context.Background(), dashUID, v1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, fetchedDash)
|
||||
|
||||
@@ -543,7 +624,7 @@ func runDashboardValidationTests(t *testing.T, ctx TestContext) {
|
||||
provisionedDash := markDashboardObjectAsProvisioned(t, fetchedDash, "test-provider", "test-external-id", "test-checksum", tc.allowsEdits)
|
||||
|
||||
// Update the dashboard to apply the provisioning annotations
|
||||
updatedDash, err := adminClient.Resource.Update(context.Background(), provisionedDash, v1.UpdateOptions{})
|
||||
updatedDash, err := adminClient().Resource.Update(context.Background(), provisionedDash, v1.UpdateOptions{})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, updatedDash)
|
||||
|
||||
@@ -570,16 +651,13 @@ func runDashboardValidationTests(t *testing.T, ctx TestContext) {
|
||||
}
|
||||
|
||||
// Clean up
|
||||
err = adminClient.Resource.Delete(context.Background(), dashUID, v1.DeleteOptions{})
|
||||
err = adminClient().Resource.Delete(context.Background(), dashUID, v1.DeleteOptions{})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Dashboard refresh interval validations", func(t *testing.T) {
|
||||
// Create test client
|
||||
adminClient := getResourceClient(t, ctx.Helper, ctx.AdminUser, getDashboardGVR())
|
||||
|
||||
// Store original settings to restore after test
|
||||
origCfg := ctx.Helper.GetEnv().Cfg
|
||||
origMinRefreshInterval := origCfg.MinRefreshInterval
|
||||
@@ -639,14 +717,14 @@ func runDashboardValidationTests(t *testing.T, ctx TestContext) {
|
||||
|
||||
_ = meta.SetSpec(specMap)
|
||||
|
||||
dash, err := adminClient.Resource.Create(context.Background(), dashObj, v1.CreateOptions{})
|
||||
dash, err := adminClient().Resource.Create(context.Background(), dashObj, v1.CreateOptions{})
|
||||
|
||||
if tc.shouldSucceed {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, dash)
|
||||
|
||||
// Clean up
|
||||
err = adminClient.Resource.Delete(context.Background(), dash.GetName(), v1.DeleteOptions{})
|
||||
err = adminClient().Resource.Delete(context.Background(), dash.GetName(), v1.DeleteOptions{})
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.Error(t, err)
|
||||
@@ -664,7 +742,7 @@ func runDashboardValidationTests(t *testing.T, ctx TestContext) {
|
||||
|
||||
// Create a dashboard with a specific UID to make it easier to manage
|
||||
specificUID := "size-limit-test-dash"
|
||||
dash, err := createDashboard(t, adminClient, "Dashboard Exceeding Size Limit", nil, &specificUID)
|
||||
dash, err := createDashboard(t, adminClient(), "Dashboard Exceeding Size Limit", nil, &specificUID)
|
||||
require.NoError(t, err)
|
||||
|
||||
meta, _ := utils.MetaAccessor(dash)
|
||||
@@ -704,44 +782,17 @@ func runDashboardValidationTests(t *testing.T, ctx TestContext) {
|
||||
require.NoError(t, err, "Failed to set spec")
|
||||
|
||||
// Try to update with too many panels
|
||||
_, err = adminClient.Resource.Update(context.Background(), dash, v1.UpdateOptions{})
|
||||
_, err = adminClient().Resource.Update(context.Background(), dash, v1.UpdateOptions{})
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "exceeds", "Error should mention size or limit exceeded")
|
||||
|
||||
// Clean up
|
||||
err = adminClient.Resource.Delete(context.Background(), specificUID, v1.DeleteOptions{})
|
||||
err = adminClient().Resource.Delete(context.Background(), specificUID, v1.DeleteOptions{})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// skipIfMode skips the current test if running in any of the specified modes
|
||||
// Usage: skipIfMode(t, rest.Mode1, rest.Mode4)
|
||||
// or with a message: skipIfMode(t, "Known issue with conflict detection", rest.Mode1, rest.Mode4)
|
||||
// nolint:unused
|
||||
func (c *TestContext) skipIfMode(t *testing.T, args ...interface{}) {
|
||||
t.Helper()
|
||||
|
||||
message := "Test not supported in this dual writer mode"
|
||||
modes := []rest.DualWriterMode{}
|
||||
|
||||
// Parse args - first string is considered a message, all rest.DualWriterMode values are modes to skip
|
||||
for _, arg := range args {
|
||||
if msg, ok := arg.(string); ok {
|
||||
message = msg
|
||||
} else if mode, ok := arg.(rest.DualWriterMode); ok {
|
||||
modes = append(modes, mode)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if current mode is in the list of modes to skip
|
||||
for _, mode := range modes {
|
||||
if c.DualWriterMode == mode {
|
||||
t.Skipf("%s (mode %d)", message, c.DualWriterMode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run tests for quota validation
|
||||
func runQuotaTests(t *testing.T, ctx TestContext) {
|
||||
t.Helper()
|
||||
@@ -1090,6 +1141,13 @@ func updateDashboard(t *testing.T, client *apis.K8sResourceClient, dashboard *un
|
||||
func runAuthorizationTests(t *testing.T, ctx TestContext) {
|
||||
t.Helper()
|
||||
|
||||
// Get a new resource client for admin user
|
||||
// TODO: we need to figure out why reusing the same client results in slower tests
|
||||
adminClient := func() *apis.K8sResourceClient {
|
||||
// admin token
|
||||
return getServiceAccountResourceClient(t, ctx.Helper, ctx.AdminServiceAccountToken, ctx.OrgID, getDashboardGVR())
|
||||
}
|
||||
|
||||
// Get clients for each identity type and role
|
||||
adminUserClient := getResourceClient(t, ctx.Helper, ctx.AdminUser, getDashboardGVR())
|
||||
editorUserClient := getResourceClient(t, ctx.Helper, ctx.EditorUser, getDashboardGVR())
|
||||
@@ -1112,11 +1170,13 @@ func runAuthorizationTests(t *testing.T, ctx TestContext) {
|
||||
{Name: "Viewer token", DashboardClient: viewerTokenClient, Type: "token"},
|
||||
}
|
||||
|
||||
// TODO: re-enable admin cleanup clients when we have figured out why reusing the same client results in slower tests
|
||||
// TODO: This is currently disabled to avoid issues with reusing the same client in tests.
|
||||
// Get admin clients for cleanup based on identity type
|
||||
adminCleanupClients := map[string]*apis.K8sResourceClient{
|
||||
"user": adminUserClient,
|
||||
"token": adminTokenClient,
|
||||
}
|
||||
// adminCleanupClients := map[string]*apis.K8sResourceClient{
|
||||
// "user": adminUserClient,
|
||||
// "token": adminTokenClient,
|
||||
// }
|
||||
|
||||
// Define test cases for different roles
|
||||
type roleTest struct {
|
||||
@@ -1162,8 +1222,9 @@ func runAuthorizationTests(t *testing.T, ctx TestContext) {
|
||||
for _, identity := range identities {
|
||||
identity := identity // Capture range variable
|
||||
t.Run(identity.Name, func(t *testing.T) {
|
||||
// TODO: This is currently disabled to avoid issues with reusing the same client in tests.
|
||||
// Get admin client for cleanup based on identity type
|
||||
adminClient := adminCleanupClients[identity.Type]
|
||||
// adminClient := adminCleanupClients[identity.Type]
|
||||
|
||||
// Get role capabilities for this identity
|
||||
roleCapabilities := authTests[identity.DashboardClient]
|
||||
@@ -1195,7 +1256,7 @@ func runAuthorizationTests(t *testing.T, ctx TestContext) {
|
||||
}
|
||||
|
||||
// Clean up
|
||||
err = adminClient.Resource.Delete(context.Background(), dash.GetName(), v1.DeleteOptions{})
|
||||
err = adminClient().Resource.Delete(context.Background(), dash.GetName(), v1.DeleteOptions{})
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
// Test cannot create dashboard
|
||||
@@ -1209,7 +1270,7 @@ func runAuthorizationTests(t *testing.T, ctx TestContext) {
|
||||
// Test dashboard updates
|
||||
t.Run("dashboard update", func(t *testing.T) {
|
||||
// Create a dashboard with admin
|
||||
dash, err := createDashboard(t, adminClient, "Dashboard to Update by "+identity.Name, nil, nil)
|
||||
dash, err := createDashboard(t, adminClient(), "Dashboard to Update by "+identity.Name, nil, nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, dash)
|
||||
|
||||
@@ -1229,14 +1290,14 @@ func runAuthorizationTests(t *testing.T, ctx TestContext) {
|
||||
}
|
||||
|
||||
// Clean up
|
||||
err = adminClient.Resource.Delete(context.Background(), dash.GetName(), v1.DeleteOptions{})
|
||||
err = adminClient().Resource.Delete(context.Background(), dash.GetName(), v1.DeleteOptions{})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
// Test dashboard deletion permissions
|
||||
t.Run("dashboard deletion", func(t *testing.T) {
|
||||
// Create a dashboard with admin
|
||||
dash, err := createDashboard(t, adminClient, "Dashboard for deletion test by "+identity.Name, nil, nil)
|
||||
dash, err := createDashboard(t, adminClient(), "Dashboard for deletion test by "+identity.Name, nil, nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, dash)
|
||||
|
||||
@@ -1247,7 +1308,7 @@ func runAuthorizationTests(t *testing.T, ctx TestContext) {
|
||||
} else {
|
||||
require.Error(t, err, "Should not be able to delete dashboard")
|
||||
// Clean up with admin if the test identity couldn't delete
|
||||
err = adminClient.Resource.Delete(context.Background(), dash.GetName(), v1.DeleteOptions{})
|
||||
err = adminClient().Resource.Delete(context.Background(), dash.GetName(), v1.DeleteOptions{})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
@@ -1256,7 +1317,7 @@ func runAuthorizationTests(t *testing.T, ctx TestContext) {
|
||||
// Test dashboard viewing for all roles
|
||||
t.Run("dashboard viewing", func(t *testing.T) {
|
||||
// Create a dashboard with admin
|
||||
dash, err := createDashboard(t, adminClient, "Dashboard for "+identity.Name+" to view", nil, nil)
|
||||
dash, err := createDashboard(t, adminClient(), "Dashboard for "+identity.Name+" to view", nil, nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, dash)
|
||||
|
||||
@@ -1266,7 +1327,7 @@ func runAuthorizationTests(t *testing.T, ctx TestContext) {
|
||||
require.NotNil(t, viewedDash)
|
||||
|
||||
// Clean up
|
||||
err = adminClient.Resource.Delete(context.Background(), dash.GetName(), v1.DeleteOptions{})
|
||||
err = adminClient().Resource.Delete(context.Background(), dash.GetName(), v1.DeleteOptions{})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
})
|
||||
@@ -2097,6 +2158,8 @@ func runDashboardHttpTest(t *testing.T, ctx TestContext, foreignOrgCtx TestConte
|
||||
|
||||
// Helper function to retrieve a dashboard via HTTP
|
||||
func getDashboardViaHTTP(t *testing.T, ctx *TestContext, dashboardPath string, user apis.User) (map[string]interface{}, error) {
|
||||
t.Helper()
|
||||
|
||||
getResp := apis.DoRequest(ctx.Helper, apis.RequestParams{
|
||||
User: user,
|
||||
Method: http.MethodGet,
|
||||
@@ -2194,7 +2257,7 @@ func testDashboardHttpUpdateMethods(t *testing.T, ctx TestContext, dashboardPath
|
||||
}
|
||||
|
||||
// Test dashboard list API with complex permission scenarios
|
||||
func runDashboardListTest(t *testing.T, ctx TestContext) {
|
||||
func runDashboardListTests(t *testing.T, ctx TestContext) {
|
||||
t.Helper()
|
||||
|
||||
// Make sure no dashboards exist before we start
|
||||
@@ -2223,8 +2286,8 @@ func runDashboardListTest(t *testing.T, ctx TestContext) {
|
||||
// Define a map of user types to their clients
|
||||
clients := map[string]struct {
|
||||
userClient *apis.K8sResourceClient
|
||||
tokenClient *apis.K8sResourceClient
|
||||
folderClient *apis.K8sResourceClient
|
||||
tokenClient *apis.K8sResourceClient
|
||||
}{
|
||||
"Admin": {
|
||||
userClient: getResourceClient(t, ctx.Helper, ctx.AdminUser, getDashboardGVR()),
|
||||
@@ -2323,6 +2386,24 @@ func runDashboardListTest(t *testing.T, ctx TestContext) {
|
||||
folders := make([]*folder.Folder, len(folderConfigs))
|
||||
folderDashboards := make([]*unstructured.Unstructured, len(folderConfigs))
|
||||
|
||||
// Clean up
|
||||
t.Cleanup(func() {
|
||||
// Delete all root dashboards
|
||||
for _, dash := range rootDashboards {
|
||||
err := adminClient.Resource.Delete(context.Background(), dash.GetName(), v1.DeleteOptions{})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// Delete all folder dashboards and folders
|
||||
for i, folder := range folders {
|
||||
err := adminClient.Resource.Delete(context.Background(), folderDashboards[i].GetName(), v1.DeleteOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = adminFolderClient.Resource.Delete(context.Background(), folder.UID, v1.DeleteOptions{})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
|
||||
// Create all test resources (folders, dashboards) in one loop
|
||||
for i, fc := range folderConfigs {
|
||||
// Create root dashboard
|
||||
@@ -2415,71 +2496,55 @@ func runDashboardListTest(t *testing.T, ctx TestContext) {
|
||||
|
||||
// Test LIST operation for each identity
|
||||
for _, identity := range identities {
|
||||
t.Run(fmt.Sprintf("LIST operation for %s", identity.Name), func(t *testing.T) {
|
||||
// Get dashboards visible to this identity
|
||||
clients := []apis.K8sResourceClient{
|
||||
*identity.DashboardClient,
|
||||
*identity.FolderClient,
|
||||
}
|
||||
// Get dashboards visible to this identity
|
||||
clients := []apis.K8sResourceClient{
|
||||
*identity.DashboardClient,
|
||||
*identity.FolderClient,
|
||||
}
|
||||
for _, client := range clients {
|
||||
t.Run(fmt.Sprintf("LIST operation for %s and %s", identity.Name, client.Args.GVR), func(t *testing.T) {
|
||||
// Use the client to list all resources
|
||||
dashList, err := client.Resource.List(context.Background(), v1.ListOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, client := range clients {
|
||||
t.Run(fmt.Sprintf("LIST operation for %s", client.Args.GVR), func(t *testing.T) {
|
||||
listOpts := v1.ListOptions{}
|
||||
dashList, err := client.Resource.List(context.Background(), listOpts)
|
||||
if len(dashList.Items) == 0 {
|
||||
t.Logf("WARNING: Got empty dashboard list for %s", identity.Name)
|
||||
}
|
||||
|
||||
require.NotEmpty(t, dashList.Items)
|
||||
|
||||
// Extract dashboard titles
|
||||
dashTitles := make([]string, 0, len(dashList.Items))
|
||||
for _, dash := range dashList.Items {
|
||||
meta, err := utils.MetaAccessor(&dash)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, dashList.Items)
|
||||
|
||||
// Extract dashboard titles
|
||||
dashTitles := make([]string, 0, len(dashList.Items))
|
||||
for _, dash := range dashList.Items {
|
||||
meta, err := utils.MetaAccessor(&dash)
|
||||
require.NoError(t, err)
|
||||
dashTitles = append(dashTitles, meta.FindTitle(""))
|
||||
}
|
||||
|
||||
dashTitles = append(dashTitles, meta.FindTitle(""))
|
||||
}
|
||||
// Verify expectations
|
||||
var expectedTitles []string
|
||||
if client.Args.GVR == getDashboardGVR() {
|
||||
expectedTitles = expectations[identity.Name]
|
||||
} else {
|
||||
expectedTitles = folderPermissions[identity.Name]
|
||||
}
|
||||
require.ElementsMatch(t, expectedTitles, dashTitles)
|
||||
|
||||
// Verify expectations
|
||||
var expectedTitles []string
|
||||
if client.Args.GVR == getDashboardGVR() {
|
||||
expectedTitles = expectations[identity.Name]
|
||||
} else {
|
||||
expectedTitles = folderPermissions[identity.Name]
|
||||
}
|
||||
require.ElementsMatch(t, expectedTitles, dashTitles)
|
||||
|
||||
// Verify all expected items are found
|
||||
for _, expected := range expectedTitles {
|
||||
found := false
|
||||
for _, title := range dashTitles {
|
||||
if title == expected {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
// Verify all expected items are found
|
||||
for _, expected := range expectedTitles {
|
||||
found := false
|
||||
for _, title := range dashTitles {
|
||||
if title == expected {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
require.True(t, found, "%s should see dashboard '%s' but didn't", identity.Name, expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
require.True(t, found, "%s should see dashboard '%s' but didn't", identity.Name, expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up
|
||||
t.Run("Cleanup dashboards and folders", func(t *testing.T) {
|
||||
// Delete all root dashboards
|
||||
for _, dash := range rootDashboards {
|
||||
err := adminClient.Resource.Delete(context.Background(), dash.GetName(), v1.DeleteOptions{})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// Delete all folder dashboards and folders
|
||||
for i, folder := range folders {
|
||||
err := adminClient.Resource.Delete(context.Background(), folderDashboards[i].GetName(), v1.DeleteOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = adminFolderClient.Resource.Delete(context.Background(), folder.UID, v1.DeleteOptions{})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func postHelper(t *testing.T, ctx *TestContext, path string, body interface{}, user apis.User) (map[string]interface{}, error) {
|
||||
|
||||
Reference in New Issue
Block a user