package tests

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"geeks-accelerator/oss/saas-starter-kit/internal/mid"
	"geeks-accelerator/oss/saas-starter-kit/internal/platform/auth"
	"geeks-accelerator/oss/saas-starter-kit/internal/platform/tests"
	"geeks-accelerator/oss/saas-starter-kit/internal/platform/web"
	"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
	"geeks-accelerator/oss/saas-starter-kit/internal/user"
	"geeks-accelerator/oss/saas-starter-kit/internal/user_account"
	"geeks-accelerator/oss/saas-starter-kit/internal/user_auth"
	"github.com/pborman/uuid"
)

type mockUser struct {
	*user.User
	password string
}

func mockUserCreateRequest() user.UserCreateRequest {
	return user.UserCreateRequest{
		FirstName:       "Lee",
		LastName:        "Brown",
		Email:           uuid.NewRandom().String() + "@geeksinthewoods.com",
		Password:        "akTechFr0n!ier",
		PasswordConfirm: "akTechFr0n!ier",
	}
}

// mockUser creates a new user for testing and associates it with the supplied account ID.
func newMockUser(accountID string, role user_account.UserAccountRole) mockUser {
	req := mockUserCreateRequest()
	u, err := appCtx.UserRepo.Create(tests.Context(), auth.Claims{}, req, time.Now().UTC().AddDate(-1, -1, -1))
	if err != nil {
		panic(err)
	}

	_, err = appCtx.UserAccountRepo.Create(tests.Context(), auth.Claims{}, user_account.UserAccountCreateRequest{
		UserID:    u.ID,
		AccountID: accountID,
		Roles:     []user_account.UserAccountRole{role},
	}, time.Now().UTC().AddDate(-1, -1, -1))
	if err != nil {
		panic(err)
	}

	return mockUser{
		User:     u,
		password: req.Password,
	}
}

