github.com/go-generalize/volcago@v1.7.0/generator/testfiles/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  	"strings"
    12  	"testing"
    13  	"time"
    14  
    15  	"cloud.google.com/go/firestore"
    16  	model "github.com/go-generalize/volcago/generator/testfiles/auto"
    17  	"golang.org/x/xerrors"
    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-google")
    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.ID != expected.ID {
    49  		t.Fatalf("unexpected identity: %s (expected: %s)", actual.ID, expected.ID)
    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  func TestFirestore(t *testing.T) {
    66  	client := initFirestoreClient(t)
    67  
    68  	taskRepo := model.NewTaskRepository(client)
    69  
    70  	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    71  	var ids []string
    72  	defer func() {
    73  		defer cancel()
    74  		if err := taskRepo.DeleteMultiByIDs(ctx, ids); err != nil {
    75  			t.Fatal(err)
    76  		}
    77  	}()
    78  
    79  	now := time.Unix(0, time.Now().UnixNano()).UTC()
    80  
    81  	t.Run("Multi", func(tr *testing.T) {
    82  		tks := make([]*model.Task, 0)
    83  		for i := int64(1); i <= 10; i++ {
    84  			tk := &model.Task{
    85  				Desc:       fmt.Sprintf("%s%d", desc, i),
    86  				Created:    now,
    87  				Done:       true,
    88  				Done2:      false,
    89  				Count:      int(i),
    90  				Count64:    0,
    91  				Proportion: 0.12345 + float64(i),
    92  				NameList:   []string{"a", "b", "c"},
    93  				Flag: map[string]float64{
    94  					"1": 1.1,
    95  					"2": 2.2,
    96  					"3": 3.3,
    97  				},
    98  			}
    99  			tks = append(tks, tk)
   100  		}
   101  		idList, err := taskRepo.InsertMulti(ctx, tks)
   102  		if err != nil {
   103  			tr.Fatalf("%+v", err)
   104  		}
   105  		ids = append(ids, idList...)
   106  
   107  		tks2 := make([]*model.Task, 0)
   108  		for i := int64(1); i <= 10; i++ {
   109  			tk := &model.Task{
   110  				ID:         ids[i-1],
   111  				Desc:       fmt.Sprintf("%s%d", desc, i),
   112  				Created:    now,
   113  				Done:       true,
   114  				Done2:      false,
   115  				Count:      int(i),
   116  				Count64:    i,
   117  				Proportion: 0.12345 + float64(i),
   118  				NameList:   []string{"a", "b", "c"},
   119  				Flag: map[string]float64{
   120  					"4": 4.4,
   121  					"5": 5.5,
   122  					"6": 6.6,
   123  				},
   124  			}
   125  			tks2 = append(tks2, tk)
   126  		}
   127  		if err := taskRepo.UpdateMulti(ctx, tks2); err != nil {
   128  			tr.Fatalf("%+v", err)
   129  		}
   130  
   131  		if tks[0].ID != tks2[0].ID {
   132  			tr.Fatalf("unexpected identity: %s (expected: %s)", tks[0].ID, tks2[0].ID)
   133  		}
   134  	})
   135  
   136  	t.Run("Single", func(tr *testing.T) {
   137  		tk := &model.Task{
   138  			Desc:       fmt.Sprintf("%s%d", desc, 1001),
   139  			Created:    now,
   140  			Done:       true,
   141  			Done2:      false,
   142  			Count:      11,
   143  			Count64:    11,
   144  			Proportion: 11.12345,
   145  			NameList:   []string{"a", "b", "c"},
   146  			Flag: map[string]float64{
   147  				"1": 1.1,
   148  				"2": 2.2,
   149  				"3": 3.3,
   150  			},
   151  		}
   152  		id, err := taskRepo.Insert(ctx, tk)
   153  		if err != nil {
   154  			tr.Fatalf("%+v", err)
   155  		}
   156  		ids = append(ids, id)
   157  
   158  		tk.Count++
   159  		tk.Flag["4"] = 4.4
   160  		if err = taskRepo.Update(ctx, tk); err != nil {
   161  			tr.Fatalf("%+v", err)
   162  		}
   163  
   164  		tsk, err := taskRepo.Get(ctx, tk.ID)
   165  		if err != nil {
   166  			tr.Fatalf("%+v", err)
   167  		}
   168  
   169  		if tsk.Count != 12 {
   170  			tr.Fatalf("unexpected Count: %d (expected: %d)", tsk.Count, 12)
   171  		}
   172  
   173  		if _, ok := tsk.Flag["4"]; !ok {
   174  			tr.Fatalf("unexpected Flag: %v (expected: %v)", ok, true)
   175  		}
   176  
   177  		tr.Run("UpdateBuilder", func(ttr *testing.T) {
   178  			desc1002 := fmt.Sprintf("%s%d", desc, 1002)
   179  
   180  			updateParam := &model.TaskUpdateParam{
   181  				Desc:       desc1002,
   182  				Created:    firestore.ServerTimestamp,
   183  				Done:       false,
   184  				Count:      firestore.Increment(1),
   185  				Count64:    firestore.Increment(2),
   186  				Proportion: firestore.Increment(0.1),
   187  			}
   188  
   189  			if err = taskRepo.StrictUpdate(ctx, tsk.ID, updateParam); err != nil {
   190  				ttr.Fatalf("%+v", err)
   191  			}
   192  
   193  			tsk, err = taskRepo.Get(ctx, tk.ID)
   194  			if err != nil {
   195  				ttr.Fatalf("%+v", err)
   196  			}
   197  
   198  			if tsk.Desc != desc1002 {
   199  				ttr.Fatalf("unexpected Desc: %s (expected: %s)", tsk.Desc, desc1002)
   200  			}
   201  
   202  			if tsk.Created.Before(now) {
   203  				ttr.Fatalf("unexpected Created > now: %t (expected: %t)", tsk.Created.Before(now), tsk.Created.After(now))
   204  			}
   205  
   206  			if tsk.Done {
   207  				ttr.Fatalf("unexpected Done: %t (expected: %t)", tsk.Done, false)
   208  			}
   209  
   210  			if tsk.Count != 13 {
   211  				ttr.Fatalf("unexpected Count: %d (expected: %d)", tsk.Count, 13)
   212  			}
   213  
   214  			if tsk.Count64 != 13 {
   215  				ttr.Fatalf("unexpected Count64: %d (expected: %d)", tsk.Count64, 13)
   216  			}
   217  
   218  			if tsk.Proportion != 11.22345 {
   219  				ttr.Fatalf("unexpected Proportion: %g (expected: %g)", tsk.Proportion, 11.22345)
   220  			}
   221  		})
   222  
   223  		tr.Run("ParentDocWithNewInstance", func(ttr *testing.T) {
   224  			subTask := &model.SubCollection{
   225  				Flag: true,
   226  			}
   227  
   228  			subTaskRepo := model.NewSubCollectionRepository(client, nil).
   229  				NewRepositoryByParent(taskRepo.GetDocRef(id))
   230  			id, err := subTaskRepo.Insert(ctx, subTask)
   231  			if err != nil {
   232  				ttr.Fatalf("unexpected err: %+v", err)
   233  			}
   234  			gotSubTask, err := subTaskRepo.Get(ctx, id)
   235  			if err != nil {
   236  				ttr.Fatalf("unexpected err: %+v", err)
   237  			}
   238  			if subTask.Flag != gotSubTask.Flag {
   239  				ttr.Fatal("wrong model")
   240  			}
   241  		})
   242  	})
   243  }
   244  
   245  func TestFirestoreTransaction_Single(t *testing.T) {
   246  	client := initFirestoreClient(t)
   247  
   248  	taskRepo := model.NewTaskRepository(client)
   249  
   250  	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
   251  	var ids []string
   252  	defer func() {
   253  		defer cancel()
   254  		if err := taskRepo.DeleteMultiByIDs(ctx, ids); err != nil {
   255  			t.Fatal(err)
   256  		}
   257  	}()
   258  
   259  	now := time.Unix(0, time.Now().UnixNano())
   260  
   261  	t.Run("Insert", func(tr *testing.T) {
   262  		err := client.RunTransaction(ctx, func(cx context.Context, tx *firestore.Transaction) error {
   263  			tk := &model.Task{
   264  				Desc:       fmt.Sprintf("%s01", desc),
   265  				Created:    now,
   266  				Done:       true,
   267  				Done2:      false,
   268  				Count:      10,
   269  				Count64:    11,
   270  				Proportion: 11.12345,
   271  				NameList:   []string{"a", "b", "c"},
   272  				Flag: map[string]float64{
   273  					"1": 1.1,
   274  					"2": 2.2,
   275  					"3": 3.3,
   276  				},
   277  			}
   278  
   279  			id, err := taskRepo.InsertWithTx(cx, tx, tk)
   280  			if err != nil {
   281  				return err
   282  			}
   283  
   284  			ids = append(ids, id)
   285  			return nil
   286  		})
   287  
   288  		if err != nil {
   289  			tr.Fatalf("error: %+v", err)
   290  		}
   291  
   292  		tsk, err := taskRepo.Get(ctx, ids[len(ids)-1])
   293  		if err != nil {
   294  			tr.Fatalf("%+v", err)
   295  		}
   296  
   297  		if tsk.Count != 10 {
   298  			tr.Fatalf("unexpected Count: %d (expected: %d)", tsk.Count, 10)
   299  		}
   300  	})
   301  
   302  	t.Run("Update", func(tr *testing.T) {
   303  		tkID := ids[len(ids)-1]
   304  		err := client.RunTransaction(ctx, func(cx context.Context, tx *firestore.Transaction) error {
   305  			tk, err := taskRepo.GetWithTx(tx, tkID)
   306  			if err != nil {
   307  				return err
   308  			}
   309  
   310  			if tk.Count != 10 {
   311  				return fmt.Errorf("unexpected Count: %d (expected: %d)", tk.Count, 10)
   312  			}
   313  
   314  			tk.Count = 11
   315  			if err = taskRepo.UpdateWithTx(cx, tx, tk); err != nil {
   316  				return err
   317  			}
   318  			return nil
   319  		})
   320  
   321  		if err != nil {
   322  			tr.Fatalf("error: %+v", err)
   323  		}
   324  
   325  		tsk, err := taskRepo.Get(ctx, tkID)
   326  		if err != nil {
   327  			tr.Fatalf("%+v", err)
   328  		}
   329  
   330  		if tsk.Count != 11 {
   331  			tr.Fatalf("unexpected Count: %d (expected: %d)", tsk.Count, 11)
   332  		}
   333  	})
   334  
   335  	t.Run("UseUpdateBuilder", func(tr *testing.T) {
   336  		tkID := ids[len(ids)-1]
   337  		desc1002 := fmt.Sprintf("%s%d", desc, 1002)
   338  		err := client.RunTransaction(ctx, func(cx context.Context, tx *firestore.Transaction) error {
   339  			tk, err := taskRepo.GetWithTx(tx, tkID)
   340  			if err != nil {
   341  				return err
   342  			}
   343  
   344  			if tk.Count != 11 {
   345  				return fmt.Errorf("unexpected Count: %d (expected: %d)", tk.Count, 11)
   346  			}
   347  
   348  			updateParam := &model.TaskUpdateParam{
   349  				Desc:       desc1002,
   350  				Created:    firestore.ServerTimestamp,
   351  				Done:       false,
   352  				Count:      firestore.Increment(1),
   353  				Count64:    firestore.Increment(2),
   354  				Proportion: firestore.Increment(0.1),
   355  			}
   356  			if err = taskRepo.StrictUpdateWithTx(tx, tk.ID, updateParam); err != nil {
   357  				return err
   358  			}
   359  			return nil
   360  		})
   361  
   362  		if err != nil {
   363  			tr.Fatalf("error: %+v", err)
   364  		}
   365  
   366  		tsk, err := taskRepo.Get(ctx, tkID)
   367  		if err != nil {
   368  			tr.Fatalf("%+v", err)
   369  		}
   370  
   371  		if tsk.Desc != desc1002 {
   372  			tr.Fatalf("unexpected Desc: %s (expected: %s)", tsk.Desc, desc1002)
   373  		}
   374  
   375  		if tsk.Created.Before(now) {
   376  			tr.Fatalf("unexpected Created > now: %t (expected: %t)", tsk.Created.Before(now), tsk.Created.After(now))
   377  		}
   378  
   379  		if tsk.Done {
   380  			tr.Fatalf("unexpected Done: %t (expected: %t)", tsk.Done, false)
   381  		}
   382  
   383  		if tsk.Count != 12 {
   384  			tr.Fatalf("unexpected Count: %d (expected: %d)", tsk.Count, 12)
   385  		}
   386  
   387  		if tsk.Count64 != 13 {
   388  			tr.Fatalf("unexpected Count64: %d (expected: %d)", tsk.Count64, 13)
   389  		}
   390  
   391  		if tsk.Proportion != 11.22345 {
   392  			tr.Fatalf("unexpected Proportion: %g (expected: %g)", tsk.Proportion, 11.22345)
   393  		}
   394  	})
   395  }
   396  
   397  func TestFirestoreTransaction_Multi(t *testing.T) {
   398  	client := initFirestoreClient(t)
   399  
   400  	taskRepo := model.NewTaskRepository(client)
   401  
   402  	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
   403  	var ids []string
   404  	defer func() {
   405  		defer cancel()
   406  		if err := taskRepo.DeleteMultiByIDs(ctx, ids); err != nil {
   407  			t.Fatal(err)
   408  		}
   409  	}()
   410  
   411  	now := time.Unix(0, time.Now().UnixNano())
   412  
   413  	tks := make([]*model.Task, 0)
   414  	t.Run("InsertMulti", func(tr *testing.T) {
   415  		for i := int64(1); i <= 10; i++ {
   416  			tk := &model.Task{
   417  				ID:         fmt.Sprintf("Task_%d", i),
   418  				Desc:       fmt.Sprintf("%s%d", desc, i),
   419  				Created:    now,
   420  				Done:       true,
   421  				Done2:      false,
   422  				Count:      int(i),
   423  				Count64:    0,
   424  				NameList:   []string{"a", "b", "c"},
   425  				Proportion: 0.12345 + float64(i),
   426  				Flag: map[string]float64{
   427  					"1": 1.1,
   428  					"2": 2.2,
   429  					"3": 3.3,
   430  				},
   431  			}
   432  			tks = append(tks, tk)
   433  		}
   434  
   435  		if err := client.RunTransaction(ctx, func(cx context.Context, tx *firestore.Transaction) error {
   436  			idList, err := taskRepo.InsertMultiWithTx(ctx, tx, tks)
   437  			if err != nil {
   438  				return err
   439  			}
   440  			ids = append(ids, idList...)
   441  			return nil
   442  		}); err != nil {
   443  			tr.Fatalf("error: %+v", err)
   444  		}
   445  	})
   446  
   447  	tks2 := make([]*model.Task, 0)
   448  	t.Run("UpdateMulti", func(tr *testing.T) {
   449  		for i := int64(1); i <= 10; i++ {
   450  			tk := &model.Task{
   451  				ID:         ids[i-1],
   452  				Desc:       fmt.Sprintf("%s%d", desc, i+1),
   453  				Created:    now,
   454  				Done:       false,
   455  				Done2:      true,
   456  				Count:      int(i),
   457  				Count64:    i,
   458  				NameList:   []string{"a", "b", "c"},
   459  				Proportion: 0.12345 + float64(i),
   460  				Flag: map[string]float64{
   461  					"1": 1.1,
   462  					"2": 2.2,
   463  					"3": 3.3,
   464  				},
   465  			}
   466  			tks2 = append(tks2, tk)
   467  		}
   468  
   469  		if err := client.RunTransaction(ctx, func(cx context.Context, tx *firestore.Transaction) error {
   470  			if err := taskRepo.UpdateMultiWithTx(cx, tx, tks2); err != nil {
   471  				return err
   472  			}
   473  			return nil
   474  		}); err != nil {
   475  			tr.Fatalf("error: %+v", err)
   476  		}
   477  
   478  		if tks[0].ID != tks2[0].ID {
   479  			tr.Fatalf("unexpected identity: %s (expected: %s)", tks[0].ID, tks2[0].ID)
   480  		}
   481  	})
   482  }
   483  
   484  func TestFirestoreQuery(t *testing.T) {
   485  	client := initFirestoreClient(t)
   486  
   487  	taskRepo := model.NewTaskRepository(client)
   488  
   489  	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
   490  	var ids []string
   491  	defer func() {
   492  		defer cancel()
   493  		if err := taskRepo.DeleteMultiByIDs(ctx, ids); err != nil {
   494  			t.Fatalf("%+v\n", err)
   495  		}
   496  	}()
   497  
   498  	now := time.Unix(0, time.Now().UnixNano())
   499  
   500  	tks := make([]*model.Task, 0)
   501  	for i := 1; i <= 10; i++ {
   502  		tk := &model.Task{
   503  			ID:         fmt.Sprintf("%d", i),
   504  			Desc:       fmt.Sprintf("%s%d", desc, i),
   505  			Created:    now.Add(time.Millisecond * time.Duration(i)),
   506  			Done:       true,
   507  			Done2:      false,
   508  			Count:      i,
   509  			Count64:    int64(i),
   510  			NameList:   []string{"a", "b", "c"},
   511  			Proportion: 0.12345 + float64(i),
   512  			Flag: map[string]float64{
   513  				"1": 1.1,
   514  				"2": 2.2,
   515  				"3": 3.3,
   516  				"4": 4.4,
   517  				"5": 5.5,
   518  			},
   519  			SliceSubTask: []*model.SubTask{
   520  				{
   521  					Name: "slice_nested",
   522  				},
   523  			},
   524  		}
   525  		tks = append(tks, tk)
   526  	}
   527  	ids, err := taskRepo.InsertMulti(ctx, tks)
   528  	if err != nil {
   529  		t.Fatalf("%+v", err)
   530  	}
   531  
   532  	t.Run("Paging", func(tr *testing.T) {
   533  		param := &model.TaskSearchParam{
   534  			Done:        model.NewQueryChainer().Equal(true),
   535  			CursorLimit: 8,
   536  		}
   537  
   538  		tasks, pagingResult, err := taskRepo.SearchByParam(ctx, param)
   539  		if err != nil {
   540  			tr.Fatalf("%+v", err)
   541  		}
   542  
   543  		if len(tasks) != 8 && pagingResult.Length == len(tasks) {
   544  			tr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 8)
   545  		}
   546  
   547  		param = &model.TaskSearchParam{
   548  			Done:        model.NewQueryChainer().Equal(true),
   549  			CursorLimit: 5,
   550  			CursorKey:   pagingResult.NextCursorKey,
   551  		}
   552  
   553  		tasks, pagingResult, err = taskRepo.SearchByParam(ctx, param)
   554  		if err != nil {
   555  			tr.Fatalf("%+v", err)
   556  		}
   557  
   558  		if len(tasks) != 2 && pagingResult.Length == len(tasks) {
   559  			tr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 2)
   560  		}
   561  	})
   562  
   563  	t.Run("Paging", func(tr *testing.T) {
   564  		param := &model.TaskSearchParam{
   565  			Done:        model.NewQueryChainer().Equal(true),
   566  			CursorLimit: 30,
   567  		}
   568  
   569  		tasks, _, err := taskRepo.SearchByParam(ctx, param)
   570  		if err != nil {
   571  			tr.Fatalf("%+v", err)
   572  		}
   573  
   574  		if len(tasks) != 10 {
   575  			tr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 10)
   576  		}
   577  	})
   578  
   579  	t.Run("document id(1件)", func(tr *testing.T) {
   580  		param := &model.TaskSearchParam{
   581  			ID: model.NewQueryChainer().Equal(ids[0]),
   582  		}
   583  
   584  		tasks, err := taskRepo.Search(ctx, param, nil)
   585  		if err != nil {
   586  			tr.Fatalf("%+v", err)
   587  		}
   588  
   589  		if len(tasks) != 1 {
   590  			tr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 1)
   591  		}
   592  	})
   593  	t.Run("document id for IN(3件)", func(tr *testing.T) {
   594  		param := &model.TaskSearchParam{
   595  			ID: model.NewQueryChainer().In([]string{ids[0], ids[1], ids[2]}),
   596  		}
   597  
   598  		tasks, err := taskRepo.Search(ctx, param, nil)
   599  		if err != nil {
   600  			tr.Fatalf("%+v", err)
   601  		}
   602  
   603  		if len(tasks) != 3 {
   604  			tr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 3)
   605  		}
   606  	})
   607  	t.Run("document id for NOT IN(7件)", func(tr *testing.T) {
   608  		param := &model.TaskSearchParam{
   609  			ID: model.NewQueryChainer().NotIn([]string{ids[0], ids[1], ids[2]}),
   610  		}
   611  
   612  		tasks, err := taskRepo.Search(ctx, param, nil)
   613  		if err != nil {
   614  			tr.Fatalf("%+v", err)
   615  		}
   616  
   617  		if len(tasks) != 7 {
   618  			tr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 7)
   619  		}
   620  	})
   621  
   622  	t.Run("int(1件)", func(tr *testing.T) {
   623  		param := &model.TaskSearchParam{
   624  			Count: model.NewQueryChainer().Equal(1),
   625  		}
   626  
   627  		tasks, err := taskRepo.Search(ctx, param, nil)
   628  		if err != nil {
   629  			tr.Fatalf("%+v", err)
   630  		}
   631  
   632  		if len(tasks) != 1 {
   633  			tr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 1)
   634  		}
   635  	})
   636  
   637  	t.Run("int64(5件)", func(tr *testing.T) {
   638  		param := &model.TaskSearchParam{
   639  			Count64: model.NewQueryChainer().LessThanOrEqual(5),
   640  		}
   641  
   642  		tasks, err := taskRepo.Search(ctx, param, nil)
   643  		if err != nil {
   644  			tr.Fatalf("%+v", err)
   645  		}
   646  
   647  		if len(tasks) != 5 {
   648  			tr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 5)
   649  		}
   650  	})
   651  
   652  	t.Run("float(1件)", func(tr *testing.T) {
   653  		param := &model.TaskSearchParam{
   654  			Proportion: model.NewQueryChainer().Equal(1.12345),
   655  		}
   656  
   657  		tasks, err := taskRepo.Search(ctx, param, nil)
   658  		if err != nil {
   659  			tr.Fatalf("%+v", err)
   660  		}
   661  
   662  		if len(tasks) != 1 {
   663  			tr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 1)
   664  		}
   665  	})
   666  
   667  	t.Run("bool(10件)", func(tr *testing.T) {
   668  		param := &model.TaskSearchParam{
   669  			Done: model.NewQueryChainer().Equal(true),
   670  		}
   671  
   672  		tasks, err := taskRepo.Search(ctx, param, nil)
   673  		if err != nil {
   674  			tr.Fatalf("%+v", err)
   675  		}
   676  
   677  		if len(tasks) != 10 {
   678  			tr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 10)
   679  		}
   680  	})
   681  
   682  	t.Run("time.Time(1件)", func(tr *testing.T) {
   683  		param := &model.TaskSearchParam{
   684  			Created: model.NewQueryChainer().Equal(now.Add(time.Millisecond)),
   685  		}
   686  
   687  		tasks, err := taskRepo.Search(ctx, param, nil)
   688  		if err != nil {
   689  			tr.Fatalf("%+v", err)
   690  		}
   691  
   692  		if len(tasks) != 1 {
   693  			tr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 1)
   694  		}
   695  	})
   696  
   697  	t.Run("[]string(10件)", func(tr *testing.T) {
   698  		param := &model.TaskSearchParam{
   699  			NameList: model.NewQueryChainer().ArrayContainsAny([]string{"a", "b"}),
   700  		}
   701  
   702  		tasks, err := taskRepo.Search(ctx, param, nil)
   703  		if err != nil {
   704  			tr.Fatalf("%+v", err)
   705  		}
   706  
   707  		if len(tasks) != 10 {
   708  			tr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 10)
   709  		}
   710  	})
   711  
   712  	t.Run("[]Object(10件)", func(tr *testing.T) {
   713  		param := &model.TaskSearchParam{
   714  			SliceSubTask: model.NewQueryChainer().ArrayContainsAny([]*model.SubTask{{Name: "slice_struct"}}),
   715  		}
   716  
   717  		tasks, err := taskRepo.Search(ctx, param, nil)
   718  		if err != nil {
   719  			tr.Fatalf("%+v", err)
   720  		}
   721  
   722  		if len(tasks) != 10 {
   723  			tr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 10)
   724  		}
   725  	})
   726  
   727  	t.Run("[]Object(0件)", func(tr *testing.T) {
   728  		param := &model.TaskSearchParam{
   729  			SliceSubTask: model.NewQueryChainer().ArrayContains("slice_struct"),
   730  		}
   731  
   732  		tasks, err := taskRepo.Search(ctx, param, nil)
   733  		if err != nil {
   734  			tr.Fatalf("%+v", err)
   735  		}
   736  
   737  		if len(tasks) != 0 {
   738  			tr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 10)
   739  		}
   740  	})
   741  
   742  	t.Run("Flag(10件)", func(tr *testing.T) {
   743  		param := &model.TaskSearchParam{
   744  			Flag: model.NewQueryChainer().Equal(map[string]float64{
   745  				"1": 1.1,
   746  				"2": 2.2,
   747  				"3": 3.3,
   748  			}),
   749  		}
   750  
   751  		tasks, err := taskRepo.Search(ctx, param, nil)
   752  		if err != nil {
   753  			tr.Fatalf("%+v", err)
   754  		}
   755  
   756  		if len(tasks) != 10 {
   757  			tr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 10)
   758  		}
   759  	})
   760  
   761  	t.Run("NotEqual(9件)", func(tr *testing.T) {
   762  		description := fmt.Sprintf("%s%d", desc, 1)
   763  		param := &model.TaskSearchParam{
   764  			Desc: model.NewQueryChainer().NotEqual(description),
   765  		}
   766  		tasks, err := taskRepo.Search(ctx, param, nil)
   767  		if err != nil {
   768  			tr.Fatalf("%+v", err)
   769  		}
   770  		if len(tasks) != 9 {
   771  			tr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 9)
   772  		}
   773  	})
   774  
   775  	t.Run("NotIn(8件)", func(tr *testing.T) {
   776  		description1 := fmt.Sprintf("%s%d", desc, 1)
   777  		description2 := fmt.Sprintf("%s%d", desc, 2)
   778  		param := &model.TaskSearchParam{
   779  			Desc: model.NewQueryChainer().NotIn([]string{description1, description2}),
   780  		}
   781  		tasks, err := taskRepo.Search(ctx, param, nil)
   782  		if err != nil {
   783  			tr.Fatalf("%+v", err)
   784  		}
   785  		if len(tasks) != 8 {
   786  			tr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 8)
   787  		}
   788  	})
   789  
   790  	t.Run("UseQueryBuilder", func(tr *testing.T) {
   791  		tr.Run("range query(3<count<8)", func(ttr *testing.T) {
   792  			qb := model.NewQueryBuilder(taskRepo.GetCollection())
   793  			qb.GreaterThan("count", 3)
   794  			qb.LessThan("count", 8)
   795  
   796  			tasks, err := taskRepo.Search(ctx, nil, qb.Query())
   797  			if err != nil {
   798  				ttr.Fatalf("%+v", err)
   799  			}
   800  
   801  			if len(tasks) != 4 {
   802  				ttr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 4)
   803  			}
   804  		})
   805  		tr.Run("!=(count!=1)", func(ttr *testing.T) {
   806  			qb := model.NewQueryBuilder(taskRepo.GetCollection())
   807  			qb.NotEqual("count", 1)
   808  
   809  			tasks, err := taskRepo.Search(ctx, nil, qb.Query())
   810  			if err != nil {
   811  				ttr.Fatalf("%+v", err)
   812  			}
   813  
   814  			if len(tasks) != 9 {
   815  				ttr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 9)
   816  			}
   817  		})
   818  		tr.Run("not-in(count not-in [1,2,3,4,5])", func(ttr *testing.T) {
   819  			qb := model.NewQueryBuilder(taskRepo.GetCollection())
   820  			qb.NotIn("count", []int{1, 2, 3, 4, 5})
   821  
   822  			tasks, err := taskRepo.Search(ctx, nil, qb.Query())
   823  			if err != nil {
   824  				ttr.Fatalf("%+v", err)
   825  			}
   826  
   827  			if len(tasks) != 5 {
   828  				ttr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 5)
   829  			}
   830  		})
   831  		tr.Run("search by document id", func(ttr *testing.T) {
   832  			qb := model.NewQueryBuilder(taskRepo.GetCollection())
   833  			qb.Equal(firestore.DocumentID, taskRepo.GetDocRef(tks[0].ID))
   834  			qb.Desc(firestore.DocumentID)
   835  
   836  			tasks, err := taskRepo.Search(ctx, nil, qb.Query())
   837  			if err != nil {
   838  				ttr.Fatalf("%+v", err)
   839  			}
   840  
   841  			if len(tasks) != 1 {
   842  				ttr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 1)
   843  			}
   844  		})
   845  	})
   846  
   847  	t.Run("Indexes", func(tr *testing.T) {
   848  		tr.Run("Equal", func(ttr *testing.T) {
   849  			param := &model.TaskSearchParam{
   850  				Desc: model.NewQueryChainer().Filters("Hello, World!1"),
   851  			}
   852  
   853  			tasks, err := taskRepo.Search(ctx, param, nil)
   854  			if err != nil {
   855  				ttr.Fatalf("%+v", err)
   856  			}
   857  
   858  			if len(tasks) != 1 {
   859  				ttr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 1)
   860  			}
   861  		})
   862  		tr.Run("Prefix", func(ttr *testing.T) {
   863  			chainer := model.NewQueryChainer
   864  			param := &model.TaskSearchParam{
   865  				Desc: chainer().Filters("Hel", model.FilterTypeAddPrefix),
   866  				Done: chainer().Equal(true),
   867  			}
   868  
   869  			tasks, err := taskRepo.Search(ctx, param, nil)
   870  			if err != nil {
   871  				ttr.Fatalf("%+v", err)
   872  			}
   873  
   874  			if len(tasks) != 10 {
   875  				ttr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 10)
   876  			}
   877  		})
   878  		tr.Run("Suffix", func(ttr *testing.T) {
   879  			param := &model.TaskSearchParam{
   880  				Desc: model.NewQueryChainer().Filters("10", model.FilterTypeAddSuffix),
   881  			}
   882  
   883  			tasks, err := taskRepo.Search(ctx, param, nil)
   884  			if err != nil {
   885  				ttr.Fatalf("%+v", err)
   886  			}
   887  
   888  			if len(tasks) != 1 {
   889  				ttr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 1)
   890  			}
   891  		})
   892  		tr.Run("Biunigrams", func(ttr *testing.T) {
   893  			ttr.Run("1", func(ttrr *testing.T) {
   894  				param := &model.TaskSearchParam{
   895  					Desc: model.NewQueryChainer().Filters("o, Wor", model.FilterTypeAddBiunigrams),
   896  				}
   897  
   898  				tasks, err := taskRepo.Search(ctx, param, nil)
   899  				if err != nil {
   900  					ttrr.Fatalf("%+v", err)
   901  				}
   902  
   903  				if len(tasks) != 10 {
   904  					ttrr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 10)
   905  				}
   906  			})
   907  			ttr.Run("2", func(ttrr *testing.T) {
   908  				param := &model.TaskSearchParam{
   909  					Desc: model.NewQueryChainer().Filters("!1", model.FilterTypeAddBiunigrams),
   910  				}
   911  
   912  				tasks, err := taskRepo.Search(ctx, param, nil)
   913  				if err != nil {
   914  					ttr.Fatalf("%+v", err)
   915  				}
   916  
   917  				if len(tasks) != 2 {
   918  					ttr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 2)
   919  				}
   920  			})
   921  			ttr.Run("3", func(ttrr *testing.T) {
   922  				param := &model.TaskSearchParam{
   923  					Desc: model.NewQueryChainer().Filters("Hello, W", model.FilterTypeAddBiunigrams),
   924  				}
   925  
   926  				tasks, err := taskRepo.Search(ctx, param, nil)
   927  				if err != nil {
   928  					ttr.Fatalf("%+v", err)
   929  				}
   930  
   931  				if len(tasks) != 10 {
   932  					ttr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 10)
   933  				}
   934  			})
   935  			ttr.Run("NG", func(ttrr *testing.T) {
   936  				param := &model.TaskSearchParam{
   937  					Desc: model.NewQueryChainer().Filters("Hello,W", model.FilterTypeAddBiunigrams),
   938  				}
   939  
   940  				tasks, err := taskRepo.Search(ctx, param, nil)
   941  				if err != nil {
   942  					ttr.Fatalf("%+v", err)
   943  				}
   944  
   945  				if len(tasks) != 0 {
   946  					ttr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 0)
   947  				}
   948  			})
   949  		})
   950  		tr.Run("Something", func(ttr *testing.T) {
   951  			param := &model.TaskSearchParam{
   952  				Proportion: model.NewQueryChainer().Filters(10.12345, model.FilterTypeAddSomething),
   953  			}
   954  
   955  			tasks, err := taskRepo.Search(ctx, param, nil)
   956  			if err != nil {
   957  				ttr.Fatalf("%+v", err)
   958  			}
   959  
   960  			if len(tasks) != 1 {
   961  				ttr.Fatalf("unexpected length: %d (expected: %d)", len(tasks), 1)
   962  			}
   963  		})
   964  	})
   965  }
   966  
   967  func TestFirestoreError(t *testing.T) {
   968  	client := initFirestoreClient(t)
   969  
   970  	taskRepo := model.NewTaskRepository(client)
   971  
   972  	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
   973  	var ids []string
   974  	defer func() {
   975  		defer cancel()
   976  		if err := taskRepo.DeleteMultiByIDs(ctx, ids); err != nil {
   977  			t.Fatalf("%+v\n", err)
   978  		}
   979  	}()
   980  
   981  	now := time.Unix(0, time.Now().UnixNano())
   982  
   983  	t.Run("Prepare", func(tr *testing.T) {
   984  		tk := &model.Task{
   985  			Desc:       desc,
   986  			Created:    now,
   987  			Done:       true,
   988  			Done2:      false,
   989  			Count:      11,
   990  			Count64:    11,
   991  			Proportion: 0.12345 + 11,
   992  			NameList:   []string{"a", "b", "c"},
   993  			Flag: map[string]float64{
   994  				"1": 1.1,
   995  				"2": 2.2,
   996  				"3": 3.3,
   997  			},
   998  		}
   999  		id, err := taskRepo.Insert(ctx, tk)
  1000  		if err != nil {
  1001  			tr.Fatalf("%+v", err)
  1002  		}
  1003  		ids = append(ids, id)
  1004  	})
  1005  
  1006  	t.Run("ErrorReadAfterWrite", func(tr *testing.T) {
  1007  		tkID := ids[len(ids)-1]
  1008  		errReadAfterWrite := xerrors.New("firestore: read after write in transaction")
  1009  
  1010  		if err := client.RunTransaction(ctx, func(cx context.Context, tx *firestore.Transaction) error {
  1011  			tk, err := taskRepo.GetWithTx(tx, tkID)
  1012  			if err != nil {
  1013  				return err
  1014  			}
  1015  
  1016  			if tk.Count != 11 {
  1017  				return fmt.Errorf("unexpected Count: %d (expected: %d)", tk.Count, 11)
  1018  			}
  1019  
  1020  			tk.Count = 12
  1021  			if err = taskRepo.UpdateWithTx(cx, tx, tk); err != nil {
  1022  				return err
  1023  			}
  1024  
  1025  			if _, err = taskRepo.GetWithTx(tx, tkID); err != nil {
  1026  				return err
  1027  			}
  1028  			return nil
  1029  		}); err != nil && xerrors.Is(xerrors.Unwrap(err), errReadAfterWrite) {
  1030  			tr.Fatalf("error: %+v", err)
  1031  		}
  1032  
  1033  		tsk, err := taskRepo.Get(ctx, tkID)
  1034  		if err != nil {
  1035  			tr.Fatalf("%+v", err)
  1036  		}
  1037  
  1038  		if tsk.Count != 11 {
  1039  			tr.Fatalf("unexpected Count: %d (expected: %d)", tsk.Count, 11)
  1040  		}
  1041  
  1042  		if err = client.RunTransaction(ctx, func(cx context.Context, tx *firestore.Transaction) error {
  1043  			id, er := taskRepo.InsertWithTx(cx, tx, new(model.Task))
  1044  			if er != nil {
  1045  				return er
  1046  			}
  1047  
  1048  			if _, err = taskRepo.GetWithTx(tx, id); err != nil {
  1049  				return err
  1050  			}
  1051  			return nil
  1052  		}); err != nil && xerrors.Is(xerrors.Unwrap(err), errReadAfterWrite) {
  1053  			tr.Fatalf("error: %+v", err)
  1054  		}
  1055  	})
  1056  }
  1057  
  1058  func TestFirestoreOfTaskRepo(t *testing.T) {
  1059  	client := initFirestoreClient(t)
  1060  
  1061  	taskRepo := model.NewTaskRepository(client)
  1062  
  1063  	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
  1064  	defer cancel()
  1065  
  1066  	now := time.Unix(time.Now().Unix(), 0)
  1067  
  1068  	id, err := taskRepo.Insert(ctx, &model.Task{
  1069  		Desc:    desc,
  1070  		Created: now,
  1071  		Done:    true,
  1072  	})
  1073  
  1074  	if err != nil {
  1075  		t.Fatalf("failed to put item: %+v", err)
  1076  	}
  1077  
  1078  	ret, err := taskRepo.Get(ctx, id)
  1079  
  1080  	if err != nil {
  1081  		t.Fatalf("failed to get item: %+v", err)
  1082  	}
  1083  
  1084  	compareTask(t, &model.Task{
  1085  		ID:      id,
  1086  		Desc:    desc,
  1087  		Created: now,
  1088  		Done:    true,
  1089  	}, ret)
  1090  
  1091  	returns, err := taskRepo.GetMulti(ctx, []string{id})
  1092  
  1093  	if err != nil {
  1094  		t.Fatalf("failed to get item: %+v", err)
  1095  	}
  1096  
  1097  	if len(returns) != 1 {
  1098  		t.Fatalf("GetMulti should return 1 item: %#v", returns)
  1099  	}
  1100  
  1101  	compareTask(t, &model.Task{
  1102  		ID:      id,
  1103  		Desc:    desc,
  1104  		Created: now,
  1105  		Done:    true,
  1106  	}, returns[0])
  1107  
  1108  	compareTask(t, &model.Task{
  1109  		ID:      id,
  1110  		Desc:    desc,
  1111  		Created: now,
  1112  		Done:    true,
  1113  	}, ret)
  1114  
  1115  	if err := taskRepo.DeleteByID(ctx, id); err != nil {
  1116  		t.Fatalf("delete failed: %+v", err)
  1117  	}
  1118  
  1119  	if _, err := taskRepo.Get(ctx, id); err == nil {
  1120  		t.Fatalf("should get an error")
  1121  	}
  1122  }
  1123  
  1124  func TestFirestoreOfLockRepo(t *testing.T) {
  1125  	client := initFirestoreClient(t)
  1126  
  1127  	lockRepo := model.NewLockRepository(client)
  1128  
  1129  	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
  1130  	ids := make([]string, 0)
  1131  	defer func() {
  1132  		defer cancel()
  1133  		mode := model.DeleteOption{Mode: model.DeleteModeHard}
  1134  		if err := lockRepo.DeleteMultiByIDs(ctx, ids, mode); err != nil {
  1135  			t.Fatal(err)
  1136  		}
  1137  	}()
  1138  
  1139  	text := "hello"
  1140  
  1141  	t.Run("insert_test", func(tr *testing.T) {
  1142  		l := &model.Lock{
  1143  			Text: text,
  1144  			Flag: nil,
  1145  			Meta: model.Meta{},
  1146  		}
  1147  
  1148  		id, err := lockRepo.Insert(ctx, l)
  1149  		if err != nil {
  1150  			tr.Fatalf("failed to put item: %+v", err)
  1151  		}
  1152  
  1153  		ids = append(ids, id)
  1154  
  1155  		ret, err := lockRepo.Get(ctx, id)
  1156  
  1157  		if err != nil {
  1158  			tr.Fatalf("failed to get item: %+v", err)
  1159  		}
  1160  
  1161  		if text != ret.Text {
  1162  			tr.Fatalf("unexpected Text: %s (expected: %s)", ret.Text, text)
  1163  		}
  1164  		if ret.CreatedAt.IsZero() {
  1165  			tr.Fatal("unexpected createdAt zero")
  1166  		}
  1167  		if ret.UpdatedAt.IsZero() {
  1168  			tr.Fatal("unexpected updatedAt zero")
  1169  		}
  1170  	})
  1171  
  1172  	t.Run("update_test", func(tr *testing.T) {
  1173  		l := &model.Lock{
  1174  			Text: text,
  1175  			Flag: nil,
  1176  			Meta: model.Meta{},
  1177  		}
  1178  
  1179  		id, err := lockRepo.Insert(ctx, l)
  1180  		if err != nil {
  1181  			tr.Fatalf("failed to put item: %+v", err)
  1182  		}
  1183  
  1184  		ids = append(ids, id)
  1185  
  1186  		text = "hello!!!"
  1187  		l.Text = text
  1188  		err = lockRepo.Update(ctx, l)
  1189  		if err != nil {
  1190  			tr.Fatalf("failed to update item: %+v", err)
  1191  		}
  1192  
  1193  		ret, err := lockRepo.Get(ctx, id)
  1194  		if err != nil {
  1195  			tr.Fatalf("failed to get item: %+v", err)
  1196  		}
  1197  
  1198  		if text != ret.Text {
  1199  			tr.Fatalf("unexpected Text: %s (expected: %s)", ret.Text, text)
  1200  		}
  1201  		if ret.CreatedAt.Equal(ret.UpdatedAt) {
  1202  			tr.Fatalf("unexpected CreatedAt == updatedAt: %d == %d",
  1203  				ret.CreatedAt.Unix(), ret.UpdatedAt.Unix())
  1204  		}
  1205  	})
  1206  
  1207  	t.Run("soft_delete_test", func(tr *testing.T) {
  1208  		l := &model.Lock{
  1209  			Text: text,
  1210  			Flag: nil,
  1211  			Meta: model.Meta{},
  1212  		}
  1213  
  1214  		id, err := lockRepo.Insert(ctx, l)
  1215  		if err != nil {
  1216  			tr.Fatalf("failed to put item: %+v", err)
  1217  		}
  1218  
  1219  		ids = append(ids, id)
  1220  
  1221  		l.Text = text
  1222  		err = lockRepo.Delete(ctx, l, model.DeleteOption{
  1223  			Mode: model.DeleteModeSoft,
  1224  		})
  1225  		if err != nil {
  1226  			tr.Fatalf("failed to soft delete item: %+v", err)
  1227  		}
  1228  
  1229  		ret, err := lockRepo.Get(ctx, id, model.GetOption{
  1230  			IncludeSoftDeleted: true,
  1231  		})
  1232  		if err != nil {
  1233  			tr.Fatalf("failed to get item: %+v", err)
  1234  		}
  1235  
  1236  		if text != ret.Text {
  1237  			tr.Fatalf("unexpected Text: %s (expected: %s)", ret.Text, text)
  1238  		}
  1239  		if ret.DeletedAt == nil {
  1240  			tr.Fatalf("unexpected DeletedAt == nil: %+v", ret.DeletedAt)
  1241  		}
  1242  	})
  1243  
  1244  	t.Run("hard_delete_test", func(tr *testing.T) {
  1245  		l := &model.Lock{
  1246  			Text: text,
  1247  			Flag: nil,
  1248  			Meta: model.Meta{},
  1249  		}
  1250  
  1251  		id, err := lockRepo.Insert(ctx, l)
  1252  		if err != nil {
  1253  			tr.Fatalf("failed to put item: %+v", err)
  1254  		}
  1255  
  1256  		l.Text = text
  1257  		err = lockRepo.Delete(ctx, l)
  1258  		if err != nil {
  1259  			tr.Fatalf("failed to hard delete item: %+v", err)
  1260  		}
  1261  
  1262  		ret, err := lockRepo.Get(ctx, id, model.GetOption{
  1263  			IncludeSoftDeleted: true,
  1264  		})
  1265  		if err != nil && !strings.Contains(err.Error(), "not found") {
  1266  			tr.Fatalf("failed to get item: %+v", err)
  1267  		}
  1268  
  1269  		if ret != nil {
  1270  			tr.Fatalf("failed to delete item (found!): %+v", ret)
  1271  		}
  1272  	})
  1273  
  1274  	t.Run("UseQueryBuilder", func(tr *testing.T) {
  1275  		l := &model.Lock{
  1276  			Text: text,
  1277  			Flag: nil,
  1278  			Meta: model.Meta{},
  1279  		}
  1280  		id, err := lockRepo.Insert(ctx, l)
  1281  		if err != nil {
  1282  			tr.Fatalf("failed to put item: %+v", err)
  1283  		}
  1284  
  1285  		ids = append(ids, id)
  1286  
  1287  		qb := model.NewQueryBuilder(lockRepo.GetCollection())
  1288  		qb.GreaterThanOrEqual("createdAt", model.SetLastThreeToZero(l.CreatedAt).Add(-100))
  1289  		qb.LessThanOrEqual("createdAt", model.SetLastThreeToZero(l.CreatedAt).Add(100))
  1290  		if err = qb.Check(); err != nil {
  1291  			tr.Fatal(err)
  1292  		}
  1293  
  1294  		locks, err := lockRepo.Search(ctx, nil, qb.Query())
  1295  		if err != nil {
  1296  			tr.Fatalf("%+v", err)
  1297  		}
  1298  
  1299  		if len(locks) != 1 {
  1300  			tr.Fatalf("unexpected length: %d (expected: %d)", len(locks), 1)
  1301  		}
  1302  
  1303  		if id != locks[0].ID {
  1304  			tr.Fatalf("unexpected length: %s (expected: %s)", locks[0].ID, id)
  1305  		}
  1306  	})
  1307  
  1308  	t.Run("UseQueryChainer", func(tr *testing.T) {
  1309  		l := &model.Lock{
  1310  			Text: "Hello",
  1311  			Flag: nil,
  1312  			Meta: model.Meta{},
  1313  		}
  1314  		id, err := lockRepo.Insert(ctx, l)
  1315  		if err != nil {
  1316  			tr.Fatalf("failed to put item: %+v", err)
  1317  		}
  1318  		ids = append(ids, id)
  1319  		l = &model.Lock{
  1320  			Text: "World",
  1321  			Flag: nil,
  1322  			Meta: model.Meta{},
  1323  		}
  1324  		id, err = lockRepo.Insert(ctx, l)
  1325  		if err != nil {
  1326  			tr.Fatalf("failed to put item: %+v", err)
  1327  		}
  1328  		ids = append(ids, id)
  1329  		param := &model.LockSearchParam{
  1330  			Text:               model.NewQueryChainer().In([]string{"Hello", "World"}),
  1331  			IncludeSoftDeleted: true,
  1332  		}
  1333  		locks, err := lockRepo.Search(ctx, param, nil)
  1334  		if err != nil {
  1335  			tr.Fatalf("%+v", err)
  1336  		}
  1337  		if len(locks) != 2 {
  1338  			tr.Fatalf("unexpected length: %d (expected: %d)", len(locks), 2)
  1339  		}
  1340  
  1341  		now := time.Now()
  1342  		param = &model.LockSearchParam{
  1343  			CreatedAt:          model.NewQueryChainer().GreaterThanOrEqual(now.Add(time.Second * 5 * -1)).LessThanOrEqual(now.Add(time.Second * 5)),
  1344  			IncludeSoftDeleted: true,
  1345  		}
  1346  		locks, err = lockRepo.Search(ctx, param, nil)
  1347  		if err != nil {
  1348  			tr.Fatalf("%+v", err)
  1349  		}
  1350  		if len(locks) != 6 {
  1351  			tr.Fatalf("unexpected length: %d (expected: %d)", len(locks), 6)
  1352  		}
  1353  
  1354  		param = &model.LockSearchParam{
  1355  			CreatedAt:          model.NewQueryChainer().Asc(),
  1356  			CursorLimit:        5,
  1357  			IncludeSoftDeleted: true,
  1358  		}
  1359  		locks, err = lockRepo.Search(ctx, param, nil)
  1360  		if err != nil {
  1361  			tr.Fatalf("%+v", err)
  1362  		}
  1363  
  1364  		if len(locks) != 5 {
  1365  			tr.Fatalf("unexpected length: %d (expected: %d)", len(locks), 5)
  1366  		}
  1367  
  1368  		param = &model.LockSearchParam{
  1369  			CreatedAt:          model.NewQueryChainer().Asc().StartAfter(locks[len(locks)-1].CreatedAt),
  1370  			CursorLimit:        5,
  1371  			IncludeSoftDeleted: true,
  1372  		}
  1373  		locks, err = lockRepo.Search(ctx, param, nil)
  1374  		if err != nil {
  1375  			tr.Fatalf("%+v", err)
  1376  		}
  1377  
  1378  		if len(locks) != 1 {
  1379  			tr.Fatalf("unexpected length: %d (expected: %d)", len(locks), 1)
  1380  		}
  1381  
  1382  		param = &model.LockSearchParam{
  1383  			CreatedAt:          model.NewQueryChainer().Asc().EndAt(locks[len(locks)-1].CreatedAt),
  1384  			IncludeSoftDeleted: true,
  1385  		}
  1386  		locks, err = lockRepo.Search(ctx, param, nil)
  1387  		if err != nil {
  1388  			tr.Fatalf("%+v", err)
  1389  		}
  1390  
  1391  		if len(locks) != 6 {
  1392  			tr.Fatalf("unexpected length: %d (expected: %d)", len(locks), 6)
  1393  		}
  1394  
  1395  		param = &model.LockSearchParam{
  1396  			CreatedAt:          model.NewQueryChainer().Asc().EndBefore(locks[len(locks)-1].CreatedAt),
  1397  			IncludeSoftDeleted: true,
  1398  		}
  1399  		locks, err = lockRepo.Search(ctx, param, nil)
  1400  		if err != nil {
  1401  			tr.Fatalf("%+v", err)
  1402  		}
  1403  
  1404  		if len(locks) != 5 {
  1405  			tr.Fatalf("unexpected length: %d (expected: %d)", len(locks), 5)
  1406  		}
  1407  	})
  1408  
  1409  	t.Run("update_builder", func(tr *testing.T) {
  1410  		l := &model.Lock{
  1411  			Text: text,
  1412  			Flag: nil,
  1413  			Meta: model.Meta{},
  1414  		}
  1415  
  1416  		id, err := lockRepo.Insert(ctx, l)
  1417  		if err != nil {
  1418  			tr.Fatalf("failed to put item: %+v", err)
  1419  		}
  1420  
  1421  		ids = append(ids, id)
  1422  
  1423  		flag := map[string]float64{"test": 123.456}
  1424  		hello := fmt.Sprintf("%s world", text)
  1425  
  1426  		t := time.NewTicker(1 * time.Millisecond)
  1427  		defer t.Stop()
  1428  		<-t.C
  1429  
  1430  		updateParam := &model.LockUpdateParam{
  1431  			Text:      hello,
  1432  			Flag:      flag,
  1433  			UpdatedAt: firestore.ServerTimestamp,
  1434  			Version:   firestore.Increment(1),
  1435  		}
  1436  
  1437  		if err = lockRepo.StrictUpdate(ctx, id, updateParam); err != nil {
  1438  			tr.Fatalf("failed to update item: %+v", err)
  1439  		}
  1440  
  1441  		ret, err := lockRepo.Get(ctx, id)
  1442  		if err != nil {
  1443  			tr.Fatalf("failed to get item: %+v", err)
  1444  		}
  1445  
  1446  		if ret.Text != hello {
  1447  			tr.Fatalf("unexpected Text: %s (expected: %s)", ret.Text, hello)
  1448  		}
  1449  
  1450  		if !reflect.DeepEqual(ret.Flag, flag) {
  1451  			tr.Fatalf("unexpected Flag: %v (expected: %v)", ret.Flag, flag)
  1452  		}
  1453  
  1454  		if ret.CreatedAt.Equal(ret.UpdatedAt) {
  1455  			tr.Fatalf("unexpected CreatedAt == UpdatedAt: %d == %d",
  1456  				ret.CreatedAt.Unix(), ret.UpdatedAt.Unix())
  1457  		}
  1458  
  1459  		if ret.UpdatedAt.Before(ret.CreatedAt) {
  1460  			tr.Fatalf("unexpected UpdatedAt > CreatedAt: %t (expected: %t)", ret.UpdatedAt.Before(ret.CreatedAt), ret.UpdatedAt.After(ret.CreatedAt))
  1461  		}
  1462  
  1463  		if ret.Version != 2 {
  1464  			tr.Fatalf("unexpected Version: %d (expected: %d)", ret.Version, 2)
  1465  		}
  1466  	})
  1467  
  1468  	t.Run("GetByXXX test", func(tr *testing.T) {
  1469  		l := &model.Lock{
  1470  			Text2: text,
  1471  		}
  1472  
  1473  		id, err := lockRepo.Insert(ctx, l)
  1474  		if err != nil {
  1475  			tr.Fatalf("failed to put item: %+v", err)
  1476  		}
  1477  
  1478  		l, err = lockRepo.GetByText2(ctx, text)
  1479  		if err != nil {
  1480  			tr.Fatalf("%+v", err)
  1481  		}
  1482  		if err = lockRepo.DeleteByID(ctx, id, model.DeleteOption{Mode: model.DeleteModeSoft}); err != nil {
  1483  			tr.Fatalf("%+v", err)
  1484  		}
  1485  		l, err = lockRepo.GetByText2(ctx, text)
  1486  		if !xerrors.Is(err, model.ErrNotFound) {
  1487  			tr.Fatalf("%+v", err)
  1488  		}
  1489  		l, err = lockRepo.GetByText2(ctx, text, model.GetOption{IncludeSoftDeleted: true})
  1490  		if err != nil {
  1491  			tr.Fatalf("%+v", err)
  1492  		}
  1493  	})
  1494  
  1495  	t.Run("UniqueConstraints", func(tr *testing.T) {
  1496  		l := &model.Lock{
  1497  			Text2: text + "2",
  1498  		}
  1499  
  1500  		if _, err := lockRepo.Insert(ctx, l); err != nil {
  1501  			tr.Fatalf("unexpected error: %+v", err)
  1502  		}
  1503  
  1504  		// Check if the documents in the Unique collection can be deleted.
  1505  		if err := lockRepo.DeleteByID(ctx, l.ID, model.DeleteOption{Mode: model.DeleteModeSoft}); err != nil {
  1506  			tr.Fatalf("unexpected err != nil: %+v", err)
  1507  		}
  1508  
  1509  		l = &model.Lock{
  1510  			Text2: text + "2",
  1511  		}
  1512  
  1513  		if _, err := lockRepo.Insert(ctx, l); err != nil {
  1514  			tr.Fatalf("unexpected error: %+v", err)
  1515  		}
  1516  	})
  1517  }