Files
grafana/apps/dashboard/pkg/migration/migrate_test.go
T
Ivan Ortega Alba d1cff535d2 Schema V2: Snapshot testing for migrations should fail when output doesn't match (#109278)
* Schema v2: Snapshot testing for migration fails when doesn't match

* Update snapshot
2025-08-06 19:50:21 +00:00

117 lines
3.8 KiB
Go

package migration_test
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/apps/dashboard/pkg/migration"
"github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion"
"github.com/grafana/grafana/apps/dashboard/pkg/migration/testutil"
)
const INPUT_DIR = "testdata/input"
const OUTPUT_DIR = "testdata/output"
func TestMigrate(t *testing.T) {
files, err := os.ReadDir(INPUT_DIR)
require.NoError(t, err)
// Use the same datasource provider as the frontend test to ensure consistency
migration.Initialize(testutil.GetTestDataSourceProvider(), testutil.GetTestPanelProvider())
t.Run("minimum version check", func(t *testing.T) {
err := migration.Migrate(map[string]interface{}{
"schemaVersion": schemaversion.MIN_VERSION - 1,
}, schemaversion.MIN_VERSION)
var minVersionErr = schemaversion.NewMinimumVersionError(schemaversion.MIN_VERSION - 1)
require.ErrorAs(t, err, &minVersionErr)
})
for _, f := range files {
if f.IsDir() {
continue
}
// Validate filename format
if !strings.HasPrefix(f.Name(), "v") || !strings.HasSuffix(f.Name(), ".json") {
t.Fatalf("input filename must use v{N}.{name}.json format, got: %s", f.Name())
}
inputDash := loadDashboard(t, filepath.Join(INPUT_DIR, f.Name()))
inputVersion := getSchemaVersion(t, inputDash)
t.Run("input check "+f.Name(), func(t *testing.T) {
// use input version as the target version to ensure there are no changes
require.NoError(t, migration.Migrate(inputDash, inputVersion), "input check migration failed")
outBytes, err := json.MarshalIndent(inputDash, "", " ")
require.NoError(t, err, "failed to marshal migrated dashboard")
// We can ignore gosec G304 here since it's a test
// nolint:gosec
expectedDash, err := os.ReadFile(filepath.Join(INPUT_DIR, f.Name()))
require.NoError(t, err, "failed to read expected output file")
require.JSONEq(t, string(expectedDash), string(outBytes), "%s input check did not match", f.Name())
})
testName := fmt.Sprintf("%s v%d to v%d", f.Name(), inputVersion, schemaversion.LATEST_VERSION)
t.Run(testName, func(t *testing.T) {
testMigration(t, inputDash, f.Name(), schemaversion.LATEST_VERSION)
})
}
}
func testMigration(t *testing.T, dash map[string]interface{}, inputFileName string, targetVersion int) {
t.Helper()
require.NoError(t, migration.Migrate(dash, targetVersion), "%d migration failed", targetVersion)
outPath := filepath.Join(OUTPUT_DIR, inputFileName)
outBytes, err := json.MarshalIndent(dash, "", " ")
require.NoError(t, err, "failed to marshal migrated dashboard")
if _, err := os.Stat(outPath); os.IsNotExist(err) {
err = os.WriteFile(outPath, outBytes, 0644)
require.NoError(t, err, "failed to write new output file", outPath)
return
}
// We can ignore gosec G304 here since it's a test
// nolint:gosec
existingBytes, err := os.ReadFile(outPath)
require.NoError(t, err, "failed to read existing output file")
require.JSONEq(t, string(existingBytes), string(outBytes), "%s did not match", outPath)
}
func getSchemaVersion(t *testing.T, dash map[string]interface{}) int {
t.Helper()
version, ok := dash["schemaVersion"]
require.True(t, ok, "dashboard missing schemaVersion")
switch v := version.(type) {
case int:
return v
case float64:
return int(v)
default:
t.Fatalf("invalid schemaVersion type: %T", version)
return 0
}
}
func loadDashboard(t *testing.T, path string) map[string]interface{} {
t.Helper()
// We can ignore gosec G304 here since it's a test
// nolint:gosec
inputBytes, err := os.ReadFile(path)
require.NoError(t, err, "failed to read input file")
var dash map[string]interface{}
require.NoError(t, json.Unmarshal(inputBytes, &dash), "failed to unmarshal dashboard JSON")
return dash
}