// TestUserCRUDAdmin tests all the user CRUD endpoints using an user with role admin.
func TestUserCRUDAdmin(t *testing.T) {
	defer tests.Recover(t)

	tr := roleTests[auth.RoleAdmin]

	// Add claims to the context for the user.
	ctx := context.WithValue(tests.Context(), auth.Key, tr.Claims)

	// Test create.
	var created user.UserResponse
	{
		expectedStatus := http.StatusCreated

		req := mockUserCreateRequest()
		rt := requestTest{
			fmt.Sprintf("Create %d w/role %s", expectedStatus, tr.Role),
			http.MethodPost,
			"/v1/users",
			req,
			tr.Token,
			tr.Claims,
			expectedStatus,
			nil,
		}
		t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)

		w, ok := executeRequestTest(t, rt, ctx)
		if !ok {
			t.Fatalf("\t%s\tExecute request failed.", tests.Failed)
		}
		t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)

		var actual user.UserResponse
		if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
			t.Logf("\t\tGot error : %+v", err)
			t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
		}
		created = actual

		expectedMap := map[string]interface{}{
			"updated_at": web.NewTimeResponse(ctx, actual.UpdatedAt.Value),
			"id":         actual.ID,
			"email":      req.Email,
			"timezone":   actual.Timezone,
			"created_at": web.NewTimeResponse(ctx, actual.CreatedAt.Value),
			"first_name": req.FirstName,
			"last_name":  req.LastName,
			"name":       req.FirstName + " " + req.LastName,
			"gravatar":   web.NewGravatarResponse(ctx, actual.Email),
		}

		var expected user.UserResponse
		if err := decodeMapToStruct(expectedMap, &expected); err != nil {
			t.Logf("\t\tGot error : %+v\nActual results to format expected : \n", err)
			printResultMap(ctx, w.Body.Bytes()) // used to help format expectedMap
			t.Fatalf("\t%s\tDecode expected failed.", tests.Failed)
		}

		if diff := cmpDiff(t, actual, expected); diff {
			if len(expectedMap) == 0 {
				printResultMap(ctx, w.Body.Bytes()) // used to help format expectedMap
			}
			t.Fatalf("\t%s\tReceived expected result.", tests.Failed)
		}
		t.Logf("\t%s\tReceived expected result.", tests.Success)

		// Only for user creation do we need to do this.
		_, err := appCtx.UserAccountRepo.Create(tests.Context(), auth.Claims{}, user_account.UserAccountCreateRequest{
			UserID:    actual.ID,
			AccountID: tr.Account.ID,
			Roles:     []user_account.UserAccountRole{user_account.UserAccountRole_User},
		}, time.Now().UTC().AddDate(-1, -1, -1))
		if err != nil {
			t.Fatalf("\t%s\tLink user to account.", tests.Failed)
		}
	}

	// Test read.
	{
		expectedStatus := http.StatusOK

		rt := requestTest{
			fmt.Sprintf("Read %d w/role %s", expectedStatus, tr.Role),
			http.MethodGet,
			fmt.Sprintf("/v1/users/%s", created.ID),
			nil,
			tr.Token,
			tr.Claims,
			expectedStatus,
			nil,
		}
		t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)

		w, ok := executeRequestTest(t, rt, ctx)
		if !ok {
			t.Fatalf("\t%s\tExecute request failed.", tests.Failed)
		}
		t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)

		var actual user.UserResponse
		if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
			t.Logf("\t\tGot error : %+v", err)
			t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
		}

		if diff := cmpDiff(t, actual, created); diff {
			t.Fatalf("\t%s\tReceived expected result.", tests.Failed)
		}
		t.Logf("\t%s\tReceived expected result.", tests.Success)
	}

	// Test Read with random ID.
	{
		expectedStatus := http.StatusNotFound

		randID := uuid.NewRandom().String()
		rt := requestTest{
			fmt.Sprintf("Read %d w/role %s using random ID", expectedStatus, tr.Role),
			http.MethodGet,
			fmt.Sprintf("/v1/users/%s", randID),
			nil,
			tr.Token,
			tr.Claims,
			expectedStatus,
			nil,
		}
		t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)

		w, ok := executeRequestTest(t, rt, ctx)
		if !ok {
			t.Fatalf("\t%s\tExecute request failed.", tests.Failed)
		}
		t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)

		var actual weberror.ErrorResponse
		if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
			t.Logf("\t\tGot error : %+v", err)
			t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
		}

		expected := weberror.ErrorResponse{
			StatusCode: expectedStatus,
			Error:      http.StatusText(expectedStatus),
			Details:    fmt.Sprintf("user %s not found: Entity not found", randID),
			StackTrace: actual.StackTrace,
		}

		if diff := cmpDiff(t, actual, expected); diff {
			t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
		}
		t.Logf("\t%s\tReceived expected error.", tests.Success)
	}

	// Test Read with forbidden ID.
	{
		expectedStatus := http.StatusNotFound

		rt := requestTest{
			fmt.Sprintf("Read %d w/role %s using forbidden ID", expectedStatus, tr.Role),
			http.MethodGet,
			fmt.Sprintf("/v1/users/%s", tr.ForbiddenUser.ID),
			nil,
			tr.Token,
			tr.Claims,
			expectedStatus,
			nil,
		}
		t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)

		w, ok := executeRequestTest(t, rt, ctx)
		if !ok {
			t.Fatalf("\t%s\tExecute request failed.", tests.Failed)
		}
		t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)

		var actual weberror.ErrorResponse
		if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
			t.Logf("\t\tGot error : %+v", err)
			t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
		}

		expected := weberror.ErrorResponse{
			StatusCode: expectedStatus,
			Error:      http.StatusText(expectedStatus),
			Details:    fmt.Sprintf("user %s not found: Entity not found", tr.ForbiddenUser.ID),
			StackTrace: actual.StackTrace,
		}

		if diff := cmpDiff(t, actual, expected); diff {
			t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
		}
		t.Logf("\t%s\tReceived expected error.", tests.Success)
	}

	// Test update.
	{
		expectedStatus := http.StatusNoContent

		newName := uuid.NewRandom().String()
		rt := requestTest{
			fmt.Sprintf("Update %d w/role %s", expectedStatus, tr.Role),
			http.MethodPatch,
			"/v1/users",
			user.UserUpdateRequest{
				ID:        created.ID,
				FirstName: &newName,
			},
			tr.Token,
			tr.Claims,
			expectedStatus,
			nil,
		}
		t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)

		w, ok := executeRequestTest(t, rt, ctx)
		if !ok {
			t.Fatalf("\t%s\tExecute request failed.", tests.Failed)
		}
		t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)

		if len(w.Body.String()) != 0 {
			if diff := cmpDiff(t, w.Body.Bytes(), nil); diff {
				t.Fatalf("\t%s\tReceived expected empty.", tests.Failed)
			}
		}
		t.Logf("\t%s\tReceived expected empty.", tests.Success)
	}

	// Test update password.
	{
		expectedStatus := http.StatusNoContent

		newPass := uuid.NewRandom().String()
		rt := requestTest{
			fmt.Sprintf("Update password %d w/role %s", expectedStatus, tr.Role),
			http.MethodPatch,
			"/v1/users/password",
			user.UserUpdatePasswordRequest{
				ID:              created.ID,
				Password:        newPass,
				PasswordConfirm: newPass,
			},
			tr.Token,
			tr.Claims,
			expectedStatus,
			nil,
		}
		t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)

		w, ok := executeRequestTest(t, rt, ctx)
		if !ok {
			t.Fatalf("\t%s\tExecute request failed.", tests.Failed)
		}
		t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)

		if len(w.Body.String()) != 0 {
			if diff := cmpDiff(t, w.Body.Bytes(), nil); diff {
				t.Fatalf("\t%s\tReceived expected empty.", tests.Failed)
			}
		}
		t.Logf("\t%s\tReceived expected empty.", tests.Success)
	}

	// Test archive.
	{
		expectedStatus := http.StatusNoContent

		rt := requestTest{
			fmt.Sprintf("Archive %d w/role %s", expectedStatus, tr.Role),
			http.MethodPatch,
			"/v1/users/archive",
			user.UserArchiveRequest{
				ID: created.ID,
			},
			tr.Token,
			tr.Claims,
			expectedStatus,
			nil,
		}
		t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)

		w, ok := executeRequestTest(t, rt, ctx)
		if !ok {
			t.Fatalf("\t%s\tExecute request failed.", tests.Failed)
		}
		t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)

		if len(w.Body.String()) != 0 {
			if diff := cmpDiff(t, w.Body.Bytes(), nil); diff {
				t.Fatalf("\t%s\tReceived expected empty.", tests.Failed)
			}
		}
		t.Logf("\t%s\tReceived expected empty.", tests.Success)
	}

	// Test delete.
	{
		expectedStatus := http.StatusNoContent

		rt := requestTest{
			fmt.Sprintf("Delete %d w/role %s", expectedStatus, tr.Role),
			http.MethodDelete,
			fmt.Sprintf("/v1/users/%s", created.ID),
			nil,
			tr.Token,
			tr.Claims,
			expectedStatus,
			nil,
		}
		t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)

		w, ok := executeRequestTest(t, rt, ctx)
		if !ok {
			t.Fatalf("\t%s\tExecute request failed.", tests.Failed)
		}
		t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)

		if len(w.Body.String()) != 0 {
			if diff := cmpDiff(t, w.Body.Bytes(), nil); diff {
				t.Fatalf("\t%s\tReceived expected empty.", tests.Failed)
			}
		}
		t.Logf("\t%s\tReceived expected empty.", tests.Success)
	}

	// Test switch account.
	{
		expectedStatus := http.StatusOK

		newAccount := newMockSignup().account
		rt := requestTest{
			fmt.Sprintf("Switch account %d w/role %s", expectedStatus, tr.Role),
			http.MethodPatch,
			fmt.Sprintf("/v1/users/switch-account/%s", newAccount.ID),
			nil,
			tr.Token,
			tr.Claims,
			expectedStatus,
			nil,
		}
		t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)

		_, err := appCtx.UserAccountRepo.Create(tests.Context(), auth.Claims{}, user_account.UserAccountCreateRequest{
			UserID:    tr.User.ID,
			AccountID: newAccount.ID,
			Roles:     []user_account.UserAccountRole{user_account.UserAccountRole_User},
		}, time.Now().UTC().AddDate(-1, -1, -1))
		if err != nil {
			t.Fatalf("\t%s\tAdd user to account failed.", tests.Failed)
		}

		w, ok := executeRequestTest(t, rt, ctx)
		if !ok {
			t.Fatalf("\t%s\tExecute request failed.", tests.Failed)
		}
		t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)

		var actual map[string]interface{}
		if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
			t.Logf("\t\tGot error : %+v", err)
			t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
		}

		// This is just for response format validation, will verify account from claims.
		expected := map[string]interface{}{
			"access_token": actual["access_token"],
			"token_type":   actual["token_type"],
			"expiry":       actual["expiry"],
			"ttl":          actual["ttl"],
			"user_id":      tr.User.ID,
			"account_id":   newAccount.ID,
		}

		if diff := cmpDiff(t, actual, expected); diff {
			t.Fatalf("\t%s\tReceived expected result.", tests.Failed)
		}
		t.Logf("\t%s\tReceived expected result.", tests.Success)

		newClaims, err := authenticator.ParseClaims(actual["access_token"].(string))
		if err != nil {
			t.Logf("\t\tGot error : %+v", err)
			t.Fatalf("\t%s\tParse claims failed.", tests.Failed)
		} else if newClaims.Audience != newAccount.ID {
			t.Logf("\t\tGot : %+v", newClaims.Audience)
			t.Logf("\t\tExpected : %+v", newAccount.ID)
			t.Fatalf("\t%s\tParse claims expected audience to match new account.", tests.Failed)
		} else if newClaims.Subject != tr.User.ID {
			t.Logf("\t\tGot : %+v", newClaims.Subject)
			t.Logf("\t\tExpected : %+v", tr.User.ID)
			t.Fatalf("\t%s\tParse claims expected Subject to match user.", tests.Failed)
		}
		t.Logf("\t%s\tParse claims valid.", tests.Success)
	}
}

