@@ -66,8 +66,8 @@ type replayInfo struct {
66
66
var (
67
67
record = flag .Bool ("record" , false , "record RPCs" )
68
68
69
- newTestClient = func (ctx context.Context , t * testing.T ) * Client {
70
- return newClient (ctx , t , nil )
69
+ newTestClient = func (ctx context.Context , t * testing.T , opts ... option. ClientOption ) * Client {
70
+ return newClient (ctx , t , nil , opts ... )
71
71
}
72
72
testParams map [string ]interface {}
73
73
@@ -109,8 +109,8 @@ func testMain(m *testing.M) int {
109
109
log .Fatalf ("closing recorder: %v" , err )
110
110
}
111
111
}()
112
- newTestClient = func (ctx context.Context , t * testing.T ) * Client {
113
- return newClient (ctx , t , rec .DialOptions ())
112
+ newTestClient = func (ctx context.Context , t * testing.T , opts ... option. ClientOption ) * Client {
113
+ return newClient (ctx , t , rec .DialOptions (), opts ... )
114
114
}
115
115
log .Printf ("recording to %s" , replayFilename )
116
116
}
@@ -172,7 +172,7 @@ func initReplay() {
172
172
log .Fatal (err )
173
173
}
174
174
175
- newTestClient = func (ctx context.Context , t * testing.T ) * Client {
175
+ newTestClient = func (ctx context.Context , t * testing.T , opts ... option. ClientOption ) * Client {
176
176
grpcHeadersEnforcer := & testutil.HeadersEnforcer {
177
177
OnFailure : t .Fatalf ,
178
178
Checkers : []* testutil.HeaderChecker {
@@ -181,7 +181,8 @@ func initReplay() {
181
181
},
182
182
}
183
183
184
- opts := append (grpcHeadersEnforcer .CallOptions (), option .WithGRPCConn (conn ))
184
+ opts = append (opts , grpcHeadersEnforcer .CallOptions ()... )
185
+ opts = append (opts , option .WithGRPCConn (conn ))
185
186
client , err := NewClientWithDatabase (ctx , ri .ProjectID , testParams ["databaseID" ].(string ), opts ... )
186
187
if err != nil {
187
188
t .Fatalf ("NewClientWithDatabase: %v" , err )
@@ -191,7 +192,7 @@ func initReplay() {
191
192
log .Printf ("replaying from %s" , replayFilename )
192
193
}
193
194
194
- func newClient (ctx context.Context , t * testing.T , dialOpts []grpc.DialOption ) * Client {
195
+ func newClient (ctx context.Context , t * testing.T , dialOpts []grpc.DialOption , opts ... option. ClientOption ) * Client {
195
196
if testing .Short () {
196
197
t .Skip ("Integration tests skipped in short mode" )
197
198
}
@@ -207,7 +208,8 @@ func newClient(ctx context.Context, t *testing.T, dialOpts []grpc.DialOption) *C
207
208
xGoogReqParamsHeaderChecker ,
208
209
},
209
210
}
210
- opts := append (grpcHeadersEnforcer .CallOptions (), option .WithTokenSource (ts ))
211
+ opts = append (opts , grpcHeadersEnforcer .CallOptions ()... )
212
+ opts = append (opts , option .WithTokenSource (ts ))
211
213
for _ , opt := range dialOpts {
212
214
opts = append (opts , option .WithGRPCDialOption (opt ))
213
215
}
@@ -264,6 +266,130 @@ func TestIntegration_Basics(t *testing.T) {
264
266
}
265
267
}
266
268
269
+ type OldX struct {
270
+ I int
271
+ J int
272
+ }
273
+ type NewX struct {
274
+ I int
275
+ j int
276
+ }
277
+
278
+ func TestIntegration_IgnoreFieldMismatch (t * testing.T ) {
279
+ ctx := context .Background ()
280
+ client := newTestClient (ctx , t , WithIgnoreFieldMismatch ())
281
+ t .Cleanup (func () {
282
+ client .Close ()
283
+ })
284
+
285
+ // Save entities with an extra field
286
+ keys := []* Key {
287
+ NameKey ("X" , "x1" , nil ),
288
+ NameKey ("X" , "x2" , nil ),
289
+ }
290
+ entitiesOld := []OldX {
291
+ {I : 10 , J : 20 },
292
+ {I : 30 , J : 40 },
293
+ }
294
+ _ , gotErr := client .PutMulti (ctx , keys , entitiesOld )
295
+ if gotErr != nil {
296
+ t .Fatalf ("Failed to save: %v\n " , gotErr )
297
+ }
298
+
299
+ var wants []NewX
300
+ for _ , oldX := range entitiesOld {
301
+ wants = append (wants , []NewX {{I : oldX .I }}... )
302
+ }
303
+
304
+ t .Cleanup (func () {
305
+ client .DeleteMulti (ctx , keys )
306
+ })
307
+
308
+ tests := []struct {
309
+ desc string
310
+ client * Client
311
+ wantErr error
312
+ }{
313
+ {
314
+ desc : "Without IgnoreFieldMismatch option" ,
315
+ client : newTestClient (ctx , t ),
316
+ wantErr : & ErrFieldMismatch {
317
+ StructType : reflect .TypeOf (NewX {}),
318
+ FieldName : "J" ,
319
+ Reason : "no such struct field" ,
320
+ },
321
+ },
322
+ {
323
+ desc : "With IgnoreFieldMismatch option" ,
324
+ client : newTestClient (ctx , t , WithIgnoreFieldMismatch ()),
325
+ },
326
+ }
327
+ for _ , test := range tests {
328
+ t .Run (test .desc , func (t * testing.T ) {
329
+ defer test .client .Close ()
330
+ // FieldMismatch error in Next
331
+ query := NewQuery ("X" ).FilterField ("I" , ">=" , 10 )
332
+ it := test .client .Run (ctx , query )
333
+ resIndex := 0
334
+ for {
335
+ var newX NewX
336
+ _ , err := it .Next (& newX )
337
+ if err == iterator .Done {
338
+ break
339
+ }
340
+
341
+ compareIgnoreFieldMismatchResults (t , []NewX {wants [resIndex ]}, []NewX {newX }, test .wantErr , err , "Next" )
342
+ resIndex ++
343
+ }
344
+
345
+ // FieldMismatch error in Get
346
+ var getX NewX
347
+ gotErr = test .client .Get (ctx , keys [0 ], & getX )
348
+ compareIgnoreFieldMismatchResults (t , []NewX {wants [0 ]}, []NewX {getX }, test .wantErr , gotErr , "Get" )
349
+
350
+ // FieldMismatch error in GetAll
351
+ var getAllX []NewX
352
+ _ , gotErr = test .client .GetAll (ctx , query , & getAllX )
353
+ compareIgnoreFieldMismatchResults (t , wants , getAllX , test .wantErr , gotErr , "GetAll" )
354
+
355
+ // FieldMismatch error in GetMulti
356
+ getMultiX := make ([]NewX , len (keys ))
357
+ gotErr = test .client .GetMulti (ctx , keys , getMultiX )
358
+ compareIgnoreFieldMismatchResults (t , wants , getMultiX , test .wantErr , gotErr , "GetMulti" )
359
+
360
+ tx , err := test .client .NewTransaction (ctx )
361
+ if err != nil {
362
+ t .Fatalf ("tx.GetMulti got: %v, want: nil\n " , err )
363
+ }
364
+
365
+ // FieldMismatch error in tx.Get
366
+ var txGetX NewX
367
+ err = tx .Get (keys [0 ], & txGetX )
368
+ compareIgnoreFieldMismatchResults (t , []NewX {wants [0 ]}, []NewX {txGetX }, test .wantErr , err , "tx.Get" )
369
+
370
+ // FieldMismatch error in tx.GetMulti
371
+ txGetMultiX := make ([]NewX , len (keys ))
372
+ err = tx .GetMulti (keys , txGetMultiX )
373
+ compareIgnoreFieldMismatchResults (t , wants , txGetMultiX , test .wantErr , err , "tx.GetMulti" )
374
+
375
+ tx .Commit ()
376
+
377
+ })
378
+ }
379
+
380
+ }
381
+
382
+ func compareIgnoreFieldMismatchResults (t * testing.T , wantX []NewX , gotX []NewX , wantErr error , gotErr error , errPrefix string ) {
383
+ if ! equalErrs (gotErr , wantErr ) {
384
+ t .Errorf ("%v: error got: %v, want: %v" , errPrefix , gotErr , wantErr )
385
+ }
386
+ for resIndex := 0 ; resIndex < len (wantX ) && gotErr == nil ; resIndex ++ {
387
+ if wantX [resIndex ].I != gotX [resIndex ].I {
388
+ t .Fatalf ("%v %v: got: %v, want: %v\n " , errPrefix , resIndex , wantX [resIndex ].I , gotX [resIndex ].I )
389
+ }
390
+ }
391
+ }
392
+
267
393
func TestIntegration_GetWithReadTime (t * testing.T ) {
268
394
ctx , cancel := context .WithTimeout (context .Background (), time .Second * 20 )
269
395
client := newTestClient (ctx , t )
0 commit comments