github.com/decred/politeia@v1.4.0/politeiawww/legacy/user/cockroachdb/cockroachdb_test.go (about) 1 // Copyright (c) 2020 The Decred developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 package cockroachdb 6 7 import ( 8 "database/sql/driver" 9 "encoding/binary" 10 "encoding/hex" 11 "encoding/json" 12 "errors" 13 "fmt" 14 "regexp" 15 "testing" 16 "time" 17 18 "github.com/DATA-DOG/go-sqlmock" 19 "github.com/decred/politeia/politeiad/api/v1/identity" 20 "github.com/decred/politeia/politeiawww/legacy/user" 21 "github.com/decred/politeia/util" 22 "github.com/google/uuid" 23 "github.com/jinzhu/gorm" 24 _ "github.com/jinzhu/gorm/dialects/postgres" 25 ) 26 27 // Custom go-sqlmock types for type assertion 28 type AnyBlob struct{} 29 type AnyTime struct{} 30 31 func (a AnyBlob) Match(v driver.Value) bool { 32 _, ok := v.([]byte) 33 return ok 34 } 35 36 func (a AnyTime) Match(v driver.Value) bool { 37 _, ok := v.(time.Time) 38 return ok 39 } 40 41 // Helpers 42 var ( 43 errSelect = fmt.Errorf("select user error") 44 errDelete = fmt.Errorf("delete user error") 45 ) 46 47 func newPaywallAddressIndex(t *testing.T, i uint64) *[]byte { 48 t.Helper() 49 50 index := make([]byte, 8) 51 binary.LittleEndian.PutUint64(index, i) 52 return &index 53 } 54 55 func newCdbUser(t *testing.T, cdb *cockroachdb) (User, []byte) { 56 t.Helper() 57 58 uuid := uuid.New() 59 u := user.User{ 60 ID: uuid, 61 Username: "test" + uuid.String(), 62 } 63 64 // Make user identity 65 fid, err := identity.New() 66 if err != nil { 67 t.Fatalf("%v", err) 68 } 69 id, err := user.NewIdentity(hex.EncodeToString(fid.Public.Key[:])) 70 if err != nil { 71 t.Fatalf("%v", err) 72 } 73 u.Identities = append(u.Identities, *id) 74 75 // Make user blob 76 eu, err := user.EncodeUser(u) 77 if err != nil { 78 t.Fatalf("%s", err) 79 } 80 eb, err := cdb.encrypt(user.VersionUser, eu) 81 if err != nil { 82 t.Fatalf("%s", err) 83 } 84 85 return convertUserFromUser(u, eb), eb 86 } 87 88 func setupTestDB(t *testing.T) (*cockroachdb, sqlmock.Sqlmock, func()) { 89 t.Helper() 90 91 db, mock, err := sqlmock.New() 92 if err != nil { 93 t.Fatalf("error %s while creating stub db conn", err) 94 } 95 96 gdb, err := gorm.Open("postgres", db) 97 if err != nil { 98 t.Fatalf("error %s while opening db with gorm", err) 99 } 100 101 b := []byte("random") 102 var key [32]byte 103 copy(key[:], b) 104 105 c := &cockroachdb{ 106 userDB: gdb, 107 encryptionKey: &key, 108 } 109 110 return c, mock, func() { 111 db.Close() 112 } 113 } 114 115 // Tests 116 func TestSetPaywallAddressIndex(t *testing.T) { 117 cdb, mock, close := setupTestDB(t) 118 defer close() 119 120 // Arguments 121 i := uint64(1) 122 index := newPaywallAddressIndex(t, i) 123 124 // Query 125 sql := `UPDATE "key_value" SET "value" = $1 WHERE "key_value"."key" = $2` 126 127 // Expectations 128 mock.ExpectBegin() 129 mock.ExpectExec(regexp.QuoteMeta(sql)). 130 WithArgs(index, keyPaywallAddressIndex). 131 WillReturnResult(sqlmock.NewResult(0, 1)) 132 mock.ExpectCommit() 133 134 // Execute method 135 err := cdb.SetPaywallAddressIndex(i) 136 if err != nil { 137 t.Errorf("SetPaywallAddressIndex unwanted err %s", err) 138 } 139 140 // Make sure query expectations were met 141 err = mock.ExpectationsWereMet() 142 if err != nil { 143 t.Errorf("unfulfilled expectations: %s", err) 144 } 145 } 146 147 func TestUserNew(t *testing.T) { 148 cdb, mock, close := setupTestDB(t) 149 defer close() 150 151 // Arguments 152 index := newPaywallAddressIndex(t, 1) 153 usr := user.User{ 154 Email: "test@test.com", 155 Username: "test", 156 } 157 158 // Queries 159 sqlSelectIndex := `SELECT * FROM "key_value" WHERE "key_value"."key" = $1` 160 sqlInsertUser := `INSERT INTO "users" ` + 161 `("id","username","blob","created_at","updated_at") ` + 162 `VALUES ($1,$2,$3,$4,$5) ` + 163 `RETURNING "users"."id"` 164 sqlUpdateIndex := `UPDATE "key_value" SET "value" = $1 ` + 165 `WHERE "key_value"."key" = $2` 166 167 // Success Expectations 168 mock.ExpectBegin() 169 // Select paywall address index 170 mock.ExpectQuery(regexp.QuoteMeta(sqlSelectIndex)). 171 WithArgs(keyPaywallAddressIndex). 172 WillReturnRows(sqlmock.NewRows([]string{"key", "value"}). 173 AddRow(keyPaywallAddressIndex, index)) 174 // Insert user to db 175 mock.ExpectQuery(regexp.QuoteMeta(sqlInsertUser)). 176 WithArgs(sqlmock.AnyArg(), usr.Username, AnyBlob{}, 177 AnyTime{}, AnyTime{}). 178 WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(usr.ID)) 179 // Update paywall address index 180 mock.ExpectExec(regexp.QuoteMeta(sqlUpdateIndex)). 181 WithArgs(sqlmock.AnyArg(), keyPaywallAddressIndex). 182 WillReturnResult(sqlmock.NewResult(0, 1)) 183 mock.ExpectCommit() 184 185 // Execute method 186 err := cdb.UserNew(usr) 187 if err != nil { 188 t.Errorf("UserNew unwanted error: %s", err) 189 } 190 191 // Negative Expectations 192 expectedError := user.ErrUserExists 193 mock.ExpectBegin() 194 mock.ExpectQuery(regexp.QuoteMeta(sqlSelectIndex)). 195 WithArgs(keyPaywallAddressIndex). 196 WillReturnRows(sqlmock.NewRows([]string{"key", "value"}). 197 AddRow(keyPaywallAddressIndex, index)) 198 // User already exists error 199 mock.ExpectQuery(regexp.QuoteMeta(sqlInsertUser)). 200 WithArgs(sqlmock.AnyArg(), usr.Username, AnyBlob{}, 201 AnyTime{}, AnyTime{}). 202 WillReturnError(expectedError) 203 mock.ExpectRollback() 204 205 // Execute method 206 err = cdb.UserNew(usr) 207 if err == nil { 208 t.Errorf("expecting error but there was none") 209 } 210 211 // Make sure we got the expected error 212 wantErr := fmt.Errorf("create user: %v", expectedError) 213 if err.Error() != wantErr.Error() { 214 t.Errorf("expecting error %s but got %s", expectedError, err) 215 } 216 217 // Make sure expectations were met for both success and failure 218 // conditions 219 err = mock.ExpectationsWereMet() 220 if err != nil { 221 t.Errorf("unfulfilled expectations: %s", err) 222 } 223 } 224 225 func TestUserUpdate(t *testing.T) { 226 cdb, mock, close := setupTestDB(t) 227 defer close() 228 229 // Arguments 230 id := uuid.New() 231 usr := user.User{ 232 ID: id, 233 Identities: []user.Identity{}, 234 Email: "test@test.com", 235 Username: "test", 236 } 237 238 // Query 239 sql := `UPDATE "users" ` + 240 `SET "username" = $1, "blob" = $2, "updated_at" = $3 ` + 241 `WHERE "users"."id" = $4` 242 243 // Success Expectations 244 mock.ExpectBegin() 245 mock.ExpectExec(regexp.QuoteMeta(sql)). 246 WithArgs(usr.Username, AnyBlob{}, AnyTime{}, usr.ID). 247 WillReturnResult(sqlmock.NewResult(1, 1)) 248 mock.ExpectCommit() 249 250 // Execute method 251 err := cdb.UserUpdate(usr) 252 if err != nil { 253 t.Errorf("UserUpdate unwanted error: %s", err) 254 } 255 256 // Make sure expectations were met 257 err = mock.ExpectationsWereMet() 258 if err != nil { 259 t.Errorf("unfulfilled expectations: %s", err) 260 } 261 } 262 263 func TestUserGetByUsername(t *testing.T) { 264 cdb, mock, close := setupTestDB(t) 265 defer close() 266 267 // Arguments 268 now := time.Now() 269 usr, blob := newCdbUser(t, cdb) 270 271 // Mock rows data 272 rows := sqlmock.NewRows([]string{ 273 "id", 274 "username", 275 "blob", 276 "created_at", 277 "updated_at", 278 }).AddRow(usr.ID, usr.Username, blob, now, now) 279 280 // Query 281 sql := `SELECT * FROM "users" WHERE (username = $1)` 282 283 // Success Expectations 284 mock.ExpectQuery(regexp.QuoteMeta(sql)). 285 WithArgs(usr.Username). 286 WillReturnRows(rows) 287 288 // Execute method 289 u, err := cdb.UserGetByUsername(usr.Username) 290 if err != nil { 291 t.Errorf("UserGetByUsername unwanted error: %s", err) 292 } 293 294 // Make sure correct user was fetched 295 if u.ID != usr.ID { 296 t.Errorf("expecting user of id %s but received %s", usr.ID, u.ID) 297 } 298 299 // Negative Expectations 300 randomUsername := "random" 301 expectedError := user.ErrUserNotFound 302 mock.ExpectQuery(regexp.QuoteMeta(sql)). 303 WithArgs(randomUsername). 304 WillReturnError(expectedError) 305 306 // Execute method 307 u, err = cdb.UserGetByUsername(randomUsername) 308 if err == nil { 309 t.Errorf("expecting error %s, but there was none", expectedError) 310 } 311 312 // Make sure no user was fetched 313 if u != nil { 314 t.Errorf("expecting nil user to be returned, but got user %s", u.ID) 315 } 316 317 // Make sure we got the expected error 318 if !errors.Is(err, expectedError) { 319 t.Errorf("expecting error %s but got %s", expectedError, err) 320 } 321 322 // Make sure expectations were met for both success and failure 323 // conditions 324 err = mock.ExpectationsWereMet() 325 if err != nil { 326 t.Errorf("unfulfilled expectations: %s", err) 327 } 328 } 329 330 func TestUserGetById(t *testing.T) { 331 cdb, mock, close := setupTestDB(t) 332 defer close() 333 334 // Arguments 335 now := time.Now() 336 usr, blob := newCdbUser(t, cdb) 337 338 // Mock rows data 339 rows := sqlmock.NewRows([]string{ 340 "id", 341 "username", 342 "blob", 343 "created_at", 344 "updated_at", 345 }).AddRow(usr.ID, usr.Username, blob, now, now) 346 347 // Query 348 sql := `SELECT * FROM "users" WHERE (id = $1)` 349 350 // Success Expectations 351 mock.ExpectQuery(regexp.QuoteMeta(sql)). 352 WithArgs(usr.ID). 353 WillReturnRows(rows) 354 355 // Execute method 356 u, err := cdb.UserGetById(usr.ID) 357 if err != nil { 358 t.Errorf("UserGetById unwanted error: %s", err) 359 } 360 361 // Make sure correct user was fetched 362 if u.ID != usr.ID { 363 t.Errorf("expecting user of id %s but received %s", usr.ID, u.ID) 364 } 365 366 // Negative Expectations 367 expectedError := user.ErrUserNotFound 368 randomID := uuid.New() 369 mock.ExpectQuery(regexp.QuoteMeta(sql)). 370 WithArgs(randomID). 371 WillReturnError(expectedError) 372 373 // Execute method 374 u, err = cdb.UserGetById(randomID) 375 if err == nil { 376 t.Errorf("expecting error %s, but there was none", expectedError) 377 } 378 379 // Make sure no user was fetched 380 if u != nil { 381 t.Errorf("expecting nil user to be returned, but got user %s", u.ID) 382 } 383 384 // Make sure we got the expected error 385 if !errors.Is(err, expectedError) { 386 t.Errorf("expecting error %s but got %s", expectedError, err) 387 } 388 389 // Make sure expectations were met for both success and failure 390 // conditions 391 err = mock.ExpectationsWereMet() 392 if err != nil { 393 t.Errorf("unfulfilled expectations: %s", err) 394 } 395 } 396 397 func TestUserGetByPubKey(t *testing.T) { 398 cdb, mock, close := setupTestDB(t) 399 defer close() 400 401 // Arguments 402 now := time.Now() 403 usr, blob := newCdbUser(t, cdb) 404 pubkey := usr.Identities[0].PublicKey 405 406 // Mock rows data 407 rows := sqlmock.NewRows([]string{ 408 "id", 409 "username", 410 "blob", 411 "created_at", 412 "updated_at", 413 }).AddRow(usr.ID, usr.Username, blob, now, now) 414 415 // Query 416 sql := `SELECT * FROM users ` + 417 `INNER JOIN identities ON users.id = identities.user_id ` + 418 `WHERE identities.public_key = $1` 419 420 // Success Expectations 421 mock.ExpectQuery(regexp.QuoteMeta(sql)). 422 WithArgs(pubkey). 423 WillReturnRows(rows) 424 425 // Execute method 426 ur, err := cdb.UserGetByPubKey(pubkey) 427 if err != nil { 428 t.Errorf("UserGetByPubKey unwanted error: %s", err) 429 } 430 431 // Make sure correct user was fetched 432 if ur.ID != usr.ID { 433 t.Errorf("expecting user of id %s but received %s", usr.ID, ur.ID) 434 } 435 436 // Negative Expectations 437 randomUsr, _ := newCdbUser(t, cdb) 438 randomPubkey := randomUsr.Identities[0].PublicKey 439 expectedError := user.ErrUserNotFound 440 mock.ExpectQuery(regexp.QuoteMeta(sql)). 441 WithArgs(randomPubkey). 442 WillReturnError(expectedError) 443 444 // Execute method 445 ur, err = cdb.UserGetByPubKey(randomPubkey) 446 if err == nil { 447 t.Errorf("expecting error user not found, but there was none") 448 } 449 450 // Make sure no user was fetched 451 if ur != nil { 452 t.Errorf("expecting nil user to be returned, but got user %s", ur.ID) 453 } 454 455 // Make sure we got the expected error 456 if !errors.Is(err, expectedError) { 457 t.Errorf("expecting error %s but got %s", expectedError, err) 458 } 459 460 // Make sure expectations were met for both success and failure 461 // conditions 462 err = mock.ExpectationsWereMet() 463 if err != nil { 464 t.Errorf("unfulfilled expectations: %s", err) 465 } 466 } 467 468 func TestUsersGetByPubKey(t *testing.T) { 469 cdb, mock, close := setupTestDB(t) 470 defer close() 471 472 // Arguments 473 now := time.Now() 474 usr, blob := newCdbUser(t, cdb) 475 pubkey := usr.Identities[0].PublicKey 476 477 // Mock data 478 rows := sqlmock.NewRows([]string{ 479 "id", 480 "username", 481 "blob", 482 "created_at", 483 "updated_at", 484 }).AddRow(usr.ID, usr.Username, blob, now, now) 485 486 // Query 487 sql := `SELECT * FROM users ` + 488 `INNER JOIN identities ON users.id = identities.user_id ` + 489 `WHERE identities.public_key IN ($1)` 490 491 // Success Expectations 492 mock.ExpectQuery(regexp.QuoteMeta(sql)). 493 WithArgs(pubkey). 494 WillReturnRows(rows) 495 496 // Execute method 497 ur, err := cdb.UsersGetByPubKey([]string{pubkey}) 498 if err != nil { 499 t.Errorf("UsersGetByPubKey unwanted error: %s", err) 500 } 501 502 // Make sure correct user was fetched 503 fetchedUser := ur[pubkey] 504 if fetchedUser.ID != usr.ID { 505 t.Errorf("expecting user of id %s but received %s", 506 usr.ID, fetchedUser.ID) 507 } 508 509 // Negative Expectations 510 randomUsr, _ := newCdbUser(t, cdb) 511 randomPubkey := randomUsr.Identities[0].PublicKey 512 expectedError := user.ErrUserNotFound 513 mock.ExpectQuery(regexp.QuoteMeta(sql)). 514 WithArgs(randomPubkey). 515 WillReturnError(expectedError) 516 517 // Execute method 518 ur, err = cdb.UsersGetByPubKey([]string{randomPubkey}) 519 if err == nil { 520 t.Errorf("expecting error but there was none") 521 } 522 523 // Make sure no user was fetched 524 if len(ur) != 0 { 525 t.Errorf("expecting nil user to be returned, but got user %s", 526 ur[randomPubkey].ID) 527 } 528 529 // Make sure we got the expected error 530 if !errors.Is(err, expectedError) { 531 t.Errorf("expecting error %s but got %s", expectedError, err) 532 } 533 534 // Make sure expectations were met for both success and failure 535 // conditions 536 err = mock.ExpectationsWereMet() 537 if err != nil { 538 t.Errorf("unfulfilled expectations: %s", err) 539 } 540 } 541 542 func TestAllUsers(t *testing.T) { 543 cdb, mock, close := setupTestDB(t) 544 defer close() 545 546 // Arguments 547 now := time.Now() 548 usr, blob := newCdbUser(t, cdb) 549 usr2, blob2 := newCdbUser(t, cdb) 550 551 // Query 552 sql := `SELECT * FROM "users"` 553 554 // Mock data 555 rows := sqlmock.NewRows([]string{ 556 "id", 557 "username", 558 "blob", 559 "created_at", 560 "updated_at", 561 }). 562 AddRow(usr.ID, usr.Username, blob, now, now). 563 AddRow(usr2.ID, usr2.Username, blob2, now, now) 564 565 // Success Expectations 566 mock.ExpectQuery(regexp.QuoteMeta(sql)). 567 WillReturnRows(rows) 568 569 // Execute method 570 var users []user.User 571 err := cdb.AllUsers(func(u *user.User) { 572 users = append(users, *u) 573 }) 574 if err != nil { 575 t.Errorf("AllUsers unwanted error: %s", err) 576 } 577 578 // Check if both mocked users were returned 579 if len(users) != 2 { 580 t.Errorf("did not return all users") 581 } 582 583 // Negative Expectations 584 expectedError := gorm.ErrRecordNotFound 585 mock.ExpectQuery(regexp.QuoteMeta(sql)). 586 WillReturnError(expectedError) 587 588 // Execute method 589 var us []user.User 590 err = cdb.AllUsers(func(u *user.User) { 591 us = append(us, *u) 592 }) 593 if err == nil { 594 t.Errorf("expecting error but there was none") 595 } 596 597 // Make sure no users were returned 598 if len(us) != 0 { 599 t.Errorf("expected no users but returned %v users", len(us)) 600 } 601 602 // Make sure we got the expected error 603 if !errors.Is(err, expectedError) { 604 t.Errorf("expecting error %s but got %s", expectedError, err) 605 } 606 607 // Make sure expectations were met for both success and failure 608 // conditions 609 err = mock.ExpectationsWereMet() 610 if err != nil { 611 t.Errorf("unfulfilled expectations: %s", err) 612 } 613 } 614 615 func TestEmailHistoriesSave(t *testing.T) { 616 cdb, mock, close := setupTestDB(t) 617 defer close() 618 619 // Arguments 620 userID := uuid.New() 621 histories := make(map[uuid.UUID]user.EmailHistory, 1) 622 histories[userID] = user.EmailHistory{ 623 Timestamps: []int64{time.Now().Unix()}, 624 LimitWarningSent: false, 625 } 626 627 // Queries 628 sqlSelect := `SELECT * FROM "email_histories" ` + 629 `WHERE "email_histories"."user_id" = $1` 630 631 sqlInsert := `INSERT INTO "email_histories" ` + 632 `("user_id","blob") ` + 633 `VALUES ($1,$2) RETURNING "email_histories"."user_id"` 634 635 // Success create expectations 636 mock.ExpectQuery(regexp.QuoteMeta(sqlSelect)). 637 WithArgs(userID). 638 WillReturnRows(sqlmock.NewRows([]string{})) 639 mock.ExpectBegin() 640 mock.ExpectQuery(regexp.QuoteMeta(sqlInsert)). 641 WithArgs(userID, AnyBlob{}). 642 WillReturnRows(sqlmock.NewRows([]string{"user_id"}).AddRow(userID)) 643 mock.ExpectCommit() 644 645 // Execute method 646 err := cdb.EmailHistoriesSave(histories) 647 if err != nil { 648 t.Errorf("EmailHistoriesSave unwanted error: %s", err) 649 } 650 651 // Mock data for updating an email history 652 rows := sqlmock.NewRows([]string{"user_id", "blob"}). 653 AddRow(userID, []byte{}) 654 655 // Query 656 sqlUpdate := `UPDATE "email_histories" ` + 657 `SET "blob" = $1 ` + 658 `WHERE "email_histories"."user_id" = $2` 659 660 // Success update expectations 661 mock.ExpectQuery(regexp.QuoteMeta(sqlSelect)). 662 WithArgs(userID). 663 WillReturnRows(rows) 664 mock.ExpectBegin() 665 mock.ExpectExec(regexp.QuoteMeta(sqlUpdate)). 666 WithArgs(AnyBlob{}, userID). 667 WillReturnResult(sqlmock.NewResult(1, 1)) 668 mock.ExpectCommit() 669 670 // Execute method 671 err = cdb.EmailHistoriesSave(histories) 672 if err != nil { 673 t.Errorf("EmailHistoriesSave unwanted error: %s", err) 674 } 675 676 // Negative expectations 677 mock.ExpectQuery(regexp.QuoteMeta(sqlSelect)). 678 WillReturnError(errSelect) 679 680 // Execute method 681 badHistories := make(map[uuid.UUID]user.EmailHistory, 1) 682 badHistories[uuid.New()] = user.EmailHistory{} 683 err = cdb.EmailHistoriesSave(badHistories) 684 if err == nil { 685 t.Errorf("expected error but there was none") 686 } 687 688 // Make sure expectations were met for both success and failure 689 // conditions 690 err = mock.ExpectationsWereMet() 691 if err != nil { 692 t.Errorf("unfulfilled expectations: %s", err) 693 } 694 } 695 696 func TestEmailHistoriesGet(t *testing.T) { 697 cdb, mock, close := setupTestDB(t) 698 defer close() 699 700 // Arguments 701 userID := uuid.New() 702 ts := time.Now().Unix() 703 history := user.EmailHistory{ 704 Timestamps: []int64{ts}, 705 LimitWarningSent: false, 706 } 707 hb, err := json.Marshal(history) 708 if err != nil { 709 t.Fatalf("%s", err) 710 } 711 eb, err := cdb.encrypt(user.VersionEmailHistory, hb) 712 if err != nil { 713 t.Fatalf("%s", err) 714 } 715 716 // Mock data 717 rows := sqlmock.NewRows([]string{"user_id", "blob"}). 718 AddRow(userID, eb) 719 720 // Query 721 sql := `SELECT * FROM "email_histories" WHERE (user_id IN ($1))` 722 723 // Success expectations 724 mock.ExpectQuery(regexp.QuoteMeta(sql)). 725 WithArgs(userID). 726 WillReturnRows(rows) 727 728 // Execute method 729 eh, err := cdb.EmailHistoriesGet([]uuid.UUID{userID}) 730 if err != nil { 731 t.Errorf("EmailHistoriesGet unwanted error: %s", err) 732 } 733 734 // Make sure correct history was returned 735 if ts != eh[userID].Timestamps[0] { 736 t.Errorf("expecting timestamp %d but got %d", 737 ts, eh[userID].Timestamps[0]) 738 } 739 740 // Negative expectations 741 randomUserID := uuid.New() 742 expectedError := errors.New("email history not found") 743 mock.ExpectQuery(regexp.QuoteMeta(sql)). 744 WithArgs(randomUserID). 745 WillReturnError(expectedError) 746 747 // Execute method 748 h, err := cdb.EmailHistoriesGet([]uuid.UUID{randomUserID}) 749 if err == nil { 750 t.Errorf("expected error but there was none") 751 } 752 753 // Make sure no sessions were returned 754 if h != nil { 755 t.Errorf("expected no email history but got %v", h) 756 } 757 758 // Make sure we got the expected error 759 if !errors.Is(err, expectedError) { 760 t.Errorf("expecting error %s but got %s", expectedError, err) 761 } 762 763 // Make sure expectations were met for both success and failure 764 // conditions 765 err = mock.ExpectationsWereMet() 766 if err != nil { 767 t.Errorf("unfulfilled expectations: %s", err) 768 } 769 } 770 771 func TestSessionSave(t *testing.T) { 772 cdb, mock, close := setupTestDB(t) 773 defer close() 774 775 // Arguments 776 session := user.Session{ 777 ID: "1", 778 UserID: uuid.New(), 779 CreatedAt: time.Now().Unix(), 780 Values: "", 781 } 782 sessionKey := hex.EncodeToString(util.Digest([]byte(session.ID))) 783 784 // Query 785 sqlSelect := `SELECT * FROM "sessions" WHERE (key = $1)` 786 787 sqlInsert := `INSERT INTO "sessions" ` + 788 `("key","user_id","created_at","blob") ` + 789 `VALUES ($1,$2,$3,$4) RETURNING "sessions"."key"` 790 791 // Success Create Expectations 792 mock.ExpectQuery(regexp.QuoteMeta(sqlSelect)). 793 WithArgs(sessionKey). 794 WillReturnRows(sqlmock.NewRows([]string{})) 795 mock.ExpectBegin() 796 mock.ExpectQuery(regexp.QuoteMeta(sqlInsert)). 797 WithArgs(sessionKey, session.UserID, session.CreatedAt, AnyBlob{}). 798 WillReturnRows(sqlmock.NewRows([]string{"key"}).AddRow(sessionKey)) 799 mock.ExpectCommit() 800 801 // Execute method 802 err := cdb.SessionSave(session) 803 if err != nil { 804 t.Errorf("SessionSave unwanted error: %s", err) 805 } 806 807 // Mock data for updating a user session 808 rows := sqlmock.NewRows([]string{"key", "user_id", "created_at", "blob"}). 809 AddRow(sessionKey, session.UserID, session.CreatedAt, []byte{}) 810 811 // Queries 812 sqlUpdate := `UPDATE "sessions" ` + 813 `SET "user_id" = $1, "created_at" = $2, "blob" = $3 ` + 814 `WHERE "sessions"."key" = $4` 815 816 // Success Update Expectations 817 mock.ExpectQuery(regexp.QuoteMeta(sqlSelect)). 818 WithArgs(sessionKey). 819 WillReturnRows(rows) 820 mock.ExpectBegin() 821 mock.ExpectExec(regexp.QuoteMeta(sqlUpdate)). 822 WithArgs(session.UserID, session.CreatedAt, AnyBlob{}, sessionKey). 823 WillReturnResult(sqlmock.NewResult(1, 1)) 824 mock.ExpectCommit() 825 826 // Execute method 827 err = cdb.SessionSave(session) 828 if err != nil { 829 t.Errorf("SessionSave unwanted error: %s", err) 830 } 831 832 // Negative Expectations 833 mock.ExpectQuery(regexp.QuoteMeta(sqlSelect)). 834 WillReturnError(errSelect) 835 836 // Execute method 837 err = cdb.SessionSave(user.Session{}) 838 if err == nil { 839 t.Errorf("expected error but there was none") 840 } 841 842 // Make sure expectations were met for both success and failure 843 // conditions 844 err = mock.ExpectationsWereMet() 845 if err != nil { 846 t.Errorf("unfulfilled expectations: %s", err) 847 } 848 } 849 850 func TestSessionGetByID(t *testing.T) { 851 cdb, mock, close := setupTestDB(t) 852 defer close() 853 854 // Arguments 855 session := user.Session{ 856 ID: "1", 857 UserID: uuid.New(), 858 CreatedAt: time.Now().Unix(), 859 Values: "", 860 } 861 sessionKey := hex.EncodeToString(util.Digest([]byte(session.ID))) 862 sb, err := user.EncodeSession(session) 863 if err != nil { 864 t.Fatalf("%s", err) 865 } 866 eb, err := cdb.encrypt(user.VersionSession, sb) 867 if err != nil { 868 t.Fatalf("%s", err) 869 } 870 871 // Mock data 872 rows := sqlmock.NewRows([]string{"key", "user_id", "created_at", "blob"}). 873 AddRow(sessionKey, session.UserID, session.CreatedAt, eb) 874 875 // Queries 876 sql := `SELECT * FROM "sessions" WHERE "sessions"."key" = $1` 877 878 // Success Expectations 879 mock.ExpectQuery(regexp.QuoteMeta(sql)). 880 WithArgs(sessionKey). 881 WillReturnRows(rows) 882 883 // Execute method 884 s, err := cdb.SessionGetByID(session.ID) 885 if err != nil { 886 t.Errorf("SessionGetByID unwanted error: %s", err) 887 } 888 889 // Make sure correct session was returned 890 if session.ID != s.ID { 891 t.Errorf("expecting session %s but got %s", session.ID, s.ID) 892 } 893 894 // Negative Expectations 895 randomID := "2" 896 randomKey := hex.EncodeToString(util.Digest([]byte(randomID))) 897 expectedError := user.ErrSessionNotFound 898 mock.ExpectQuery(regexp.QuoteMeta(sql)). 899 WithArgs(randomKey). 900 WillReturnError(expectedError) 901 902 // Execute method 903 s, err = cdb.SessionGetByID(randomID) 904 if err == nil { 905 t.Errorf("expected error but there was none") 906 } 907 908 // Make sure no sessions were returned 909 if s != nil { 910 t.Errorf("expected no session but got %v", s) 911 } 912 913 // Make sure we got the expected error 914 if !errors.Is(err, expectedError) { 915 t.Errorf("expecting error %s but got %s", expectedError, err) 916 } 917 918 // Make sure expectations were met for both success and failure 919 // conditions 920 err = mock.ExpectationsWereMet() 921 if err != nil { 922 t.Errorf("unfulfilled expectations: %s", err) 923 } 924 } 925 926 func TestSessionDeleteByID(t *testing.T) { 927 cdb, mock, close := setupTestDB(t) 928 defer close() 929 930 // Arguments 931 session := user.Session{ 932 ID: "1", 933 UserID: uuid.New(), 934 CreatedAt: time.Now().Unix(), 935 Values: "", 936 } 937 sessionKey := hex.EncodeToString(util.Digest([]byte(session.ID))) 938 sb, err := user.EncodeSession(session) 939 if err != nil { 940 t.Fatalf("%s", err) 941 } 942 eb, err := cdb.encrypt(user.VersionSession, sb) 943 if err != nil { 944 t.Fatalf("%s", err) 945 } 946 947 // Mock data 948 sqlmock.NewRows([]string{"key", "user_id", "created_at", "blob"}). 949 AddRow(sessionKey, session.UserID, session.CreatedAt, eb) 950 951 // Queries 952 sql := `DELETE FROM "sessions" WHERE "sessions"."key" = $1` 953 954 // Success Expectations 955 mock.ExpectBegin() 956 mock.ExpectExec(regexp.QuoteMeta(sql)). 957 WithArgs(sessionKey). 958 WillReturnResult(sqlmock.NewResult(1, 1)) 959 mock.ExpectCommit() 960 961 // Execute method 962 err = cdb.SessionDeleteByID(session.ID) 963 if err != nil { 964 t.Errorf("SessionDeleteByID unwanted error: %s", err) 965 } 966 967 // Negative Expectations 968 randomID := "random" 969 randomKey := hex.EncodeToString(util.Digest([]byte(randomID))) 970 mock.ExpectBegin() 971 mock.ExpectExec(regexp.QuoteMeta(sql)). 972 WithArgs(randomKey). 973 WillReturnError(errDelete) 974 mock.ExpectRollback() 975 976 // Execute method 977 err = cdb.SessionDeleteByID(randomID) 978 if err == nil { 979 t.Errorf("expected error but there was none") 980 } 981 982 // Make sure we got the expected error 983 if !errors.Is(err, errDelete) { 984 t.Errorf("expecting error %s but got %s", errDelete, err) 985 } 986 987 // Make sure expectations were met for both success and failure 988 // conditions 989 err = mock.ExpectationsWereMet() 990 if err != nil { 991 t.Errorf("unfulfilled expectations: %s", err) 992 } 993 } 994 995 func TestSessionsDeleteByUserID(t *testing.T) { 996 cdb, mock, close := setupTestDB(t) 997 defer close() 998 999 // Arguments 1000 session := user.Session{ 1001 ID: "1", 1002 UserID: uuid.New(), 1003 CreatedAt: time.Now().Unix(), 1004 Values: "", 1005 } 1006 sessionKey := hex.EncodeToString(util.Digest([]byte(session.ID))) 1007 sb, err := user.EncodeSession(session) 1008 if err != nil { 1009 t.Fatalf("%s", err) 1010 } 1011 eb, err := cdb.encrypt(user.VersionSession, sb) 1012 if err != nil { 1013 t.Fatalf("%s", err) 1014 } 1015 1016 // Mock data 1017 sqlmock.NewRows([]string{"key", "user_id", "created_at", "blob"}). 1018 AddRow(sessionKey, session.UserID, session.CreatedAt, eb) 1019 1020 // Queries 1021 sql := `DELETE FROM "sessions" WHERE (user_id = $1)` 1022 1023 // Success Expectations 1024 mock.ExpectBegin() 1025 mock.ExpectExec(regexp.QuoteMeta(sql)). 1026 WithArgs(session.UserID). 1027 WillReturnResult(sqlmock.NewResult(1, 1)) 1028 mock.ExpectCommit() 1029 1030 // Execute method 1031 err = cdb.SessionsDeleteByUserID(session.UserID, []string{}) 1032 if err != nil { 1033 t.Errorf("SessionsDeleteByUserID unwanted error: %s", err) 1034 } 1035 1036 // Negative Expectations 1037 randomID := uuid.New() 1038 mock.ExpectBegin() 1039 mock.ExpectExec(regexp.QuoteMeta(sql)). 1040 WithArgs(randomID). 1041 WillReturnError(errDelete) 1042 mock.ExpectRollback() 1043 1044 // Execute method 1045 err = cdb.SessionsDeleteByUserID(randomID, []string{}) 1046 if err == nil { 1047 t.Errorf("expecting error but got none") 1048 } 1049 1050 // Make sure we got the expected error 1051 if !errors.Is(err, errDelete) { 1052 t.Errorf("expecting error %s but got %s", errDelete, err) 1053 } 1054 1055 // Make sure expectations were met for both success and failure 1056 // conditions 1057 err = mock.ExpectationsWereMet() 1058 if err != nil { 1059 t.Errorf("unfulfilled expectations: %s", err) 1060 } 1061 }