// TestUserCRUDUser tests all the user CRUD endpoints using an user with role user.
func TestUserCRUDUser(t *testing.T) {
	defer tests.Recover(t)

	tr := roleTests[auth.RoleUser]

	// Add claims to the context for the user.
	ctx := context.WithValue(tests.Context(), auth.Key, tr.Claims)

	// Test create.
	{
		expectedStatus := http.StatusForbidden

		req := mockUserCreateRequest()
		rt := requestTest{
			fmt.Sprintf("Create %d w/role %s", expectedStatus, tr.Role),
			http.MethodPost,
			"/v1/users",
			req,
			tr.Token,
			tr.Claims,
			expectedStatus,
			nil,
		}
		t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)

		w, ok := executeRequestTest(t, rt, ctx)
		if !ok {
			t.Fatalf("\t%s\tExecute request failed.", tests.Failed)
		}
		t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)

		var actual weberror.ErrorResponse
		if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
			t.Logf("\t\tGot error : %+v", err)
			t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
		}

		expected := mid.ErrorForbidden(ctx).(*weberror.Error).Response(ctx, false)
		expected.StackTrace = actual.StackTrace

		if diff := cmpDiff(t, actual, expected); diff {
			t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
		}
		t.Logf("\t%s\tReceived expected error.", tests.Success)
	}

	// Since role doesn't support create, bypass auth to test other endpoints.
	created := newMockUser(tr.Account.ID, user_account.UserAccountRole_User).Response(ctx)

	// Test read.
	{
		expectedStatus := http.StatusOK

		rt := requestTest{
			fmt.Sprintf("Read %d w/role %s", expectedStatus, tr.Role),
			http.MethodGet,
			fmt.Sprintf("/v1/users/%s", created.ID),
			nil,
			tr.Token,
			tr.Claims,
			expectedStatus,
			nil,
		}
		t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)

		w, ok := executeRequestTest(t, rt, ctx)
		if !ok {
			t.Fatalf("\t%s\tExecute request failed.", tests.Failed)
		}
		t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)

		var actual *user.UserResponse
		if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
			t.Logf("\t\tGot error : %+v", err)
			t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
		}

		if diff := cmpDiff(t, actual, created); diff {
			t.Fatalf("\t%s\tReceived expected result.", tests.Failed)
		}
		t.Logf("\t%s\tReceived expected result.", tests.Success)
	}

	// Test Read with random ID.
	{
		expectedStatus := http.StatusNotFound

		randID := uuid.NewRandom().String()
		rt := requestTest{
			fmt.Sprintf("Read %d w/role %s using random ID", expectedStatus, tr.Role),
			http.MethodGet,
			fmt.Sprintf("/v1/users/%s", randID),
			nil,
			tr.Token,
			tr.Claims,
			expectedStatus,
			nil,
		}
		t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)

		w, ok := executeRequestTest(t, rt, ctx)
		if !ok {
			t.Fatalf("\t%s\tExecute request failed.", tests.Failed)
		}
		t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)

		var actual weberror.ErrorResponse
		if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
			t.Logf("\t\tGot error : %+v", err)
			t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
		}

		expected := weberror.ErrorResponse{
			StatusCode: expectedStatus,
			Error:      http.StatusText(expectedStatus),
			Details:    fmt.Sprintf("user %s not found: Entity not found", randID),
			StackTrace: actual.StackTrace,
		}

		if diff := cmpDiff(t, actual, expected); diff {
			t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
		}
		t.Logf("\t%s\tReceived expected error.", tests.Success)
	}

	// Test Read with forbidden ID.
	{
		expectedStatus := http.StatusNotFound

		rt := requestTest{
			fmt.Sprintf("Read %d w/role %s using forbidden ID", expectedStatus, tr.Role),
			http.MethodGet,
			fmt.Sprintf("/v1/users/%s", tr.ForbiddenUser.ID),
			nil,
			tr.Token,
			tr.Claims,
			expectedStatus,
			nil,
		}
		t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)

		w, ok := executeRequestTest(t, rt, ctx)
		if !ok {
			t.Fatalf("\t%s\tExecute request failed.", tests.Failed)
		}
		t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)

		var actual weberror.ErrorResponse
		if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
			t.Logf("\t\tGot error : %+v", err)
			t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
		}

		expected := weberror.ErrorResponse{
			StatusCode: expectedStatus,
			Error:      http.StatusText(expectedStatus),
			Details:    fmt.Sprintf("user %s not found: Entity not found", tr.ForbiddenUser.ID),
			StackTrace: actual.StackTrace,
		}

		if diff := cmpDiff(t, actual, expected); diff {
			t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
		}
		t.Logf("\t%s\tReceived expected error.", tests.Success)
	}

	// Test update.
	{
		expectedStatus := http.StatusForbidden

		newName := uuid.NewRandom().String()
		rt := requestTest{
			fmt.Sprintf("Update %d w/role %s", expectedStatus, tr.Role),
			http.MethodPatch,
			"/v1/users",
			user.UserUpdateRequest{
				ID:        created.ID,
				FirstName: &newName,
			},
			tr.Token,
			tr.Claims,
			expectedStatus,
			nil,
		}
		t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)

		w, ok := executeRequestTest(t, rt, ctx)
		if !ok {
			t.Fatalf("\t%s\tExecute request failed.", tests.Failed)
		}
		t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)

		var actual weberror.ErrorResponse
		if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
			t.Logf("\t\tGot error : %+v", err)
			t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
		}

		expected := weberror.ErrorResponse{
			StatusCode: expectedStatus,
			Error:      http.StatusText(expectedStatus),
			Details:    user.ErrForbidden.Error(),
			StackTrace: actual.StackTrace,
		}

		if diff := cmpDiff(t, actual, expected); diff {
			t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
		}
		t.Logf("\t%s\tReceived expected error.", tests.Success)
	}

	// Test update password.
	{
		expectedStatus := http.StatusForbidden

		newPass := uuid.NewRandom().String()
		rt := requestTest{
			fmt.Sprintf("Update password %d w/role %s", expectedStatus, tr.Role),
			http.MethodPatch,
			"/v1/users/password",
			user.UserUpdatePasswordRequest{
				ID:              created.ID,
				Password:        newPass,
				PasswordConfirm: newPass,
			},
			tr.Token,
			tr.Claims,
			expectedStatus,
			nil,
		}
		t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)

		w, ok := executeRequestTest(t, rt, ctx)
		if !ok {
			t.Fatalf("\t%s\tExecute request failed.", tests.Failed)
		}
		t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)

		var actual weberror.ErrorResponse
		if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
			t.Logf("\t\tGot error : %+v", err)
			t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
		}

		expected := weberror.ErrorResponse{
			StatusCode: expectedStatus,
			Error:      http.StatusText(expectedStatus),
			Details:    user.ErrForbidden.Error(),
			StackTrace: actual.StackTrace,
		}

		if diff := cmpDiff(t, actual, expected); diff {
			t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
		}
		t.Logf("\t%s\tReceived expected error.", tests.Success)
	}

	// Test archive.
	{
		expectedStatus := http.StatusForbidden

		rt := requestTest{
			fmt.Sprintf("Archive %d w/role %s", expectedStatus, tr.Role),
			http.MethodPatch,
			"/v1/users/archive",
			user.UserArchiveRequest{
				ID: created.ID,
			},
			tr.Token,
			tr.Claims,
			expectedStatus,
			nil,
		}
		t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)

		w, ok := executeRequestTest(t, rt, ctx)
		if !ok {
			t.Fatalf("\t%s\tExecute request failed.", tests.Failed)
		}
		t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)

		var actual weberror.ErrorResponse
		if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
			t.Logf("\t\tGot error : %+v", err)
			t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
		}

		expected := mid.ErrorForbidden(ctx).(*weberror.Error).Response(ctx, false)
		expected.StackTrace = actual.StackTrace

		if diff := cmpDiff(t, actual, expected); diff {
			t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
		}
		t.Logf("\t%s\tReceived expected error.", tests.Success)
	}

	// Test delete.
	{
		expectedStatus := http.StatusForbidden

		rt := requestTest{
			fmt.Sprintf("Delete %d w/role %s", expectedStatus, tr.Role),
			http.MethodDelete,
			fmt.Sprintf("/v1/users/%s", created.ID),
			nil,
			tr.Token,
			tr.Claims,
			expectedStatus,
			nil,
		}
		t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)

		w, ok := executeRequestTest(t, rt, ctx)
		if !ok {
			t.Fatalf("\t%s\tExecute request failed.", tests.Failed)
		}
		t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)

		var actual weberror.ErrorResponse
		if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
			t.Logf("\t\tGot error : %+v", err)
			t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
		}

		expected := mid.ErrorForbidden(ctx).(*weberror.Error).Response(ctx, false)
		expected.StackTrace = actual.StackTrace

		if diff := cmpDiff(t, actual, expected); diff {
			t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
		}
		t.Logf("\t%s\tReceived expected error.", tests.Success)
	}

	// Test switch account.
	{
		expectedStatus := http.StatusOK

		newAccount := newMockSignup().account
		rt := requestTest{
			fmt.Sprintf("Switch account %d w/role %s", expectedStatus, tr.Role),
			http.MethodPatch,
			fmt.Sprintf("/v1/users/switch-account/%s", newAccount.ID),
			nil,
			tr.Token,
			tr.Claims,
			expectedStatus,
			nil,
		}
		t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)

		_, err := appCtx.UserAccountRepo.Create(tests.Context(), auth.Claims{}, user_account.UserAccountCreateRequest{
			UserID:    tr.User.ID,
			AccountID: newAccount.ID,
			Roles:     []user_account.UserAccountRole{user_account.UserAccountRole_User},
		}, time.Now().UTC().AddDate(-1, -1, -1))
		if err != nil {
			t.Fatalf("\t%s\tAdd user to account failed.", tests.Failed)
		}

		w, ok := executeRequestTest(t, rt, ctx)
		if !ok {
			t.Fatalf("\t%s\tExecute request failed.", tests.Failed)
		}
		t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)

		var actual map[string]interface{}
		if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
			t.Logf("\t\tGot error : %+v", err)
			t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
		}

		// This is just for response format validation, will verify account from claims.
		expected := map[string]interface{}{
			"access_token": actual["access_token"],
			"token_type":   actual["token_type"],
			"expiry":       actual["expiry"],
			"ttl":          actual["ttl"],
			"user_id":      tr.User.ID,
			"account_id":   newAccount.ID,
		}

		if diff := cmpDiff(t, actual, expected); diff {
			t.Fatalf("\t%s\tReceived expected result.", tests.Failed)
		}
		t.Logf("\t%s\tReceived expected result.", tests.Success)

		newClaims, err := authenticator.ParseClaims(actual["access_token"].(string))
		if err != nil {
			t.Logf("\t\tGot error : %+v", err)
			t.Fatalf("\t%s\tParse claims failed.", tests.Failed)
		} else if newClaims.Audience != newAccount.ID {
			t.Logf("\t\tGot : %+v", newClaims.Audience)
			t.Logf("\t\tExpected : %+v", newAccount.ID)
			t.Fatalf("\t%s\tParse claims expected audience to match new account.", tests.Failed)
		} else if newClaims.Subject != tr.User.ID {
			t.Logf("\t\tGot : %+v", newClaims.Subject)
			t.Logf("\t\tExpected : %+v", tr.User.ID)
			t.Fatalf("\t%s\tParse claims expected Subject to match user.", tests.Failed)
		}
		t.Logf("\t%s\tParse claims valid.", tests.Success)
	}
}

