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  }