Repository: clouditor/clouditor
Pull request #774 (2022-08-10 21:15:21)

Providing a serialiser for protobuf message `google.protobuf.Any` in GORM
This PR adds a GORM serialiser, which can serialise a `google.protobuf.Any` protobuf message.

codecov[bot]: # [Codecov](https://codecov.io/gh/clouditor/clouditor/pull/774?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor) Report
> :exclamation: No coverage uploaded for pull request base (`main@87923c2`). [Click here to learn what that means](https://docs.codecov.io/docs/error-reference?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor#section-missing-base-commit).
> The diff coverage is `85.29%`.

```diff
@@           Coverage Diff           @@
##             main     #774   +/-   ##
=======================================
  Coverage        ?   80.94%           
=======================================
  Files           ?       71           
  Lines           ?     6097           
  Branches        ?        0           
=======================================
  Hits            ?     4935           
  Misses          ?      916           
  Partials        ?      246           
```

| Flag | Coverage Δ | |
|---|---|---|
| unittests | `80.94% <85.29%> (?)` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/clouditor/clouditor/pull/774?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor) | Coverage Δ | |
|---|---|---|
| [service/assessment/assessment.go](https://codecov.io/gh/clouditor/clouditor/pull/774/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor#diff-c2VydmljZS9hc3Nlc3NtZW50L2Fzc2Vzc21lbnQuZ28=) | `79.94% <0.00%> (ø)` | |
| [persistence/gorm/serializer.go](https://codecov.io/gh/clouditor/clouditor/pull/774/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor#diff-cGVyc2lzdGVuY2UvZ29ybS9zZXJpYWxpemVyLmdv) | `89.83% <89.83%> (ø)` | |
| [persistence/gorm/gorm.go](https://codecov.io/gh/clouditor/clouditor/pull/774/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor#diff-cGVyc2lzdGVuY2UvZ29ybS9nb3JtLmdv) | `93.20% <100.00%> (ø)` | |
| [service/auth\_middleware.go](https://codecov.io/gh/clouditor/clouditor/pull/774/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor#diff-c2VydmljZS9hdXRoX21pZGRsZXdhcmUuZ28=) | `93.93% <100.00%> (ø)` | |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor)


Patch:
diff --git a/persistence/gorm/gorm.go b/persistence/gorm/gorm.go
index 2f3aeb46..a56850e8 100644
--- a/persistence/gorm/gorm.go
+++ b/persistence/gorm/gorm.go
@@ -26,11 +26,8 @@
 package gorm
 
 import (
-	"context"
 	"errors"
 	"fmt"
-	"reflect"
-	"time"
 
 	"clouditor.io/clouditor/api/assessment"
 	"clouditor.io/clouditor/api/auth"
@@ -38,7 +35,6 @@ import (
 	"clouditor.io/clouditor/persistence"
 
 	"github.com/sirupsen/logrus"
-	"google.golang.org/protobuf/types/known/timestamppb"
 	"gorm.io/driver/postgres"
 	"gorm.io/driver/sqlite"
 	"gorm.io/gorm"
@@ -150,6 +146,7 @@ func NewStorage(opts ...StorageOption) (s persistence.Storage, err error) {
 	}
 
 	schema.RegisterSerializer("timestamppb", &TimestampSerializer{})
+	schema.RegisterSerializer("anypb", &AnySerializer{})
 
 	// After successful DB initialization, migrate the schema
 	if err = g.db.AutoMigrate(g.types...); err != nil {
@@ -224,45 +221,3 @@ func (s *storage) Delete(r any, conds ...any) error {
 
 	return nil
 }
-
-// TimestampSerializer is a GORM serializer that allows the serialization and deserialization of the
-// google.protobuf.Timestamp protobuf message type.
-type TimestampSerializer struct{}
-
-// Value implements https://pkg.go.dev/gorm.io/gorm/schema#SerializerValuerInterface to indicate
-// how this struct will be saved into an SQL database field.
-func (TimestampSerializer) Value(_ context.Context, _ *schema.Field, _ reflect.Value, fieldValue interface{}) (interface{}, error) {
-	var (
-		t  *timestamppb.Timestamp
-		ok bool
-	)
-
-	if fieldValue == nil {
-		return nil, nil
-	}
-
-	if t, ok = fieldValue.(*timestamppb.Timestamp); !ok {
-		return nil, persistence.ErrUnsupportedType
-	}
-
-	return t.AsTime(), nil
-}
-
-// Scan implements https://pkg.go.dev/gorm.io/gorm/schema#SerializerInterface to indicate how
-// this struct can be loaded from an SQL database field.
-func (TimestampSerializer) Scan(ctx context.Context, field *schema.Field, dst reflect.Value, dbValue interface{}) (err error) {
-	var t *timestamppb.Timestamp
-
-	if dbValue != nil {
-		switch v := dbValue.(type) {
-		case time.Time:
-			t = timestamppb.New(v)
-		default:
-			return persistence.ErrUnsupportedType
-		}
-
-		field.ReflectValueOf(ctx, dst).Set(reflect.ValueOf(t))
-	}
-
-	return
-}
diff --git a/persistence/gorm/serializer.go b/persistence/gorm/serializer.go
new file mode 100644
index 00000000..46a11f73
--- /dev/null
+++ b/persistence/gorm/serializer.go
@@ -0,0 +1,135 @@
+// Copyright 2016-2022 Fraunhofer AISEC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//           $$\                           $$\ $$\   $$\
+//           $$ |                          $$ |\__|  $$ |
+//  $$$$$$$\ $$ | $$$$$$\  $$\   $$\  $$$$$$$ |$$\ $$$$$$\    $$$$$$\   $$$$$$\
+// $$  _____|$$ |$$  __$$\ $$ |  $$ |$$  __$$ |$$ |\_$$  _|  $$  __$$\ $$  __$$\
+// $$ /      $$ |$$ /  $$ |$$ |  $$ |$$ /  $$ |$$ |  $$ |    $$ /  $$ |$$ | \__|
+// $$ |      $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$\ $$ |  $$ |$$ |
+// \$$$$$$\  $$ |\$$$$$   |\$$$$$   |\$$$$$$  |$$ |  \$$$   |\$$$$$   |$$ |
+//  \_______|\__| \______/  \______/  \_______|\__|   \____/  \______/ \__|
+//
+// This file is part of Clouditor Community Edition.
+
+package gorm
+
+import (
+	"context"
+	"fmt"
+	"reflect"
+	"time"
+
+	"google.golang.org/protobuf/encoding/protojson"
+	"google.golang.org/protobuf/types/known/anypb"
+	"google.golang.org/protobuf/types/known/timestamppb"
+	"gorm.io/gorm/schema"
+
+	"clouditor.io/clouditor/persistence"
+)
+
+// TimestampSerializer is a GORM serializer that allows the serialization and deserialization of the
+// google.protobuf.Timestamp protobuf message type.
+type TimestampSerializer struct{}
+
+// Value implements https://pkg.go.dev/gorm.io/gorm/schema#SerializerValuerInterface to indicate
+// how this struct will be saved into an SQL database field.
+func (TimestampSerializer) Value(_ context.Context, _ *schema.Field, _ reflect.Value, fieldValue interface{}) (interface{}, error) {
+	var (
+		t  *timestamppb.Timestamp
+		ok bool
+	)
+
+	if fieldValue == nil {
+		return nil, nil
+	}
+
+	if t, ok = fieldValue.(*timestamppb.Timestamp); !ok {
+		return nil, persistence.ErrUnsupportedType
+	}
+
+	return t.AsTime(), nil
+}
+
+// Scan implements https://pkg.go.dev/gorm.io/gorm/schema#SerializerInterface to indicate how
+// this struct can be loaded from an SQL database field.
+func (TimestampSerializer) Scan(ctx context.Context, field *schema.Field, dst reflect.Value, dbValue interface{}) (err error) {
+	var t *timestamppb.Timestamp
+
+	if dbValue != nil {
+		switch v := dbValue.(type) {
+		case time.Time:
+			t = timestamppb.New(v)
+		default:
+			return persistence.ErrUnsupportedType
+		}
+
+		field.ReflectValueOf(ctx, dst).Set(reflect.ValueOf(t))
+	}
+
+	return
+}
+
+// AnySerializer is a GORM serializer that allows the serialization and deserialization of the
+// google.protobuf.Any protobuf message type using a JSONB field.
+type AnySerializer struct{}
+
+// Value implements https://pkg.go.dev/gorm.io/gorm/schema#SerializerValuerInterface to indicate
+// how this struct will be saved into an SQL database field.
+func (AnySerializer) Value(_ context.Context, _ *schema.Field, _ reflect.Value, fieldValue interface{}) (interface{}, error) {
+	var (
+		a  *anypb.Any
+		ok bool
+	)
+
+	if fieldValue == nil {
+		return nil, nil
+	}
+
+	if a, ok = fieldValue.(*anypb.Any); !ok {
+		return nil, persistence.ErrUnsupportedType
+	}
+
+	return protojson.Marshal(a)
+}
+
+// Scan implements https://pkg.go.dev/gorm.io/gorm/schema#SerializerInterface to indicate how
+// this struct can be loaded from an SQL database field.
+func (AnySerializer) Scan(ctx context.Context, field *schema.Field, dst reflect.Value, dbValue interface{}) (err error) {
+	var (
+		a anypb.Any
+	)
+
+	if dbValue != nil {
+		var bytes []byte
+		switch v := dbValue.(type) {
+		case []byte:
+			bytes = v
+		case string:
+			bytes = []byte(v)
+		default:
+			return persistence.ErrUnsupportedType
+		}
+
+		fmt.Printf("%s", bytes)
+
+		err = protojson.Unmarshal(bytes, &a)
+		if err != nil {
+			return fmt.Errorf("could not unmarshal JSONB value into protobuf message: %w", err)
+		}
+	}
+
+	field.ReflectValueOf(ctx, dst).Set(reflect.ValueOf(&a))
+	return
+}
diff --git a/service/assessment/assessment.go b/service/assessment/assessment.go
index 532afe48..2f424c84 100644
--- a/service/assessment/assessment.go
+++ b/service/assessment/assessment.go
@@ -168,6 +168,13 @@ func WithOAuth2Authorizer(config *clientcredentials.Config) service.Option[Servi
 	}
 }
 
+// WithAuthorizer is an option to use a pre-created authorizer
+func WithAuthorizer(auth api.Authorizer) service.Option[Service] {
+	return func(s *Service) {
+		s.SetAuthorizer(auth)
+	}
+}
+
 // WithRegoPackageName is an option to configure the Rego package name
 func WithRegoPackageName(pkg string) service.Option[Service] {
 	return func(s *Service) {
diff --git a/service/auth_middleware.go b/service/auth_middleware.go
index 419bc8b6..000cfb41 100644
--- a/service/auth_middleware.go
+++ b/service/auth_middleware.go
@@ -131,6 +131,8 @@ func ConfigureAuth(opts ...AuthOption) *AuthConfig {
 
 		token, err := grpc_auth.AuthFromMD(ctx, "bearer")
 		if err != nil {
+			log.Debugf("Could not retrieve bearer token from header metadata: %v", err)
+
 			// We do not want to disclose any error details which could be security related,
 			// so we do not wrap the original error
 			return nil, status.Error(codes.Unauthenticated, "invalid auth token")
@@ -138,6 +140,8 @@ func ConfigureAuth(opts ...AuthOption) *AuthConfig {
 
 		tokenInfo, err := parseToken(token, config)
 		if err != nil {
+			log.Debugf("Could not parse token in request: %v", err)
+
 			// We do not want to disclose any error details which could be security related,
 			// so we do not wrap the original error
 			return nil, status.Errorf(codes.Unauthenticated, "invalid auth token")


Test patch:
diff --git a/persistence/gorm/gorm_test.go b/persistence/gorm/gorm_test.go
index 6305c63f..dfb05c15 100644
--- a/persistence/gorm/gorm_test.go
+++ b/persistence/gorm/gorm_test.go
@@ -1,15 +1,12 @@
 package gorm
 
 import (
-	"context"
 	"fmt"
-	"reflect"
 	"testing"
 	"time"
 
 	"github.com/stretchr/testify/assert"
 	"google.golang.org/protobuf/types/known/timestamppb"
-	"gorm.io/gorm/schema"
 
 	"clouditor.io/clouditor/api/assessment"
 	"clouditor.io/clouditor/api/auth"
@@ -488,109 +485,3 @@ func Test_storage_Delete(t *testing.T) {
 	assert.Contains(t, s.Delete("Unsupported Type").Error(), "unsupported data type")
 
 }
-
-func TestTimestampSerializer_Value(t *testing.T) {
-	type args struct {
-		ctx        context.Context
-		field      *schema.Field
-		dst        reflect.Value
-		fieldValue interface{}
-	}
-	tests := []struct {
-		name    string
-		tr      TimestampSerializer
-		args    args
-		want    interface{}
-		wantErr assert.ErrorAssertionFunc
-	}{
-		{
-			name: "ok field",
-			args: args{
-				field:      &schema.Field{Name: "timestamp"},
-				dst:        reflect.Value{},
-				fieldValue: timestamppb.New(time.Date(2000, 1, 1, 1, 1, 1, 1, time.UTC)),
-			},
-			want:    time.Date(2000, 1, 1, 1, 1, 1, 1, time.UTC),
-			wantErr: nil,
-		},
-		{
-			name: "nil field",
-			args: args{
-				field:      &schema.Field{Name: "timestamp"},
-				dst:        reflect.Value{},
-				fieldValue: nil,
-			},
-			want:    nil,
-			wantErr: nil,
-		},
-		{
-			name: "field wrong type",
-			args: args{
-				field:      &schema.Field{Name: "timestamp"},
-				dst:        reflect.Value{},
-				fieldValue: "string",
-			},
-			want: nil,
-			wantErr: func(tt assert.TestingT, err error, i ...interface{}) bool {
-				return assert.ErrorIs(t, err, persistence.ErrUnsupportedType)
-			},
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			tr := TimestampSerializer{}
-
-			got, err := tr.Value(tt.args.ctx, tt.args.field, tt.args.dst, tt.args.fieldValue)
-
-			if tt.wantErr != nil {
-				tt.wantErr(t, err, tt.args)
-			} else {
-				assert.Nil(t, err)
-			}
-
-			if !reflect.DeepEqual(got, tt.want) {
-				t.Errorf("TimestampSerializer.Value() = %v, want %v", got, tt.want)
-			}
-		})
-	}
-}
-
-func TestTimestampSerializer_Scan(t *testing.T) {
-	type args struct {
-		ctx     context.Context
-		field   *schema.Field
-		dst     reflect.Value
-		dbValue interface{}
-	}
-	tests := []struct {
-		name    string
-		tr      TimestampSerializer
-		args    args
-		wantErr assert.ErrorAssertionFunc
-	}{
-		{
-			name: "db wrong type",
-			args: args{
-				field:   &schema.Field{},
-				dbValue: "string",
-			},
-			wantErr: func(tt assert.TestingT, err error, i ...interface{}) bool {
-				return assert.ErrorIs(t, err, persistence.ErrUnsupportedType)
-			},
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			tr := TimestampSerializer{}
-			err := tr.Scan(tt.args.ctx, tt.args.field, tt.args.dst, tt.args.dbValue)
-
-			if tt.wantErr != nil {
-				tt.wantErr(t, err, tt.args)
-			} else {
-				assert.Nil(t, err)
-			}
-		})
-	}
-}
diff --git a/persistence/gorm/serializer_test.go b/persistence/gorm/serializer_test.go
new file mode 100644
index 00000000..593393f6
--- /dev/null
+++ b/persistence/gorm/serializer_test.go
@@ -0,0 +1,249 @@
+package gorm
+
+import (
+	"context"
+	"encoding/json"
+	"reflect"
+	"testing"
+	"time"
+
+	"clouditor.io/clouditor/api/orchestrator"
+	"clouditor.io/clouditor/persistence"
+	"github.com/stretchr/testify/assert"
+	"google.golang.org/protobuf/types/known/anypb"
+	"google.golang.org/protobuf/types/known/timestamppb"
+	"gorm.io/gorm/schema"
+)
+
+func TestTimestampSerializer_Value(t *testing.T) {
+	type args struct {
+		ctx        context.Context
+		field      *schema.Field
+		dst        reflect.Value
+		fieldValue interface{}
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    interface{}
+		wantErr assert.ErrorAssertionFunc
+	}{
+		{
+			name: "ok field",
+			args: args{
+				field:      &schema.Field{Name: "timestamp"},
+				dst:        reflect.Value{},
+				fieldValue: timestamppb.New(time.Date(2000, 1, 1, 1, 1, 1, 1, time.UTC)),
+			},
+			want:    time.Date(2000, 1, 1, 1, 1, 1, 1, time.UTC),
+			wantErr: nil,
+		},
+		{
+			name: "nil field",
+			args: args{
+				field:      &schema.Field{Name: "timestamp"},
+				dst:        reflect.Value{},
+				fieldValue: nil,
+			},
+			want:    nil,
+			wantErr: nil,
+		},
+		{
+			name: "field wrong type",
+			args: args{
+				field:      &schema.Field{Name: "timestamp"},
+				dst:        reflect.Value{},
+				fieldValue: "string",
+			},
+			want: nil,
+			wantErr: func(tt assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorIs(t, err, persistence.ErrUnsupportedType)
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			tr := TimestampSerializer{}
+
+			got, err := tr.Value(tt.args.ctx, tt.args.field, tt.args.dst, tt.args.fieldValue)
+
+			if tt.wantErr != nil {
+				tt.wantErr(t, err, tt.args)
+			} else {
+				assert.Nil(t, err)
+			}
+
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("TimestampSerializer.Value() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestTimestampSerializer_Scan(t *testing.T) {
+	type args struct {
+		ctx     context.Context
+		field   *schema.Field
+		dst     reflect.Value
+		dbValue interface{}
+	}
+	tests := []struct {
+		name    string
+		args    args
+		wantErr assert.ErrorAssertionFunc
+	}{
+		{
+			name: "db wrong type",
+			args: args{
+				field:   &schema.Field{},
+				dbValue: "string",
+			},
+			wantErr: func(tt assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorIs(t, err, persistence.ErrUnsupportedType)
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			tr := TimestampSerializer{}
+			err := tr.Scan(tt.args.ctx, tt.args.field, tt.args.dst, tt.args.dbValue)
+
+			if tt.wantErr != nil {
+				tt.wantErr(t, err, tt.args)
+			} else {
+				assert.Nil(t, err)
+			}
+		})
+	}
+}
+
+func TestAnySerializer_Value(t *testing.T) {
+	type args struct {
+		ctx        context.Context
+		field      *schema.Field
+		dst        reflect.Value
+		fieldValue interface{}
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    assert.ValueAssertionFunc
+		wantErr assert.ErrorAssertionFunc
+	}{
+		{
+			name: "ok field",
+			args: args{
+				field: &schema.Field{Name: "config"},
+				dst:   reflect.Value{},
+				fieldValue: func() *anypb.Any {
+					a, _ := anypb.New(&orchestrator.CloudService{
+						Id: "my-service",
+					})
+					return a
+				}(),
+			},
+			want: func(tt assert.TestingT, i1 interface{}, i2 ...interface{}) bool {
+				// output of protojson is randomized (see
+				// https://github.com/protocolbuffers/protobuf-go/commit/582ab3de426ef0758666e018b422dd20390f7f26),
+				// so we need to unmarshal it to compare the contents in a
+				// stable way
+				b, ok := i1.([]byte)
+				if assert.True(t, ok) {
+					return false
+				}
+
+				var m map[string]interface{}
+				err := json.Unmarshal(b, &m)
+				assert.NoError(t, err)
+
+				return assert.Equal(t, m, map[string]interface{}{
+					"@type": "type.googleapis.com/clouditor.CloudService",
+					"id":    "my-service",
+				})
+			},
+			wantErr: nil,
+		},
+		{
+			name: "nil field",
+			args: args{
+				field:      &schema.Field{Name: "config"},
+				dst:        reflect.Value{},
+				fieldValue: nil,
+			},
+			want:    nil,
+			wantErr: nil,
+		},
+		{
+			name: "field wrong type",
+			args: args{
+				field:      &schema.Field{Name: "config"},
+				dst:        reflect.Value{},
+				fieldValue: "string",
+			},
+			want: nil,
+			wantErr: func(tt assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorIs(t, err, persistence.ErrUnsupportedType)
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			tr := AnySerializer{}
+
+			got, err := tr.Value(tt.args.ctx, tt.args.field, tt.args.dst, tt.args.fieldValue)
+
+			if tt.wantErr != nil {
+				tt.wantErr(t, err, tt.args)
+			} else {
+				assert.Nil(t, err)
+			}
+
+			if !reflect.DeepEqual(got, tt.want) {
+				if tt.want != nil {
+					tt.want(t, got)
+				}
+			}
+		})
+	}
+}
+
+func TestAnySerializer_Scan(t *testing.T) {
+	type args struct {
+		ctx     context.Context
+		field   *schema.Field
+		dst     reflect.Value
+		dbValue interface{}
+	}
+	tests := []struct {
+		name    string
+		args    args
+		wantErr assert.ErrorAssertionFunc
+	}{
+		{
+			name: "db wrong type",
+			args: args{
+				field:   &schema.Field{},
+				dbValue: "string",
+			},
+			wantErr: func(tt assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, "could not unmarshal JSONB value into protobuf message")
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			tr := AnySerializer{}
+			err := tr.Scan(tt.args.ctx, tt.args.field, tt.args.dst, tt.args.dbValue)
+
+			if tt.wantErr != nil {
+				tt.wantErr(t, err, tt.args)
+			} else {
+				assert.Nil(t, err)
+			}
+		})
+	}
+}