// TestUserCreate validates create user endpoint.
func TestUserCreate(t *testing.T) {
	defer tests.Recover(t)

	tr := roleTests[auth.RoleAdmin]

	// Add claims to the context for the user.
	ctx := context.WithValue(tests.Context(), auth.Key, tr.Claims)

	// Test create with invalid data.
	{
		expectedStatus := http.StatusBadRequest

		req := mockUserCreateRequest()
		req.Email = "invalid email address.com"
		rt := requestTest{
			fmt.Sprintf("Create %d w/role %s using invalid data", expectedStatus, tr.Role),
			http.MethodPost,
			"/v1/users",
			req,
			tr.Token,
			tr.Claims,
			expectedStatus,
			nil,
		}
		t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)

		w, ok := executeRequestTest(t, rt, ctx)
		if !ok {
			t.Fatalf("\t%s\tExecute request failed.", tests.Failed)
		}
		t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)

		var actual weberror.ErrorResponse
		if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
			t.Logf("\t\tGot error : %+v", err)
			t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
		}

		expected := weberror.ErrorResponse{
			StatusCode: expectedStatus,
			Error:      "Field validation error",
			Fields: []weberror.FieldError{
				//{Field: "email", Error: "Key: 'UserCreateRequest.email' Error:Field validation for 'email' failed on the 'email' tag"},
				{
					Field:   "email",
					Value:   req.Email,
					Tag:     "email",
					Error:   "email must be a valid email address",
					Display: "email must be a valid email address",
				},
			},
			Details:    actual.Details,
			StackTrace: actual.StackTrace,
		}

		if diff := cmpDiff(t, actual, expected); diff {
			t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
		}
		t.Logf("\t%s\tReceived expected error.", tests.Success)
	}
}

