full coverage for convert

This commit is contained in:
Ryan McKinley
2025-08-26 10:56:36 +03:00
parent 29e9ae1918
commit ebb4cfadff
20 changed files with 210 additions and 50 deletions
+1
View File
@@ -1484,6 +1484,7 @@ github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg=
github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
github.com/uber-go/atomic v1.4.0 h1:yOuPqEq4ovnhEjpHmfFwsqBXDYbQeT6Nb0bwD6XnD5o=
github.com/uber-go/atomic v1.4.0/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g=
github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/vertica/vertica-sql-go v1.3.3 h1:fL+FKEAEy5ONmsvya2WH5T8bhkvY27y/Ik3ReR2T+Qw=
+16 -10
View File
@@ -6,6 +6,7 @@ import (
"fmt"
"iter"
"maps"
"slices"
"strconv"
"strings"
"time"
@@ -24,11 +25,16 @@ import (
type converter struct {
mapper request.NamespaceMapper
group string // the expected group
dstype string // the expected pluginId
group string // the expected group
plugin string // the expected pluginId
alias []string // optional alias for the pluginId
}
func (r *converter) asDataSource(ds *datasources.DataSource) (*datasourceV0.DataSource, error) {
if !(ds.Type == r.plugin || slices.Contains(r.alias, ds.Type)) {
return nil, fmt.Errorf("expected datasource type: %s %v // not: %s", r.plugin, r.alias, ds.Type)
}
obj := &datasourceV0.DataSource{
ObjectMeta: metav1.ObjectMeta{
Name: ds.UID,
@@ -109,7 +115,7 @@ func (r *converter) toAddCommand(ds *datasourceV0.DataSource) (*datasources.AddD
Name: ds.Spec.Title(),
UID: ds.Name,
OrgID: info.OrgID,
Type: r.dstype,
Type: r.plugin,
Access: datasources.DsAccess(ds.Spec.Access()),
URL: ds.Spec.URL(),
@@ -127,8 +133,8 @@ func (r *converter) toAddCommand(ds *datasourceV0.DataSource) (*datasources.AddD
cmd.JsonData = simplejson.NewFromAny(jsonData)
}
cmd.SecureJsonData, err = toSecureJsonData(ds)
return cmd, err
cmd.SecureJsonData = toSecureJsonData(ds)
return cmd, nil
}
func (r *converter) toUpdateCommand(ds *datasourceV0.DataSource) (*datasources.UpdateDataSourceCommand, error) {
@@ -144,7 +150,7 @@ func (r *converter) toUpdateCommand(ds *datasourceV0.DataSource) (*datasources.U
Name: ds.Spec.Title(),
UID: ds.Name,
OrgID: info.OrgID,
Type: r.dstype,
Type: r.plugin,
Access: datasources.DsAccess(ds.Spec.Access()),
URL: ds.Spec.URL(),
@@ -164,13 +170,13 @@ func (r *converter) toUpdateCommand(ds *datasourceV0.DataSource) (*datasources.U
if jsonData != nil {
cmd.JsonData = simplejson.NewFromAny(jsonData)
}
cmd.SecureJsonData, err = toSecureJsonData(ds)
cmd.SecureJsonData = toSecureJsonData(ds)
return cmd, err
}
func toSecureJsonData(ds *datasourceV0.DataSource) (map[string]string, error) {
func toSecureJsonData(ds *datasourceV0.DataSource) map[string]string {
if ds == nil || len(ds.Secure) < 1 {
return nil, nil
return nil
}
secure := map[string]string{}
@@ -182,5 +188,5 @@ func toSecureJsonData(ds *datasourceV0.DataSource) (map[string]string, error) {
secure[k] = "" // Weirdly, this is the best we can do with the legacy API :(
}
}
return secure, nil
return secure
}
+69 -32
View File
@@ -2,6 +2,7 @@ package datasource
import (
"encoding/json"
"errors"
"os"
"path/filepath"
"testing"
@@ -18,24 +19,41 @@ func TestConverter(t *testing.T) {
t.Run("resource to command", func(t *testing.T) {
converter := converter{
mapper: types.OrgNamespaceFormatter,
dstype: "test-datasource",
plugin: "grafana-testdata-datasource",
alias: []string{"testdata"},
group: "testdata.grafana.datasource.app",
}
check := []string{
"convert-resource-A",
tests := []struct {
name string
expectedErr string
}{
{"convert-resource-full", ""},
{"convert-resource-empty", ""},
{"convert-resource-invalid", "expecting APIGroup: testdata.grafana.datasource.app"},
{"convert-resource-invalid2", "invalid stack id"},
}
for _, name := range check {
t.Run(name, func(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
obj := &v0alpha1.DataSource{}
fpath := filepath.Join("testdata", name+".json")
fpath := filepath.Join("testdata", tt.name+".json")
raw, err := os.ReadFile(fpath) // nolint:gosec
require.NoError(t, err)
err = json.Unmarshal(raw, obj)
require.NoError(t, err)
// The add command
fpath = filepath.Join("testdata", name+"-to-cmd-add.json")
fpath = filepath.Join("testdata", tt.name+"-to-cmd-add.json")
add, err := converter.toAddCommand(obj)
if tt.expectedErr != "" {
require.ErrorContains(t, err, tt.expectedErr)
require.Nil(t, add, "cmd should be nil when error exists")
update, err := converter.toUpdateCommand(obj)
require.ErrorContains(t, err, tt.expectedErr)
require.Nil(t, update, "cmd should be nil when error exists")
return
}
require.NoError(t, err)
out, err := json.MarshalIndent(add, "", " ")
require.NoError(t, err)
@@ -45,9 +63,10 @@ func TestConverter(t *testing.T) {
}
// The update command
fpath = filepath.Join("testdata", name+"-to-cmd-update.json")
fpath = filepath.Join("testdata", tt.name+"-to-cmd-update.json")
update, err := converter.toUpdateCommand(obj)
require.NoError(t, err)
out, err = json.MarshalIndent(update, "", " ")
require.NoError(t, err)
raw, _ = os.ReadFile(fpath) // nolint:gosec
@@ -63,7 +82,7 @@ func TestConverter(t *testing.T) {
roundtrip, err := converter.asDataSource(ds)
require.NoError(t, err)
fpath = filepath.Join("testdata", name+"-to-cmd-update-roundtrip.json")
fpath = filepath.Join("testdata", tt.name+"-to-cmd-update-roundtrip.json")
out, err = json.MarshalIndent(roundtrip, "", " ")
require.NoError(t, err)
raw, _ = os.ReadFile(fpath) // nolint:gosec
@@ -74,41 +93,59 @@ func TestConverter(t *testing.T) {
}
})
t.Run("db to resource", func(t *testing.T) {
t.Run("dto to resource", func(t *testing.T) {
converter := converter{
mapper: types.OrgNamespaceFormatter,
dstype: "test-datasource",
plugin: "grafana-testdata-datasource",
alias: []string{"testdata"},
group: "testdata.grafana.datasource.app",
}
check := []string{
"convert-db-B",
tests := []struct {
name string
expectedErr string
}{
{
name: "convert-dto-testdata",
},
{
name: "convert-dto-empty",
},
{
name: "convert-dto-invalid",
expectedErr: "expected datasource type: grafana-testdata-datasource [testdata]",
},
}
for _, name := range check {
t.Run(name, func(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ds := &datasources.DataSource{}
fpath := filepath.Join("testdata", name+".json")
fpath := filepath.Join("testdata", tt.name+".json")
raw, err := os.ReadFile(fpath) // nolint:gosec
require.NoError(t, err)
err = json.Unmarshal(raw, ds)
require.NoError(t, err)
// if true {
// ds.Created = time.Unix(100000000, 0).UTC()
// ds.Updated = time.Unix(500000000, 0).UTC()
// out, _ := json.MarshalIndent(ds, "", " ")
// fmt.Printf("%s\n", string(out))
// t.FailNow()
// }
// As an object
fpath = filepath.Join("testdata", name+"-to-resource.json")
obj, err := converter.asDataSource(ds)
require.NoError(t, err)
out, err := json.MarshalIndent(obj, "", " ")
require.NoError(t, err)
raw, _ = os.ReadFile(fpath) // nolint:gosec
if !assert.JSONEq(t, string(raw), string(out)) {
_ = os.WriteFile(fpath, out, 0600)
if tt.expectedErr != "" {
require.ErrorContains(t, err, tt.expectedErr)
require.Nil(t, obj, "object should be nil when error exists")
} else {
require.NoError(t, err)
}
// Verify the result
fpath = filepath.Join("testdata", tt.name+"-to-resource.json")
if obj == nil {
_, err := os.Stat(fpath)
require.Error(t, err, "file should not exist")
require.True(t, errors.Is(err, os.ErrNotExist))
} else {
out, err := json.MarshalIndent(obj, "", " ")
require.NoError(t, err)
raw, _ = os.ReadFile(fpath) // nolint:gosec
if !assert.JSONEq(t, string(raw), string(out)) {
_ = os.WriteFile(fpath, out, 0600)
}
}
})
}
@@ -77,7 +77,8 @@ func (q *cachingDatasourceProvider) GetDatasourceProvider(pluginJson plugins.JSO
contextProvider: q.contextProvider,
converter: &converter{
mapper: q.converter.mapper,
dstype: pluginJson.ID,
plugin: pluginJson.ID,
alias: pluginJson.AliasIDs,
group: group,
},
}
+1 -1
View File
@@ -8,11 +8,11 @@ import (
"net/url"
"strings"
"github.com/grafana/grafana-plugin-sdk-go/backend"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/rest"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana/pkg/plugins/httpresponsesender"
)
@@ -0,0 +1,16 @@
{
"metadata": {
"name": "unique-identifier",
"namespace": "org-0",
"uid": "YpaSG5GQAdxtLZtF6BqQWCeYXOhbVi5C4Cg4oILnJC0X",
"generation": 8,
"creationTimestamp": "2002-03-04T01:00:00Z",
"labels": {
"grafana.app/deprecatedInternalID": "456"
}
},
"spec": {
"jsonData": null,
"title": "Display name"
}
}
@@ -0,0 +1,8 @@
{
"id": 456,
"version": 8,
"name": "Display name",
"uid": "unique-identifier",
"type": "grafana-testdata-datasource",
"created": "2002-03-04T01:00:00Z"
}
@@ -0,0 +1,8 @@
{
"id": 456,
"version": 8,
"name": "Hello",
"uid": "unique-identifier",
"type": "not-valid-plugin",
"created": "2002-03-04T01:00:00Z"
}
@@ -33,7 +33,7 @@
},
"secure": {
"password": {
"name": "ds-c429ac622e"
"name": "ds-d5c1b093af"
}
}
}
@@ -3,7 +3,7 @@
"version": 2,
"name": "Hello",
"uid": "unique-identifier",
"type": "grafana-test-datasource",
"type": "grafana-testdata-datasource",
"access": "proxy",
"url": "http://something/",
"user": "A",
@@ -0,0 +1,15 @@
{
"name": "Hello testdata",
"type": "grafana-testdata-datasource",
"access": "",
"url": "",
"user": "",
"database": "",
"basicAuth": false,
"basicAuthUser": "",
"withCredentials": false,
"isDefault": false,
"jsonData": null,
"secureJsonData": null,
"uid": "cejobd88i85j4d"
}
@@ -0,0 +1,12 @@
{
"metadata": {
"name": "cejobd88i85j4d",
"namespace": "org-0",
"uid": "boDNh7zU3nXj46rOXIJI7r44qaxjs8yy9I9dOj1MyBoX",
"creationTimestamp": null
},
"spec": {
"jsonData": null,
"title": "Hello testdata"
}
}
@@ -0,0 +1,16 @@
{
"name": "Hello testdata",
"type": "grafana-testdata-datasource",
"access": "",
"url": "",
"user": "",
"database": "",
"basicAuth": false,
"basicAuthUser": "",
"withCredentials": false,
"isDefault": false,
"jsonData": null,
"secureJsonData": null,
"uid": "cejobd88i85j4d",
"version": 0
}
@@ -0,0 +1,8 @@
{
"metadata": {
"name": "cejobd88i85j4d"
},
"spec": {
"title": "Hello testdata"
}
}
@@ -1,6 +1,6 @@
{
"name": "Hello testdata",
"type": "test-datasource",
"type": "grafana-testdata-datasource",
"access": "proxy",
"url": "http://something/",
"user": "",
@@ -15,6 +15,7 @@
"ccc": 1.234
},
"secureJsonData": {
"extra": "",
"password": "XXXX"
},
"uid": "cejobd88i85j4d"
@@ -22,8 +22,11 @@
"withCredentials": true
},
"secure": {
"extra": {
"name": "ds-bb8b5d8b32"
},
"password": {
"name": "ds-fdb9f6717d"
"name": "ds-973a1eb29d"
}
}
}
@@ -1,6 +1,6 @@
{
"name": "Hello testdata",
"type": "test-datasource",
"type": "grafana-testdata-datasource",
"access": "proxy",
"url": "http://something/",
"user": "",
@@ -15,6 +15,7 @@
"ccc": 1.234
},
"secureJsonData": {
"extra": "",
"password": "XXXX"
},
"uid": "cejobd88i85j4d",
@@ -27,6 +27,7 @@
}
},
"secure": {
"password": { "create": "XXXX" }
"password": { "create": "XXXX" },
"extra": { "remove": true }
}
}
@@ -0,0 +1,17 @@
{
"apiVersion": "something/else",
"metadata": {
"name": "cejobd88i85j4d",
"namespace": "default",
"uid": "IGIUtEQS21DtLpBG2rSGfuDoUX8cwsGrtb5aXauYeA4X",
"resourceVersion": "1745320815000",
"generation": 2,
"creationTimestamp": "2025-04-22T11:20:11Z",
"labels": {
"grafana.app/deprecatedInternalID": "12345"
}
},
"spec": {
"title": "Hello testdata"
}
}
@@ -0,0 +1,9 @@
{
"metadata": {
"name": "cejobd88i85j4d",
"namespace": "stacks-invalid"
},
"spec": {
"title": "Hello testdata"
}
}