github.com/ydb-platform/ydb-go-sdk/v3@v3.57.0/internal/table/scanner/scanner_test.go (about)

     1  package scanner
     2  
     3  import (
     4  	"encoding/binary"
     5  	"encoding/json"
     6  	"math"
     7  	"strconv"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/stretchr/testify/require"
    12  	"github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
    13  
    14  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
    15  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/xrand"
    16  	"github.com/ydb-platform/ydb-go-sdk/v3/table/result/indexed"
    17  	"github.com/ydb-platform/ydb-go-sdk/v3/table/result/named"
    18  	"github.com/ydb-platform/ydb-go-sdk/v3/table/types"
    19  )
    20  
    21  //nolint:gocyclo
    22  func valueFromPrimitiveTypeID(c *column, r xrand.Rand) (*Ydb.Value, interface{}) {
    23  	rv := r.Int64(math.MaxInt16)
    24  	switch c.typeID {
    25  	case Ydb.Type_BOOL:
    26  		v := rv%2 == 1
    27  		ydbval := &Ydb.Value{
    28  			Value: &Ydb.Value_BoolValue{
    29  				BoolValue: v,
    30  			},
    31  		}
    32  		if c.optional && !c.testDefault {
    33  			vp := &v
    34  
    35  			return ydbval, &vp
    36  		}
    37  
    38  		return ydbval, &v
    39  	case Ydb.Type_INT8:
    40  		v := int8(rv)
    41  		ydbval := &Ydb.Value{
    42  			Value: &Ydb.Value_Int32Value{
    43  				Int32Value: int32(v),
    44  			},
    45  		}
    46  		if c.optional && !c.testDefault {
    47  			vp := &v
    48  
    49  			return ydbval, &vp
    50  		}
    51  
    52  		return ydbval, &v
    53  	case Ydb.Type_UINT8:
    54  		if c.nilValue {
    55  			ydbval := &Ydb.Value{
    56  				Value: &Ydb.Value_NullFlagValue{},
    57  			}
    58  			if c.testDefault {
    59  				var dv uint8
    60  
    61  				return ydbval, &dv
    62  			}
    63  			var dv *uint8
    64  
    65  			return ydbval, &dv
    66  		}
    67  		v := uint8(rv)
    68  		ydbval := &Ydb.Value{
    69  			Value: &Ydb.Value_Uint32Value{
    70  				Uint32Value: uint32(v),
    71  			},
    72  		}
    73  		if c.optional && !c.testDefault {
    74  			vp := &v
    75  
    76  			return ydbval, &vp
    77  		}
    78  
    79  		return ydbval, &v
    80  	case Ydb.Type_INT16:
    81  		v := int16(rv)
    82  		ydbval := &Ydb.Value{
    83  			Value: &Ydb.Value_Int32Value{
    84  				Int32Value: int32(v),
    85  			},
    86  		}
    87  		if c.optional && !c.testDefault {
    88  			vp := &v
    89  
    90  			return ydbval, &vp
    91  		}
    92  
    93  		return ydbval, &v
    94  	case Ydb.Type_UINT16:
    95  		v := uint16(rv)
    96  		ydbval := &Ydb.Value{
    97  			Value: &Ydb.Value_Uint32Value{
    98  				Uint32Value: uint32(v),
    99  			},
   100  		}
   101  		if c.optional && !c.testDefault {
   102  			vp := &v
   103  
   104  			return ydbval, &vp
   105  		}
   106  
   107  		return ydbval, &v
   108  	case Ydb.Type_INT32:
   109  		if c.nilValue {
   110  			ydbval := &Ydb.Value{
   111  				Value: &Ydb.Value_NullFlagValue{},
   112  			}
   113  			if c.testDefault {
   114  				var dv int32
   115  
   116  				return ydbval, &dv
   117  			}
   118  			var dv *int32
   119  
   120  			return ydbval, &dv
   121  		}
   122  		v := int32(rv)
   123  		ydbval := &Ydb.Value{
   124  			Value: &Ydb.Value_Int32Value{
   125  				Int32Value: v,
   126  			},
   127  		}
   128  		if c.optional && !c.testDefault {
   129  			vp := &v
   130  
   131  			return ydbval, &vp
   132  		}
   133  
   134  		return ydbval, &v
   135  	case Ydb.Type_UINT32:
   136  		v := uint32(rv)
   137  		ydbval := &Ydb.Value{
   138  			Value: &Ydb.Value_Uint32Value{
   139  				Uint32Value: v,
   140  			},
   141  		}
   142  		if c.optional && !c.testDefault {
   143  			vp := &v
   144  
   145  			return ydbval, &vp
   146  		}
   147  
   148  		return ydbval, &v
   149  	case Ydb.Type_INT64:
   150  		v := rv
   151  		ydbval := &Ydb.Value{
   152  			Value: &Ydb.Value_Int64Value{
   153  				Int64Value: v,
   154  			},
   155  		}
   156  		if c.ydbvalue {
   157  			vp := types.Int64Value(v)
   158  
   159  			return ydbval, &vp
   160  		}
   161  		if c.scanner {
   162  			s := intIncScanner(v + 10)
   163  
   164  			return ydbval, &s
   165  		}
   166  		if c.optional && !c.testDefault {
   167  			vp := &v
   168  
   169  			return ydbval, &vp
   170  		}
   171  
   172  		return ydbval, &v
   173  	case Ydb.Type_UINT64:
   174  		v := uint64(rv)
   175  		ydbval := &Ydb.Value{
   176  			Value: &Ydb.Value_Uint64Value{
   177  				Uint64Value: v,
   178  			},
   179  		}
   180  		if c.optional && !c.testDefault {
   181  			vp := &v
   182  
   183  			return ydbval, &vp
   184  		}
   185  
   186  		return ydbval, &v
   187  	case Ydb.Type_FLOAT:
   188  		v := float32(rv)
   189  		ydbval := &Ydb.Value{
   190  			Value: &Ydb.Value_FloatValue{
   191  				FloatValue: v,
   192  			},
   193  		}
   194  		if c.ydbvalue {
   195  			vp := types.FloatValue(v)
   196  
   197  			return ydbval, &vp
   198  		}
   199  		if c.optional && !c.testDefault {
   200  			vp := &v
   201  
   202  			return ydbval, &vp
   203  		}
   204  
   205  		return ydbval, &v
   206  	case Ydb.Type_DOUBLE:
   207  		v := float64(rv)
   208  		ydbval := &Ydb.Value{
   209  			Value: &Ydb.Value_DoubleValue{
   210  				DoubleValue: v,
   211  			},
   212  		}
   213  		if c.optional && !c.testDefault {
   214  			vp := &v
   215  
   216  			return ydbval, &vp
   217  		}
   218  
   219  		return ydbval, &v
   220  	case Ydb.Type_DATE:
   221  		v := uint32(rv)
   222  		ydbval := &Ydb.Value{
   223  			Value: &Ydb.Value_Uint32Value{
   224  				Uint32Value: v,
   225  			},
   226  		}
   227  		src := value.DateToTime(v)
   228  		if c.scanner {
   229  			s := dateScanner(src)
   230  
   231  			return ydbval, &s
   232  		}
   233  		if c.optional && !c.testDefault {
   234  			vp := &src
   235  
   236  			return ydbval, &vp
   237  		}
   238  
   239  		return ydbval, &src
   240  	case Ydb.Type_DATETIME:
   241  		v := uint32(rv)
   242  		ydbval := &Ydb.Value{
   243  			Value: &Ydb.Value_Uint32Value{
   244  				Uint32Value: v,
   245  			},
   246  		}
   247  		src := value.DatetimeToTime(v)
   248  		if c.optional && !c.testDefault {
   249  			vp := &src
   250  
   251  			return ydbval, &vp
   252  		}
   253  
   254  		return ydbval, &src
   255  	case Ydb.Type_TIMESTAMP:
   256  		v := uint64(rv)
   257  		ydbval := &Ydb.Value{
   258  			Value: &Ydb.Value_Uint64Value{
   259  				Uint64Value: v,
   260  			},
   261  		}
   262  		src := value.TimestampToTime(v)
   263  		if c.optional && !c.testDefault {
   264  			vp := &src
   265  
   266  			return ydbval, &vp
   267  		}
   268  
   269  		return ydbval, &src
   270  	case Ydb.Type_INTERVAL:
   271  		if c.nilValue {
   272  			ydbval := &Ydb.Value{
   273  				Value: &Ydb.Value_NullFlagValue{},
   274  			}
   275  			if c.testDefault {
   276  				var dv time.Duration
   277  
   278  				return ydbval, &dv
   279  			}
   280  			var dv *time.Duration
   281  
   282  			return ydbval, &dv
   283  		}
   284  		rv %= time.Now().Unix()
   285  		v := rv
   286  		ydbval := &Ydb.Value{
   287  			Value: &Ydb.Value_Int64Value{
   288  				Int64Value: v,
   289  			},
   290  		}
   291  		src := value.IntervalToDuration(v)
   292  		if c.optional && !c.testDefault {
   293  			vp := &src
   294  
   295  			return ydbval, &vp
   296  		}
   297  
   298  		return ydbval, &src
   299  	case Ydb.Type_TZ_DATE:
   300  		v := time.Now().Format(value.LayoutDate) + ",Europe/Berlin"
   301  		ydbval := &Ydb.Value{
   302  			Value: &Ydb.Value_TextValue{
   303  				TextValue: v,
   304  			},
   305  		}
   306  		src, _ := value.TzDateToTime(v)
   307  		if c.optional && !c.testDefault {
   308  			vp := &src
   309  
   310  			return ydbval, &vp
   311  		}
   312  
   313  		return ydbval, &src
   314  	case Ydb.Type_TZ_DATETIME:
   315  		if c.nilValue {
   316  			ydbval := &Ydb.Value{
   317  				Value: &Ydb.Value_NullFlagValue{},
   318  			}
   319  			if c.testDefault {
   320  				var dv time.Time
   321  
   322  				return ydbval, &dv
   323  			}
   324  			var dv *time.Time
   325  
   326  			return ydbval, &dv
   327  		}
   328  		rv %= time.Now().Unix()
   329  		v := value.DatetimeToTime(uint32(rv)).Format(value.LayoutTzDatetime) + ",Europe/Berlin"
   330  		ydbval := &Ydb.Value{
   331  			Value: &Ydb.Value_TextValue{
   332  				TextValue: v,
   333  			},
   334  		}
   335  		src, _ := value.TzDatetimeToTime(v)
   336  		if c.optional && !c.testDefault {
   337  			vp := &src
   338  
   339  			return ydbval, &vp
   340  		}
   341  
   342  		return ydbval, &src
   343  	case Ydb.Type_TZ_TIMESTAMP:
   344  		rv %= time.Now().Unix()
   345  		v := value.TimestampToTime(uint64(rv)).Format(value.LayoutTzTimestamp) + ",Europe/Berlin"
   346  		ydbval := &Ydb.Value{
   347  			Value: &Ydb.Value_TextValue{
   348  				TextValue: v,
   349  			},
   350  		}
   351  		src, _ := value.TzTimestampToTime(v)
   352  		if c.optional && !c.testDefault {
   353  			vp := &src
   354  
   355  			return ydbval, &vp
   356  		}
   357  
   358  		return ydbval, &src
   359  	case Ydb.Type_STRING:
   360  		if c.nilValue {
   361  			ydbval := &Ydb.Value{
   362  				Value: &Ydb.Value_NullFlagValue{},
   363  			}
   364  			if c.testDefault {
   365  				var dv []byte
   366  
   367  				return ydbval, &dv
   368  			}
   369  			var dv *[]byte
   370  
   371  			return ydbval, &dv
   372  		}
   373  		v := make([]byte, 16)
   374  		binary.BigEndian.PutUint64(v[0:8], uint64(rv))
   375  		binary.BigEndian.PutUint64(v[8:16], uint64(rv))
   376  		ydbval := &Ydb.Value{
   377  			Value: &Ydb.Value_BytesValue{
   378  				BytesValue: v,
   379  			},
   380  		}
   381  		src := v
   382  		if c.optional && !c.testDefault {
   383  			vp := &src
   384  
   385  			return ydbval, &vp
   386  		}
   387  
   388  		return ydbval, &src
   389  	case Ydb.Type_UTF8:
   390  		v := strconv.FormatUint(uint64(rv), 10)
   391  		ydbval := &Ydb.Value{
   392  			Value: &Ydb.Value_TextValue{
   393  				TextValue: v,
   394  			},
   395  		}
   396  		if c.optional && !c.testDefault {
   397  			vp := &v
   398  
   399  			return ydbval, &vp
   400  		}
   401  
   402  		return ydbval, &v
   403  	case Ydb.Type_YSON:
   404  		if c.nilValue {
   405  			ydbval := &Ydb.Value{
   406  				Value: &Ydb.Value_NullFlagValue{},
   407  			}
   408  			if c.testDefault {
   409  				var dv []byte
   410  
   411  				return ydbval, &dv
   412  			}
   413  			var dv *[]byte
   414  
   415  			return ydbval, &dv
   416  		}
   417  		v := strconv.FormatUint(uint64(rv), 10)
   418  		ydbval := &Ydb.Value{
   419  			Value: &Ydb.Value_TextValue{
   420  				TextValue: v,
   421  			},
   422  		}
   423  		src := []byte(v)
   424  		if c.optional && !c.testDefault {
   425  			vp := &src
   426  
   427  			return ydbval, &vp
   428  		}
   429  
   430  		return ydbval, &src
   431  	case Ydb.Type_JSON:
   432  		v := strconv.FormatUint(uint64(rv), 10)
   433  		ydbval := &Ydb.Value{
   434  			Value: &Ydb.Value_TextValue{
   435  				TextValue: v,
   436  			},
   437  		}
   438  		if c.ydbvalue {
   439  			vp := types.JSONValue(v)
   440  
   441  			return ydbval, &vp
   442  		}
   443  		src := []byte(v)
   444  		if c.optional && !c.testDefault {
   445  			vp := &src
   446  
   447  			return ydbval, &vp
   448  		}
   449  
   450  		return ydbval, &src
   451  	case Ydb.Type_UUID:
   452  		if c.nilValue {
   453  			ydbval := &Ydb.Value{
   454  				Value: &Ydb.Value_NullFlagValue{},
   455  			}
   456  			if c.testDefault {
   457  				var dv [16]byte
   458  
   459  				return ydbval, &dv
   460  			}
   461  			var dv *[16]byte
   462  
   463  			return ydbval, &dv
   464  		}
   465  		v := [16]byte{}
   466  		binary.BigEndian.PutUint64(v[0:8], uint64(rv))
   467  		binary.BigEndian.PutUint64(v[8:16], uint64(rv))
   468  		ydbval := &Ydb.Value{
   469  			High_128: binary.BigEndian.Uint64(v[0:8]),
   470  			Value: &Ydb.Value_Low_128{
   471  				Low_128: binary.BigEndian.Uint64(v[8:16]),
   472  			},
   473  		}
   474  		if c.optional && !c.testDefault {
   475  			vp := &v
   476  
   477  			return ydbval, &vp
   478  		}
   479  
   480  		return ydbval, &v
   481  	case Ydb.Type_JSON_DOCUMENT:
   482  		v := strconv.FormatUint(uint64(rv), 10)
   483  		ydbval := &Ydb.Value{
   484  			Value: &Ydb.Value_TextValue{
   485  				TextValue: v,
   486  			},
   487  		}
   488  		src := []byte(v)
   489  		if c.optional && !c.testDefault {
   490  			vp := &src
   491  
   492  			return ydbval, &vp
   493  		}
   494  
   495  		return ydbval, &src
   496  	case Ydb.Type_DYNUMBER:
   497  		v := strconv.FormatUint(uint64(rv), 10)
   498  		ydbval := &Ydb.Value{
   499  			Value: &Ydb.Value_TextValue{
   500  				TextValue: v,
   501  			},
   502  		}
   503  		if c.optional && !c.testDefault {
   504  			vp := &v
   505  
   506  			return ydbval, &vp
   507  		}
   508  
   509  		return ydbval, &v
   510  	default:
   511  		panic("ydb: unexpected types")
   512  	}
   513  }
   514  
   515  func getResultSet(count int, col []*column) (result *Ydb.ResultSet, testValues [][]indexed.RequiredOrOptional) {
   516  	result = &Ydb.ResultSet{}
   517  	for _, c := range col {
   518  		t := &Ydb.Type{
   519  			Type: &Ydb.Type_TypeId{
   520  				TypeId: c.typeID,
   521  			},
   522  		}
   523  		if c.optional {
   524  			t = &Ydb.Type{
   525  				Type: &Ydb.Type_OptionalType{
   526  					OptionalType: &Ydb.OptionalType{
   527  						Item: t,
   528  					},
   529  				},
   530  			}
   531  		}
   532  		result.Columns = append(
   533  			result.GetColumns(),
   534  			&Ydb.Column{
   535  				Name: c.name,
   536  				Type: t,
   537  			},
   538  		)
   539  	}
   540  
   541  	r := xrand.New(xrand.WithLock())
   542  	testValues = make([][]indexed.RequiredOrOptional, count)
   543  	for i := 0; i < count; i++ {
   544  		var items []*Ydb.Value
   545  		var vals []indexed.RequiredOrOptional
   546  		for j := range result.GetColumns() {
   547  			v, val := valueFromPrimitiveTypeID(col[j], r)
   548  			vals = append(vals, val)
   549  			items = append(items, v)
   550  		}
   551  		result.Rows = append(result.GetRows(), &Ydb.Value{
   552  			Items: items,
   553  		})
   554  		testValues[i] = vals
   555  	}
   556  
   557  	return result, testValues
   558  }
   559  
   560  func TestScanSqlTypes(t *testing.T) {
   561  	s := initScanner()
   562  	for _, test := range scannerData {
   563  		t.Run(test.name, func(t *testing.T) {
   564  			set, expected := getResultSet(test.count, test.columns)
   565  			s.reset(set, test.setColumns...)
   566  			for s.NextRow() {
   567  				if test.columns[0].testDefault {
   568  					if err := s.ScanWithDefaults(func() (values []indexed.Required) {
   569  						for _, v := range test.values {
   570  							values = append(values, v)
   571  						}
   572  
   573  						return values
   574  					}()...); err != nil {
   575  						t.Fatalf("test: %s; error: %s", test.name, err)
   576  					}
   577  				} else {
   578  					if err := s.Scan(test.values...); err != nil {
   579  						t.Fatalf("test: %s; error: %s", test.name, err)
   580  					}
   581  				}
   582  				if test.setColumnIndexes != nil {
   583  					for i, v := range test.setColumnIndexes {
   584  						require.Equal(t, expected[0][v], test.values[i])
   585  					}
   586  				} else {
   587  					require.Equal(t, expected[0], test.values)
   588  				}
   589  				expected = expected[1:]
   590  			}
   591  		})
   592  	}
   593  }
   594  
   595  func TestScanNamed(t *testing.T) {
   596  	s := initScanner()
   597  	or := func(columns []string, i int, defaultValue string) string {
   598  		if columns == nil {
   599  			return defaultValue
   600  		}
   601  
   602  		return columns[i]
   603  	}
   604  	for _, test := range scannerData {
   605  		t.Run(test.name, func(t *testing.T) {
   606  			set, expected := getResultSet(test.count, test.columns)
   607  			s.reset(set)
   608  			for s.NextRow() {
   609  				values := make([]named.Value, 0, len(test.values))
   610  				//nolint:nestif
   611  				if test.columns[0].testDefault {
   612  					for i := range test.values {
   613  						values = append(
   614  							values,
   615  							named.OptionalWithDefault(
   616  								or(test.setColumns, i, test.columns[i].name),
   617  								test.values[i],
   618  							),
   619  						)
   620  					}
   621  					if err := s.ScanNamed(values...); err != nil {
   622  						t.Fatalf("test: %s; error: %s", test.name, err)
   623  					}
   624  				} else {
   625  					for i := range test.values {
   626  						if test.columns[i].optional {
   627  							if test.columns[i].testDefault {
   628  								values = append(
   629  									values,
   630  									named.OptionalWithDefault(
   631  										or(test.setColumns, i, test.columns[i].name),
   632  										test.values[i],
   633  									),
   634  								)
   635  							} else {
   636  								values = append(
   637  									values,
   638  									named.Optional(
   639  										or(test.setColumns, i, test.columns[i].name),
   640  										test.values[i],
   641  									),
   642  								)
   643  							}
   644  						} else {
   645  							values = append(
   646  								values,
   647  								named.Required(
   648  									or(test.setColumns, i, test.columns[i].name),
   649  									test.values[i],
   650  								),
   651  							)
   652  						}
   653  					}
   654  					if err := s.ScanNamed(values...); err != nil {
   655  						t.Fatalf("test: %s; error: %s", test.name, err)
   656  					}
   657  				}
   658  				if test.setColumnIndexes != nil {
   659  					for i, v := range test.setColumnIndexes {
   660  						require.Equal(t, expected[0][v], test.values[i])
   661  					}
   662  				} else {
   663  					require.Equal(t, expected[0], test.values)
   664  				}
   665  				expected = expected[1:]
   666  			}
   667  		})
   668  	}
   669  }
   670  
   671  type jsonUnmarshaller struct {
   672  	bytes []byte
   673  }
   674  
   675  func (json *jsonUnmarshaller) UnmarshalJSON(bytes []byte) error {
   676  	json.bytes = bytes
   677  
   678  	return nil
   679  }
   680  
   681  var _ json.Unmarshaler = &jsonUnmarshaller{}
   682  
   683  func TestScanToJsonUnmarshaller(t *testing.T) {
   684  	s := initScanner()
   685  	for _, test := range []struct {
   686  		name    string
   687  		count   int
   688  		columns []*column
   689  		values  []indexed.RequiredOrOptional
   690  	}{
   691  		{
   692  			name:  "<optional JSONDocument, required JSON> to json.Unmarshaller",
   693  			count: 2,
   694  			columns: []*column{{
   695  				name:     "jsondocument",
   696  				typeID:   Ydb.Type_JSON_DOCUMENT,
   697  				optional: true,
   698  			}, {
   699  				name:   "json",
   700  				typeID: Ydb.Type_JSON,
   701  			}},
   702  			values: []indexed.RequiredOrOptional{
   703  				new(jsonUnmarshaller),
   704  				new(jsonUnmarshaller),
   705  			},
   706  		}, {
   707  			name:  "<required JSONDocument, optional JSON> to json.Unmarshaller",
   708  			count: 2,
   709  			columns: []*column{{
   710  				name:   "jsondocument",
   711  				typeID: Ydb.Type_JSON_DOCUMENT,
   712  			}, {
   713  				name:     "json",
   714  				typeID:   Ydb.Type_JSON,
   715  				optional: true,
   716  			}},
   717  			values: []indexed.RequiredOrOptional{
   718  				new(jsonUnmarshaller),
   719  				new(jsonUnmarshaller),
   720  			},
   721  		}, {
   722  			name:  "<optional JSONDocument, optional JSON> to json.Unmarshaller",
   723  			count: 2,
   724  			columns: []*column{{
   725  				name:     "jsondocument",
   726  				typeID:   Ydb.Type_JSON_DOCUMENT,
   727  				optional: true,
   728  			}, {
   729  				name:     "json",
   730  				typeID:   Ydb.Type_JSON,
   731  				optional: true,
   732  			}},
   733  			values: []indexed.RequiredOrOptional{
   734  				new(jsonUnmarshaller),
   735  				new(jsonUnmarshaller),
   736  			},
   737  		}, {
   738  			name:  "<required JSONDocument, required JSON> to json.Unmarshaller",
   739  			count: 2,
   740  			columns: []*column{{
   741  				name:   "jsondocument",
   742  				typeID: Ydb.Type_JSON_DOCUMENT,
   743  			}, {
   744  				name:   "json",
   745  				typeID: Ydb.Type_JSON,
   746  			}},
   747  			values: []indexed.RequiredOrOptional{
   748  				new(jsonUnmarshaller),
   749  				new(jsonUnmarshaller),
   750  			},
   751  		},
   752  	} {
   753  		set, _ := getResultSet(test.count, test.columns)
   754  		s.reset(set)
   755  		for s.NextRow() {
   756  			values := make([]indexed.RequiredOrOptional, 0, len(test.values))
   757  			for i, col := range test.columns {
   758  				if col.optional {
   759  					values = append(
   760  						values,
   761  						indexed.Optional(
   762  							test.values[i],
   763  						),
   764  					)
   765  				} else {
   766  					values = append(
   767  						values,
   768  						indexed.Required(
   769  							test.values[i],
   770  						),
   771  					)
   772  				}
   773  			}
   774  			if err := s.Scan(values...); err != nil {
   775  				t.Fatalf("test: %s; error: %s", test.name, err)
   776  			}
   777  		}
   778  	}
   779  }