1// Copyright 2016 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package datastore
16
17import (
18	"fmt"
19	"reflect"
20	"testing"
21	"time"
22
23	"cloud.google.com/go/civil"
24	"cloud.google.com/go/internal/testutil"
25	pb "google.golang.org/genproto/googleapis/datastore/v1"
26	"google.golang.org/protobuf/types/known/timestamppb"
27)
28
29type Simple struct {
30	I int64
31}
32
33type SimpleWithTag struct {
34	I int64 `datastore:"II"`
35}
36
37type NestedSimpleWithTag struct {
38	A SimpleWithTag `datastore:"AA"`
39}
40
41type NestedSliceOfSimple struct {
42	A []Simple
43}
44
45type SimpleTwoFields struct {
46	S  string
47	SS string
48}
49
50type NestedSimpleAnonymous struct {
51	Simple
52	X string
53}
54
55type NestedSimple struct {
56	A Simple
57	I int
58}
59
60type NestedSimple1 struct {
61	A Simple
62	X string
63}
64
65type NestedSimple2X struct {
66	AA NestedSimple
67	A  SimpleTwoFields
68	S  string
69}
70
71type BDotB struct {
72	B string `datastore:"B.B"`
73}
74
75type ABDotB struct {
76	A BDotB
77}
78
79type MultiAnonymous struct {
80	Simple
81	SimpleTwoFields
82	X string
83}
84
85func TestLoadEntityNestedLegacy(t *testing.T) {
86	testCases := []struct {
87		desc string
88		src  *pb.Entity
89		want interface{}
90	}{
91		{
92			desc: "nested",
93			src: &pb.Entity{
94				Key: keyToProto(testKey0),
95				Properties: map[string]*pb.Value{
96					"X":   {ValueType: &pb.Value_StringValue{StringValue: "two"}},
97					"A.I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 2}},
98				},
99			},
100			want: &NestedSimple1{
101				A: Simple{I: 2},
102				X: "two",
103			},
104		},
105		{
106			desc: "nested with tag",
107			src: &pb.Entity{
108				Key: keyToProto(testKey0),
109				Properties: map[string]*pb.Value{
110					"AA.II": {ValueType: &pb.Value_IntegerValue{IntegerValue: 2}},
111				},
112			},
113			want: &NestedSimpleWithTag{
114				A: SimpleWithTag{I: 2},
115			},
116		},
117		{
118			desc: "nested with anonymous struct field",
119			src: &pb.Entity{
120				Key: keyToProto(testKey0),
121				Properties: map[string]*pb.Value{
122					"X": {ValueType: &pb.Value_StringValue{StringValue: "two"}},
123					"I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 2}},
124				},
125			},
126			want: &NestedSimpleAnonymous{
127				Simple: Simple{I: 2},
128				X:      "two",
129			},
130		},
131		{
132			desc: "nested with dotted field tag",
133			src: &pb.Entity{
134				Key: keyToProto(testKey0),
135				Properties: map[string]*pb.Value{
136					"A.B.B": {ValueType: &pb.Value_StringValue{StringValue: "bb"}},
137				},
138			},
139			want: &ABDotB{
140				A: BDotB{
141					B: "bb",
142				},
143			},
144		},
145		{
146			desc: "nested with multiple anonymous fields",
147			src: &pb.Entity{
148				Key: keyToProto(testKey0),
149				Properties: map[string]*pb.Value{
150					"I":  {ValueType: &pb.Value_IntegerValue{IntegerValue: 3}},
151					"S":  {ValueType: &pb.Value_StringValue{StringValue: "S"}},
152					"SS": {ValueType: &pb.Value_StringValue{StringValue: "s"}},
153					"X":  {ValueType: &pb.Value_StringValue{StringValue: "s"}},
154				},
155			},
156			want: &MultiAnonymous{
157				Simple:          Simple{I: 3},
158				SimpleTwoFields: SimpleTwoFields{S: "S", SS: "s"},
159				X:               "s",
160			},
161		},
162	}
163
164	for _, tc := range testCases {
165		dst := reflect.New(reflect.TypeOf(tc.want).Elem()).Interface()
166		err := loadEntityProto(dst, tc.src)
167		if err != nil {
168			t.Errorf("loadEntityProto: %s: %v", tc.desc, err)
169			continue
170		}
171
172		if !testutil.Equal(tc.want, dst) {
173			t.Errorf("%s: compare:\ngot:  %#v\nwant: %#v", tc.desc, dst, tc.want)
174		}
175	}
176}
177
178type WithKey struct {
179	X string
180	I int
181	K *Key `datastore:"__key__"`
182}
183
184type NestedWithKey struct {
185	Y string
186	N WithKey
187}
188
189var (
190	incompleteKey = newKey("", nil)
191	invalidKey    = newKey("s", incompleteKey)
192)
193
194func TestLoadEntityNested(t *testing.T) {
195	testCases := []struct {
196		desc string
197		src  *pb.Entity
198		want interface{}
199	}{
200		{
201			desc: "nested basic",
202			src: &pb.Entity{
203				Properties: map[string]*pb.Value{
204					"A": {ValueType: &pb.Value_EntityValue{
205						EntityValue: &pb.Entity{
206							Properties: map[string]*pb.Value{
207								"I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 3}},
208							},
209						},
210					}},
211					"I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 10}},
212				},
213			},
214			want: &NestedSimple{
215				A: Simple{I: 3},
216				I: 10,
217			},
218		},
219		{
220			desc: "nested with struct tags",
221			src: &pb.Entity{
222				Properties: map[string]*pb.Value{
223					"AA": {ValueType: &pb.Value_EntityValue{
224						EntityValue: &pb.Entity{
225							Properties: map[string]*pb.Value{
226								"II": {ValueType: &pb.Value_IntegerValue{IntegerValue: 1}},
227							},
228						},
229					}},
230				},
231			},
232			want: &NestedSimpleWithTag{
233				A: SimpleWithTag{I: 1},
234			},
235		},
236		{
237			desc: "nested 2x",
238			src: &pb.Entity{
239				Properties: map[string]*pb.Value{
240					"AA": {ValueType: &pb.Value_EntityValue{
241						EntityValue: &pb.Entity{
242							Properties: map[string]*pb.Value{
243								"A": {ValueType: &pb.Value_EntityValue{
244									EntityValue: &pb.Entity{
245										Properties: map[string]*pb.Value{
246											"I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 3}},
247										},
248									},
249								}},
250								"I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 1}},
251							},
252						},
253					}},
254					"A": {ValueType: &pb.Value_EntityValue{
255						EntityValue: &pb.Entity{
256							Properties: map[string]*pb.Value{
257								"S":  {ValueType: &pb.Value_StringValue{StringValue: "S"}},
258								"SS": {ValueType: &pb.Value_StringValue{StringValue: "s"}},
259							},
260						},
261					}},
262					"S": {ValueType: &pb.Value_StringValue{StringValue: "SS"}},
263				},
264			},
265			want: &NestedSimple2X{
266				AA: NestedSimple{
267					A: Simple{I: 3},
268					I: 1,
269				},
270				A: SimpleTwoFields{S: "S", SS: "s"},
271				S: "SS",
272			},
273		},
274		{
275			desc: "nested anonymous",
276			src: &pb.Entity{
277				Properties: map[string]*pb.Value{
278					"I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 3}},
279					"X": {ValueType: &pb.Value_StringValue{StringValue: "SomeX"}},
280				},
281			},
282			want: &NestedSimpleAnonymous{
283				Simple: Simple{I: 3},
284				X:      "SomeX",
285			},
286		},
287		{
288			desc: "nested simple with slice",
289			src: &pb.Entity{
290				Properties: map[string]*pb.Value{
291					"A": {ValueType: &pb.Value_ArrayValue{
292						ArrayValue: &pb.ArrayValue{
293							Values: []*pb.Value{
294								{ValueType: &pb.Value_EntityValue{
295									EntityValue: &pb.Entity{
296										Properties: map[string]*pb.Value{
297											"I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 3}},
298										},
299									},
300								}},
301								{ValueType: &pb.Value_EntityValue{
302									EntityValue: &pb.Entity{
303										Properties: map[string]*pb.Value{
304											"I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 4}},
305										},
306									},
307								}},
308							},
309						},
310					}},
311				},
312			},
313
314			want: &NestedSliceOfSimple{
315				A: []Simple{{I: 3}, {I: 4}},
316			},
317		},
318		{
319			desc: "nested with multiple anonymous fields",
320			src: &pb.Entity{
321				Properties: map[string]*pb.Value{
322					"I":  {ValueType: &pb.Value_IntegerValue{IntegerValue: 3}},
323					"S":  {ValueType: &pb.Value_StringValue{StringValue: "S"}},
324					"SS": {ValueType: &pb.Value_StringValue{StringValue: "s"}},
325					"X":  {ValueType: &pb.Value_StringValue{StringValue: "ss"}},
326				},
327			},
328			want: &MultiAnonymous{
329				Simple:          Simple{I: 3},
330				SimpleTwoFields: SimpleTwoFields{S: "S", SS: "s"},
331				X:               "ss",
332			},
333		},
334		{
335			desc: "nested with dotted field tag",
336			src: &pb.Entity{
337				Properties: map[string]*pb.Value{
338					"A": {ValueType: &pb.Value_EntityValue{
339						EntityValue: &pb.Entity{
340							Properties: map[string]*pb.Value{
341								"B.B": {ValueType: &pb.Value_StringValue{StringValue: "bb"}},
342							},
343						},
344					}},
345				},
346			},
347			want: &ABDotB{
348				A: BDotB{
349					B: "bb",
350				},
351			},
352		},
353		{
354			desc: "nested entity with key",
355			src: &pb.Entity{
356				Key: keyToProto(testKey0),
357				Properties: map[string]*pb.Value{
358					"Y": {ValueType: &pb.Value_StringValue{StringValue: "yyy"}},
359					"N": {ValueType: &pb.Value_EntityValue{
360						EntityValue: &pb.Entity{
361							Key: keyToProto(testKey1a),
362							Properties: map[string]*pb.Value{
363								"X": {ValueType: &pb.Value_StringValue{StringValue: "two"}},
364								"I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 2}},
365							},
366						},
367					}},
368				},
369			},
370			want: &NestedWithKey{
371				Y: "yyy",
372				N: WithKey{
373					X: "two",
374					I: 2,
375					K: testKey1a,
376				},
377			},
378		},
379		{
380			desc: "nested entity with invalid key",
381			src: &pb.Entity{
382				Key: keyToProto(testKey0),
383				Properties: map[string]*pb.Value{
384					"Y": {ValueType: &pb.Value_StringValue{StringValue: "yyy"}},
385					"N": {ValueType: &pb.Value_EntityValue{
386						EntityValue: &pb.Entity{
387							Key: keyToProto(invalidKey),
388							Properties: map[string]*pb.Value{
389								"X": {ValueType: &pb.Value_StringValue{StringValue: "two"}},
390								"I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 2}},
391							},
392						},
393					}},
394				},
395			},
396			want: &NestedWithKey{
397				Y: "yyy",
398				N: WithKey{
399					X: "two",
400					I: 2,
401					K: invalidKey,
402				},
403			},
404		},
405	}
406
407	for _, tc := range testCases {
408		dst := reflect.New(reflect.TypeOf(tc.want).Elem()).Interface()
409		err := loadEntityProto(dst, tc.src)
410		if err != nil {
411			t.Errorf("loadEntityProto: %s: %v", tc.desc, err)
412			continue
413		}
414
415		if !testutil.Equal(tc.want, dst) {
416			t.Errorf("%s: compare:\ngot:  %#v\nwant: %#v", tc.desc, dst, tc.want)
417		}
418	}
419}
420
421type NestedStructPtrs struct {
422	*SimpleTwoFields
423	Nest      *SimpleTwoFields
424	TwiceNest *NestedSimple2
425	I         int
426}
427
428type NestedSimple2 struct {
429	A *Simple
430	I int
431	U interface{}
432}
433
434type withTypedInterface struct {
435	Field fmt.Stringer
436}
437
438type withUntypedInterface struct {
439	Field interface{}
440}
441
442func TestLoadToInterface(t *testing.T) {
443	testCases := []struct {
444		name    string
445		src     *pb.Entity
446		dst     interface{}
447		want    interface{}
448		wantErr string
449	}{
450		{
451			name: "Typed interface",
452			src: &pb.Entity{
453				Key: keyToProto(testKey0),
454				Properties: map[string]*pb.Value{
455					"Field": {ValueType: &pb.Value_StringValue{
456						StringValue: "Foo",
457					}},
458				},
459			},
460			dst:     &withTypedInterface{},
461			wantErr: `datastore: cannot load field "Field" into a "datastore.withTypedInterface": "string" is not assignable to "fmt.Stringer"`,
462		},
463		{
464			name: "Untyped interface, fresh struct",
465			src: &pb.Entity{
466				Key: keyToProto(testKey0),
467				Properties: map[string]*pb.Value{
468					"Field": {ValueType: &pb.Value_StringValue{
469						StringValue: "Foo",
470					}},
471				},
472			},
473			dst:  &withUntypedInterface{},
474			want: &withUntypedInterface{Field: "Foo"},
475		},
476		{
477			name: "Untyped interface, already set",
478			src: &pb.Entity{
479				Key: keyToProto(testKey0),
480				Properties: map[string]*pb.Value{
481					"Field": {ValueType: &pb.Value_StringValue{
482						StringValue: "Newly set",
483					}},
484				},
485			},
486			dst:  &withUntypedInterface{Field: 1e9},
487			want: &withUntypedInterface{Field: "Newly set"},
488		},
489		{
490			name: "struct with civil.Date",
491			src: &pb.Entity{
492				Key: keyToProto(testKey0),
493				Properties: map[string]*pb.Value{
494					"Date": {ValueType: &pb.Value_TimestampValue{TimestampValue: &timestamppb.Timestamp{Seconds: 1605474000}}},
495				},
496			},
497			dst: &struct{ Date civil.Date }{
498				Date: civil.Date{},
499			},
500			want: &struct{ Date civil.Date }{
501				Date: civil.Date{
502					Year:  2020,
503					Month: 11,
504					Day:   15,
505				},
506			},
507		},
508		{
509			name: "struct with civil.DateTime",
510			src: &pb.Entity{
511				Key: keyToProto(testKey0),
512				Properties: map[string]*pb.Value{
513					"DateTime": {ValueType: &pb.Value_TimestampValue{TimestampValue: &timestamppb.Timestamp{Seconds: 1605504600}}},
514				},
515			},
516			dst: &struct{ DateTime civil.DateTime }{
517				DateTime: civil.DateTime{},
518			},
519			want: &struct{ DateTime civil.DateTime }{
520				DateTime: civil.DateTime{
521					Date: civil.Date{
522						Year:  2020,
523						Month: 11,
524						Day:   16,
525					},
526					Time: civil.Time{
527						Hour:   5,
528						Minute: 30,
529					},
530				},
531			},
532		},
533		{
534			name: "struct with civil.Time",
535			src: &pb.Entity{
536				Key: keyToProto(testKey0),
537				Properties: map[string]*pb.Value{
538					"Time": {ValueType: &pb.Value_TimestampValue{TimestampValue: &timestamppb.Timestamp{Seconds: 1605504600}}},
539				},
540			},
541			dst: &struct{ Time civil.Time }{
542				Time: civil.Time{},
543			},
544			want: &struct{ Time civil.Time }{
545				Time: civil.Time{
546					Hour:   5,
547					Minute: 30,
548				},
549			},
550		},
551	}
552
553	for _, tc := range testCases {
554		t.Run(tc.name, func(t *testing.T) {
555			err := loadEntityProto(tc.dst, tc.src)
556			if tc.wantErr != "" {
557				if err == nil || err.Error() != tc.wantErr {
558					t.Fatalf("Error mismatch\nGot:  %s\nWant: %s", err, tc.wantErr)
559				}
560			} else {
561				if err != nil {
562					t.Fatalf("loadEntityProto: %v", err)
563				}
564				if diff := testutil.Diff(tc.dst, tc.want); diff != "" {
565					t.Fatalf("Mismatch: got - want +\n%s", diff)
566				}
567			}
568		})
569	}
570}
571
572// Expect Local times to be represented in UTC
573func TestTimezone(t *testing.T) {
574	src := &pb.Entity{
575		Key: keyToProto(testKey0),
576		Properties: map[string]*pb.Value{
577			"Time": {ValueType: &pb.Value_TimestampValue{TimestampValue: &timestamppb.Timestamp{Seconds: 1605504600}}},
578		},
579	}
580
581	dst := &struct{ Time time.Time }{
582		Time: time.Time{},
583	}
584	want := &struct{ Time time.Time }{
585		Time: time.Unix(1605504600, 0).In(time.UTC),
586	}
587
588	err := loadEntityProto(dst, src)
589	if err != nil {
590		t.Fatalf("loadEntityProto: %v", err)
591	}
592
593	if diff := testutil.Diff(dst, want); diff != "" {
594		t.Fatalf("Mismatch: got - want +\n%s", diff)
595	}
596	// Also, the Zones need to be compared as comparing times will not detect this difference.
597	dstZone, _ := dst.Time.Zone()
598	wantZone, _ := want.Time.Zone()
599	if diff := testutil.Diff(dstZone, wantZone); diff != "" {
600		t.Fatalf("Mismatch: got - want +\n%s", diff)
601	}
602}
603
604func TestAlreadyPopulatedDst(t *testing.T) {
605	testCases := []struct {
606		desc string
607		src  *pb.Entity
608		dst  interface{}
609		want interface{}
610	}{
611		{
612			desc: "simple already populated, nil properties",
613			src: &pb.Entity{
614				Key: keyToProto(testKey0),
615				Properties: map[string]*pb.Value{
616					"I": {ValueType: &pb.Value_NullValue{}},
617				},
618			},
619			dst: &Simple{
620				I: 12,
621			},
622			want: &Simple{},
623		},
624		{
625			desc: "nested structs already populated",
626			src: &pb.Entity{
627				Key: keyToProto(testKey0),
628				Properties: map[string]*pb.Value{
629					"SS": {ValueType: &pb.Value_StringValue{StringValue: "world"}},
630				},
631			},
632			dst:  &SimpleTwoFields{S: "hello" /* SS: "" */},
633			want: &SimpleTwoFields{S: "hello", SS: "world"},
634		},
635		{
636			desc: "nested structs already populated, pValues nil",
637			src: &pb.Entity{
638				Key: keyToProto(testKey0),
639				Properties: map[string]*pb.Value{
640					"S":    {ValueType: &pb.Value_NullValue{}},
641					"SS":   {ValueType: &pb.Value_StringValue{StringValue: "ss hello"}},
642					"Nest": {ValueType: &pb.Value_NullValue{}},
643					"TwiceNest": {ValueType: &pb.Value_EntityValue{
644						EntityValue: &pb.Entity{
645							Properties: map[string]*pb.Value{
646								"A": {ValueType: &pb.Value_NullValue{}},
647								"I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 2}},
648								"U": {ValueType: &pb.Value_StringValue{StringValue: "replaced"}},
649							},
650						},
651					}},
652					"I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 5}},
653				},
654			},
655			dst: &NestedStructPtrs{
656				&SimpleTwoFields{S: "hello" /* SS: "" */},
657				&SimpleTwoFields{ /* S: "" */ SS: "twice hello"},
658				&NestedSimple2{
659					A: &Simple{I: 2},
660					/* I: 0 */
661					U: 1e9,
662				},
663				0,
664			},
665			want: &NestedStructPtrs{
666				&SimpleTwoFields{ /* S: "" */ SS: "ss hello"},
667				nil,
668				&NestedSimple2{
669					/* A: nil, */
670					I: 2,
671					U: "replaced",
672				},
673				5,
674			},
675		},
676	}
677
678	for _, tc := range testCases {
679		err := loadEntityProto(tc.dst, tc.src)
680		if err != nil {
681			t.Errorf("loadEntityProto: %s: %v", tc.desc, err)
682			continue
683		}
684
685		if !testutil.Equal(tc.want, tc.dst) {
686			t.Errorf("%s: compare:\ngot:  %#v\nwant: %#v", tc.desc, tc.dst, tc.want)
687		}
688	}
689}
690
691type PLS0 struct {
692	A string
693}
694
695func (p *PLS0) Load(props []Property) error {
696	for _, pp := range props {
697		if pp.Name == "A" {
698			p.A = pp.Value.(string)
699		}
700	}
701	return nil
702}
703
704func (p *PLS0) Save() (props []Property, err error) {
705	return []Property{{Name: "A", Value: p.A}}, nil
706}
707
708type KeyLoader1 struct {
709	A string
710	K *Key
711}
712
713func (kl *KeyLoader1) Load(props []Property) error {
714	for _, pp := range props {
715		if pp.Name == "A" {
716			kl.A = pp.Value.(string)
717		}
718	}
719	return nil
720}
721
722func (kl *KeyLoader1) Save() (props []Property, err error) {
723	return []Property{{Name: "A", Value: kl.A}}, nil
724}
725
726func (kl *KeyLoader1) LoadKey(k *Key) error {
727	kl.K = k
728	return nil
729}
730
731type KeyLoader2 struct {
732	B   int
733	Key *Key
734}
735
736func (kl *KeyLoader2) Load(props []Property) error {
737	for _, pp := range props {
738		if pp.Name == "B" {
739			kl.B = int(pp.Value.(int64))
740		}
741	}
742	return nil
743}
744
745func (kl *KeyLoader2) Save() (props []Property, err error) {
746	return []Property{{Name: "B", Value: int64(kl.B)}}, nil
747}
748
749func (kl *KeyLoader2) LoadKey(k *Key) error {
750	kl.Key = k
751	return nil
752}
753
754type KeyLoader3 struct {
755	C bool
756	K *Key
757}
758
759func (kl *KeyLoader3) Load(props []Property) error {
760	for _, pp := range props {
761		if pp.Name == "C" {
762			kl.C = pp.Value.(bool)
763		}
764	}
765	return nil
766}
767
768func (kl *KeyLoader3) Save() (props []Property, err error) {
769	return []Property{{Name: "C", Value: kl.C}}, nil
770}
771
772func (kl *KeyLoader3) LoadKey(k *Key) error {
773	kl.K = k
774	return nil
775}
776
777type KeyLoader4 struct {
778	PLS0
779	K *Key
780}
781
782func (kl *KeyLoader4) LoadKey(k *Key) error {
783	kl.K = k
784	return nil
785}
786
787type PLS1 struct {
788	A string
789}
790
791func (p *PLS1) Load(props []Property) error {
792	for _, pp := range props {
793		if pp.Name == "A" {
794			p.A = pp.Value.(string)
795		}
796	}
797	return nil
798}
799
800func (p *PLS1) Save() (props []Property, err error) {
801	return []Property{{Name: "A", Value: p.A}}, nil
802}
803
804type KeyLoader6 struct {
805	A string
806	B string
807	K *Key
808}
809
810func (kl *KeyLoader6) Load(props []Property) error {
811	for _, pp := range props {
812		if pp.Name == "A" {
813			kl.A = pp.Value.(string)
814		}
815	}
816	return &ErrFieldMismatch{
817		StructType: reflect.TypeOf(kl),
818		FieldName:  "B",
819		Reason:     "no value found",
820	}
821}
822
823func (kl *KeyLoader6) LoadKey(k *Key) error {
824	kl.K = k
825	return nil
826}
827
828func (kl *KeyLoader6) Save() (props []Property, err error) {
829	return []Property{{}}, nil
830}
831
832type KeyLoader7 struct {
833	A string
834	K *Key
835}
836
837func (kl *KeyLoader7) Load(props []Property) error {
838	for _, pp := range props {
839		if pp.Name == "A" {
840			kl.A = pp.Value.(string)
841		}
842	}
843	return nil
844}
845
846func (kl *KeyLoader7) LoadKey(k *Key) error {
847	return &ErrFieldMismatch{
848		StructType: reflect.TypeOf(kl),
849		FieldName:  "key",
850		Reason:     "no value found",
851	}
852}
853
854func (kl *KeyLoader7) Save() (props []Property, err error) {
855	return []Property{{}}, nil
856}
857
858type KeyLoader8 struct {
859	A string
860	B string
861	K *Key
862}
863
864type customLoadError struct{}
865
866func (e *customLoadError) Error() string {
867	return "custom load error"
868}
869
870func (kl *KeyLoader8) Load(props []Property) error {
871	for _, pp := range props {
872		if pp.Name == "A" {
873			kl.A = pp.Value.(string)
874		}
875	}
876	return &customLoadError{}
877}
878
879func (kl *KeyLoader8) LoadKey(k *Key) error {
880	return &ErrFieldMismatch{
881		StructType: reflect.TypeOf(kl),
882		FieldName:  "key",
883		Reason:     "no value found",
884	}
885}
886
887func (kl *KeyLoader8) Save() (props []Property, err error) {
888	return []Property{{}}, nil
889}
890
891type NotKeyLoader struct {
892	A string
893	K *Key
894}
895
896func (p *NotKeyLoader) Load(props []Property) error {
897	for _, pp := range props {
898		if pp.Name == "A" {
899			p.A = pp.Value.(string)
900		}
901	}
902	return nil
903}
904
905func (p *NotKeyLoader) Save() (props []Property, err error) {
906	return []Property{{Name: "A", Value: p.A}}, nil
907}
908
909type NotPLSKeyLoader struct {
910	A string
911	K *Key `datastore:"__key__"`
912}
913
914type NestedKeyLoaders struct {
915	Two   *KeyLoader2
916	Three []*KeyLoader3
917	Four  *KeyLoader4
918	PLS   *NotKeyLoader
919}
920
921func TestKeyLoader(t *testing.T) {
922	testCases := []struct {
923		desc string
924		src  *pb.Entity
925		dst  interface{}
926		want interface{}
927	}{
928		{
929			desc: "simple key loader",
930			src: &pb.Entity{
931				Key: keyToProto(testKey0),
932				Properties: map[string]*pb.Value{
933					"A": {ValueType: &pb.Value_StringValue{StringValue: "hello"}},
934				},
935			},
936			dst: &KeyLoader1{},
937			want: &KeyLoader1{
938				A: "hello",
939				K: testKey0,
940			},
941		},
942		{
943			desc: "simple key loader with unmatched properties",
944			src: &pb.Entity{
945				Key: keyToProto(testKey0),
946				Properties: map[string]*pb.Value{
947					"A": {ValueType: &pb.Value_StringValue{StringValue: "hello"}},
948					"B": {ValueType: &pb.Value_StringValue{StringValue: "unmatched"}},
949				},
950			},
951			dst: &NotPLSKeyLoader{},
952			want: &NotPLSKeyLoader{
953				A: "hello",
954				K: testKey0,
955			},
956		},
957		{
958			desc: "embedded PLS key loader",
959			src: &pb.Entity{
960				Key: keyToProto(testKey0),
961				Properties: map[string]*pb.Value{
962					"A": {ValueType: &pb.Value_StringValue{StringValue: "hello"}},
963				},
964			},
965			dst: &KeyLoader4{},
966			want: &KeyLoader4{
967				PLS0: PLS0{A: "hello"},
968				K:    testKey0,
969			},
970		},
971		{
972			desc: "nested key loaders",
973			src: &pb.Entity{
974				Key: keyToProto(testKey0),
975				Properties: map[string]*pb.Value{
976					"Two": {ValueType: &pb.Value_EntityValue{
977						EntityValue: &pb.Entity{
978							Properties: map[string]*pb.Value{
979								"B": {ValueType: &pb.Value_IntegerValue{IntegerValue: 12}},
980							},
981							Key: keyToProto(testKey1a),
982						},
983					}},
984					"Three": {ValueType: &pb.Value_ArrayValue{
985						ArrayValue: &pb.ArrayValue{
986							Values: []*pb.Value{
987								{ValueType: &pb.Value_EntityValue{
988									EntityValue: &pb.Entity{
989										Properties: map[string]*pb.Value{
990											"C": {ValueType: &pb.Value_BooleanValue{BooleanValue: true}},
991										},
992										Key: keyToProto(testKey1b),
993									},
994								}},
995								{ValueType: &pb.Value_EntityValue{
996									EntityValue: &pb.Entity{
997										Properties: map[string]*pb.Value{
998											"C": {ValueType: &pb.Value_BooleanValue{BooleanValue: false}},
999										},
1000										Key: keyToProto(testKey0),
1001									},
1002								}},
1003							},
1004						},
1005					}},
1006					"Four": {ValueType: &pb.Value_EntityValue{
1007						EntityValue: &pb.Entity{
1008							Properties: map[string]*pb.Value{
1009								"A": {ValueType: &pb.Value_StringValue{StringValue: "testing"}},
1010							},
1011							Key: keyToProto(testKey2a),
1012						},
1013					}},
1014					"PLS": {ValueType: &pb.Value_EntityValue{
1015						EntityValue: &pb.Entity{
1016							Properties: map[string]*pb.Value{
1017								"A": {ValueType: &pb.Value_StringValue{StringValue: "something"}},
1018							},
1019
1020							Key: keyToProto(testKey1a),
1021						},
1022					}},
1023				},
1024			},
1025			dst: &NestedKeyLoaders{},
1026			want: &NestedKeyLoaders{
1027				Two: &KeyLoader2{B: 12, Key: testKey1a},
1028				Three: []*KeyLoader3{
1029					{
1030						C: true,
1031						K: testKey1b,
1032					},
1033					{
1034						C: false,
1035						K: testKey0,
1036					},
1037				},
1038				Four: &KeyLoader4{
1039					PLS0: PLS0{A: "testing"},
1040					K:    testKey2a,
1041				},
1042				PLS: &NotKeyLoader{A: "something"},
1043			},
1044		},
1045		{
1046			desc: "simple key loader with ErrFieldMismatch error",
1047			src: &pb.Entity{
1048				Key: keyToProto(testKey0),
1049				Properties: map[string]*pb.Value{
1050					"A": {ValueType: &pb.Value_StringValue{StringValue: "hello"}},
1051				},
1052			},
1053			dst: &KeyLoader6{},
1054			want: &KeyLoader6{
1055				A: "hello",
1056				B: "",
1057				K: testKey0,
1058			},
1059		},
1060		{
1061			desc: "simple key loader with ErrFieldMismatch during key load",
1062			src: &pb.Entity{
1063				Key: keyToProto(testKey0),
1064				Properties: map[string]*pb.Value{
1065					"A": {ValueType: &pb.Value_StringValue{StringValue: "hello"}},
1066				},
1067			},
1068			dst: &KeyLoader7{},
1069			want: &KeyLoader7{
1070				A: "hello",
1071				K: nil,
1072			},
1073		},
1074		{
1075			desc: "simple key loader with other error during Load and ErrFieldMismatch during KeyLoad",
1076			src: &pb.Entity{
1077				Key: keyToProto(testKey0),
1078				Properties: map[string]*pb.Value{
1079					"A": {ValueType: &pb.Value_StringValue{StringValue: "hello"}},
1080				},
1081			},
1082			dst: &KeyLoader8{},
1083			want: &KeyLoader8{
1084				A: "hello",
1085				B: "",
1086				K: nil,
1087			},
1088		},
1089	}
1090
1091	for _, tc := range testCases {
1092		err := loadEntityProto(tc.dst, tc.src)
1093		if err != nil {
1094			// While loadEntityProto may return an error, if that error is
1095			// ErrFieldMismatch, then there is still data in tc.dst to compare.
1096			if _, ok := err.(*ErrFieldMismatch); !ok {
1097				t.Errorf("loadEntityProto: %s: %v", tc.desc, err)
1098				continue
1099			}
1100		}
1101
1102		if !testutil.Equal(tc.want, tc.dst) {
1103			t.Errorf("%s: compare:\ngot:  %+v\nwant: %+v", tc.desc, tc.dst, tc.want)
1104		}
1105	}
1106}
1107
1108func TestLoadPointers(t *testing.T) {
1109	for _, test := range []struct {
1110		desc string
1111		in   []Property
1112		want Pointers
1113	}{
1114		{
1115			desc: "nil properties load as nil pointers",
1116			in: []Property{
1117				{Name: "Pi", Value: nil},
1118				{Name: "Ps", Value: nil},
1119				{Name: "Pb", Value: nil},
1120				{Name: "Pf", Value: nil},
1121				{Name: "Pg", Value: nil},
1122				{Name: "Pt", Value: nil},
1123			},
1124			want: Pointers{},
1125		},
1126		{
1127			desc: "missing properties load as nil pointers",
1128			in:   []Property(nil),
1129			want: Pointers{},
1130		},
1131		{
1132			desc: "non-nil properties load as the appropriate values",
1133			in: []Property{
1134				{Name: "Pi", Value: int64(1)},
1135				{Name: "Ps", Value: "x"},
1136				{Name: "Pb", Value: true},
1137				{Name: "Pf", Value: 3.14},
1138				{Name: "Pg", Value: GeoPoint{Lat: 1, Lng: 2}},
1139				{Name: "Pt", Value: time.Unix(100, 0)},
1140			},
1141			want: func() Pointers {
1142				p := populatedPointers()
1143				*p.Pi = 1
1144				*p.Ps = "x"
1145				*p.Pb = true
1146				*p.Pf = 3.14
1147				*p.Pg = GeoPoint{Lat: 1, Lng: 2}
1148				*p.Pt = time.Unix(100, 0)
1149				return *p
1150			}(),
1151		},
1152	} {
1153		var got Pointers
1154		if err := LoadStruct(&got, test.in); err != nil {
1155			t.Fatalf("%s: %v", test.desc, err)
1156		}
1157		if !testutil.Equal(got, test.want) {
1158			t.Errorf("%s:\ngot  %+v\nwant %+v", test.desc, got, test.want)
1159		}
1160	}
1161}
1162
1163func TestLoadNonArrayIntoSlice(t *testing.T) {
1164	// Loading a non-array value into a slice field results in a slice of size 1.
1165	var got struct{ S []string }
1166	if err := LoadStruct(&got, []Property{{Name: "S", Value: "x"}}); err != nil {
1167		t.Fatal(err)
1168	}
1169	if want := []string{"x"}; !testutil.Equal(got.S, want) {
1170		t.Errorf("got %#v, want %#v", got.S, want)
1171	}
1172}
1173
1174func TestLoadEmptyArrayIntoSlice(t *testing.T) {
1175	// Loading an empty array into a slice field is a no-op.
1176	var got = struct{ S []string }{[]string{"x"}}
1177	if err := LoadStruct(&got, []Property{{Name: "S", Value: []interface{}{}}}); err != nil {
1178		t.Fatal(err)
1179	}
1180	if want := []string{"x"}; !testutil.Equal(got.S, want) {
1181		t.Errorf("got %#v, want %#v", got.S, want)
1182	}
1183}
1184
1185func TestLoadNull(t *testing.T) {
1186	// Loading a Datastore Null into a basic type (int, float, etc.) results in a zero value.
1187	// Loading a Null into a slice of basic type results in a slice of size 1 containing the zero value.
1188	// (As expected from the behavior of slices and nulls with basic types.)
1189	type S struct {
1190		I int64
1191		F float64
1192		S string
1193		B bool
1194		A []string
1195	}
1196	got := S{
1197		I: 1,
1198		F: 1.0,
1199		S: "1",
1200		B: true,
1201		A: []string{"X"},
1202	}
1203	want := S{A: []string{""}}
1204	props := []Property{{Name: "I"}, {Name: "F"}, {Name: "S"}, {Name: "B"}, {Name: "A"}}
1205	if err := LoadStruct(&got, props); err != nil {
1206		t.Fatal(err)
1207	}
1208	if !testutil.Equal(got, want) {
1209		t.Errorf("got %+v, want %+v", got, want)
1210	}
1211
1212	// Loading a Null into a pointer to struct field results in a nil field.
1213	got2 := struct{ X *S }{X: &S{}}
1214	if err := LoadStruct(&got2, []Property{{Name: "X"}}); err != nil {
1215		t.Fatal(err)
1216	}
1217	if got2.X != nil {
1218		t.Errorf("got %v, want nil", got2.X)
1219	}
1220
1221	// Loading a Null into a struct field is an error.
1222	got3 := struct{ X S }{}
1223	err := LoadStruct(&got3, []Property{{Name: "X"}})
1224	if err == nil {
1225		t.Error("got nil, want error")
1226	}
1227}
1228
1229// 	var got2 struct{ S []Pet }
1230// 	if err := LoadStruct(&got2, []Property{{Name: "S", Value: nil}}); err != nil {
1231// 		t.Fatal(err)
1232// 	}
1233
1234// }
1235