Files
grafana/pkg/services/dashboards/service/client/client_test.go
T
Igor Suleymanov 4161f3a5ca Support dashboard restore across API versions (#110694)
What

This commit refactors the logic to restore a dashboard from a version.
The logic is moved from the API handler to the dashboard versions service,
which now supports restoring dashboards of different API versions.

Why

To make sure that dashboard version restoration works with v2 dashboards
API, as well as future API versions.

Signed-off-by: Igor Suleymanov <igor.suleymanov@grafana.com>
2025-09-11 13:35:58 +03:00

758 lines
25 KiB
Go

package client
import (
"context"
"errors"
"testing"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1"
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1"
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/apiserver/client"
)
type testSetup struct {
t *testing.T
handler *K8sClientWithFallback
mockMetrics *k8sClientMetrics
mockFactoryCalls map[string]int
mockClientV0Alpha1 *client.MockK8sHandler
mockClientV1Beta1 *client.MockK8sHandler
mockClientV2Alpha1 *client.MockK8sHandler
mockClientV2Beta1 *client.MockK8sHandler
}
func setupTest(t *testing.T) *testSetup {
var (
mockClientV0Alpha1 = &client.MockK8sHandler{}
mockClientV1Beta1 = &client.MockK8sHandler{}
mockClientV2Alpha1 = &client.MockK8sHandler{}
mockClientV2Beta1 = &client.MockK8sHandler{}
)
mockMetrics := newK8sClientMetrics(prometheus.NewRegistry())
mockFactoryCalls := make(map[string]int)
handler := &K8sClientWithFallback{
K8sHandler: mockClientV1Beta1,
newClientFunc: func(_ context.Context, version string) client.K8sHandler {
mockFactoryCalls[version]++
switch version {
case v0alpha1.VERSION:
return mockClientV0Alpha1
case v1beta1.VERSION:
return mockClientV1Beta1
case v2alpha1.VERSION:
return mockClientV2Alpha1
case v2beta1.VERSION:
return mockClientV2Beta1
case "v1":
return mockClientV1Beta1
default:
t.Fatalf("Unexpected call to newClientFunc with version %s", version)
return nil
}
},
log: log.New("test"),
metrics: mockMetrics,
}
return &testSetup{
t: t,
handler: handler,
mockMetrics: mockMetrics,
mockFactoryCalls: mockFactoryCalls,
mockClientV0Alpha1: mockClientV0Alpha1,
mockClientV1Beta1: mockClientV1Beta1,
mockClientV2Alpha1: mockClientV2Alpha1,
mockClientV2Beta1: mockClientV2Beta1,
}
}
func TestK8sHandlerWithFallback_Get(t *testing.T) {
t.Run("Get without fallback", func(t *testing.T) {
setup := setupTest(t)
ctx := context.Background()
name := "test-dashboard"
orgID := int64(1)
options := metav1.GetOptions{}
expectedResult := &unstructured.Unstructured{
Object: map[string]interface{}{
"metadata": map[string]interface{}{
"name": name,
},
"status": map[string]interface{}{
"someOtherStatus": "ok",
},
},
}
setup.mockClientV1Beta1.On("Get", mock.Anything, name, orgID, options, mock.Anything).Return(expectedResult, nil).Once()
result, err := setup.handler.Get(ctx, name, orgID, options)
require.NoError(t, err)
require.Equal(t, expectedResult, result)
require.Equal(t, 0, len(setup.mockFactoryCalls), "Factory should not be called for non-fallback case")
setup.mockClientV1Beta1.AssertExpectations(t)
setup.mockClientV2Alpha1.AssertExpectations(t)
})
t.Run("Get with fallback due to conversion error", func(t *testing.T) {
setup := setupTest(t)
ctx := context.Background()
name := "test-dashboard-fallback"
orgID := int64(2)
options := metav1.GetOptions{ResourceVersion: "123"}
storedVersion := v2alpha1.VERSION
conversionErr := "failed to convert"
v1alpha1Result := &unstructured.Unstructured{
Object: map[string]interface{}{
"metadata": map[string]interface{}{
"name": name,
},
"status": map[string]interface{}{
"conversion": map[string]interface{}{
"failed": true,
"storedVersion": storedVersion,
"error": conversionErr,
},
},
},
}
expectedResultFallback := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "dashboard/v2alpha1",
"kind": "Dashboard",
"metadata": map[string]interface{}{
"name": name,
},
},
}
setup.mockClientV1Beta1.On("Get", mock.Anything, name, orgID, options, mock.Anything).Return(v1alpha1Result, nil).Once()
setup.mockClientV2Alpha1.On("Get", mock.Anything, name, orgID, options, mock.Anything).Return(expectedResultFallback, nil).Once()
result, err := setup.handler.Get(ctx, name, orgID, options)
require.NoError(t, err)
require.Equal(t, expectedResultFallback, result)
require.Equal(t, 1, setup.mockFactoryCalls[v2alpha1.VERSION], "Factory should be called once with v2alpha1")
setup.mockClientV1Beta1.AssertExpectations(t)
setup.mockClientV2Alpha1.AssertExpectations(t)
})
t.Run("Get initial error", func(t *testing.T) {
setup := setupTest(t)
ctx := context.Background()
name := "test-dashboard-error"
orgID := int64(3)
options := metav1.GetOptions{}
expectedErr := errors.New("initial get failed")
setup.mockClientV1Beta1.On("Get", mock.Anything, name, orgID, options, mock.Anything).Return(nil, expectedErr).Once()
_, err := setup.handler.Get(ctx, name, orgID, options)
require.Error(t, err)
require.Equal(t, expectedErr, err)
require.Equal(t, 0, len(setup.mockFactoryCalls), "Factory should not be called for error case")
setup.mockClientV1Beta1.AssertExpectations(t)
setup.mockClientV2Alpha1.AssertExpectations(t)
})
t.Run("Get with fallback fails", func(t *testing.T) {
setup := setupTest(t)
ctx := context.Background()
name := "test-dashboard-fallback-error"
orgID := int64(4)
options := metav1.GetOptions{}
storedVersion := v2alpha1.VERSION
conversionErr := "failed to convert again"
fallbackErr := errors.New("fallback get failed")
v1alpha1Result := &unstructured.Unstructured{
Object: map[string]interface{}{
"metadata": map[string]interface{}{
"name": name,
},
"status": map[string]interface{}{
"conversion": map[string]interface{}{
"failed": true,
"storedVersion": storedVersion,
"error": conversionErr,
},
},
},
}
setup.mockClientV1Beta1.On("Get", mock.Anything, name, orgID, options, mock.Anything).Return(v1alpha1Result, nil).Once()
setup.mockClientV2Alpha1.On("Get", mock.Anything, name, orgID, options, mock.Anything).Return(nil, fallbackErr).Once()
_, err := setup.handler.Get(ctx, name, orgID, options)
require.Error(t, err)
require.Equal(t, fallbackErr, err)
require.Equal(t, 1, setup.mockFactoryCalls[v2alpha1.VERSION], "Factory should be called once with v2alpha1")
setup.mockClientV1Beta1.AssertExpectations(t)
setup.mockClientV2Alpha1.AssertExpectations(t)
})
}
func TestK8sHandlerWithFallback_List(t *testing.T) {
// Helper function to create a dashboard item
createDashboard := func(name, resourceVersion string, status map[string]interface{}) unstructured.Unstructured {
return unstructured.Unstructured{
Object: map[string]interface{}{
"metadata": map[string]interface{}{
"name": name,
"resourceVersion": resourceVersion,
},
"status": status,
},
}
}
// Helper function to create a fallback dashboard item
createFallbackDashboard := func(name, resourceVersion, apiVersion string) unstructured.Unstructured {
return unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": apiVersion,
"kind": "Dashboard",
"metadata": map[string]interface{}{
"name": name,
"resourceVersion": resourceVersion,
},
},
}
}
// Helper function to create conversion status
conversionStatus := func(failed bool, storedVersion, errorMsg string) map[string]interface{} {
return map[string]interface{}{
"conversion": map[string]interface{}{
"failed": failed,
"storedVersion": storedVersion,
"error": errorMsg,
},
}
}
t.Run("List without fallback needed", func(t *testing.T) {
setup := setupTest(t)
expectedResult := &unstructured.UnstructuredList{
Items: []unstructured.Unstructured{
createDashboard("dashboard-1", "123", map[string]interface{}{"someOtherStatus": "ok"}),
createDashboard("dashboard-2", "456", map[string]interface{}{"anotherStatus": "ok"}),
},
}
setup.mockClientV1Beta1.On("List", mock.Anything, int64(1), metav1.ListOptions{}).Return(expectedResult, nil).Once()
result, err := setup.handler.List(context.Background(), 1, metav1.ListOptions{})
require.NoError(t, err)
require.Equal(t, expectedResult, result)
setup.mockClientV1Beta1.AssertExpectations(t)
setup.mockClientV2Alpha1.AssertExpectations(t)
})
t.Run("List with some items needing fallback", func(t *testing.T) {
setup := setupTest(t)
initialResult := &unstructured.UnstructuredList{
Items: []unstructured.Unstructured{
createDashboard("dashboard-ok", "123", map[string]interface{}{"someOtherStatus": "ok"}),
createDashboard("dashboard-fallback", "456", conversionStatus(true, v2alpha1.VERSION, "conversion failed")),
},
}
fallbackResult := createFallbackDashboard("dashboard-fallback", "456", "dashboard/"+v2alpha1.VERSION)
setup.mockClientV1Beta1.On("List", mock.Anything, int64(2), metav1.ListOptions{}).Return(initialResult, nil).Once()
setup.mockClientV2Alpha1.On("Get", mock.Anything, "dashboard-fallback", int64(2), metav1.GetOptions{ResourceVersion: "456"}, mock.Anything).Return(&fallbackResult, nil).Once()
result, err := setup.handler.List(context.Background(), 2, metav1.ListOptions{})
require.NoError(t, err)
require.Len(t, result.Items, 2)
expectedItems := []unstructured.Unstructured{
createDashboard("dashboard-ok", "123", map[string]interface{}{"someOtherStatus": "ok"}),
fallbackResult,
}
require.ElementsMatch(t, expectedItems, result.Items)
setup.mockClientV1Beta1.AssertExpectations(t)
setup.mockClientV2Alpha1.AssertExpectations(t)
})
t.Run("List with all items needing fallback", func(t *testing.T) {
setup := setupTest(t)
initialResult := &unstructured.UnstructuredList{
Items: []unstructured.Unstructured{
createDashboard("dashboard-1-fallback", "111", conversionStatus(true, v2alpha1.VERSION, "conversion failed 1")),
createDashboard("dashboard-2-fallback", "222", conversionStatus(true, v2alpha1.VERSION, "conversion failed 2")),
},
}
fallbackResult1 := createFallbackDashboard("dashboard-1-fallback", "111", "dashboard/"+v2alpha1.VERSION)
fallbackResult2 := createFallbackDashboard("dashboard-2-fallback", "222", "dashboard/"+v2alpha1.VERSION)
setup.mockClientV1Beta1.On("List", mock.Anything, int64(3), metav1.ListOptions{}).Return(initialResult, nil).Once()
setup.mockClientV2Alpha1.On("Get", mock.Anything, "dashboard-1-fallback", int64(3), metav1.GetOptions{ResourceVersion: "111"}, mock.Anything).Return(&fallbackResult1, nil).Once()
setup.mockClientV2Alpha1.On("Get", mock.Anything, "dashboard-2-fallback", int64(3), metav1.GetOptions{ResourceVersion: "222"}, mock.Anything).Return(&fallbackResult2, nil).Once()
result, err := setup.handler.List(context.Background(), 3, metav1.ListOptions{})
require.NoError(t, err)
require.Len(t, result.Items, 2)
expectedItems := []unstructured.Unstructured{fallbackResult1, fallbackResult2}
require.ElementsMatch(t, expectedItems, result.Items)
setup.mockClientV1Beta1.AssertExpectations(t)
setup.mockClientV2Alpha1.AssertExpectations(t)
})
t.Run("List with different versions needing fallback", func(t *testing.T) {
setup := setupTest(t)
initialResult := &unstructured.UnstructuredList{
Items: []unstructured.Unstructured{
createDashboard("dashboard-v2alpha1", "333", conversionStatus(true, v2alpha1.VERSION, "conversion failed v2alpha1")),
createDashboard("dashboard-v1beta1", "444", conversionStatus(true, v1beta1.VERSION, "conversion failed v1beta1")),
},
}
fallbackResultV2Alpha1 := createFallbackDashboard("dashboard-v2alpha1", "333", "dashboard/"+v2alpha1.VERSION)
fallbackResultV1Beta1 := createFallbackDashboard("dashboard-v1beta1", "444", "dashboard/"+v1beta1.VERSION)
setup.mockClientV1Beta1.On("List", mock.Anything, int64(4), metav1.ListOptions{}).Return(initialResult, nil).Once()
setup.mockClientV2Alpha1.On("Get", mock.Anything, "dashboard-v2alpha1", int64(4), metav1.GetOptions{ResourceVersion: "333"}, mock.Anything).Return(&fallbackResultV2Alpha1, nil).Once()
setup.mockClientV1Beta1.On("Get", mock.Anything, "dashboard-v1beta1", int64(4), metav1.GetOptions{ResourceVersion: "444"}, mock.Anything).Return(&fallbackResultV1Beta1, nil).Once()
result, err := setup.handler.List(context.Background(), 4, metav1.ListOptions{})
require.NoError(t, err)
require.Len(t, result.Items, 2)
expectedItems := []unstructured.Unstructured{fallbackResultV2Alpha1, fallbackResultV1Beta1}
require.ElementsMatch(t, expectedItems, result.Items)
setup.mockClientV1Beta1.AssertExpectations(t)
setup.mockClientV2Alpha1.AssertExpectations(t)
})
t.Run("List with initial fetch error", func(t *testing.T) {
setup := setupTest(t)
expectedErr := errors.New("initial list failed")
setup.mockClientV1Beta1.On("List", mock.Anything, int64(5), metav1.ListOptions{}).Return(nil, expectedErr).Once()
_, err := setup.handler.List(context.Background(), 5, metav1.ListOptions{})
require.Error(t, err)
require.Equal(t, expectedErr, err)
setup.mockClientV1Beta1.AssertExpectations(t)
setup.mockClientV2Alpha1.AssertExpectations(t)
})
t.Run("List with fallback fetch error", func(t *testing.T) {
setup := setupTest(t)
initialResult := &unstructured.UnstructuredList{
Items: []unstructured.Unstructured{
createDashboard("dashboard-fallback-error", "555", conversionStatus(true, v2alpha1.VERSION, "conversion failed")),
},
}
fallbackErr := errors.New("fallback get failed")
setup.mockClientV1Beta1.On("List", mock.Anything, int64(6), metav1.ListOptions{}).Return(initialResult, nil).Once()
setup.mockClientV2Alpha1.On("Get", mock.Anything, "dashboard-fallback-error", int64(6), metav1.GetOptions{ResourceVersion: "555"}, mock.Anything).Return(nil, fallbackErr).Once()
_, err := setup.handler.List(context.Background(), 6, metav1.ListOptions{})
require.Error(t, err)
require.Equal(t, fallbackErr, err)
setup.mockClientV1Beta1.AssertExpectations(t)
setup.mockClientV2Alpha1.AssertExpectations(t)
})
t.Run("List with empty result", func(t *testing.T) {
setup := setupTest(t)
emptyResult := &unstructured.UnstructuredList{Items: []unstructured.Unstructured{}}
setup.mockClientV1Beta1.On("List", mock.Anything, int64(7), metav1.ListOptions{}).Return(emptyResult, nil).Once()
result, err := setup.handler.List(context.Background(), 7, metav1.ListOptions{})
require.NoError(t, err)
require.Len(t, result.Items, 0)
setup.mockClientV1Beta1.AssertExpectations(t)
setup.mockClientV2Alpha1.AssertExpectations(t)
})
}
func TestK8sHandlerWithFallback_Update(t *testing.T) {
t.Run("Update without fallback", func(t *testing.T) {
setup := setupTest(t)
ctx := context.Background()
obj := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": v0alpha1.VERSION,
"metadata": map[string]interface{}{
"name": "test-dashboard",
},
"spec": map[string]interface{}{
"title": "Updated Dashboard",
},
},
}
orgID := int64(1)
options := metav1.UpdateOptions{}
expectedResult := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": v0alpha1.VERSION,
"metadata": map[string]interface{}{
"name": "test-dashboard",
"resourceVersion": "123",
},
"spec": map[string]interface{}{
"title": "Updated Dashboard",
},
},
}
setup.mockClientV0Alpha1.On("Update", mock.Anything, obj, orgID, options).Return(expectedResult, nil).Once()
result, err := setup.handler.Update(ctx, obj, orgID, options)
require.NoError(t, err)
require.Equal(t, expectedResult, result)
require.Equal(t, 1, setup.mockFactoryCalls[v0alpha1.VERSION], "Factory should be called once with v0alpha1")
setup.mockClientV0Alpha1.AssertExpectations(t)
setup.mockClientV2Alpha1.AssertExpectations(t)
})
t.Run("Update with different API version", func(t *testing.T) {
setup := setupTest(t)
ctx := context.Background()
obj := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": v2alpha1.VERSION,
"metadata": map[string]interface{}{
"name": "test-dashboard-v2",
},
"spec": map[string]interface{}{
"title": "Updated Dashboard V2",
},
},
}
orgID := int64(2)
options := metav1.UpdateOptions{}
expectedResult := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": v2alpha1.VERSION,
"metadata": map[string]interface{}{
"name": "test-dashboard-v2",
"resourceVersion": "456",
},
"spec": map[string]interface{}{
"title": "Updated Dashboard V2",
},
},
}
setup.mockClientV2Alpha1.On("Update", mock.Anything, obj, orgID, options).Return(expectedResult, nil).Once()
result, err := setup.handler.Update(ctx, obj, orgID, options)
require.NoError(t, err)
require.Equal(t, expectedResult, result)
require.Equal(t, 1, setup.mockFactoryCalls[v2alpha1.VERSION], "Factory should be called once with v2alpha1")
setup.mockClientV1Beta1.AssertExpectations(t)
setup.mockClientV2Alpha1.AssertExpectations(t)
})
t.Run("Update with error", func(t *testing.T) {
setup := setupTest(t)
ctx := context.Background()
obj := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": v0alpha1.VERSION,
"metadata": map[string]interface{}{
"name": "test-dashboard-error",
},
"spec": map[string]interface{}{
"title": "Error Dashboard",
},
},
}
orgID := int64(3)
options := metav1.UpdateOptions{}
expectedErr := errors.New("update failed")
setup.mockClientV0Alpha1.On("Update", mock.Anything, obj, orgID, options).Return(nil, expectedErr).Once()
result, err := setup.handler.Update(ctx, obj, orgID, options)
require.Error(t, err)
require.Nil(t, result)
require.Equal(t, expectedErr, err)
require.Equal(t, 1, setup.mockFactoryCalls[v0alpha1.VERSION], "Factory should be called once with v0alpha1")
setup.mockClientV0Alpha1.AssertExpectations(t)
setup.mockClientV2Alpha1.AssertExpectations(t)
})
t.Run("Update with different version and error", func(t *testing.T) {
setup := setupTest(t)
ctx := context.Background()
obj := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": v2alpha1.VERSION,
"metadata": map[string]interface{}{
"name": "test-dashboard-v2-error",
},
"spec": map[string]interface{}{
"title": "Error Dashboard V2",
},
},
}
orgID := int64(4)
options := metav1.UpdateOptions{}
expectedErr := errors.New("v2alpha1 update failed")
setup.mockClientV2Alpha1.On("Update", mock.Anything, obj, orgID, options).Return(nil, expectedErr).Once()
result, err := setup.handler.Update(ctx, obj, orgID, options)
require.Error(t, err)
require.Nil(t, result)
require.Equal(t, expectedErr, err)
require.Equal(t, 1, setup.mockFactoryCalls[v2alpha1.VERSION], "Factory should be called once with v2alpha1")
setup.mockClientV1Beta1.AssertExpectations(t)
setup.mockClientV2Alpha1.AssertExpectations(t)
})
t.Run("Update with unknown API version", func(t *testing.T) {
setup := setupTest(t)
ctx := context.Background()
obj := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "unknown/v1",
"metadata": map[string]interface{}{
"name": "test-dashboard-unknown",
},
"spec": map[string]interface{}{
"title": "Unknown Dashboard",
},
},
}
orgID := int64(5)
options := metav1.UpdateOptions{}
expectedResult := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "unknown/v1",
"metadata": map[string]interface{}{
"name": "test-dashboard-unknown",
"resourceVersion": "789",
},
"spec": map[string]interface{}{
"title": "Unknown Dashboard",
},
},
}
setup.mockClientV1Beta1.On("Update", mock.Anything, obj, orgID, options).Return(expectedResult, nil).Once()
result, err := setup.handler.Update(ctx, obj, orgID, options)
require.NoError(t, err)
require.Equal(t, expectedResult, result)
require.Equal(t, 1, setup.mockFactoryCalls["v1"], "Factory should be called once with v1")
setup.mockClientV1Beta1.AssertExpectations(t)
setup.mockClientV2Alpha1.AssertExpectations(t)
})
}
func TestGetConversionStatus(t *testing.T) {
tests := []struct {
name string
obj *unstructured.Unstructured
expectedFailed bool
expectedStoredVersion string
expectedError string
}{
{
name: "No status field",
obj: &unstructured.Unstructured{Object: map[string]interface{}{
"metadata": map[string]interface{}{"name": "test"},
}},
expectedFailed: false,
expectedStoredVersion: "",
expectedError: "",
},
{
name: "Status field, but no conversion field",
obj: &unstructured.Unstructured{Object: map[string]interface{}{
"metadata": map[string]interface{}{"name": "test"},
"status": map[string]interface{}{"someOtherStatus": "ok"},
}},
expectedFailed: false,
expectedStoredVersion: "",
expectedError: "",
},
{
name: "Conversion field, failed=true, with storedVersion and error",
obj: &unstructured.Unstructured{Object: map[string]interface{}{
"metadata": map[string]interface{}{"name": "test"},
"status": map[string]interface{}{
"conversion": map[string]interface{}{
"failed": true,
"storedVersion": v2alpha1.VERSION,
"error": "conversion failed",
},
},
}},
expectedFailed: true,
expectedStoredVersion: "v2alpha1",
expectedError: "conversion failed",
},
{
name: "Conversion field, failed=false",
obj: &unstructured.Unstructured{Object: map[string]interface{}{
"metadata": map[string]interface{}{"name": "test"},
"status": map[string]interface{}{
"conversion": map[string]interface{}{
"failed": false,
"storedVersion": "v1alpha1",
"error": "",
},
},
}},
expectedFailed: false,
expectedStoredVersion: "v1alpha1",
expectedError: "",
},
{
name: "Conversion field, missing failed (defaults to false)",
obj: &unstructured.Unstructured{Object: map[string]interface{}{
"metadata": map[string]interface{}{"name": "test"},
"status": map[string]interface{}{
"conversion": map[string]interface{}{
"storedVersion": "v1alpha1",
"error": "",
},
},
}},
expectedFailed: false,
expectedStoredVersion: "v1alpha1",
expectedError: "",
},
{
name: "Conversion field, failed=true, missing storedVersion",
obj: &unstructured.Unstructured{Object: map[string]interface{}{
"metadata": map[string]interface{}{"name": "test"},
"status": map[string]interface{}{
"conversion": map[string]interface{}{
"failed": true,
"error": "conversion failed",
},
},
}},
expectedFailed: true,
expectedStoredVersion: "",
expectedError: "conversion failed",
},
{
name: "Conversion field, failed=true, missing error",
obj: &unstructured.Unstructured{Object: map[string]interface{}{
"metadata": map[string]interface{}{"name": "test"},
"status": map[string]interface{}{
"conversion": map[string]interface{}{
"failed": true,
"storedVersion": v2alpha1.VERSION,
},
},
}},
expectedFailed: true,
expectedStoredVersion: "v2alpha1",
expectedError: "",
},
{
name: "Empty object",
obj: &unstructured.Unstructured{Object: map[string]interface{}{}},
expectedFailed: false,
expectedStoredVersion: "",
expectedError: "",
},
{
name: "Nil object",
obj: nil,
expectedFailed: false,
expectedStoredVersion: "",
expectedError: "",
},
{
name: "Status not a map",
obj: &unstructured.Unstructured{Object: map[string]interface{}{
"metadata": map[string]interface{}{"name": "test"},
"status": "not a map",
}},
expectedFailed: false,
expectedStoredVersion: "",
expectedError: "",
},
{
name: "Conversion not a map",
obj: &unstructured.Unstructured{Object: map[string]interface{}{
"metadata": map[string]interface{}{"name": "test"},
"status": map[string]interface{}{
"conversion": "not a map",
},
}},
expectedFailed: false,
expectedStoredVersion: "",
expectedError: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var input *unstructured.Unstructured
if tt.obj != nil {
input = tt.obj.DeepCopy()
} else {
input = &unstructured.Unstructured{Object: map[string]interface{}{}}
}
failed, storedVersion, conversionErr := getConversionStatus(input)
require.Equal(t, tt.expectedFailed, failed, "failed mismatch")
require.Equal(t, tt.expectedStoredVersion, storedVersion, "storedVersion mismatch")
require.Equal(t, tt.expectedError, conversionErr, "conversionErr mismatch")
})
}
}