// TestUserUpdate validates update user endpoint.
func TestUserUpdate(t *testing.T) {
	defer tests.Recover(t)

	tr := roleTests[auth.RoleAdmin]

	// Add claims to the context for the user.
	ctx := context.WithValue(tests.Context(), auth.Key, tr.Claims)

	// Test update with invalid data.
	{
		expectedStatus := http.StatusBadRequest

		invalidEmail := "invalid email address"
		rt := requestTest{
			fmt.Sprintf("Update %d w/role %s using invalid data", expectedStatus, tr.Role),
			http.MethodPatch,
			"/v1/users",
			user.UserUpdateRequest{
				ID:    tr.User.ID,
				Email: &invalidEmail,
			},
			tr.Token,
			tr.Claims,
			expectedStatus,
			nil,
		}
		t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)

		w, ok := executeRequestTest(t, rt, ctx)
		if !ok {
			t.Fatalf("\t%s\tExecute request failed.", tests.Failed)
		}
		t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)

		var actual weberror.ErrorResponse
		if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
			t.Logf("\t\tGot error : %+v", err)
			t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
		}

		expected := weberror.ErrorResponse{
			StatusCode: expectedStatus,
			Error:      "Field validation error",
			Fields: []weberror.FieldError{
				//{Field: "email", Error: "Key: 'UserUpdateRequest.email' Error:Field validation for 'email' failed on the 'email' tag"},
				{
					Field:   "email",
					Value:   invalidEmail,
					Tag:     "email",
					Error:   "email must be a valid email address",
					Display: "email must be a valid email address",
				},
			},
			Details:    actual.Details,
			StackTrace: actual.StackTrace,
		}

		if diff := cmpDiff(t, actual, expected); diff {
			t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
		}
		t.Logf("\t%s\tReceived expected error.", tests.Success)
	}
}

