github.com/hzck/speedroute@v0.0.0-20201115191102-403b7d0e443f/main_test.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"net/http"
     8  	"net/http/httptest"
     9  	"os"
    10  	"regexp"
    11  	"strings"
    12  	"testing"
    13  	"time"
    14  
    15  	model "github.com/hzck/speedroute/model"
    16  )
    17  
    18  var app App
    19  
    20  func TestMain(m *testing.M) {
    21  	app.InitConfigFile()
    22  	app.InitDB()
    23  	app.InitRoutes()
    24  	code := m.Run()
    25  	os.Exit(code)
    26  }
    27  
    28  func TestCreateAccount(t *testing.T) {
    29  	username := "valid_username_7"
    30  	password := "val!dP@s5word"
    31  
    32  	req := createPostRequestForCreateAccount(username, password)
    33  
    34  	response := executeRequest(req)
    35  	defer clearAccountInDB(username)
    36  
    37  	checkResponseCode(t, http.StatusCreated, response.Code)
    38  
    39  	var a model.Account
    40  	query := "SELECT * FROM account WHERE username=$1"
    41  	err := app.Dbpool.QueryRow(context.Background(), query, username).Scan(&a.ID, &a.Username, &a.Password, &a.Created, &a.LastUpdated)
    42  	if err != nil {
    43  		panic(err)
    44  	}
    45  	if a.ID <= 0 {
    46  		t.Errorf("ID field is not valid")
    47  	}
    48  	if a.Username != username {
    49  		t.Errorf("Username '%s' is not the expected '%s'", a.Username, username)
    50  	}
    51  	match, _ := regexp.MatchString("^\\$argon2id\\$v=19\\$m=65536,t=8,p=1\\$.{22}\\$.{43}$", a.Password)
    52  	if !match {
    53  		t.Errorf("Password field is not set")
    54  	}
    55  	if a.Created.IsZero() {
    56  		t.Errorf("Created field is not set")
    57  	}
    58  	if a.LastUpdated.IsZero() {
    59  		t.Errorf("LastUpdated field is not set")
    60  	}
    61  }
    62  
    63  func TestCreateAccountInvalidJSON(t *testing.T) {
    64  	req := createPostRequestForCreateAccount("\",{invalidjson", "val!dP@s5word")
    65  	response := executeRequest(req)
    66  	checkResponseCode(t, http.StatusBadRequest, response.Code)
    67  }
    68  
    69  func TestCreateAccountUsernameNotPopulated(t *testing.T) {
    70  	req := createPostRequestForCreateAccount("", "val!dP@s5word")
    71  	response := executeRequest(req)
    72  	checkResponseCode(t, http.StatusBadRequest, response.Code)
    73  }
    74  
    75  func TestCreateAccountPasswordNotPopulated(t *testing.T) {
    76  	req := createPostRequestForCreateAccount("passwordnotpopulated", "")
    77  	response := executeRequest(req)
    78  	checkResponseCode(t, http.StatusBadRequest, response.Code)
    79  }
    80  
    81  func TestCreateAccountDuplicateUsername(t *testing.T) {
    82  	username := "duplicate"
    83  	createAccountInDB(username)
    84  	defer clearAccountInDB(username)
    85  	req := createPostRequestForCreateAccount(username, "val!dP@s5word")
    86  	response := executeRequest(req)
    87  	checkResponseCode(t, http.StatusConflict, response.Code)
    88  }
    89  
    90  func TestCreateAccountLowerCaseUsername(t *testing.T) {
    91  	username := "CaMeLcAsE_8"
    92  	createAccountInDB(strings.ToLower(username))
    93  	defer clearAccountInDB(strings.ToLower(username))
    94  	req := createPostRequestForCreateAccount(username, "val!dP@s5word")
    95  	response := executeRequest(req)
    96  	checkResponseCode(t, http.StatusConflict, response.Code)
    97  }
    98  
    99  func TestCreateAccountUsernameTooShort(t *testing.T) {
   100  	req := createPostRequestForCreateAccount("1", "val!dP@s5word")
   101  	response := executeRequest(req)
   102  	checkResponseCode(t, http.StatusBadRequest, response.Code)
   103  }
   104  
   105  func TestCreateAccountUsernameTooLong(t *testing.T) {
   106  	req := createPostRequestForCreateAccount("this_user_is_31_characters_long", "val!dP@s5word")
   107  	response := executeRequest(req)
   108  	checkResponseCode(t, http.StatusBadRequest, response.Code)
   109  }
   110  
   111  func TestCreateAccountPasswordTooShort(t *testing.T) {
   112  	req := createPostRequestForCreateAccount("shortpassword", "tooshrt")
   113  	response := executeRequest(req)
   114  	checkResponseCode(t, http.StatusBadRequest, response.Code)
   115  }
   116  
   117  func TestCreateAccountInvalidCharacters(t *testing.T) {
   118  	// Note: Not all invalid characters are tested for obvious reasons
   119  	invalidChars := `!"#ยค%&/()=?<>|[]{}+-.,;:^*`
   120  	for _, ch := range invalidChars {
   121  		req := createPostRequestForCreateAccount(fmt.Sprintf("username_invalid%c", ch), "val!dP@s5word")
   122  		response := executeRequest(req)
   123  		checkResponseCode(t, http.StatusBadRequest, response.Code)
   124  	}
   125  }
   126  
   127  func createPostRequestForCreateAccount(username, password string) *http.Request {
   128  	usernameJSON := ""
   129  	if username != "" {
   130  		usernameJSON = `"username":"` + username + `"`
   131  	}
   132  	passwordJSON := ""
   133  	if password != "" {
   134  		passwordJSON = `"password":"` + password + `"`
   135  	}
   136  	commaJSON := ""
   137  	if usernameJSON != "" && passwordJSON != "" {
   138  		commaJSON = ","
   139  	}
   140  	var jsonStr = []byte("{" + usernameJSON + commaJSON + passwordJSON + "}")
   141  	req, err := http.NewRequest("POST", "/account", bytes.NewBuffer(jsonStr))
   142  	if err != nil {
   143  		panic(err)
   144  	}
   145  	req.Header.Set("Content-Type", "application/json")
   146  	return req
   147  }
   148  
   149  func executeRequest(req *http.Request) *httptest.ResponseRecorder {
   150  	rr := httptest.NewRecorder()
   151  	app.Router.ServeHTTP(rr, req)
   152  	return rr
   153  }
   154  
   155  func checkResponseCode(t *testing.T, expected, actual int) {
   156  	if expected != actual {
   157  		t.Errorf("Expected response code %d. Got %d\n", expected, actual)
   158  	}
   159  }
   160  
   161  func createAccountInDB(username string) {
   162  	query := "INSERT INTO account (username, password, created, last_updated) VALUES ($1, $2, $3, $3)"
   163  	_, err := app.Dbpool.Exec(context.Background(), query, username, "password", time.Now())
   164  	if err != nil {
   165  		panic(err)
   166  	}
   167  }
   168  
   169  func clearAccountInDB(username string) {
   170  	query := "DELETE FROM account WHERE username=$1"
   171  	_, err := app.Dbpool.Exec(context.Background(), query, username)
   172  	if err != nil {
   173  		panic(err)
   174  	}
   175  }