Files
grafana/apps/dashboard/pkg/migration/schemaversion/datasource_utils_test.go
Ivan Ortega Alba 93c14c52da Migrations: Compare backend and frontend outputs to ensure feature parity (#106851)
* wip: trying to understand how to get the ds info from migrator

* add datasource info provider

* Use DS service to fetch DS data

* add more tests cases to match with migrator cases

* Add snapshots

* Non-existing DS

* Add different DS for snapshots

* fix import

* Fix tests: guard against double initialization

* don't use full datasource package in test

* min version should be 35

* fix test

* fix conversion test

* Dashboards: Support schemaVersion v35 migration in backend

* Dashboards: Support schemaVersion v34 migration in backend

* Dashboards: Support schemaVersion v33 migration in backend

* Apply suggestions from code review

Co-authored-by: Stephanie Hingtgen <stephanie.hingtgen@grafana.com>

* Apply feedback

* Remove unused parameters

* Refactor to follow Go patterns

* Update logic

* Only write final migration result as output

* Compare backend and frontend results

* Improve snapshots to cover all possible use cases

* Linter

* wip make it consistent v33

* apply feedback

* Return default when the ref cannot be found

* Update apps/dashboard/pkg/migration/schemaversion/v33.go

Co-authored-by: Stephanie Hingtgen <stephanie.hingtgen@grafana.com>

* apply feedback

* Use same mocks backend/frontend

* restore migrations

* update snapshots

* Adapt migration tests to use min versions

* Ensure v40-v41 works

* Ensure v39-v40 works

* Simplify the naming of the files

* adjust jest to new input convention

* Ensure every migration v36-v41 works

* Improve v38 naming

* Ensure v36 migrates correctly

* Skip v36 refs migrations on rows

* Treat rows as frontend and ensure same results for v36

* Ensure v34 runs with the same logic than the frontend

* Leave empty stadistics as valid option

* ensure v33 is working as the frontend

* Update tests

* Undo frontend changes for legend handling

* Remove filtering by version in the frontend

* linter

* Clean up v33 input JSON

---------

Co-authored-by: Todd Treece <360020+toddtreece@users.noreply.github.com>
Co-authored-by: Haris Rozajac <haris.rozajac12@gmail.com>
Co-authored-by: Stephanie Hingtgen <stephanie.hingtgen@grafana.com>
2025-07-03 12:23:51 +02:00

421 lines
11 KiB
Go

package schemaversion_test
import (
"testing"
"github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion"
"github.com/stretchr/testify/assert"
)
func TestGetDataSourceRef(t *testing.T) {
tests := []struct {
name string
input *schemaversion.DataSourceInfo
expected map[string]interface{}
}{
{
name: "nil datasource should return nil",
input: nil,
expected: nil,
},
{
name: "datasource without apiVersion",
input: &schemaversion.DataSourceInfo{
UID: "test-uid",
Type: "prometheus",
Name: "Test DS",
},
expected: map[string]interface{}{
"uid": "test-uid",
"type": "prometheus",
},
},
{
name: "datasource with apiVersion",
input: &schemaversion.DataSourceInfo{
UID: "test-uid",
Type: "elasticsearch",
Name: "Test ES",
APIVersion: "v2",
},
expected: map[string]interface{}{
"uid": "test-uid",
"type": "elasticsearch",
"apiVersion": "v2",
},
},
{
name: "datasource with empty apiVersion",
input: &schemaversion.DataSourceInfo{
UID: "test-uid",
Type: "prometheus",
Name: "Test",
APIVersion: "",
},
expected: map[string]interface{}{
"uid": "test-uid",
"type": "prometheus",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := schemaversion.GetDataSourceRef(tt.input)
assert.Equal(t, tt.expected, result)
})
}
}
func TestGetDefaultDSInstanceSettings(t *testing.T) {
tests := []struct {
name string
datasources []schemaversion.DataSourceInfo
expected *schemaversion.DataSourceInfo
}{
{
name: "empty datasources list",
datasources: []schemaversion.DataSourceInfo{},
expected: nil,
},
{
name: "no default datasource",
datasources: []schemaversion.DataSourceInfo{
{UID: "existing-ref-uid", Type: "prometheus", Name: "Existing Ref Name", Default: false},
{UID: "existing-target-uid", Type: "elasticsearch", Name: "Existing Target Name", Default: false},
},
expected: nil,
},
{
name: "single default datasource",
datasources: []schemaversion.DataSourceInfo{
{UID: "existing-ref-uid", Type: "prometheus", Name: "Existing Ref Name", Default: false},
{UID: "default-ds-uid", Type: "prometheus", Name: "Default Test Datasource Name", Default: true, APIVersion: "v1"},
{UID: "existing-target-uid", Type: "elasticsearch", Name: "Existing Target Name", Default: false},
},
expected: &schemaversion.DataSourceInfo{
UID: "default-ds-uid",
Type: "prometheus",
Name: "Default Test Datasource Name",
APIVersion: "v1",
},
},
{
name: "multiple default datasources returns first",
datasources: []schemaversion.DataSourceInfo{
{UID: "first-default", Type: "prometheus", Name: "First Default", Default: true, APIVersion: "v1"},
{UID: "second-default", Type: "elasticsearch", Name: "Second Default", Default: true, APIVersion: "v2"},
},
expected: &schemaversion.DataSourceInfo{
UID: "first-default",
Type: "prometheus",
Name: "First Default",
APIVersion: "v1",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := schemaversion.GetDefaultDSInstanceSettings(tt.datasources)
assert.Equal(t, tt.expected, result)
})
}
}
func TestGetInstanceSettings(t *testing.T) {
datasources := []schemaversion.DataSourceInfo{
{UID: "default-ds-uid", Type: "prometheus", Name: "Default Test Datasource Name", Default: true, APIVersion: "v1"},
{UID: "existing-target-uid", Type: "elasticsearch", Name: "Existing Target Name", Default: false, APIVersion: "v2"},
{UID: "existing-ref-uid", Type: "prometheus", Name: "Existing Ref Name", Default: false, APIVersion: "v1"},
}
tests := []struct {
name string
nameOrRef interface{}
expected *schemaversion.DataSourceInfo
}{
{
name: "nil should return default",
nameOrRef: nil,
expected: &schemaversion.DataSourceInfo{
UID: "default-ds-uid",
Type: "prometheus",
Name: "Default Test Datasource Name",
APIVersion: "v1",
},
},
{
name: "default string should return default",
nameOrRef: "default",
expected: &schemaversion.DataSourceInfo{
UID: "default-ds-uid",
Type: "prometheus",
Name: "Default Test Datasource Name",
APIVersion: "v1",
},
},
{
name: "lookup by UID",
nameOrRef: "existing-target-uid",
expected: &schemaversion.DataSourceInfo{
UID: "existing-target-uid",
Type: "elasticsearch",
Name: "Existing Target Name",
APIVersion: "v2",
},
},
{
name: "lookup by name",
nameOrRef: "Existing Target Name",
expected: &schemaversion.DataSourceInfo{
UID: "existing-target-uid",
Type: "elasticsearch",
Name: "Existing Target Name",
APIVersion: "v2",
},
},
{
name: "lookup by UID without apiVersion",
nameOrRef: "existing-ref-uid",
expected: &schemaversion.DataSourceInfo{
UID: "existing-ref-uid",
Type: "prometheus",
Name: "Existing Ref Name",
APIVersion: "v1",
},
},
{
name: "lookup by reference object with UID",
nameOrRef: map[string]interface{}{
"uid": "existing-target-uid",
},
expected: &schemaversion.DataSourceInfo{
UID: "existing-target-uid",
Type: "elasticsearch",
Name: "Existing Target Name",
APIVersion: "v2",
},
},
{
name: "lookup by reference object without UID",
nameOrRef: map[string]interface{}{
"type": "prometheus",
},
expected: &schemaversion.DataSourceInfo{
UID: "default-ds-uid",
Type: "prometheus",
Name: "Default Test Datasource Name",
APIVersion: "v1",
},
},
{
name: "unknown datasource should return nil",
nameOrRef: "unknown-ds",
expected: nil,
},
{
name: "empty string should return nil",
nameOrRef: "",
expected: nil,
},
{
name: "unsupported input type should return default",
nameOrRef: 123,
expected: &schemaversion.DataSourceInfo{
UID: "default-ds-uid",
Type: "prometheus",
Name: "Default Test Datasource Name",
APIVersion: "v1",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := schemaversion.GetInstanceSettings(tt.nameOrRef, datasources)
assert.Equal(t, tt.expected, result)
})
}
}
func TestMigrateDatasourceNameToRef(t *testing.T) {
datasources := []schemaversion.DataSourceInfo{
{UID: "default-ds-uid", Type: "prometheus", Name: "Default Test Datasource Name", Default: true, APIVersion: "v1"},
{UID: "existing-target-uid", Type: "elasticsearch", Name: "Existing Target Name", Default: false, APIVersion: "v2"},
{UID: "existing-ref-uid", Type: "prometheus", Name: "Existing Ref Name", Default: false, APIVersion: "v1"},
}
t.Run("returnDefaultAsNull: true", func(t *testing.T) {
options := map[string]bool{"returnDefaultAsNull": true}
tests := []struct {
name string
nameOrRef interface{}
expected map[string]interface{}
}{
{
name: "nil should return nil",
nameOrRef: nil,
expected: nil,
},
{
name: "default should return nil",
nameOrRef: "default",
expected: nil,
},
{
name: "existing reference should be preserved",
nameOrRef: map[string]interface{}{
"uid": "existing-uid",
"type": "existing-type",
},
expected: map[string]interface{}{
"uid": "existing-uid",
"type": "existing-type",
},
},
{
name: "lookup by UID",
nameOrRef: "existing-target-uid",
expected: map[string]interface{}{
"uid": "existing-target-uid",
"type": "elasticsearch",
"apiVersion": "v2",
},
},
{
name: "lookup by name",
nameOrRef: "Existing Target Name",
expected: map[string]interface{}{
"uid": "existing-target-uid",
"type": "elasticsearch",
"apiVersion": "v2",
},
},
{
name: "unknown datasource should preserve as UID",
nameOrRef: "unknown-ds",
expected: map[string]interface{}{
"uid": "unknown-ds",
},
},
{
name: "empty string should return empty object",
nameOrRef: "",
expected: map[string]interface{}{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := schemaversion.MigrateDatasourceNameToRef(tt.nameOrRef, options, datasources)
assert.Equal(t, tt.expected, result)
})
}
})
t.Run("returnDefaultAsNull: false", func(t *testing.T) {
options := map[string]bool{"returnDefaultAsNull": false}
tests := []struct {
name string
nameOrRef interface{}
expected map[string]interface{}
}{
{
name: "nil should return default reference",
nameOrRef: nil,
expected: map[string]interface{}{
"uid": "default-ds-uid",
"type": "prometheus",
"apiVersion": "v1",
},
},
{
name: "default should return default reference",
nameOrRef: "default",
expected: map[string]interface{}{
"uid": "default-ds-uid",
"type": "prometheus",
"apiVersion": "v1",
},
},
{
name: "existing reference should be preserved",
nameOrRef: map[string]interface{}{
"uid": "existing-uid",
"type": "existing-type",
},
expected: map[string]interface{}{
"uid": "existing-uid",
"type": "existing-type",
},
},
{
name: "lookup by UID",
nameOrRef: "existing-target-uid",
expected: map[string]interface{}{
"uid": "existing-target-uid",
"type": "elasticsearch",
"apiVersion": "v2",
},
},
{
name: "unknown datasource should preserve as UID",
nameOrRef: "unknown-ds",
expected: map[string]interface{}{
"uid": "unknown-ds",
},
},
{
name: "empty string should return empty object",
nameOrRef: "",
expected: map[string]interface{}{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := schemaversion.MigrateDatasourceNameToRef(tt.nameOrRef, options, datasources)
assert.Equal(t, tt.expected, result)
})
}
})
t.Run("edge cases", func(t *testing.T) {
options := map[string]bool{"returnDefaultAsNull": false}
t.Run("reference without uid should lookup default", func(t *testing.T) {
nameOrRef := map[string]interface{}{
"type": "prometheus",
}
result := schemaversion.MigrateDatasourceNameToRef(nameOrRef, options, datasources)
expected := map[string]interface{}{
"uid": "default-ds-uid",
"type": "prometheus",
"apiVersion": "v1",
}
assert.Equal(t, expected, result)
})
t.Run("integer input should return default reference", func(t *testing.T) {
result := schemaversion.MigrateDatasourceNameToRef(123, options, datasources)
expected := map[string]interface{}{
"uid": "default-ds-uid",
"type": "prometheus",
"apiVersion": "v1",
}
assert.Equal(t, expected, result)
})
t.Run("empty datasources list", func(t *testing.T) {
result := schemaversion.MigrateDatasourceNameToRef("any-ds", options, []schemaversion.DataSourceInfo{})
expected := map[string]interface{}{
"uid": "any-ds",
}
assert.Equal(t, expected, result)
})
})
}