1/*
2Copyright 2017 Google LLC
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package spanner
18
19import (
20	"sort"
21	"strings"
22	"testing"
23
24	proto3 "github.com/golang/protobuf/ptypes/struct"
25	sppb "google.golang.org/genproto/googleapis/spanner/v1"
26)
27
28// keysetProto returns protobuf encoding of valid spanner.KeySet.
29func keysetProto(t *testing.T, ks KeySet) *sppb.KeySet {
30	k, err := ks.keySetProto()
31	if err != nil {
32		t.Fatalf("cannot convert keyset %v to protobuf: %v", ks, err)
33	}
34	return k
35}
36
37// Test encoding from spanner.Mutation to protobuf.
38func TestMutationToProto(t *testing.T) {
39	for i, test := range []struct {
40		m    *Mutation
41		want *sppb.Mutation
42	}{
43		// Delete Mutation
44		{
45			&Mutation{opDelete, "t_foo", Key{"foo"}, nil, nil},
46			&sppb.Mutation{
47				Operation: &sppb.Mutation_Delete_{
48					Delete: &sppb.Mutation_Delete{
49						Table:  "t_foo",
50						KeySet: keysetProto(t, Key{"foo"}),
51					},
52				},
53			},
54		},
55		// Insert Mutation
56		{
57			&Mutation{opInsert, "t_foo", KeySets(), []string{"col1", "col2"}, []interface{}{int64(1), int64(2)}},
58			&sppb.Mutation{
59				Operation: &sppb.Mutation_Insert{
60					Insert: &sppb.Mutation_Write{
61						Table:   "t_foo",
62						Columns: []string{"col1", "col2"},
63						Values: []*proto3.ListValue{
64							{
65								Values: []*proto3.Value{intProto(1), intProto(2)},
66							},
67						},
68					},
69				},
70			},
71		},
72		// InsertOrUpdate Mutation
73		{
74			&Mutation{opInsertOrUpdate, "t_foo", KeySets(), []string{"col1", "col2"}, []interface{}{1.0, 2.0}},
75			&sppb.Mutation{
76				Operation: &sppb.Mutation_InsertOrUpdate{
77					InsertOrUpdate: &sppb.Mutation_Write{
78						Table:   "t_foo",
79						Columns: []string{"col1", "col2"},
80						Values: []*proto3.ListValue{
81							{
82								Values: []*proto3.Value{floatProto(1.0), floatProto(2.0)},
83							},
84						},
85					},
86				},
87			},
88		},
89		// Replace Mutation
90		{
91			&Mutation{opReplace, "t_foo", KeySets(), []string{"col1", "col2"}, []interface{}{"one", 2.0}},
92			&sppb.Mutation{
93				Operation: &sppb.Mutation_Replace{
94					Replace: &sppb.Mutation_Write{
95						Table:   "t_foo",
96						Columns: []string{"col1", "col2"},
97						Values: []*proto3.ListValue{
98							{
99								Values: []*proto3.Value{stringProto("one"), floatProto(2.0)},
100							},
101						},
102					},
103				},
104			},
105		},
106		// Update Mutation
107		{
108			&Mutation{opUpdate, "t_foo", KeySets(), []string{"col1", "col2"}, []interface{}{"one", []byte(nil)}},
109			&sppb.Mutation{
110				Operation: &sppb.Mutation_Update{
111					Update: &sppb.Mutation_Write{
112						Table:   "t_foo",
113						Columns: []string{"col1", "col2"},
114						Values: []*proto3.ListValue{
115							{
116								Values: []*proto3.Value{stringProto("one"), nullProto()},
117							},
118						},
119					},
120				},
121			},
122		},
123	} {
124		if got, err := test.m.proto(); err != nil || !testEqual(got, test.want) {
125			t.Errorf("%d: (%#v).proto() = (%v, %v), want (%v, nil)", i, test.m, got, err, test.want)
126		}
127	}
128}
129
130// mutationColumnSorter implements sort.Interface for sorting column-value pairs in a Mutation by column names.
131type mutationColumnSorter struct {
132	Mutation
133}
134
135// newMutationColumnSorter creates new instance of mutationColumnSorter by duplicating the input Mutation so that
136// sorting won't change the input Mutation.
137func newMutationColumnSorter(m *Mutation) *mutationColumnSorter {
138	return &mutationColumnSorter{
139		Mutation{
140			m.op,
141			m.table,
142			m.keySet,
143			append([]string(nil), m.columns...),
144			append([]interface{}(nil), m.values...),
145		},
146	}
147}
148
149// Len implements sort.Interface.Len.
150func (ms *mutationColumnSorter) Len() int {
151	return len(ms.columns)
152}
153
154// Swap implements sort.Interface.Swap.
155func (ms *mutationColumnSorter) Swap(i, j int) {
156	ms.columns[i], ms.columns[j] = ms.columns[j], ms.columns[i]
157	ms.values[i], ms.values[j] = ms.values[j], ms.values[i]
158}
159
160// Less implements sort.Interface.Less.
161func (ms *mutationColumnSorter) Less(i, j int) bool {
162	return strings.Compare(ms.columns[i], ms.columns[j]) < 0
163}
164
165// mutationEqual returns true if two mutations in question are equal
166// to each other.
167func mutationEqual(t *testing.T, m1, m2 Mutation) bool {
168	// Two mutations are considered to be equal even if their column values have different
169	// orders.
170	ms1 := newMutationColumnSorter(&m1)
171	ms2 := newMutationColumnSorter(&m2)
172	sort.Sort(ms1)
173	sort.Sort(ms2)
174	return testEqual(ms1, ms2)
175}
176
177// Test helper functions which help to generate spanner.Mutation.
178func TestMutationHelpers(t *testing.T) {
179	for _, test := range []struct {
180		m    string
181		got  *Mutation
182		want *Mutation
183	}{
184		{
185			"Insert",
186			Insert("t_foo", []string{"col1", "col2"}, []interface{}{int64(1), int64(2)}),
187			&Mutation{opInsert, "t_foo", nil, []string{"col1", "col2"}, []interface{}{int64(1), int64(2)}},
188		},
189		{
190			"InsertMap",
191			InsertMap("t_foo", map[string]interface{}{"col1": int64(1), "col2": int64(2)}),
192			&Mutation{opInsert, "t_foo", nil, []string{"col1", "col2"}, []interface{}{int64(1), int64(2)}},
193		},
194		{
195			"InsertStruct",
196			func() *Mutation {
197				m, err := InsertStruct(
198					"t_foo",
199					struct {
200						notCol bool
201						Col1   int64 `spanner:"col1"`
202						Col2   int64 `spanner:"col2"`
203					}{false, int64(1), int64(2)},
204				)
205				if err != nil {
206					t.Errorf("cannot convert struct into mutation: %v", err)
207				}
208				return m
209			}(),
210			&Mutation{opInsert, "t_foo", nil, []string{"col1", "col2"}, []interface{}{int64(1), int64(2)}},
211		},
212		{
213			"Update",
214			Update("t_foo", []string{"col1", "col2"}, []interface{}{"one", []byte(nil)}),
215			&Mutation{opUpdate, "t_foo", nil, []string{"col1", "col2"}, []interface{}{"one", []byte(nil)}},
216		},
217		{
218			"UpdateMap",
219			UpdateMap("t_foo", map[string]interface{}{"col1": "one", "col2": []byte(nil)}),
220			&Mutation{opUpdate, "t_foo", nil, []string{"col1", "col2"}, []interface{}{"one", []byte(nil)}},
221		},
222		{
223			"UpdateStruct",
224			func() *Mutation {
225				m, err := UpdateStruct(
226					"t_foo",
227					struct {
228						Col1   string `spanner:"col1"`
229						notCol int
230						Col2   []byte `spanner:"col2"`
231					}{"one", 1, nil},
232				)
233				if err != nil {
234					t.Errorf("cannot convert struct into mutation: %v", err)
235				}
236				return m
237			}(),
238			&Mutation{opUpdate, "t_foo", nil, []string{"col1", "col2"}, []interface{}{"one", []byte(nil)}},
239		},
240		{
241			"InsertOrUpdate",
242			InsertOrUpdate("t_foo", []string{"col1", "col2"}, []interface{}{1.0, 2.0}),
243			&Mutation{opInsertOrUpdate, "t_foo", nil, []string{"col1", "col2"}, []interface{}{1.0, 2.0}},
244		},
245		{
246			"InsertOrUpdateMap",
247			InsertOrUpdateMap("t_foo", map[string]interface{}{"col1": 1.0, "col2": 2.0}),
248			&Mutation{opInsertOrUpdate, "t_foo", nil, []string{"col1", "col2"}, []interface{}{1.0, 2.0}},
249		},
250		{
251			"InsertOrUpdateStruct",
252			func() *Mutation {
253				m, err := InsertOrUpdateStruct(
254					"t_foo",
255					struct {
256						Col1   float64 `spanner:"col1"`
257						Col2   float64 `spanner:"col2"`
258						notCol float64
259					}{1.0, 2.0, 3.0},
260				)
261				if err != nil {
262					t.Errorf("cannot convert struct into mutation: %v", err)
263				}
264				return m
265			}(),
266			&Mutation{opInsertOrUpdate, "t_foo", nil, []string{"col1", "col2"}, []interface{}{1.0, 2.0}},
267		},
268		{
269			"Replace",
270			Replace("t_foo", []string{"col1", "col2"}, []interface{}{"one", 2.0}),
271			&Mutation{opReplace, "t_foo", nil, []string{"col1", "col2"}, []interface{}{"one", 2.0}},
272		},
273		{
274			"ReplaceMap",
275			ReplaceMap("t_foo", map[string]interface{}{"col1": "one", "col2": 2.0}),
276			&Mutation{opReplace, "t_foo", nil, []string{"col1", "col2"}, []interface{}{"one", 2.0}},
277		},
278		{
279			"ReplaceStruct",
280			func() *Mutation {
281				m, err := ReplaceStruct(
282					"t_foo",
283					struct {
284						Col1   string  `spanner:"col1"`
285						Col2   float64 `spanner:"col2"`
286						notCol string
287					}{"one", 2.0, "foo"},
288				)
289				if err != nil {
290					t.Errorf("cannot convert struct into mutation: %v", err)
291				}
292				return m
293			}(),
294			&Mutation{opReplace, "t_foo", nil, []string{"col1", "col2"}, []interface{}{"one", 2.0}},
295		},
296		{
297			"Delete",
298			Delete("t_foo", Key{"foo"}),
299			&Mutation{opDelete, "t_foo", Key{"foo"}, nil, nil},
300		},
301		{
302			"DeleteRange",
303			Delete("t_foo", KeyRange{Key{"bar"}, Key{"foo"}, ClosedClosed}),
304			&Mutation{opDelete, "t_foo", KeyRange{Key{"bar"}, Key{"foo"}, ClosedClosed}, nil, nil},
305		},
306	} {
307		if !mutationEqual(t, *test.got, *test.want) {
308			t.Errorf("%v: got Mutation %v, want %v", test.m, test.got, test.want)
309		}
310	}
311}
312
313// Test encoding non-struct types by using *Struct helpers.
314func TestBadStructs(t *testing.T) {
315	val := "i_am_not_a_struct"
316	wantErr := errNotStruct(val)
317	if _, gotErr := InsertStruct("t_test", val); !testEqual(gotErr, wantErr) {
318		t.Errorf("InsertStruct(%q) returns error %v, want %v", val, gotErr, wantErr)
319	}
320	if _, gotErr := InsertOrUpdateStruct("t_test", val); !testEqual(gotErr, wantErr) {
321		t.Errorf("InsertOrUpdateStruct(%q) returns error %v, want %v", val, gotErr, wantErr)
322	}
323	if _, gotErr := UpdateStruct("t_test", val); !testEqual(gotErr, wantErr) {
324		t.Errorf("UpdateStruct(%q) returns error %v, want %v", val, gotErr, wantErr)
325	}
326	if _, gotErr := ReplaceStruct("t_test", val); !testEqual(gotErr, wantErr) {
327		t.Errorf("ReplaceStruct(%q) returns error %v, want %v", val, gotErr, wantErr)
328	}
329}
330
331func TestStructToMutationParams(t *testing.T) {
332	// Tests cases not covered elsewhere.
333	type S struct{ F interface{} }
334
335	for _, test := range []struct {
336		in       interface{}
337		wantCols []string
338		wantVals []interface{}
339		wantErr  error
340	}{
341		{nil, nil, nil, errNotStruct(nil)},
342		{3, nil, nil, errNotStruct(3)},
343		{(*S)(nil), nil, nil, nil},
344		{&S{F: 1}, []string{"F"}, []interface{}{1}, nil},
345		{&S{F: CommitTimestamp}, []string{"F"}, []interface{}{CommitTimestamp}, nil},
346	} {
347		gotCols, gotVals, gotErr := structToMutationParams(test.in)
348		if !testEqual(gotCols, test.wantCols) {
349			t.Errorf("%#v: got cols %v, want %v", test.in, gotCols, test.wantCols)
350		}
351		if !testEqual(gotVals, test.wantVals) {
352			t.Errorf("%#v: got vals %v, want %v", test.in, gotVals, test.wantVals)
353		}
354		if !testEqual(gotErr, test.wantErr) {
355			t.Errorf("%#v: got err %v, want %v", test.in, gotErr, test.wantErr)
356		}
357	}
358}
359
360// Test encoding Mutation into proto.
361func TestEncodeMutation(t *testing.T) {
362	for _, test := range []struct {
363		name      string
364		mutation  Mutation
365		wantProto *sppb.Mutation
366		wantErr   error
367	}{
368		{
369			"OpDelete",
370			Mutation{opDelete, "t_test", Key{1}, nil, nil},
371			&sppb.Mutation{
372				Operation: &sppb.Mutation_Delete_{
373					Delete: &sppb.Mutation_Delete{
374						Table: "t_test",
375						KeySet: &sppb.KeySet{
376							Keys: []*proto3.ListValue{listValueProto(intProto(1))},
377						},
378					},
379				},
380			},
381			nil,
382		},
383		{
384			"OpDelete - Key error",
385			Mutation{opDelete, "t_test", Key{struct{}{}}, nil, nil},
386			&sppb.Mutation{
387				Operation: &sppb.Mutation_Delete_{
388					Delete: &sppb.Mutation_Delete{
389						Table:  "t_test",
390						KeySet: &sppb.KeySet{},
391					},
392				},
393			},
394			errInvdKeyPartType(struct{}{}),
395		},
396		{
397			"OpInsert",
398			Mutation{opInsert, "t_test", nil, []string{"key", "val"}, []interface{}{"foo", 1}},
399			&sppb.Mutation{
400				Operation: &sppb.Mutation_Insert{
401					Insert: &sppb.Mutation_Write{
402						Table:   "t_test",
403						Columns: []string{"key", "val"},
404						Values:  []*proto3.ListValue{listValueProto(stringProto("foo"), intProto(1))},
405					},
406				},
407			},
408			nil,
409		},
410		{
411			"OpInsert - Value Type Error",
412			Mutation{opInsert, "t_test", nil, []string{"key", "val"}, []interface{}{struct{}{}, 1}},
413			&sppb.Mutation{
414				Operation: &sppb.Mutation_Insert{
415					Insert: &sppb.Mutation_Write{},
416				},
417			},
418			errEncoderUnsupportedType(struct{}{}),
419		},
420		{
421			"OpInsertOrUpdate",
422			Mutation{opInsertOrUpdate, "t_test", nil, []string{"key", "val"}, []interface{}{"foo", 1}},
423			&sppb.Mutation{
424				Operation: &sppb.Mutation_InsertOrUpdate{
425					InsertOrUpdate: &sppb.Mutation_Write{
426						Table:   "t_test",
427						Columns: []string{"key", "val"},
428						Values:  []*proto3.ListValue{listValueProto(stringProto("foo"), intProto(1))},
429					},
430				},
431			},
432			nil,
433		},
434		{
435			"OpInsertOrUpdate - Value Type Error",
436			Mutation{opInsertOrUpdate, "t_test", nil, []string{"key", "val"}, []interface{}{struct{}{}, 1}},
437			&sppb.Mutation{
438				Operation: &sppb.Mutation_InsertOrUpdate{
439					InsertOrUpdate: &sppb.Mutation_Write{},
440				},
441			},
442			errEncoderUnsupportedType(struct{}{}),
443		},
444		{
445			"OpReplace",
446			Mutation{opReplace, "t_test", nil, []string{"key", "val"}, []interface{}{"foo", 1}},
447			&sppb.Mutation{
448				Operation: &sppb.Mutation_Replace{
449					Replace: &sppb.Mutation_Write{
450						Table:   "t_test",
451						Columns: []string{"key", "val"},
452						Values:  []*proto3.ListValue{listValueProto(stringProto("foo"), intProto(1))},
453					},
454				},
455			},
456			nil,
457		},
458		{
459			"OpReplace - Value Type Error",
460			Mutation{opReplace, "t_test", nil, []string{"key", "val"}, []interface{}{struct{}{}, 1}},
461			&sppb.Mutation{
462				Operation: &sppb.Mutation_Replace{
463					Replace: &sppb.Mutation_Write{},
464				},
465			},
466			errEncoderUnsupportedType(struct{}{}),
467		},
468		{
469			"OpUpdate",
470			Mutation{opUpdate, "t_test", nil, []string{"key", "val"}, []interface{}{"foo", 1}},
471			&sppb.Mutation{
472				Operation: &sppb.Mutation_Update{
473					Update: &sppb.Mutation_Write{
474						Table:   "t_test",
475						Columns: []string{"key", "val"},
476						Values:  []*proto3.ListValue{listValueProto(stringProto("foo"), intProto(1))},
477					},
478				},
479			},
480			nil,
481		},
482		{
483			"OpUpdate - Value Type Error",
484			Mutation{opUpdate, "t_test", nil, []string{"key", "val"}, []interface{}{struct{}{}, 1}},
485			&sppb.Mutation{
486				Operation: &sppb.Mutation_Update{
487					Update: &sppb.Mutation_Write{},
488				},
489			},
490			errEncoderUnsupportedType(struct{}{}),
491		},
492		{
493			"OpKnown - Unknown Mutation Operation Code",
494			Mutation{op(100), "t_test", nil, nil, nil},
495			&sppb.Mutation{},
496			errInvdMutationOp(Mutation{op(100), "t_test", nil, nil, nil}),
497		},
498	} {
499		gotProto, gotErr := test.mutation.proto()
500		if gotErr != nil {
501			if !testEqual(gotErr, test.wantErr) {
502				t.Errorf("%s: %v.proto() returns error %v, want %v", test.name, test.mutation, gotErr, test.wantErr)
503			}
504			continue
505		}
506		if !testEqual(gotProto, test.wantProto) {
507			t.Errorf("%s: %v.proto() = (%v, nil), want (%v, nil)", test.name, test.mutation, gotProto, test.wantProto)
508		}
509	}
510}
511
512// Test Encoding an array of mutations.
513func TestEncodeMutationArray(t *testing.T) {
514	for _, test := range []struct {
515		name    string
516		ms      []*Mutation
517		want    []*sppb.Mutation
518		wantErr error
519	}{
520		{
521			"Multiple Mutations",
522			[]*Mutation{
523				{opDelete, "t_test", Key{"bar"}, nil, nil},
524				{opInsertOrUpdate, "t_test", nil, []string{"key", "val"}, []interface{}{"foo", 1}},
525			},
526			[]*sppb.Mutation{
527				{
528					Operation: &sppb.Mutation_Delete_{
529						Delete: &sppb.Mutation_Delete{
530							Table: "t_test",
531							KeySet: &sppb.KeySet{
532								Keys: []*proto3.ListValue{listValueProto(stringProto("bar"))},
533							},
534						},
535					},
536				},
537				{
538					Operation: &sppb.Mutation_InsertOrUpdate{
539						InsertOrUpdate: &sppb.Mutation_Write{
540							Table:   "t_test",
541							Columns: []string{"key", "val"},
542							Values:  []*proto3.ListValue{listValueProto(stringProto("foo"), intProto(1))},
543						},
544					},
545				},
546			},
547			nil,
548		},
549		{
550			"Multiple Mutations - Bad Mutation",
551			[]*Mutation{
552				{opDelete, "t_test", Key{"bar"}, nil, nil},
553				{opInsertOrUpdate, "t_test", nil, []string{"key", "val"}, []interface{}{"foo", struct{}{}}},
554			},
555			[]*sppb.Mutation{},
556			errEncoderUnsupportedType(struct{}{}),
557		},
558	} {
559		gotProto, gotErr := mutationsProto(test.ms)
560		if gotErr != nil {
561			if !testEqual(gotErr, test.wantErr) {
562				t.Errorf("%v: mutationsProto(%v) returns error %v, want %v", test.name, test.ms, gotErr, test.wantErr)
563			}
564			continue
565		}
566		if !testEqual(gotProto, test.want) {
567			t.Errorf("%v: mutationsProto(%v) = (%v, nil), want (%v, nil)", test.name, test.ms, gotProto, test.want)
568		}
569	}
570}
571