github.com/go-generalize/volcago@v1.7.0/generator/testfiles/not_auto/tests/model_test.go (about)

     1  //go:build internal
     2  // +build internal
     3  
     4  package tests
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"os"
    10  	"reflect"
    11  	"testing"
    12  	"time"
    13  
    14  	"cloud.google.com/go/firestore"
    15  	model "github.com/go-generalize/volcago/generator/testfiles/not_auto"
    16  	"golang.org/x/xerrors"
    17  	"google.golang.org/genproto/googleapis/type/latlng"
    18  )
    19  
    20  var desc = "Hello, World!"
    21  
    22  func initFirestoreClient(t *testing.T) *firestore.Client {
    23  	t.Helper()
    24  
    25  	if os.Getenv("FIRESTORE_EMULATOR_HOST") == "" {
    26  		os.Setenv("FIRESTORE_EMULATOR_HOST", "localhost:8000")
    27  	}
    28  
    29  	if os.Getenv("FIRESTORE_PROJECT_ID") == "" {
    30  		os.Setenv("FIRESTORE_PROJECT_ID", "project-id-in-google2")
    31  	}
    32  
    33  	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    34  	defer cancel()
    35  
    36  	client, err := firestore.NewClient(ctx, os.Getenv("FIRESTORE_PROJECT_ID"))
    37  
    38  	if err != nil {
    39  		t.Fatalf("failed to initialize firestore client: %+v", err)
    40  	}
    41  
    42  	return client
    43  }
    44  
    45  func compareTask(t *testing.T, expected, actual *model.Task) {
    46  	t.Helper()
    47  
    48  	if actual.Identity != expected.Identity {
    49  		t.Fatalf("unexpected identity: %s (expected: %s)", actual.Identity, expected.Identity)
    50  	}
    51  
    52  	if !actual.Created.Equal(expected.Created) {
    53  		t.Fatalf("unexpected time: %s(expected: %s)", actual.Created, expected.Created)
    54  	}
    55  
    56  	if actual.Desc != expected.Desc {
    57  		t.Fatalf("unexpected desc: %s(expected: %s)", actual.Desc, expected.Created)
    58  	}
    59  
    60  	if actual.Done != expected.Done {
    61  		t.Fatalf("unexpected done: %v(expected: %v)", actual.Done, expected.Done)
    62  	}
    63  }
    64  
    65  type uniqueError struct{}
    66  
    67  func newUniqueError() model.UniqueRepositoryMiddleware {
    68  	return &uniqueError{}
    69  }
    70  
    71  func (e *uniqueError) WrapError(_ context.Context, err error, _ []*model.Unique) error {
    72  	// processing
    73  	return xerrors.Errorf("WrapError: %w", err)
    74  }
    75  
    76  func TestFirestore(t *testing.T) {
    77  	client := initFirestoreClient(t)
    78  
    79  	taskRepo := model.NewTaskRepository(client)
    80  
    81  	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    82  	var ids []string
    83  	defer func() {
    84  		defer cancel()
    85  		if err := taskRepo.DeleteMultiByIdentities(ctx, ids); err != nil {
    86  			t.Fatal(err)
    87  		}
    88  	}()
    89  
    90  	ctx = context.WithValue(ctx, model.UniqueMiddlewareKey{}, newUniqueError())
    91  
    92  	now := time.Unix(0, time.Now().UnixNano()).UTC()
    93  
    94  	t.Run("Multi", func(tr *testing.T) {
    95  		tks := make([]*model.Task, 0)
    96  		for i := int64(1); i <= 10; i++ {
    97  			tk := &model.Task{
    98  				Identity:   fmt.Sprintf("Task_%d", i),
    99  				Desc:       fmt.Sprintf("%s%d", desc, i),
   100  				Created:    now,
   101  				Done:       true,
   102  				Done2:      false,
   103  				Count:      int(i),
   104  				Count64:    0,
   105  				Proportion: 0.12345 + float64(i),
   106  				NameList:   []string{"a", "b", "c"},
   107  				Flag:       model.Flag(true),
   108  			}
   109  			tks = append(tks, tk)
   110  		}
   111  		idList, err := taskRepo.InsertMulti(ctx, tks)
   112  		if err != nil {
   113  			tr.Fatalf("%+v", err)
   114  		}
   115  		ids = append(ids, idList...)
   116  
   117  		tks2 := make([]*model.Task, 0)
   118  		for i := int64(1); i <= 10; i++ {
   119  			tk := &model.Task{
   120  				Identity:   ids[i-1],
   121  				Desc:       fmt.Sprintf("%s%d", desc, i),
   122  				Created:    now,
   123  				Done:       true,
   124  				Done2:      false,
   125  				Count:      int(i),
   126  				Count64:    i,
   127  				Proportion: 0.12345 + float64(i),
   128  				NameList:   []string{"a", "b", "c"},
   129  				Flag:       model.Flag(true),
   130  			}
   131  			tks2 = append(tks2, tk)
   132  		}
   133  		if err = taskRepo.UpdateMulti(ctx, tks2); err != nil {
   134  			tr.Fatalf("%+v", err)
   135  		}
   136  
   137  		if tks[0].Identity != tks2[0].Identity {
   138  			tr.Fatalf("unexpected identity: %s (expected: %s)", tks[0].Identity, tks2[0].Identity)
   139  		}
   140  	})
   141  
   142  	t.Run("Single", func(tr *testing.T) {
   143  		tk := &model.Task{
   144  			Identity:   "Single",
   145  			Desc:       fmt.Sprintf("%s%d", desc, 1001),
   146  			Created:    now,
   147  			Done:       true,
   148  			Done2:      false,
   149  			Count:      11,
   150  			Count64:    11,
   151  			Proportion: 11.12345,
   152  			NameList:   []string{"a", "b", "c"},
   153  			Flag:       model.Flag(true),
   154  		}
   155  		id, err := taskRepo.Insert(ctx, tk)
   156  		if err != nil {
   157  			tr.Fatalf("%+v", err)
   158  		}
   159  		ids = append(ids, id)
   160  
   161  		tr.Run("SubCollection", func(tr *testing.T) {
   162  			ids2 := make([]string, 0, 3)
   163  			doc := taskRepo.GetDocRef(id)
   164  			subRepo := model.NewSubTaskRepository(client, doc)
   165  			defer func() {
   166  				if err = subRepo.DeleteMultiByIDs(ctx, ids2); err != nil {
   167  					tr.Fatalf("%+v", err)
   168  				}
   169  			}()
   170  			st := &model.SubTask{IsSubCollection: true}
   171  			id, err = subRepo.Insert(ctx, st)
   172  			if err != nil {
   173  				tr.Fatalf("%+v", err)
   174  			}
   175  			ids2 = append(ids2, id)
   176  
   177  			sts := []*model.SubTask{
   178  				{IsSubCollection: true},
   179  				{IsSubCollection: false},
   180  			}
   181  			stsIDs, er := subRepo.InsertMulti(ctx, sts)
   182  			if er != nil {
   183  				tr.Fatalf("%+v", er)
   184  			}
   185  			ids2 = append(ids2, stsIDs...)
   186  
   187  			param := &model.SubTaskSearchParam{IsSubCollection: model.NewQueryChainer().Equal(true)}
   188  			sts, err = subRepo.Search(ctx, param, nil)
   189  			if err != nil {
   190  				tr.Fatalf("%+v", err)
   191  			}
   192  
   193  			if len(sts) != 2 {
   194  				tr.Fatal("not match")
   195  			}
   196  
   197  			tr.Run("CollectionGroup", func(ttrr *testing.T) {
   198  				sts, err = model.NewSubTaskCollectionGroupRepository(client).Search(ctx, param, nil)
   199  				if err != nil {
   200  					tr.Fatalf("%+v", err)
   201  				}
   202  
   203  				if len(sts) != 2 {
   204  					tr.Fatal("not match")
   205  				}
   206  			})
   207  
   208  			tr.Run("Reference", func(tr2 *testing.T) {
   209  				tk.Sub = subRepo.GetDocRef(sts[1].ID)
   210  				if err = taskRepo.Update(ctx, tk); err != nil {
   211  					tr2.Fatalf("%+v", err)
   212  				}
   213  
   214  				tkr, er := taskRepo.Get(ctx, doc.ID)
   215  				if er != nil {
   216  					tr2.Fatalf("%+v", er)
   217  				}
   218  
   219  				sub, er := subRepo.GetWithDoc(ctx, tkr.Sub)
   220  				if er != nil {
   221  					tr2.Fatalf("%+v", er)
   222  				}
   223  
   224  				if sub.ID != sts[1].ID {
   225  					tr2.Fatal("not match")
   226  				}
   227  
   228  				taskSearchParam := &model.TaskSearchParam{Sub: model.NewQueryChainer().Equal(tk.Sub)}
   229  				tks, er := taskRepo.Search(ctx, taskSearchParam, nil)
   230  				if er != nil {
   231  					tr2.Fatalf("%+v", er)
   232  				}
   233  				if len(tks) != 1 {
   234  					tr2.Fatal("not match")
   235  				}
   236  			})
   237  		})
   238  
   239  		tk.Count++
   240  		if err = taskRepo.Update(ctx, tk); err != nil {
   241  			tr.Fatalf("%+v", err)
   242  		}
   243  
   244  		tsk, err := taskRepo.Get(ctx, tk.Identity)
   245  		if err != nil {
   246  			tr.Fatalf("%+v", err)
   247  		}
   248  
   249  		if tsk.Count != 12 {
   250  			tr.Fatalf("unexpected Count: %d (expected: %d)", tsk.Count, 12)
   251  		}
   252  
   253  		tr.Run("UpdateBuilder", func(ttr *testing.T) {
   254  			desc1002 := fmt.Sprintf("%s%d", desc, 1002)
   255  
   256  			updateParam := &model.TaskUpdateParam{
   257  				Desc2:      desc1002,
   258  				Created:    firestore.ServerTimestamp,
   259  				Done:       false,
   260  				Count:      firestore.Increment(1),
   261  				Count64:    firestore.Increment(2),
   262  				Proportion: firestore.Increment(0.1),
   263  			}
   264  
   265  			if err = taskRepo.StrictUpdate(ctx, tsk.Identity, updateParam); err != nil {
   266  				ttr.Fatalf("%+v", err)
   267  			}
   268  
   269  			tsk, err = taskRepo.Get(ctx, tk.Identity)
   270  			if err != nil {
   271  				ttr.Fatalf("%+v", err)
   272  			}
   273  
   274  			if tsk.Desc2 != desc1002 {
   275  				ttr.Fatalf("unexpected Desc: %s (expected: %s)", tsk.Desc, desc1002)
   276  			}
   277  
   278  			if tsk.Created.Before(now) {
   279  				ttr.Fatalf("unexpected Created > now: %t (expected: %t)", tsk.Created.Before(now), tsk.Created.After(now))
   280  			}
   281  
   282  			if tsk.Done {
   283  				ttr.Fatalf("unexpected Done: %t (expected: %t)", tsk.Done, false)
   284  			}
   285  
   286  			if tsk.Count != 13 {
   287  				ttr.Fatalf("unexpected Count: %d (expected: %d)", tsk.Count, 13)
   288  			}
   289  
   290  			if tsk.Count64 != 13 {
   291  				ttr.Fatalf("unexpected Count64: %d (expected: %d)", tsk.Count64, 13)
   292  			}
   293  
   294  			if tsk.Proportion != 11.22345 {
   295  				ttr.Fatalf("unexpected Proportion: %g (expected: %g)", tsk.Proportion, 11.22345)
   296  			}
   297  		})
   298  
   299  		tr.Run("UniqueConstraints", func(ttrr *testing.T) {
   300  			tk = &model.Task{
   301  				Identity:   "Single",
   302  				Desc:       fmt.Sprintf("%s%d", desc, 1001),
   303  				Created:    now,
   304  				Done:       true,
   305  				Done2:      false,
   306  				Count:      11,
   307  				Count64:    11,
   308  				Proportion: 11.12345,
   309  				NameList:   []string{"a", "b", "c"},
   310  				Flag:       true,
   311  			}
   312  			if _, err = taskRepo.Insert(ctx, tk); err == nil {
   313  				ttrr.Fatalf("expected err != nil")
   314  			} else if !xerrors.Is(err, model.ErrUniqueConstraint) {
   315  				fmt.Printf("[CheckUnique] Fin %+v\n", tk)
   316  				ttrr.Fatalf("expected err == ErrUniqueConstraint: %+v", err)
   317  			}
   318  
   319  			// Check if the documents in the Unique collection can be deleted.
   320  			if err = taskRepo.DeleteByIdentity(ctx, tk.Identity, model.DeleteOption{Mode: model.DeleteModeSoft}); err != nil {
   321  				ttrr.Fatalf("unexpected err != nil: %+v", err)
   322  			}
   323  
   324  			if _, err = taskRepo.Insert(ctx, tk); err != nil {
   325  				ttrr.Fatalf("unexpected error: %+v", err)
   326  			}
   327  		})
   328  
   329  		tr.Run("GetByXXX", func(ttr *testing.T) {
   330  			if _, err := taskRepo.GetByDesc(ctx, fmt.Sprintf("%s%d", desc, 1001)); err != nil {
   331  				ttr.Fatalf("%+v", err)
   332  			}
   333  		})
   334  	})
   335  }
   336  
   337  func TestFirestoreTransaction_Single(t *testing.T) {
   338  	client := initFirestoreClient(t)
   339  
   340  	taskRepo := model.NewTaskRepository(client)
   341  
   342  	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
   343  	var ids []string
   344  	defer func() {
   345  		defer cancel()
   346  		if err := taskRepo.DeleteMultiByIdentities(ctx, ids); err != nil {
   347  			t.Fatal(err)
   348  		}
   349  	}()
   350  
   351  	now := time.Unix(0, time.Now().UnixNano())
   352  	latLng := &latlng.LatLng{
   353  		Latitude:  35.678803,
   354  		Longitude: 139.756263,
   355  	}
   356  
   357  	t.Run("Insert", func(tr *testing.T) {
   358  		if err := client.RunTransaction(ctx, func(cx context.Context, tx *firestore.Transaction) error {
   359  			tk := &model.Task{
   360  				Identity:   "identity",
   361  				Desc:       fmt.Sprintf("%s01", desc),
   362  				Created:    now,
   363  				Done:       true,
   364  				Done2:      false,
   365  				Count:      10,
   366  				Count64:    11,
   367  				NameList:   []string{"a", "b", "c"},
   368  				Proportion: 0.12345 + 11,
   369  				Geo:        latLng,
   370  				Flag:       true,
   371  			}
   372  
   373  			id, err := taskRepo.InsertWithTx(cx, tx, tk)
   374  			if err != nil {
   375  				return err
   376  			}
   377  
   378  			ids = append(ids, id)
   379  			return nil
   380  		}); err != nil {
   381  			tr.Fatalf("error: %+v", err)
   382  		}
   383  
   384  		tsk, err := taskRepo.Get(ctx, ids[len(ids)-1])
   385  		if err != nil {
   386  			tr.Fatalf("%+v", err)
   387  		}
   388  
   389  		if reflect.DeepEqual(tsk.Geo, latLng) {
   390  			tr.Fatalf("unexpected Geo: %+v (expected: %+v)", tsk.Geo, latLng)
   391  		}
   392  	})
   393  
   394  	t.Run("Update", func(tr *testing.T) {
   395  		id := ids[len(ids)-1]
   396  		if err := client.RunTransaction(ctx, func(cx context.Context, tx *firestore.Transaction) error {
   397  			tk, err := taskRepo.GetWithTx(tx, id)
   398  			if err != nil {
   399  				return err
   400  			}
   401  
   402  			if tk.Count != 10 {
   403  				return fmt.Errorf("unexpected Count: %d (expected: %d)", tk.Count, 10)
   404  			}
   405  
   406  			tk.Count = 11
   407  			if err = taskRepo.UpdateWithTx(cx, tx, tk); err != nil {
   408  				return err
   409  			}
   410  
   411  			return nil
   412  		}); err != nil {
   413  			tr.Fatalf("error: %+v", err)
   414  		}
   415  
   416  		tsk, err := taskRepo.Get(ctx, id)
   417  		if err != nil {
   418  			tr.Fatalf("%+v", err)
   419  		}
   420  
   421  		if tsk.Count != 11 {
   422  			tr.Fatalf("unexpected Count: %d (expected: %d)", tsk.Count, 11)
   423  		}
   424  	})
   425  
   426  	t.Run("UseUpdateBuilder", func(tr *testing.T) {
   427  		tkID := ids[len(ids)-1]
   428  		desc1002 := fmt.Sprintf("%s%d", desc, 1002)
   429  		err := client.RunTransaction(ctx, func(cx context.Context, tx *firestore.Transaction) error {
   430  			tk, err := taskRepo.GetWithTx(tx, tkID)
   431  			if err != nil {
   432  				return err
   433  			}
   434  
   435  			if tk.Count != 11 {
   436  				return fmt.Errorf("unexpected Count: %d (expected: %d)", tk.Count, 11)
   437  			}
   438  
   439  			updateParam := &model.TaskUpdateParam{
   440  				Desc2:      desc1002,
   441  				Created:    firestore.ServerTimestamp,
   442  				Done:       false,
   443  				Count:      firestore.Increment(1),
   444  				Count64:    firestore.Increment(2),
   445  				Proportion: firestore.Increment(0.1),
   446  			}
   447  			if err = taskRepo.StrictUpdateWithTx(tx, tk.Identity, updateParam); err != nil {
   448  				return err
   449  			}
   450  			return nil
   451  		})
   452  
   453  		if err != nil {
   454  			tr.Fatalf("error: %+v", err)
   455  		}
   456  
   457  		tsk, err := taskRepo.Get(ctx, tkID)
   458  		if err != nil {
   459  			tr.Fatalf("%+v", err)
   460  		}
   461  
   462  		if tsk.Desc2 != desc1002 {
   463  			tr.Fatalf("unexpected Desc: %s (expected: %s)", tsk.Desc, desc1002)
   464  		}
   465  
   466  		if tsk.Created.Before(now) {
   467  			tr.Fatalf("unexpected Created > now: %t (expected: %t)", tsk.Created.Before(now), tsk.Created.After(now))
   468  		}
   469  
   470  		if tsk.Done {
   471  			tr.Fatalf("unexpected Done: %t (expected: %t)", tsk.Done, false)
   472  		}
   473  
   474  		if tsk.Count != 12 {
   475  			tr.Fatalf("unexpected Count: %d (expected: %d)", tsk.Count, 12)
   476  		}
   477  
   478  		if tsk.Count64 != 13 {
   479  			tr.Fatalf("unexpected Count64: %d (expected: %d)", tsk.Count64, 13)
   480  		}
   481  
   482  		if tsk.Proportion != 11.22345 {
   483  			tr.Fatalf("unexpected Proportion: %g (expected: %g)", tsk.Proportion, 11.22345)
   484  		}
   485  	})
   486  
   487  	t.Run("UniqueConstraints", func(tr *testing.T) {
   488  		if err := client.RunTransaction(ctx, func(cx context.Context, tx *firestore.Transaction) error {
   489  			tk := &model.Task{
   490  				Identity:   "identity",
   491  				Desc:       fmt.Sprintf("%s01", desc),
   492  				Created:    now,
   493  				Done:       true,
   494  				Done2:      false,
   495  				Count:      10,
   496  				Count64:    11,
   497  				NameList:   []string{"a", "b", "c"},
   498  				Proportion: 0.12345 + 11,
   499  				Geo:        latLng,
   500  				Flag:       true,
   501  			}
   502  
   503  			if _, err := taskRepo.InsertWithTx(cx, tx, tk); err != nil {
   504  				return err
   505  			}
   506  
   507  			return nil
   508  		}); err == nil {
   509  			tr.Fatalf("unexpected err != nil")
   510  		} else if !xerrors.Is(err, model.ErrUniqueConstraint) {
   511  			tr.Fatalf("unexpected err == ErrUniqueConstraint err:%+v", err)
   512  		}
   513  	})
   514  
   515  	t.Run("GetByXXXWithTx", func(tr *testing.T) {
   516  		if err := client.RunTransaction(ctx, func(cx context.Context, tx *firestore.Transaction) error {
   517  			if _, err := taskRepo.GetByDescWithTx(tx, fmt.Sprintf("%s01", desc)); err != nil {
   518  				return err
   519  			}
   520  
   521  			return nil
   522  		}); err != nil {
   523  			tr.Fatalf("error: %+v", err)
   524  		}
   525  	})
   526  }
   527  
   528  func TestFirestoreTransaction_Multi(t *testing.T) {
   529  	client := initFirestoreClient(t)
   530  
   531  	taskRepo := model.NewTaskRepository(client)
   532  
   533  	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
   534  	var ids []string
   535  	defer func() {
   536  		defer cancel()
   537  		if err := taskRepo.DeleteMultiByIdentities(ctx, ids); err != nil {
   538  			t.Fatal(err)
   539  		}
   540  	}()
   541  
   542  	now := time.Unix(0, time.Now().UnixNano())
   543  	latLng := &latlng.LatLng{
   544  		Latitude:  35.678803,
   545  		Longitude: 139.756263,
   546  	}
   547  
   548  	tks := make([]*model.Task, 0)
   549  	t.Run("InsertMulti", func(tr *testing.T) {
   550  		for i := int64(1); i <= 10; i++ {
   551  			tk := &model.Task{
   552  				Identity:   fmt.Sprintf("Task_%d", i),
   553  				Desc:       fmt.Sprintf("%s%d", desc, i),
   554  				Created:    now,
   555  				Done:       true,
   556  				Done2:      false,
   557  				Count:      int(i),
   558  				Count64:    0,
   559  				NameList:   []string{"a", "b", "c"},
   560  				Proportion: 0.12345 + float64(i),
   561  				Geo:        latLng,
   562  				Flag:       model.Flag(true),
   563  			}
   564  			tks = append(tks, tk)
   565  		}
   566  
   567  		if err := client.RunTransaction(ctx, func(cx context.Context, tx *firestore.Transaction) error {
   568  			idList, err := taskRepo.InsertMultiWithTx(ctx, tx, tks)
   569  			if err != nil {
   570  				return err
   571  			}
   572  			ids = append(ids, idList...)
   573  			return nil
   574  		}); err != nil {
   575  			tr.Fatalf("error: %+v", err)
   576  		}
   577  	})
   578  
   579  	t.Run("UniqueConstraints", func(tr *testing.T) {
   580  		tks2 := make([]*model.Task, 10)
   581  		for i := int64(1); i <= 10; i++ {
   582  			tks2[i-1] = &model.Task{
   583  				Identity:   ids[i-1],
   584  				Desc:       fmt.Sprintf("%s%d", desc, i+1),
   585  				Created:    now,
   586  				Done:       false,
   587  				Done2:      true,
   588  				Count:      int(i),
   589  				Count64:    i,
   590  				NameList:   []string{"a", "b", "c"},
   591  				Proportion: 0.12345 + float64(i),
   592  				Geo:        latLng,
   593  				Flag:       true,
   594  			}
   595  		}
   596  
   597  		if err := client.RunTransaction(ctx, func(cx context.Context, tx *firestore.Transaction) error {
   598  			if err := taskRepo.UpdateMultiWithTx(cx, tx, tks2); err != nil {
   599  				return err
   600  			}
   601  			return nil
   602  		}); err == nil {
   603  			tr.Fatalf("unexpected err != nil")
   604  		} else if !xerrors.Is(err, model.ErrUniqueConstraint) {
   605  			tr.Fatalf("unexpected err == ErrUniqueConstraint err:%+v", err)
   606  		}
   607  	})
   608  
   609  	t.Run("UpdateMulti", func(tr *testing.T) {
   610  		tks2 := make([]*model.Task, 0)
   611  		for i := int64(1); i <= 10; i++ {
   612  			tk := &model.Task{
   613  				Identity:   ids[i-1],
   614  				Desc:       fmt.Sprintf("%s%d", desc, i+10),
   615  				Created:    now,
   616  				Done:       false,
   617  				Done2:      true,
   618  				Count:      int(i),
   619  				Count64:    i,
   620  				NameList:   []string{"a", "b", "c"},
   621  				Proportion: 0.12345 + float64(i),
   622  				Geo:        latLng,
   623  				Flag:       model.Flag(true),
   624  			}
   625  			tks2 = append(tks2, tk)
   626  		}
   627  
   628  		if err := client.RunTransaction(ctx, func(cx context.Context, tx *firestore.Transaction) error {
   629  			if err := taskRepo.UpdateMultiWithTx(cx, tx, tks2); err != nil {
   630  				return err
   631  			}
   632  			return nil
   633  		}); err != nil {
   634  			tr.Fatalf("error: %+v", err)
   635  		}
   636  
   637  		if tks[0].Identity != tks2[0].Identity {
   638  			tr.Fatalf("unexpected identity: %s (expected: %s)", tks[0].Identity, tks2[0].Identity)
   639  		}
   640  	})
   641  }
   642  
   643  func TestFirestoreQuery(t *testing.T) {
   644  	client := initFirestoreClient(t)
   645  
   646  	taskRepo := model.NewTaskRepository(client)
   647  
   648  	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
   649  	var ids []string
   650  	defer func() {
   651  		defer cancel()
   652  		if err := taskRepo.DeleteMultiByIdentities(ctx, ids); err != nil {
   653  			t.Fatalf("%+v\n", err)
   654  		}
   655  	}()
   656  
   657  	now := time.Unix(0, time.Now().UnixNano())
   658  	latLng := &latlng.LatLng{
   659  		Latitude:  35.678803,
   660  		Longitude: 139.756263,
   661  	}
   662  	skyTreeLatLng := &latlng.LatLng{
   663  		Latitude:  35.7100069,
   664  		Longitude: 139.8108103,
   665  	}
   666  
   667  	tks := make([]*model.Task, 0)
   668  	for i := 1; i <= 9; i++ {
   669  		tk := &model.Task{
   670  			Identity:     fmt.Sprintf("%d", i),
   671  			Desc:         fmt.Sprintf("%s%d", desc, i),
   672  			Created:      now,
   673  			ReservedDate: &now,
   674  			Done:         true,
   675  			Done2:        false,
   676  			Count:        i,
   677  			Count64:      int64(i),
   678  			NameList:     []string{"a", "b", "c"},
   679  			Proportion:   0.12345 + float64(i),
   680  			Geo:          latLng,
   681  			Flag:         model.Flag(true),
   682  		}
   683  		tks = append(tks, tk)
   684  	}
   685  
   686  	{
   687  		tk := &model.Task{
   688  			Identity:   fmt.Sprintf("%d", 10),
   689  			Desc:       fmt.Sprintf("%s%d", desc, 10),
   690  			Created:    now,
   691  			Done:       true,
   692  			Done2:      false,
   693  			Count:      10,
   694  			Count64:    10,
   695  			NameList:   []string{"a", "b", "c"},
   696  			Proportion: 10.12345,
   697  			Geo:        skyTreeLatLng,
   698  			Flag:       model.Flag(true),
   699  		}
   700  		tks = append(tks, tk)
   701  	}
   702  
   703  	ids, err := taskRepo.InsertMulti(ctx, tks)
   704  	if err != nil {
   705  		t.Fatalf("%+v", err)
   706  	}
   707  
   708  	t.Run("int(1件)", func(t *testing.T) {
   709  		param := &model.TaskSearchParam{
   710  			Count: model.NewQueryChainer().Equal(1),
   711  		}
   712  
   713  		tasks, err := taskRepo.Search(ctx, param, nil)
   714  		if err != nil {
   715  			t.Fatalf("%+v", err)
   716  		}
   717  
   718  		if len(tasks) != 1 {
   719  			t.Fatal("not match")
   720  		}
   721  	})
   722  
   723  	t.Run("int64(6件)", func(tr *testing.T) {
   724  		param := &model.TaskSearchParam{
   725  			Count64: model.NewQueryChainer().GreaterThanOrEqual(5),
   726  		}
   727  
   728  		tasks, err := taskRepo.Search(ctx, param, nil)
   729  		if err != nil {
   730  			tr.Fatalf("%+v", err)
   731  		}
   732  
   733  		if len(tasks) != 6 {
   734  			tr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 6)
   735  		}
   736  	})
   737  
   738  	t.Run("float(1件)", func(t *testing.T) {
   739  		param := &model.TaskSearchParam{
   740  			Proportion: model.NewQueryChainer().Equal(1.12345),
   741  		}
   742  
   743  		tasks, err := taskRepo.Search(ctx, param, nil)
   744  		if err != nil {
   745  			t.Fatalf("%+v", err)
   746  		}
   747  
   748  		if len(tasks) != 1 {
   749  			t.Fatal("not match")
   750  		}
   751  	})
   752  
   753  	t.Run("bool(10件)", func(t *testing.T) {
   754  		param := &model.TaskSearchParam{
   755  			Done: model.NewQueryChainer().Equal(true),
   756  		}
   757  
   758  		tasks, err := taskRepo.Search(ctx, param, nil)
   759  		if err != nil {
   760  			t.Fatalf("%+v", err)
   761  		}
   762  
   763  		if len(tasks) != 10 {
   764  			t.Fatal("not match")
   765  		}
   766  	})
   767  
   768  	t.Run("time.Time(10件)", func(t *testing.T) {
   769  		param := &model.TaskSearchParam{
   770  			Created: model.NewQueryChainer().Equal(now),
   771  		}
   772  
   773  		tasks, err := taskRepo.Search(ctx, param, nil)
   774  		if err != nil {
   775  			t.Fatalf("%+v", err)
   776  		}
   777  
   778  		if len(tasks) != 10 {
   779  			t.Fatal("not match")
   780  		}
   781  	})
   782  
   783  	t.Run("*time.Time(9件)", func(t *testing.T) {
   784  		param := &model.TaskSearchParam{
   785  			ReservedDate: model.NewQueryChainer().Equal(&now),
   786  		}
   787  
   788  		tasks, err := taskRepo.Search(ctx, param, nil)
   789  		if err != nil {
   790  			t.Fatalf("%+v", err)
   791  		}
   792  
   793  		if len(tasks) != 9 {
   794  			t.Fatal("not match")
   795  		}
   796  	})
   797  
   798  	t.Run("[]string(10件)", func(t *testing.T) {
   799  		param := &model.TaskSearchParam{
   800  			NameList: model.NewQueryChainer().ArrayContainsAny([]string{"a", "b"}),
   801  		}
   802  
   803  		tasks, err := taskRepo.Search(ctx, param, nil)
   804  		if err != nil {
   805  			t.Fatalf("%+v", err)
   806  		}
   807  
   808  		if len(tasks) != 10 {
   809  			t.Fatal("not match")
   810  		}
   811  	})
   812  
   813  	t.Run("Flag(10件)", func(t *testing.T) {
   814  		param := &model.TaskSearchParam{
   815  			Flag: model.NewQueryChainer().Equal(true),
   816  		}
   817  
   818  		tasks, err := taskRepo.Search(ctx, param, nil)
   819  		if err != nil {
   820  			t.Fatalf("%+v", err)
   821  		}
   822  
   823  		if len(tasks) != 10 {
   824  			t.Fatal("not match")
   825  		}
   826  	})
   827  
   828  	t.Run("Geo(9件)", func(t *testing.T) {
   829  		param := &model.TaskSearchParam{
   830  			Geo: model.NewQueryChainer().Equal(latLng),
   831  		}
   832  
   833  		tasks, err := taskRepo.Search(ctx, param, nil)
   834  		if err != nil {
   835  			t.Fatalf("%+v", err)
   836  		}
   837  
   838  		if len(tasks) != 9 {
   839  			t.Fatal("not match")
   840  		}
   841  	})
   842  
   843  	t.Run("NotEqual(1件)", func(tr *testing.T) {
   844  		param := &model.TaskSearchParam{
   845  			Geo: model.NewQueryChainer().NotEqual(latLng),
   846  		}
   847  		tasks, err := taskRepo.Search(ctx, param, nil)
   848  		if err != nil {
   849  			tr.Fatalf("%+v", err)
   850  		}
   851  		if len(tasks) != 1 {
   852  			tr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 1)
   853  		}
   854  	})
   855  
   856  	t.Run("NotIn(9件)", func(tr *testing.T) {
   857  		param := &model.TaskSearchParam{
   858  			Geo: model.NewQueryChainer().NotIn([]*latlng.LatLng{skyTreeLatLng}),
   859  		}
   860  		tasks, err := taskRepo.Search(ctx, param, nil)
   861  		if err != nil {
   862  			tr.Fatalf("%+v", err)
   863  		}
   864  		if len(tasks) != 9 {
   865  			tr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 9)
   866  		}
   867  	})
   868  
   869  	t.Run("UseQueryBuilder", func(tr *testing.T) {
   870  		tr.Run("range query(3<count<8)", func(ttr *testing.T) {
   871  			qb := model.NewQueryBuilder(taskRepo.GetCollection())
   872  			qb.GreaterThan("count", 3)
   873  			qb.LessThan("count", 8)
   874  
   875  			tasks, err := taskRepo.Search(ctx, nil, qb.Query())
   876  			if err != nil {
   877  				ttr.Fatalf("%+v", err)
   878  			}
   879  
   880  			if len(tasks) != 4 {
   881  				ttr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 4)
   882  			}
   883  		})
   884  		tr.Run("!=(count!=1)", func(ttr *testing.T) {
   885  			qb := model.NewQueryBuilder(taskRepo.GetCollection())
   886  			qb.NotEqual("count", 1)
   887  
   888  			tasks, err := taskRepo.Search(ctx, nil, qb.Query())
   889  			if err != nil {
   890  				ttr.Fatalf("%+v", err)
   891  			}
   892  
   893  			if len(tasks) != 9 {
   894  				ttr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 9)
   895  			}
   896  		})
   897  		tr.Run("not-in(count not-in [1,2,3,4,5])", func(ttr *testing.T) {
   898  			qb := model.NewQueryBuilder(taskRepo.GetCollection())
   899  			qb.NotIn("count", []int{1, 2, 3, 4, 5})
   900  
   901  			tasks, err := taskRepo.Search(ctx, nil, qb.Query())
   902  			if err != nil {
   903  				ttr.Fatalf("%+v", err)
   904  			}
   905  
   906  			if len(tasks) != 5 {
   907  				ttr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 5)
   908  			}
   909  		})
   910  	})
   911  }
   912  
   913  func TestFirestoreError(t *testing.T) {
   914  	client := initFirestoreClient(t)
   915  
   916  	taskRepo := model.NewTaskRepository(client)
   917  
   918  	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
   919  	var ids []string
   920  	defer func() {
   921  		defer cancel()
   922  		if err := taskRepo.DeleteMultiByIdentities(ctx, ids); err != nil {
   923  			t.Fatalf("%+v\n", err)
   924  		}
   925  	}()
   926  
   927  	now := time.Unix(0, time.Now().UnixNano())
   928  
   929  	t.Run("Prepare", func(tr *testing.T) {
   930  		tk := &model.Task{
   931  			Identity:   "identity",
   932  			Desc:       desc,
   933  			Created:    now,
   934  			Done:       true,
   935  			Done2:      false,
   936  			Count:      11,
   937  			Count64:    11,
   938  			Proportion: 0.12345 + 11,
   939  			NameList:   []string{"a", "b", "c"},
   940  			Flag:       model.Flag(true),
   941  		}
   942  		id, err := taskRepo.Insert(ctx, tk)
   943  		if err != nil {
   944  			tr.Fatalf("%+v", err)
   945  		}
   946  		ids = append(ids, id)
   947  	})
   948  
   949  	t.Run("Create test", func(tr *testing.T) {
   950  		tk := &model.Task{
   951  			Identity:   "identity",
   952  			Desc:       desc + "2",
   953  			Created:    now,
   954  			Done:       true,
   955  			Done2:      false,
   956  			Count:      11,
   957  			Count64:    11,
   958  			Proportion: 0.12345 + 11,
   959  			NameList:   []string{"a", "b", "c"},
   960  			Flag:       model.Flag(true),
   961  		}
   962  		id, err := taskRepo.Insert(ctx, tk)
   963  		if err != nil {
   964  			if !xerrors.Is(err, model.ErrAlreadyExists) {
   965  				tr.Fatalf("%+v", err)
   966  			}
   967  		} else {
   968  			ids = append(ids, id)
   969  		}
   970  	})
   971  
   972  	t.Run("ErrorReadAfterWrite", func(tr *testing.T) {
   973  		tkID := ids[len(ids)-1]
   974  		errReadAfterWrite := xerrors.New("firestore: read after write in transaction")
   975  
   976  		if err := client.RunTransaction(ctx, func(cx context.Context, tx *firestore.Transaction) error {
   977  			tk, err := taskRepo.GetWithTx(tx, tkID)
   978  			if err != nil {
   979  				return err
   980  			}
   981  
   982  			if tk.Count != 11 {
   983  				return fmt.Errorf("unexpected Count: %d (expected: %d)", tk.Count, 11)
   984  			}
   985  
   986  			tk.Count = 12
   987  			if err = taskRepo.UpdateWithTx(cx, tx, tk); err != nil {
   988  				return err
   989  			}
   990  
   991  			if _, err = taskRepo.GetWithTx(tx, tkID); err != nil {
   992  				return err
   993  			}
   994  			return nil
   995  		}); err != nil && xerrors.Is(xerrors.Unwrap(err), errReadAfterWrite) {
   996  			tr.Fatalf("error: %+v", err)
   997  		}
   998  
   999  		tsk, err := taskRepo.Get(ctx, tkID)
  1000  		if err != nil {
  1001  			tr.Fatalf("%+v", err)
  1002  		}
  1003  
  1004  		if tsk.Count != 11 {
  1005  			tr.Fatalf("unexpected Count: %d (expected: %d)", tsk.Count, 11)
  1006  		}
  1007  
  1008  		if err = client.RunTransaction(ctx, func(cx context.Context, tx *firestore.Transaction) error {
  1009  			id, er := taskRepo.InsertWithTx(cx, tx, new(model.Task))
  1010  			if er != nil {
  1011  				return er
  1012  			}
  1013  
  1014  			if _, er = taskRepo.GetWithTx(tx, id); er != nil {
  1015  				return er
  1016  			}
  1017  			return nil
  1018  		}); err != nil && xerrors.Is(xerrors.Unwrap(err), errReadAfterWrite) {
  1019  			tr.Fatalf("error: %+v", err)
  1020  		}
  1021  	})
  1022  }
  1023  
  1024  func TestFirestoreValueCheck(t *testing.T) {
  1025  	client := initFirestoreClient(t)
  1026  
  1027  	taskRepo := model.NewTaskRepository(client)
  1028  
  1029  	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
  1030  	defer cancel()
  1031  
  1032  	now := time.Unix(time.Now().Unix(), 0)
  1033  	desc = "hello"
  1034  
  1035  	id, err := taskRepo.Insert(ctx, &model.Task{
  1036  		Identity: "TestID",
  1037  		Desc:     desc,
  1038  		Created:  now,
  1039  		Done:     true,
  1040  	})
  1041  
  1042  	if err != nil {
  1043  		t.Fatalf("failed to put item: %+v", err)
  1044  	}
  1045  
  1046  	ret, err := taskRepo.Get(ctx, id)
  1047  
  1048  	if err != nil {
  1049  		t.Fatalf("failed to get item: %+v", err)
  1050  	}
  1051  
  1052  	compareTask(t, &model.Task{
  1053  		Identity: id,
  1054  		Desc:     desc,
  1055  		Created:  now,
  1056  		Done:     true,
  1057  	}, ret)
  1058  
  1059  	returns, err := taskRepo.GetMulti(ctx, []string{id})
  1060  
  1061  	if err != nil {
  1062  		t.Fatalf("failed to get item: %+v", err)
  1063  	}
  1064  
  1065  	if len(returns) != 1 {
  1066  		t.Fatalf("GetMulti should return 1 item: %#v", returns)
  1067  	}
  1068  
  1069  	compareTask(t, &model.Task{
  1070  		Identity: id,
  1071  		Desc:     desc,
  1072  		Created:  now,
  1073  		Done:     true,
  1074  	}, returns[0])
  1075  
  1076  	compareTask(t, &model.Task{
  1077  		Identity: id,
  1078  		Desc:     desc,
  1079  		Created:  now,
  1080  		Done:     true,
  1081  	}, ret)
  1082  
  1083  	if err = taskRepo.DeleteByIdentity(ctx, id); err != nil {
  1084  		t.Fatalf("delete failed: %+v", err)
  1085  	}
  1086  
  1087  	if _, err = taskRepo.Get(ctx, id); err == nil {
  1088  		t.Fatalf("should get an error")
  1089  	}
  1090  }