// Copyright 2021 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package kubeclient import ( "io" "testing" "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" ) func Test_maybeRestoreGVK(t *testing.T) { type args struct { unknown *runtime.Unknown origGVK, newGVK schema.GroupVersionKind } tests := []struct { name string args args want runtime.Object wantChanged bool wantErr string }{ { name: "should update gvk via JSON", args: args{ unknown: &runtime.Unknown{ TypeMeta: runtime.TypeMeta{ APIVersion: "new/v1", Kind: "Tree", }, Raw: []byte(`{"apiVersion":"new/v1","kind":"Tree","spec":{"pandas":"love"}}`), ContentType: runtime.ContentTypeJSON, }, origGVK: schema.GroupVersionKind{ Group: "old", Version: "v1", Kind: "Tree", }, newGVK: schema.GroupVersionKind{ Group: "new", Version: "v1", Kind: "Tree", }, }, want: &runtime.Unknown{ TypeMeta: runtime.TypeMeta{ APIVersion: "old/v1", Kind: "Tree", }, Raw: []byte(`{"apiVersion":"old/v1","kind":"Tree","spec":{"pandas":"love"}}`), ContentType: runtime.ContentTypeJSON, }, wantChanged: true, wantErr: "", }, { name: "should update gvk via protobuf", args: args{ unknown: &runtime.Unknown{ TypeMeta: runtime.TypeMeta{ APIVersion: "new/v1", Kind: "Tree", }, Raw: []byte(`assumed to be valid and does not change`), ContentType: runtime.ContentTypeProtobuf, }, origGVK: schema.GroupVersionKind{ Group: "original", Version: "v1", Kind: "Tree", }, newGVK: schema.GroupVersionKind{ Group: "new", Version: "v1", Kind: "Tree", }, }, want: &runtime.Unknown{ TypeMeta: runtime.TypeMeta{ APIVersion: "original/v1", Kind: "Tree", }, Raw: []byte(`assumed to be valid and does not change`), ContentType: runtime.ContentTypeProtobuf, }, wantChanged: true, wantErr: "", }, { name: "should ignore because gvk is different", args: args{ unknown: &runtime.Unknown{ TypeMeta: runtime.TypeMeta{ APIVersion: "new/v1", Kind: "Tree", }, }, newGVK: schema.GroupVersionKind{ Group: "new", Version: "v1", Kind: "Forest", }, }, want: nil, wantChanged: false, wantErr: "", }, { name: "empty raw is ignored", args: args{ unknown: &runtime.Unknown{}, }, want: nil, wantChanged: false, wantErr: "", }, { name: "invalid content type errors", args: args{ unknown: &runtime.Unknown{ TypeMeta: runtime.TypeMeta{ APIVersion: "walrus.tld/v1", Kind: "Seal", }, Raw: []byte(`data that should be ignored because we do not used YAML`), ContentType: runtime.ContentTypeYAML, }, origGVK: schema.GroupVersionKind{ Group: "pinniped.dev", Version: "v1", Kind: "Seal", }, newGVK: schema.GroupVersionKind{ Group: "walrus.tld", Version: "v1", Kind: "Seal", }, }, want: nil, wantChanged: false, wantErr: "unknown content type: application/yaml", }, { name: "invalid JSON should error", args: args{ unknown: &runtime.Unknown{ TypeMeta: runtime.TypeMeta{ APIVersion: "ocean/v1", Kind: "Water", }, Raw: []byte(`lol not JSON`), ContentType: runtime.ContentTypeJSON, }, origGVK: schema.GroupVersionKind{ Group: "dirt", Version: "v1", Kind: "Land", }, newGVK: schema.GroupVersionKind{ Group: "ocean", Version: "v1", Kind: "Water", }, }, want: nil, wantChanged: false, wantErr: "failed to unmarshall json keys: invalid character 'l' looking for beginning of value", }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { serializer := &testSerializer{unknown: tt.args.unknown} respData := []byte(`original`) result := &mutationResult{origGVK: tt.args.origGVK, newGVK: tt.args.newGVK, gvkChanged: tt.args.origGVK != tt.args.newGVK} newRespData, err := maybeRestoreGVK(serializer, respData, result) if len(tt.wantErr) > 0 { require.EqualError(t, err, tt.wantErr) require.Nil(t, newRespData) require.Nil(t, serializer.obj) return } require.NoError(t, err) if tt.wantChanged { require.Equal(t, []byte(`changed`), newRespData) } else { require.Equal(t, []byte(`original`), newRespData) } require.Equal(t, tt.want, serializer.obj) }) } } type testSerializer struct { unknown *runtime.Unknown obj runtime.Object } func (s *testSerializer) Encode(obj runtime.Object, w io.Writer) error { s.obj = obj _, err := w.Write([]byte(`changed`)) return err } func (s *testSerializer) Decode(_ []byte, _ *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) { u := into.(*runtime.Unknown) *u = *s.unknown return u, nil, nil } func (s *testSerializer) Identifier() runtime.Identifier { panic("not called") }