// TestUserUpdatePassword validates update user password endpoint.
func TestUserUpdatePassword(t *testing.T) {
	defer tests.Recover(t)

	tr := roleTests[auth.RoleAdmin]

	// Add claims to the context for the user.
	ctx := context.WithValue(tests.Context(), auth.Key, tr.Claims)

	// Since role doesn't support create, bypass auth to test other endpoints.
	created := newMockUser(tr.Account.ID, user_account.UserAccountRole_User).Response(ctx)

	// Test update user password with invalid data.
	{
		expectedStatus := http.StatusBadRequest

		newPass := uuid.NewRandom().String()
		diffPass := "different"

		rt := requestTest{
			fmt.Sprintf("Update password %d w/role %s using invalid data", expectedStatus, tr.Role),
			http.MethodPatch,
			"/v1/users/password",
			user.UserUpdatePasswordRequest{
				ID:              created.ID,
				Password:        newPass,
				PasswordConfirm: diffPass,
			},
			tr.Token,
			tr.Claims,
			expectedStatus,
			nil,
		}
		t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)

		w, ok := executeRequestTest(t, rt, ctx)
		if !ok {
			t.Fatalf("\t%s\tExecute request failed.", tests.Failed)
		}
		t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)

		var actual weberror.ErrorResponse
		if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
			t.Logf("\t\tGot error : %+v", err)
			t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
		}

		expected := weberror.ErrorResponse{
			StatusCode: expectedStatus,
			Error:      "Field validation error",
			Fields: []weberror.FieldError{
				//{Field: "password_confirm", Error: "Key: 'UserUpdatePasswordRequest.password_confirm' Error:Field validation for 'password_confirm' failed on the 'eqfield' tag"},
				{
					Field:   "password_confirm",
					Value:   diffPass,
					Tag:     "eqfield",
					Error:   "password_confirm must be equal to Password",
					Display: "password_confirm must be equal to Password",
				},
			},
			Details:    actual.Details,
			StackTrace: actual.StackTrace,
		}

		if diff := cmpDiff(t, actual, expected); diff {
			t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
		}
		t.Logf("\t%s\tReceived expected error.", tests.Success)
	}
}

// TestUserArchive validates archive user endpoint.
func TestUserArchive(t *testing.T) {
	defer tests.Recover(t)

	tr := roleTests[auth.RoleAdmin]

	// Add claims to the context for the user.
	ctx := context.WithValue(tests.Context(), auth.Key, tr.Claims)

	// Test archive user with invalid data.
	{
		expectedStatus := http.StatusBadRequest

		invalidId := "a"

		rt := requestTest{
			fmt.Sprintf("Archive %d w/role %s using invalid data", expectedStatus, tr.Role),
			http.MethodPatch,
			"/v1/users/archive",
			user.UserArchiveRequest{
				ID: invalidId,
			},
			tr.Token,
			tr.Claims,
			expectedStatus,
			nil,
		}
		t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)

		w, ok := executeRequestTest(t, rt, ctx)
		if !ok {
			t.Fatalf("\t%s\tExecute request failed.", tests.Failed)
		}
		t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)

		var actual weberror.ErrorResponse
		if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
			t.Logf("\t\tGot error : %+v", err)
			t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
		}

		expected := weberror.ErrorResponse{
			StatusCode: expectedStatus,
			Error:      "Field validation error",
			Fields: []weberror.FieldError{
				//{Field: "id", Error: "Key: 'UserArchiveRequest.id' Error:Field validation for 'id' failed on the 'uuid' tag"},
				{
					Field:   "id",
					Value:   invalidId,
					Tag:     "uuid",
					Error:   "id must be a valid UUID",
					Display: "id must be a valid UUID",
				},
			},
			Details:    actual.Details,
			StackTrace: actual.StackTrace,
		}

		if diff := cmpDiff(t, actual, expected); diff {
			t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
		}
		t.Logf("\t%s\tReceived expected error.", tests.Success)
	}

	// Test archive user with forbidden ID.
	{
		expectedStatus := http.StatusForbidden

		rt := requestTest{
			fmt.Sprintf("Archive %d w/role %s using forbidden ID", expectedStatus, tr.Role),
			http.MethodPatch,
			"/v1/users/archive",
			user.UserArchiveRequest{
				ID: tr.ForbiddenUser.ID,
			},
			tr.Token,
			tr.Claims,
			expectedStatus,
			nil,
		}
		t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)

		w, ok := executeRequestTest(t, rt, ctx)
		if !ok {
			t.Fatalf("\t%s\tExecute request failed.", tests.Failed)
		}
		t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)

		var actual weberror.ErrorResponse
		if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
			t.Logf("\t\tGot error : %+v", err)
			t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
		}

		expected := weberror.ErrorResponse{
			StatusCode: expectedStatus,
			Error:      http.StatusText(expectedStatus),
			Details:    user.ErrForbidden.Error(),
			StackTrace: actual.StackTrace,
		}

		if diff := cmpDiff(t, actual, expected); diff {
			t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
		}
		t.Logf("\t%s\tReceived expected error.", tests.Success)
	}
}

