Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 115 additions & 4 deletions spanner/row_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2288,11 +2288,30 @@ func TestSelectAll(t *testing.T) {
Col3 string `spanner:"taG3"`
Col4 time.Time `spanner:"TAG4"`
}

type Address struct {
Street string
ZipCode string
City string
}

type Person struct {
Name string
Address Address
BirthDate civil.Date
}
type PersonEmbedded struct {
Name string
Address
BirthDate civil.Date
}

tests := []struct {
name string
args args
wantErr bool
want interface{}
name string
args args
wantErr bool
wantPanic bool
want interface{}
}{
{
name: "success: using slice of primitives",
Expand Down Expand Up @@ -2612,9 +2631,101 @@ func TestSelectAll(t *testing.T) {
want: &[]int64{},
wantErr: true,
},
{
name: "failure: nested named structs",
args: args{
destination: &[]*Person{},
mock: newMockIterator(
&Row{
[]*sppb.StructType_Field{
{Name: "Name", Type: stringType()},
{Name: "Street", Type: stringType()},
{Name: "ZipCode", Type: stringType()},
{Name: "City", Type: stringType()},
{Name: "BirthDate", Type: dateType()},
},
[]*proto3.Value{
stringProto("Name1"),
stringProto("Street1"),
stringProto("ZipCode1"),
stringProto("City1"),
dateProto(civil.Date{Year: 2000, Month: 11, Day: 14}),
},
},
&Row{
[]*sppb.StructType_Field{
{Name: "Name", Type: stringType()},
{Name: "Street", Type: stringType()},
{Name: "ZipCode", Type: stringType()},
{Name: "City", Type: stringType()},
{Name: "BirthDate", Type: dateType()},
},
[]*proto3.Value{
stringProto("Name2"),
stringProto("Street2"),
stringProto("ZipCode2"),
stringProto("City2"),
dateProto(civil.Date{Year: 2001, Month: 11, Day: 14}),
},
},
iterator.Done,
),
},
want: &[]*Person{},
wantErr: true,
},
{
name: "failure: nested unnamed structs",
args: args{
destination: &[]*PersonEmbedded{},
mock: newMockIterator(
&Row{
[]*sppb.StructType_Field{
{Name: "Name", Type: stringType()},
{Name: "Street", Type: stringType()},
{Name: "ZipCode", Type: stringType()},
{Name: "City", Type: stringType()},
{Name: "BirthDate", Type: dateType()},
},
[]*proto3.Value{
stringProto("Name1"),
stringProto("Street1"),
stringProto("ZipCode1"),
stringProto("City1"),
dateProto(civil.Date{Year: 2000, Month: 11, Day: 14}),
},
},
&Row{
[]*sppb.StructType_Field{
{Name: "Name", Type: stringType()},
{Name: "Street", Type: stringType()},
{Name: "ZipCode", Type: stringType()},
{Name: "City", Type: stringType()},
{Name: "BirthDate", Type: dateType()},
},
[]*proto3.Value{
stringProto("Name2"),
stringProto("Street2"),
stringProto("ZipCode2"),
stringProto("City2"),
dateProto(civil.Date{Year: 2001, Month: 11, Day: 14}),
},
},
iterator.Done,
),
},
wantPanic: true,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For clarity and to prevent an additional failure message if the code stops panicking in the future, it's good practice to specify the expected state of destination even in a panic test. Since SelectAll shouldn't modify the destination if it panics (or errors out before completion), the expected value want should be the same as the initial destination.

			want:      &[]*PersonEmbedded{},
			wantPanic: true,

},
Comment on lines +2634 to +2718
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The mock iterator creation logic is duplicated between the failure: nested named structs and failure: nested unnamed structs test cases. To improve maintainability and reduce code duplication, you could extract this logic into a helper function that returns a new mock iterator on each call. This also allows you to de-duplicate the []*sppb.StructType_Field slice which is identical for both rows in the iterator.

For example, you could define a helper function:

func newPersonMockIterator() *mockIterator {
	personFields := []*sppb.StructType_Field{
		{Name: "Name", Type: stringType()},
		{Name: "Street", Type: stringType()},
		{Name: "ZipCode", Type: stringType()},
		{Name: "City", Type: stringType()},
		{Name: "BirthDate", Type: dateType()},
	}
	return newMockIterator(
		&Row{
			personFields,
			[]*proto3.Value{
				stringProto("Name1"), stringProto("Street1"), stringProto("ZipCode1"), stringProto("City1"), dateProto(civil.Date{Year: 2000, Month: 11, Day: 14}),
			},
		},
		&Row{
			personFields,
			[]*proto3.Value{
				stringProto("Name2"), stringProto("Street2"), stringProto("ZipCode2"), stringProto("City2"), dateProto(civil.Date{Year: 2001, Month: 11, Day: 14}),
			},
		},
		iterator.Done,
	)
}

And then call it in your test cases:

//...
{
	name: "failure: nested named structs",
	args: args{
		destination: &[]*Person{},
		mock:        newPersonMockIterator(),
	},
	want:    &[]*Person{},
	wantErr: true,
},
{
	name: "failure: nested unnamed structs",
	args: args{
		destination: &[]*PersonEmbedded{},
		mock:        newPersonMockIterator(),
	},
	want: &[]*PersonEmbedded{},
	wantPanic: true,
},
//...

}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.wantPanic {
defer func() {
if r := recover(); r == nil {
t.Error("SelectAll() did not panic")
}
}()
}
mockIterator := tt.args.mock
if err := SelectAll(mockIterator, tt.args.destination, tt.args.options...); (err != nil) != tt.wantErr {
t.Errorf("SelectAll() error = %v, wantErr %v", err, tt.wantErr)
Expand Down
Loading