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 }