// TestUserDelete validates delete user endpoint.
func TestUserDelete(t *testing.T) {
	defer tests.Recover(t)

	tr := roleTests[auth.RoleAdmin]

	// Add claims to the context for the user.
	ctx := context.WithValue(tests.Context(), auth.Key, tr.Claims)

	// Test delete user with invalid data.
	{
		expectedStatus := http.StatusBadRequest

		invalidId := "345345"

		rt := requestTest{
			fmt.Sprintf("Delete %d w/role %s using invalid data", expectedStatus, tr.Role),
			http.MethodDelete,
			fmt.Sprintf("/v1/users/%s", invalidId),
			nil,
			tr.Token,
			tr.Claims,
			expectedStatus,
			nil,
		}
		t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)

		w, ok := executeRequestTest(t, rt, ctx)
		if !ok {
			t.Fatalf("\t%s\tExecute request failed.", tests.Failed)
		}
		t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)

		var actual weberror.ErrorResponse
		if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
			t.Logf("\t\tGot error : %+v", err)
			t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
		}

		expected := weberror.ErrorResponse{
			StatusCode: expectedStatus,
			Error:      "Field validation error",
			Fields: []weberror.FieldError{
				//{Field: "id", Error: "Key: 'id' Error:Field validation for 'id' failed on the 'uuid' tag"},
				{
					Field:   "id",
					Value:   invalidId,
					Tag:     "uuid",
					Error:   "id must be a valid UUID",
					Display: "id must be a valid UUID",
				},
			},
			Details:    actual.Details,
			StackTrace: actual.StackTrace,
		}

		if diff := cmpDiff(t, actual, expected); diff {
			t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
		}
		t.Logf("\t%s\tReceived expected error.", tests.Success)
	}

	// Test delete user with forbidden ID.
	{
		expectedStatus := http.StatusForbidden

		rt := requestTest{
			fmt.Sprintf("Delete %d w/role %s using forbidden ID", expectedStatus, tr.Role),
			http.MethodDelete,
			fmt.Sprintf("/v1/users/%s", tr.ForbiddenUser.ID),
			nil,
			tr.Token,
			tr.Claims,
			expectedStatus,
			nil,
		}
		t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)

		w, ok := executeRequestTest(t, rt, ctx)
		if !ok {
			t.Fatalf("\t%s\tExecute request failed.", tests.Failed)
		}
		t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)

		var actual weberror.ErrorResponse
		if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
			t.Logf("\t\tGot error : %+v", err)
			t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
		}

		expected := weberror.ErrorResponse{
			StatusCode: expectedStatus,
			Error:      http.StatusText(expectedStatus),
			Details:    user.ErrForbidden.Error(),
			StackTrace: actual.StackTrace,
		}

		if diff := cmpDiff(t, actual, expected); diff {
			t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
		}
		t.Logf("\t%s\tReceived expected error.", tests.Success)
	}
}

// TestUserSwitchAccount validates user switch account endpoint.
func TestUserSwitchAccount(t *testing.T) {
	defer tests.Recover(t)

	tr := roleTests[auth.RoleAdmin]

	// Add claims to the context for the user.
	ctx := context.WithValue(tests.Context(), auth.Key, tr.Claims)

	// Test user switch account with invalid data.
	{
		expectedStatus := http.StatusBadRequest

		invalidAccountId := "sf"

		rt := requestTest{
			fmt.Sprintf("Switch account %d w/role %s using invalid data", expectedStatus, tr.Role),
			http.MethodPatch,
			"/v1/users/switch-account/" + invalidAccountId,
			nil,
			tr.Token,
			tr.Claims,
			expectedStatus,
			nil,
		}
		t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)

		w, ok := executeRequestTest(t, rt, ctx)
		if !ok {
			t.Fatalf("\t%s\tExecute request failed.", tests.Failed)
		}
		t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)

		var actual weberror.ErrorResponse
		if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
			t.Logf("\t\tGot error : %+v", err)
			t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
		}

		expected := weberror.ErrorResponse{
			StatusCode: expectedStatus,
			Error:      "Field validation error",
			Fields: []weberror.FieldError{
				{
					Field:   "account_id",
					Value:   invalidAccountId,
					Tag:     "uuid",
					Error:   "account_id must be a valid UUID",
					Display: "account_id must be a valid UUID",
				},
			},
			Details:    actual.Details,
			StackTrace: actual.StackTrace,
		}

		if diff := cmpDiff(t, expected, actual); diff {
			t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
		}
		t.Logf("\t%s\tReceived expected error.", tests.Success)
	}

	// Test user switch account with forbidden ID.
	{
		expectedStatus := http.StatusUnauthorized

		rt := requestTest{
			fmt.Sprintf("Switch account %d w/role %s using forbidden ID", expectedStatus, tr.Role),
			http.MethodPatch,
			fmt.Sprintf("/v1/users/switch-account/%s", tr.ForbiddenAccount.ID),
			nil,
			tr.Token,
			tr.Claims,
			expectedStatus,
			nil,
		}
		t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)

		w, ok := executeRequestTest(t, rt, ctx)
		if !ok {
			t.Fatalf("\t%s\tExecute request failed.", tests.Failed)
		}
		t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)

		var actual weberror.ErrorResponse
		if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
			t.Logf("\t\tGot error : %+v", err)
			t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
		}

		expected := weberror.ErrorResponse{
			StatusCode: expectedStatus,
			Error:      http.StatusText(expectedStatus),
			Details:    user_auth.ErrAuthenticationFailure.Error(),
			StackTrace: actual.StackTrace,
		}

		if diff := cmpDiff(t, actual, expected); diff {
			t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
		}
		t.Logf("\t%s\tReceived expected error.", tests.Success)
	}
}

