github.com/decred/politeia@v1.4.0/politeiawww/legacy/user/mysql/mysql_test.go (about)

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