mirror of
https://github.com/dutchcoders/transfer.sh.git
synced 2024-12-02 17:10:18 +01:00
82493d6dcb
* GDrive provider support * More reliable basedir ownership * Fix mimetype
466 lines
12 KiB
Go
466 lines
12 KiB
Go
// Copyright 2018 Google LLC
|
|
//
|
|
// 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.
|
|
|
|
// +build go1.7
|
|
|
|
package spanner
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
"cloud.google.com/go/civil"
|
|
proto "github.com/golang/protobuf/proto"
|
|
proto3 "github.com/golang/protobuf/ptypes/struct"
|
|
"golang.org/x/net/context"
|
|
sppb "google.golang.org/genproto/googleapis/spanner/v1"
|
|
)
|
|
|
|
func TestEncodeStructValueDynamicStructs(t *testing.T) {
|
|
dynStructType := reflect.StructOf([]reflect.StructField{
|
|
{Name: "A", Type: reflect.TypeOf(0), Tag: `spanner:"a"`},
|
|
{Name: "B", Type: reflect.TypeOf(""), Tag: `spanner:"b"`},
|
|
})
|
|
dynNullableStructType := reflect.PtrTo(dynStructType)
|
|
dynStructArrType := reflect.SliceOf(dynStructType)
|
|
dynNullableStructArrType := reflect.SliceOf(dynNullableStructType)
|
|
|
|
dynStructValue := reflect.New(dynStructType)
|
|
dynStructValue.Elem().Field(0).SetInt(10)
|
|
dynStructValue.Elem().Field(1).SetString("abc")
|
|
|
|
dynStructArrValue := reflect.MakeSlice(dynNullableStructArrType, 2, 2)
|
|
dynStructArrValue.Index(0).Set(reflect.Zero(dynNullableStructType))
|
|
dynStructArrValue.Index(1).Set(dynStructValue)
|
|
|
|
structProtoType := structType(
|
|
mkField("a", intType()),
|
|
mkField("b", stringType()))
|
|
|
|
arrProtoType := listType(structProtoType)
|
|
|
|
for _, test := range []encodeTest{
|
|
{
|
|
"Dynanic non-NULL struct value.",
|
|
dynStructValue.Elem().Interface(),
|
|
listProto(intProto(10), stringProto("abc")),
|
|
structProtoType,
|
|
},
|
|
{
|
|
"Dynanic NULL struct value.",
|
|
reflect.Zero(dynNullableStructType).Interface(),
|
|
nullProto(),
|
|
structProtoType,
|
|
},
|
|
{
|
|
"Empty array of dynamic structs.",
|
|
reflect.MakeSlice(dynStructArrType, 0, 0).Interface(),
|
|
listProto([]*proto3.Value{}...),
|
|
arrProtoType,
|
|
},
|
|
{
|
|
"NULL array of non-NULL-able dynamic structs.",
|
|
reflect.Zero(dynStructArrType).Interface(),
|
|
nullProto(),
|
|
arrProtoType,
|
|
},
|
|
{
|
|
"NULL array of NULL-able(nil) dynamic structs.",
|
|
reflect.Zero(dynNullableStructArrType).Interface(),
|
|
nullProto(),
|
|
arrProtoType,
|
|
},
|
|
{
|
|
"Array containing NULL(nil) dynamic-typed struct elements.",
|
|
dynStructArrValue.Interface(),
|
|
listProto(
|
|
nullProto(),
|
|
listProto(intProto(10), stringProto("abc"))),
|
|
arrProtoType,
|
|
},
|
|
} {
|
|
encodeStructValue(test, t)
|
|
}
|
|
}
|
|
|
|
func TestEncodeStructValueEmptyStruct(t *testing.T) {
|
|
emptyListValue := listProto([]*proto3.Value{}...)
|
|
emptyStructType := structType([]*sppb.StructType_Field{}...)
|
|
emptyStruct := struct{}{}
|
|
nullEmptyStruct := (*struct{})(nil)
|
|
|
|
dynamicEmptyStructType := reflect.StructOf(make([]reflect.StructField, 0, 0))
|
|
dynamicStructArrType := reflect.SliceOf(reflect.PtrTo((dynamicEmptyStructType)))
|
|
|
|
dynamicEmptyStruct := reflect.New(dynamicEmptyStructType)
|
|
dynamicNullEmptyStruct := reflect.Zero(reflect.PtrTo(dynamicEmptyStructType))
|
|
|
|
dynamicStructArrValue := reflect.MakeSlice(dynamicStructArrType, 2, 2)
|
|
dynamicStructArrValue.Index(0).Set(dynamicNullEmptyStruct)
|
|
dynamicStructArrValue.Index(1).Set(dynamicEmptyStruct)
|
|
|
|
for _, test := range []encodeTest{
|
|
{
|
|
"Go empty struct.",
|
|
emptyStruct,
|
|
emptyListValue,
|
|
emptyStructType,
|
|
},
|
|
{
|
|
"Dynamic empty struct.",
|
|
dynamicEmptyStruct.Interface(),
|
|
emptyListValue,
|
|
emptyStructType,
|
|
},
|
|
{
|
|
"Go NULL empty struct.",
|
|
nullEmptyStruct,
|
|
nullProto(),
|
|
emptyStructType,
|
|
},
|
|
{
|
|
"Dynamic NULL empty struct.",
|
|
dynamicNullEmptyStruct.Interface(),
|
|
nullProto(),
|
|
emptyStructType,
|
|
},
|
|
{
|
|
"Non-empty array of dynamic NULL and non-NULL empty structs.",
|
|
dynamicStructArrValue.Interface(),
|
|
listProto(nullProto(), emptyListValue),
|
|
listType(emptyStructType),
|
|
},
|
|
{
|
|
"Non-empty array of nullable empty structs.",
|
|
[]*struct{}{nullEmptyStruct, &emptyStruct},
|
|
listProto(nullProto(), emptyListValue),
|
|
listType(emptyStructType),
|
|
},
|
|
{
|
|
"Empty array of empty struct.",
|
|
[]struct{}{},
|
|
emptyListValue,
|
|
listType(emptyStructType),
|
|
},
|
|
{
|
|
"Null array of empty structs.",
|
|
[]struct{}(nil),
|
|
nullProto(),
|
|
listType(emptyStructType),
|
|
},
|
|
} {
|
|
encodeStructValue(test, t)
|
|
}
|
|
}
|
|
|
|
func TestEncodeStructValueMixedStructTypes(t *testing.T) {
|
|
type staticStruct struct {
|
|
F int `spanner:"fStatic"`
|
|
}
|
|
s1 := staticStruct{10}
|
|
s2 := (*staticStruct)(nil)
|
|
|
|
var f float64
|
|
dynStructType := reflect.StructOf([]reflect.StructField{
|
|
{Name: "A", Type: reflect.TypeOf(f), Tag: `spanner:"fDynamic"`},
|
|
})
|
|
s3 := reflect.New(dynStructType)
|
|
s3.Elem().Field(0).SetFloat(3.14)
|
|
|
|
for _, test := range []encodeTest{
|
|
{
|
|
"'struct' with static and dynamic *struct, []*struct, []struct fields",
|
|
struct {
|
|
A []staticStruct
|
|
B []*staticStruct
|
|
C interface{}
|
|
}{
|
|
[]staticStruct{s1, s1},
|
|
[]*staticStruct{&s1, s2},
|
|
s3.Interface(),
|
|
},
|
|
listProto(
|
|
listProto(listProto(intProto(10)), listProto(intProto(10))),
|
|
listProto(listProto(intProto(10)), nullProto()),
|
|
listProto(floatProto(3.14))),
|
|
structType(
|
|
mkField("A", listType(structType(mkField("fStatic", intType())))),
|
|
mkField("B", listType(structType(mkField("fStatic", intType())))),
|
|
mkField("C", structType(mkField("fDynamic", floatType())))),
|
|
},
|
|
} {
|
|
encodeStructValue(test, t)
|
|
}
|
|
}
|
|
|
|
func TestBindParamsDynamic(t *testing.T) {
|
|
// Verify Statement.bindParams generates correct values and types.
|
|
st := Statement{
|
|
SQL: "SELECT id from t_foo WHERE col = @var",
|
|
Params: map[string]interface{}{"var": nil},
|
|
}
|
|
want := &sppb.ExecuteSqlRequest{
|
|
Params: &proto3.Struct{
|
|
Fields: map[string]*proto3.Value{"var": nil},
|
|
},
|
|
ParamTypes: map[string]*sppb.Type{"var": nil},
|
|
}
|
|
var (
|
|
t1, _ = time.Parse(time.RFC3339Nano, "2016-11-15T15:04:05.999999999Z")
|
|
// Boundaries
|
|
t2, _ = time.Parse(time.RFC3339Nano, "0001-01-01T00:00:00.000000000Z")
|
|
)
|
|
dynamicStructType := reflect.StructOf([]reflect.StructField{
|
|
{Name: "A", Type: reflect.TypeOf(t1), Tag: `spanner:"field"`},
|
|
{Name: "B", Type: reflect.TypeOf(3.14), Tag: `spanner:""`},
|
|
})
|
|
dynamicStructArrType := reflect.SliceOf(reflect.PtrTo(dynamicStructType))
|
|
dynamicEmptyStructType := reflect.StructOf(make([]reflect.StructField, 0, 0))
|
|
|
|
dynamicStructTypeProto := structType(
|
|
mkField("field", timeType()),
|
|
mkField("", floatType()))
|
|
|
|
s3 := reflect.New(dynamicStructType)
|
|
s3.Elem().Field(0).Set(reflect.ValueOf(t1))
|
|
s3.Elem().Field(1).SetFloat(1.4)
|
|
|
|
s4 := reflect.New(dynamicStructType)
|
|
s4.Elem().Field(0).Set(reflect.ValueOf(t2))
|
|
s4.Elem().Field(1).SetFloat(-13.3)
|
|
|
|
dynamicStructArrayVal := reflect.MakeSlice(dynamicStructArrType, 2, 2)
|
|
dynamicStructArrayVal.Index(0).Set(s3)
|
|
dynamicStructArrayVal.Index(1).Set(s4)
|
|
|
|
for i, test := range []struct {
|
|
val interface{}
|
|
wantField *proto3.Value
|
|
wantType *sppb.Type
|
|
}{
|
|
{
|
|
s3.Interface(),
|
|
listProto(timeProto(t1), floatProto(1.4)),
|
|
structType(
|
|
mkField("field", timeType()),
|
|
mkField("", floatType())),
|
|
},
|
|
{
|
|
reflect.Zero(reflect.PtrTo(dynamicEmptyStructType)).Interface(),
|
|
nullProto(),
|
|
structType([]*sppb.StructType_Field{}...),
|
|
},
|
|
{
|
|
dynamicStructArrayVal.Interface(),
|
|
listProto(
|
|
listProto(timeProto(t1), floatProto(1.4)),
|
|
listProto(timeProto(t2), floatProto(-13.3))),
|
|
listType(dynamicStructTypeProto),
|
|
},
|
|
{
|
|
[]*struct {
|
|
F1 time.Time `spanner:"field"`
|
|
F2 float64 `spanner:""`
|
|
}{
|
|
nil,
|
|
{t1, 1.4},
|
|
},
|
|
listProto(
|
|
nullProto(),
|
|
listProto(timeProto(t1), floatProto(1.4))),
|
|
listType(dynamicStructTypeProto),
|
|
},
|
|
} {
|
|
st.Params["var"] = test.val
|
|
want.Params.Fields["var"] = test.wantField
|
|
want.ParamTypes["var"] = test.wantType
|
|
got := &sppb.ExecuteSqlRequest{}
|
|
if err := st.bindParams(got); err != nil || !proto.Equal(got, want) {
|
|
// handle NaN
|
|
if test.wantType.Code == floatType().Code && proto.MarshalTextString(got) == proto.MarshalTextString(want) {
|
|
continue
|
|
}
|
|
t.Errorf("#%d: bind result: \n(%v, %v)\nwant\n(%v, %v)\n", i, got, err, want, nil)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestStructParametersBind(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
client, _, tearDown := prepare(ctx, t, nil)
|
|
defer tearDown()
|
|
|
|
type tRow []interface{}
|
|
type tRows []struct{ trow tRow }
|
|
|
|
type allFields struct {
|
|
Stringf string
|
|
Intf int
|
|
Boolf bool
|
|
Floatf float64
|
|
Bytef []byte
|
|
Timef time.Time
|
|
Datef civil.Date
|
|
}
|
|
allColumns := []string{
|
|
"Stringf",
|
|
"Intf",
|
|
"Boolf",
|
|
"Floatf",
|
|
"Bytef",
|
|
"Timef",
|
|
"Datef",
|
|
}
|
|
s1 := allFields{"abc", 300, false, 3.45, []byte("foo"), t1, d1}
|
|
s2 := allFields{"def", -300, false, -3.45, []byte("bar"), t2, d2}
|
|
|
|
dynamicStructType := reflect.StructOf([]reflect.StructField{
|
|
{Name: "A", Type: reflect.TypeOf(t1), Tag: `spanner:"ff1"`},
|
|
})
|
|
s3 := reflect.New(dynamicStructType)
|
|
s3.Elem().Field(0).Set(reflect.ValueOf(t1))
|
|
|
|
for i, test := range []struct {
|
|
param interface{}
|
|
sql string
|
|
cols []string
|
|
trows tRows
|
|
}{
|
|
// Struct value.
|
|
{
|
|
s1,
|
|
"SELECT" +
|
|
" @p.Stringf," +
|
|
" @p.Intf," +
|
|
" @p.Boolf," +
|
|
" @p.Floatf," +
|
|
" @p.Bytef," +
|
|
" @p.Timef," +
|
|
" @p.Datef",
|
|
allColumns,
|
|
tRows{
|
|
{tRow{"abc", 300, false, 3.45, []byte("foo"), t1, d1}},
|
|
},
|
|
},
|
|
// Array of struct value.
|
|
{
|
|
[]allFields{s1, s2},
|
|
"SELECT * FROM UNNEST(@p)",
|
|
allColumns,
|
|
tRows{
|
|
{tRow{"abc", 300, false, 3.45, []byte("foo"), t1, d1}},
|
|
{tRow{"def", -300, false, -3.45, []byte("bar"), t2, d2}},
|
|
},
|
|
},
|
|
// Null struct.
|
|
{
|
|
(*allFields)(nil),
|
|
"SELECT @p IS NULL",
|
|
[]string{""},
|
|
tRows{
|
|
{tRow{true}},
|
|
},
|
|
},
|
|
// Null Array of struct.
|
|
{
|
|
[]allFields(nil),
|
|
"SELECT @p IS NULL",
|
|
[]string{""},
|
|
tRows{
|
|
{tRow{true}},
|
|
},
|
|
},
|
|
// Empty struct.
|
|
{
|
|
struct{}{},
|
|
"SELECT @p IS NULL ",
|
|
[]string{""},
|
|
tRows{
|
|
{tRow{false}},
|
|
},
|
|
},
|
|
// Empty array of struct.
|
|
{
|
|
[]allFields{},
|
|
"SELECT * FROM UNNEST(@p) ",
|
|
allColumns,
|
|
tRows{},
|
|
},
|
|
// Struct with duplicate fields.
|
|
{
|
|
struct {
|
|
A int `spanner:"field"`
|
|
B int `spanner:"field"`
|
|
}{10, 20},
|
|
"SELECT * FROM UNNEST([@p]) ",
|
|
[]string{"field", "field"},
|
|
tRows{
|
|
{tRow{10, 20}},
|
|
},
|
|
},
|
|
// Struct with unnamed fields.
|
|
{
|
|
struct {
|
|
A string `spanner:""`
|
|
}{"hello"},
|
|
"SELECT * FROM UNNEST([@p]) ",
|
|
[]string{""},
|
|
tRows{
|
|
{tRow{"hello"}},
|
|
},
|
|
},
|
|
// Mixed struct.
|
|
{
|
|
struct {
|
|
DynamicStructField interface{} `spanner:"f1"`
|
|
ArrayStructField []*allFields `spanner:"f2"`
|
|
}{
|
|
DynamicStructField: s3.Interface(),
|
|
ArrayStructField: []*allFields{nil},
|
|
},
|
|
"SELECT @p.f1.ff1, ARRAY_LENGTH(@p.f2), @p.f2[OFFSET(0)] IS NULL ",
|
|
[]string{"ff1", "", ""},
|
|
tRows{
|
|
{tRow{t1, 1, true}},
|
|
},
|
|
},
|
|
} {
|
|
iter := client.Single().Query(ctx, Statement{
|
|
SQL: test.sql,
|
|
Params: map[string]interface{}{"p": test.param},
|
|
})
|
|
var gotRows []*Row
|
|
err := iter.Do(func(r *Row) error {
|
|
gotRows = append(gotRows, r)
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Errorf("Failed to execute test case %d, error: %v", i, err)
|
|
}
|
|
|
|
var wantRows []*Row
|
|
for j, row := range test.trows {
|
|
r, err := NewRow(test.cols, row.trow)
|
|
if err != nil {
|
|
t.Errorf("Invalid row %d in test case %d", j, i)
|
|
}
|
|
wantRows = append(wantRows, r)
|
|
}
|
|
if !testEqual(gotRows, wantRows) {
|
|
t.Errorf("%d: Want result %v, got result %v", i, wantRows, gotRows)
|
|
}
|
|
}
|
|
}
|