// TestUserToken validates user token endpoint.
func TestUserToken(t *testing.T) {
	defer tests.Recover(t)

	// Test user token with empty credentials.
	{
		expectedStatus := http.StatusBadRequest

		rt := requestTest{
			fmt.Sprintf("Token %d using empty request", expectedStatus),
			http.MethodPost,
			"/v1/oauth/token",
			nil,
			user_auth.Token{},
			auth.Claims{},
			expectedStatus,
			nil,
		}
		t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)

		w, ok := executeRequestTest(t, rt, tests.Context())
		if !ok {
			t.Fatalf("\t%s\tExecute request failed.", tests.Failed)
		}
		t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)

		var actual weberror.ErrorResponse
		if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
			t.Logf("\t\tGot error : %+v", err)
			t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
		}

		expected := weberror.ErrorResponse{
			StatusCode: expectedStatus,
			Error:      "Field validation error",
			Fields: []weberror.FieldError{
				{
					Field:   "username",
					Value:   "",
					Tag:     "required",
					Error:   "username is a required field",
					Display: "username is a required field",
				},
				{
					Field:   "password",
					Value:   "",
					Tag:     "required",
					Error:   "password is a required field",
					Display: "password is a required field",
				},
			},
			Details:    actual.Details,
			StackTrace: actual.StackTrace,
		}

		if diff := cmpDiff(t, actual, expected); diff {
			t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
		}
		t.Logf("\t%s\tReceived expected error.", tests.Success)
	}

	// Test user token with invalid email.
	{
		expectedStatus := http.StatusBadRequest

		rt := requestTest{
			fmt.Sprintf("Token %d using invalid email", expectedStatus),
			http.MethodPost,
			"/v1/oauth/token",
			nil,
			user_auth.Token{},
			auth.Claims{},
			expectedStatus,
			nil,
		}
		t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)

		r := httptest.NewRequest(rt.method, rt.url, nil)

		invalidEmail := "invalid email.com"
		r.SetBasicAuth(invalidEmail, "some random password")

		w := httptest.NewRecorder()
		r.Header.Set("Content-Type", web.MIMEApplicationJSONCharsetUTF8)

		a.ServeHTTP(w, r)

		if w.Code != expectedStatus {
			t.Logf("\t\tBody : %s\n", w.Body.String())
			t.Logf("\t\tShould receive a status code of %d for the response : %v", rt.statusCode, w.Code)
			t.Fatalf("\t%s\tExecute request failed.", tests.Failed)
		}
		t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)

		var actual weberror.ErrorResponse
		if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
			t.Logf("\t\tGot error : %+v", err)
			t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
		}

		expected := weberror.ErrorResponse{
			StatusCode: expectedStatus,
			Error:      "Field validation error",
			Fields: []weberror.FieldError{
				{
					Field:   "email",
					Value:   invalidEmail,
					Tag:     "email",
					Error:   "email must be a valid email address",
					Display: "email must be a valid email address",
				},
			},
			Details:    actual.Details,
			StackTrace: actual.StackTrace,
		}

		if diff := cmpDiff(t, actual, expected); diff {
			t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
		}
		t.Logf("\t%s\tReceived expected error.", tests.Success)
	}

	// Test user token with invalid password.
	{
		for _, tr := range roleTests {
			expectedStatus := http.StatusUnauthorized

			rt := requestTest{
				fmt.Sprintf("Token %d w/role %s using invalid password", expectedStatus, tr.Role),
				http.MethodPost,
				"/v1/oauth/token",
				nil,
				user_auth.Token{},
				auth.Claims{},
				expectedStatus,
				nil,
			}
			t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)

			r := httptest.NewRequest(rt.method, rt.url, nil)
			r.SetBasicAuth(tr.User.Email, "invalid password")

			w := httptest.NewRecorder()
			r.Header.Set("Content-Type", web.MIMEApplicationJSONCharsetUTF8)

			a.ServeHTTP(w, r)

			if w.Code != expectedStatus {
				t.Logf("\t\tBody : %s\n", w.Body.String())
				t.Logf("\t\tShould receive a status code of %d for the response : %v", rt.statusCode, w.Code)
				t.Fatalf("\t%s\tExecute request failed.", tests.Failed)
			}
			t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)

			var actual weberror.ErrorResponse
			if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
				t.Logf("\t\tGot error : %+v", err)
				t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
			}

			expected := weberror.ErrorResponse{
				StatusCode: expectedStatus,
				Error:      http.StatusText(expectedStatus),
				Details:    user_auth.ErrAuthenticationFailure.Error(),
				StackTrace: actual.StackTrace,
			}

			if diff := cmpDiff(t, actual, expected); diff {
				t.Fatalf("\t%s\tReceived expected error.", tests.Failed)
			}
			t.Logf("\t%s\tReceived expected error.", tests.Success)
		}
	}

	// Test user token with valid email and password.
	{
		for _, tr := range roleTests {
			expectedStatus := http.StatusOK

			rt := requestTest{
				fmt.Sprintf("Token %d w/role %s using valid credentials", expectedStatus, tr.Role),
				http.MethodPost,
				"/v1/oauth/token?account_id=" + tr.Account.ID,
				nil,
				user_auth.Token{},
				auth.Claims{},
				expectedStatus,
				nil,
			}
			t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)

			r := httptest.NewRequest(rt.method, rt.url, nil)
			r.SetBasicAuth(tr.User.Email, tr.User.password)

			w := httptest.NewRecorder()
			r.Header.Set("Content-Type", web.MIMEApplicationJSONCharsetUTF8)

			a.ServeHTTP(w, r)

			if w.Code != expectedStatus {
				t.Logf("\t\tBody : %s\n", w.Body.String())
				t.Logf("\t\tShould receive a status code of %d for the response : %v", rt.statusCode, w.Code)
				t.Fatalf("\t%s\tExecute request failed.", tests.Failed)
			}
			t.Logf("\t%s\tReceived valid status code of %d.", tests.Success, w.Code)

			var actual map[string]interface{}
			if err := json.Unmarshal(w.Body.Bytes(), &actual); err != nil {
				t.Logf("\t\tGot error : %+v", err)
				t.Fatalf("\t%s\tDecode response body failed.", tests.Failed)
			}

			// This is just for response format validation, will verify account from claims.
			expected := map[string]interface{}{
				"access_token": actual["access_token"],
				"token_type":   actual["token_type"],
				"expiry":       actual["expiry"],
				"ttl":          actual["ttl"],
				"user_id":      tr.User.ID,
				"account_id":   tr.Account.ID,
			}

			if diff := cmpDiff(t, actual, expected); diff {
				t.Fatalf("\t%s\tReceived expected result.", tests.Failed)
			}
			t.Logf("\t%s\tReceived expected result.", tests.Success)
		}
	}
}
