mirror of
https://github.com/raseels-repos/golang-saas-starter-kit.git
synced 2025-06-06 23:46:29 +02:00
505 lines
15 KiB
Go
505 lines
15 KiB
Go
package account_preference
|
|
|
|
import (
|
|
"geeks-accelerator/oss/saas-starter-kit/internal/account"
|
|
"math/rand"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"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/user_account"
|
|
"github.com/dgrijalva/jwt-go"
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/pborman/uuid"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
var test *tests.Test
|
|
|
|
// TestMain is the entry point for testing.
|
|
func TestMain(m *testing.M) {
|
|
os.Exit(testMain(m))
|
|
}
|
|
|
|
func testMain(m *testing.M) int {
|
|
test = tests.New()
|
|
defer test.TearDown()
|
|
return m.Run()
|
|
}
|
|
|
|
// TestSetValidation ensures all the validation tags work on Set.
|
|
func TestSetValidation(t *testing.T) {
|
|
|
|
invalidName := AccountPreferenceName("xxxxxx")
|
|
|
|
var prefTests = []struct {
|
|
name string
|
|
req AccountPreferenceSetRequest
|
|
error error
|
|
}{
|
|
{"Required Fields",
|
|
AccountPreferenceSetRequest{},
|
|
errors.New("Key: 'AccountPreferenceSetRequest.{{account_id}}' Error:Field validation for '{{account_id}}' failed on the 'required' tag\n" +
|
|
"Key: 'AccountPreferenceSetRequest.{{name}}' Error:Field validation for '{{name}}' failed on the 'required' tag\n" +
|
|
"Key: 'AccountPreferenceSetRequest.{{value}}' Error:Field validation for '{{value}}' failed on the 'required' tag"),
|
|
},
|
|
{"Valid Name",
|
|
AccountPreferenceSetRequest{
|
|
AccountID: uuid.NewRandom().String(),
|
|
Name: invalidName,
|
|
Value: uuid.NewRandom().String(),
|
|
},
|
|
errors.New("Key: 'AccountPreferenceSetRequest.{{name}}' Error:Field validation for '{{name}}' failed on the 'oneof' tag\n" +
|
|
"Key: 'AccountPreferenceSetRequest.{{value}}' Error:Field validation for '{{value}}' failed on the 'preference_value' tag"),
|
|
},
|
|
}
|
|
|
|
now := time.Date(2018, time.October, 1, 0, 0, 0, 0, time.UTC)
|
|
|
|
t.Log("Given the need ensure all validation tags are working for account preference set.")
|
|
{
|
|
for i, tt := range prefTests {
|
|
t.Logf("\tTest: %d\tWhen running test: %s", i, tt.name)
|
|
{
|
|
ctx := tests.Context()
|
|
|
|
err := Set(ctx, auth.Claims{}, test.MasterDB, tt.req, now)
|
|
if err != tt.error {
|
|
// TODO: need a better way to handle validation errors as they are
|
|
// of type interface validator.ValidationErrorsTranslations
|
|
var errStr string
|
|
if err != nil {
|
|
errStr = strings.Replace(err.Error(), "{{", "", -1)
|
|
errStr = strings.Replace(errStr, "}}", "", -1)
|
|
}
|
|
var expectStr string
|
|
if tt.error != nil {
|
|
expectStr = strings.Replace(tt.error.Error(), "{{", "", -1)
|
|
expectStr = strings.Replace(expectStr, "}}", "", -1)
|
|
}
|
|
if errStr != expectStr {
|
|
t.Logf("\t\tGot : %+v", errStr)
|
|
t.Logf("\t\tWant: %+v", expectStr)
|
|
t.Fatalf("\t%s\tSet failed.", tests.Failed)
|
|
}
|
|
}
|
|
|
|
// If there was an error that was expected, then don't go any further
|
|
if tt.error != nil {
|
|
t.Logf("\t%s\tSet ok.", tests.Success)
|
|
continue
|
|
}
|
|
|
|
t.Logf("\t%s\tSet ok.", tests.Success)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestCrud validates the full set of CRUD operations for account preferences and ensures ACLs are correctly applied
|
|
// by claims.
|
|
func TestCrud(t *testing.T) {
|
|
defer tests.Recover(t)
|
|
|
|
now := time.Date(2018, time.October, 1, 0, 0, 0, 0, time.UTC)
|
|
|
|
// Create a test user and account.
|
|
usrAcc, err := user_account.MockUserAccount(tests.Context(), test.MasterDB, now, user_account.UserAccountRole_Admin)
|
|
if err != nil {
|
|
t.Log("Got :", err)
|
|
t.Fatalf("%s\tCreate account failed.", tests.Failed)
|
|
}
|
|
|
|
type prefTest struct {
|
|
name string
|
|
claims func(string, string) auth.Claims
|
|
set AccountPreferenceSetRequest
|
|
writeErr error
|
|
findErr error
|
|
}
|
|
|
|
var prefTests []prefTest
|
|
|
|
// Internal request, should bypass ACL.
|
|
prefTests = append(prefTests, prefTest{"EmptyClaims",
|
|
func(accountID, userId string) auth.Claims {
|
|
return auth.Claims{}
|
|
},
|
|
AccountPreferenceSetRequest{
|
|
AccountID: usrAcc.AccountID,
|
|
Name: AccountPreference_Datetime_Format,
|
|
Value: AccountPreference_Datetime_Format_Default,
|
|
},
|
|
nil,
|
|
nil,
|
|
})
|
|
|
|
// Role of account but claim account does not match update account so forbidden.
|
|
prefTests = append(prefTests, prefTest{"RoleAccountPreferenceDiffAccountPreference",
|
|
func(accountID, userId string) auth.Claims {
|
|
return auth.Claims{
|
|
Roles: []string{auth.RoleAdmin},
|
|
StandardClaims: jwt.StandardClaims{
|
|
Audience: uuid.NewRandom().String(),
|
|
Subject: userId,
|
|
},
|
|
}
|
|
},
|
|
AccountPreferenceSetRequest{
|
|
AccountID: usrAcc.AccountID,
|
|
Name: AccountPreference_Datetime_Format,
|
|
Value: AccountPreference_Datetime_Format_Default,
|
|
},
|
|
account.ErrForbidden,
|
|
ErrNotFound,
|
|
})
|
|
|
|
// Role of account AND claim account matches update account so OK.
|
|
prefTests = append(prefTests, prefTest{"RoleAccountPreferenceSameAccountPreference",
|
|
func(accountID, userId string) auth.Claims {
|
|
return auth.Claims{
|
|
Roles: []string{auth.RoleAdmin},
|
|
StandardClaims: jwt.StandardClaims{
|
|
Audience: accountID,
|
|
Subject: userId,
|
|
},
|
|
}
|
|
},
|
|
AccountPreferenceSetRequest{
|
|
AccountID: usrAcc.AccountID,
|
|
Name: AccountPreference_Date_Format,
|
|
Value: AccountPreference_Date_Format_Default,
|
|
},
|
|
nil,
|
|
nil,
|
|
})
|
|
|
|
// Role of admin but claim account does not match update account so forbidden.
|
|
prefTests = append(prefTests, prefTest{"RoleAdminDiffAccountPreference",
|
|
func(accountID, userID string) auth.Claims {
|
|
return auth.Claims{
|
|
Roles: []string{auth.RoleAdmin},
|
|
StandardClaims: jwt.StandardClaims{
|
|
Audience: uuid.NewRandom().String(),
|
|
Subject: uuid.NewRandom().String(),
|
|
},
|
|
}
|
|
},
|
|
AccountPreferenceSetRequest{
|
|
AccountID: usrAcc.AccountID,
|
|
Name: AccountPreference_Time_Format,
|
|
Value: AccountPreference_Time_Format_Default,
|
|
},
|
|
account.ErrForbidden,
|
|
ErrNotFound,
|
|
})
|
|
|
|
// Role of admin and claim account matches update account so ok.
|
|
prefTests = append(prefTests, prefTest{"RoleAdminSameAccountPreference",
|
|
func(accountID, userId string) auth.Claims {
|
|
return auth.Claims{
|
|
Roles: []string{auth.RoleAdmin},
|
|
StandardClaims: jwt.StandardClaims{
|
|
Audience: uuid.NewRandom().String(),
|
|
Subject: userId,
|
|
},
|
|
}
|
|
},
|
|
AccountPreferenceSetRequest{
|
|
AccountID: usrAcc.AccountID,
|
|
Name: AccountPreference_Time_Format,
|
|
Value: AccountPreference_Time_Format_Default,
|
|
},
|
|
account.ErrForbidden,
|
|
ErrNotFound,
|
|
})
|
|
|
|
t.Log("Given the need to ensure claims are applied as ACL for set account preference.")
|
|
{
|
|
|
|
for i, tt := range prefTests {
|
|
t.Logf("\tTest: %d\tWhen running test: %s", i, tt.name)
|
|
{
|
|
ctx := tests.Context()
|
|
|
|
err := Set(ctx, tt.claims(usrAcc.AccountID, usrAcc.UserID), test.MasterDB, tt.set, now)
|
|
if err != nil && errors.Cause(err) != tt.writeErr {
|
|
t.Logf("\t\tGot : %+v", err)
|
|
t.Logf("\t\tWant: %+v", tt.writeErr)
|
|
t.Fatalf("\t%s\tFind failed.", tests.Failed)
|
|
}
|
|
|
|
// If user doesn't have access to set, create one anyways to test the other endpoints.
|
|
if tt.writeErr != nil {
|
|
err := Set(ctx, auth.Claims{}, test.MasterDB, tt.set, now)
|
|
if err != nil {
|
|
t.Log("\t\tGot :", err)
|
|
t.Fatalf("\t%s\tCreate failed.", tests.Failed)
|
|
}
|
|
}
|
|
|
|
// Find the account and make sure the set where made.
|
|
readRes, err := Read(ctx, tt.claims(usrAcc.AccountID, usrAcc.UserID), test.MasterDB, AccountPreferenceReadRequest{
|
|
AccountID: tt.set.AccountID,
|
|
Name: tt.set.Name,
|
|
})
|
|
if err != nil && errors.Cause(err) != tt.findErr {
|
|
t.Logf("\t\tGot : %+v", err)
|
|
t.Logf("\t\tWant: %+v", tt.findErr)
|
|
t.Fatalf("\t%s\tFind failed.", tests.Failed)
|
|
} else if tt.findErr == nil {
|
|
findExpected := &AccountPreference{
|
|
AccountID: tt.set.AccountID,
|
|
Name: tt.set.Name,
|
|
Value: tt.set.Value,
|
|
CreatedAt: readRes.CreatedAt,
|
|
UpdatedAt: readRes.UpdatedAt,
|
|
}
|
|
|
|
if diff := cmp.Diff(readRes, findExpected); diff != "" {
|
|
t.Fatalf("\t%s\tExpected find result to match update. Diff:\n%s", tests.Failed, diff)
|
|
}
|
|
t.Logf("\t%s\tRead ok.", tests.Success)
|
|
}
|
|
|
|
// Archive (soft-delete) the account.
|
|
err = Archive(ctx, tt.claims(usrAcc.AccountID, usrAcc.UserID), test.MasterDB, AccountPreferenceArchiveRequest{
|
|
AccountID: tt.set.AccountID,
|
|
Name: tt.set.Name,
|
|
}, now)
|
|
if err != nil && errors.Cause(err) != tt.writeErr {
|
|
t.Logf("\t\tGot : %+v", err)
|
|
t.Logf("\t\tWant: %+v", tt.writeErr)
|
|
t.Fatalf("\t%s\tArchive failed.", tests.Failed)
|
|
} else if tt.findErr == nil {
|
|
// Trying to find the archived account with the includeArchived false should result in not found.
|
|
_, err = Read(ctx, tt.claims(usrAcc.AccountID, usrAcc.UserID), test.MasterDB, AccountPreferenceReadRequest{
|
|
AccountID: tt.set.AccountID,
|
|
Name: tt.set.Name,
|
|
})
|
|
if err != nil && errors.Cause(err) != ErrNotFound {
|
|
t.Logf("\t\tGot : %+v", err)
|
|
t.Logf("\t\tWant: %+v", ErrNotFound)
|
|
t.Fatalf("\t%s\tArchive Read failed.", tests.Failed)
|
|
}
|
|
|
|
// Trying to find the archived account with the includeArchived true should result no error.
|
|
_, err = Read(ctx, tt.claims(usrAcc.AccountID, usrAcc.UserID), test.MasterDB, AccountPreferenceReadRequest{
|
|
AccountID: tt.set.AccountID,
|
|
Name: tt.set.Name,
|
|
IncludeArchived: true,
|
|
})
|
|
if err != nil {
|
|
t.Log("\t\tGot :", err)
|
|
t.Fatalf("\t%s\tArchive Read failed.", tests.Failed)
|
|
}
|
|
}
|
|
t.Logf("\t%s\tArchive ok.", tests.Success)
|
|
|
|
// Delete (hard-delete) the account.
|
|
err = Delete(ctx, tt.claims(usrAcc.AccountID, usrAcc.UserID), test.MasterDB, AccountPreferenceDeleteRequest{
|
|
AccountID: tt.set.AccountID,
|
|
Name: tt.set.Name,
|
|
})
|
|
if err != nil && errors.Cause(err) != tt.writeErr {
|
|
t.Logf("\t\tGot : %+v", err)
|
|
t.Logf("\t\tWant: %+v", tt.writeErr)
|
|
t.Fatalf("\t%s\tDelete failed.", tests.Failed)
|
|
} else if tt.writeErr == nil {
|
|
// Trying to find the deleted account with the includeArchived true should result in not found.
|
|
_, err = Read(ctx, tt.claims(usrAcc.AccountID, usrAcc.UserID), test.MasterDB, AccountPreferenceReadRequest{
|
|
AccountID: tt.set.AccountID,
|
|
Name: tt.set.Name,
|
|
IncludeArchived: true,
|
|
})
|
|
if errors.Cause(err) != ErrNotFound {
|
|
t.Logf("\t\tGot : %+v", err)
|
|
t.Logf("\t\tWant: %+v", ErrNotFound)
|
|
t.Fatalf("\t%s\tDelete Read failed.", tests.Failed)
|
|
}
|
|
}
|
|
t.Logf("\t%s\tDelete ok.", tests.Success)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestFind validates all the request params are correctly parsed into a select query.
|
|
func TestFind(t *testing.T) {
|
|
|
|
now := time.Now().Add(time.Hour * -1).UTC()
|
|
|
|
// Create a test user and account.
|
|
usrAcc, err := user_account.MockUserAccount(tests.Context(), test.MasterDB, now, user_account.UserAccountRole_Admin)
|
|
if err != nil {
|
|
t.Log("Got :", err)
|
|
t.Fatalf("%s\tCreate account failed.", tests.Failed)
|
|
}
|
|
|
|
startTime := now.Truncate(time.Millisecond)
|
|
var endTime time.Time
|
|
|
|
reqs := []AccountPreferenceSetRequest{
|
|
{
|
|
AccountID: usrAcc.AccountID,
|
|
Name: AccountPreference_Datetime_Format,
|
|
Value: AccountPreference_Datetime_Format_Default,
|
|
},
|
|
{
|
|
AccountID: usrAcc.AccountID,
|
|
Name: AccountPreference_Date_Format,
|
|
Value: AccountPreference_Date_Format_Default,
|
|
},
|
|
{
|
|
AccountID: usrAcc.AccountID,
|
|
Name: AccountPreference_Time_Format,
|
|
Value: AccountPreference_Time_Format_Default,
|
|
},
|
|
}
|
|
|
|
var prefs []*AccountPreference
|
|
for idx, req := range reqs {
|
|
err = Set(tests.Context(), auth.Claims{}, test.MasterDB, req, now.Add(time.Second*time.Duration(idx)))
|
|
if err != nil {
|
|
t.Logf("\t\tGot : %+v", err)
|
|
t.Logf("\t\tRequest : %+v", req)
|
|
t.Fatalf("\t%s\tSet failed.", tests.Failed)
|
|
}
|
|
|
|
pref, err := Read(tests.Context(), auth.Claims{}, test.MasterDB, AccountPreferenceReadRequest{
|
|
AccountID: req.AccountID,
|
|
Name: req.Name,
|
|
})
|
|
if err != nil {
|
|
t.Logf("\t\tGot : %+v", err)
|
|
t.Logf("\t\tRequest : %+v", req)
|
|
t.Fatalf("\t%s\tSet failed.", tests.Failed)
|
|
}
|
|
|
|
prefs = append(prefs, pref)
|
|
endTime = pref.CreatedAt
|
|
}
|
|
|
|
type accountTest struct {
|
|
name string
|
|
req AccountPreferenceFindRequest
|
|
expected []*AccountPreference
|
|
error error
|
|
}
|
|
|
|
var prefTests []accountTest
|
|
|
|
createdFilter := "created_at BETWEEN ? AND ?"
|
|
|
|
// Test sort accounts.
|
|
prefTests = append(prefTests, accountTest{"Find all order by created_at asc",
|
|
AccountPreferenceFindRequest{
|
|
Where: createdFilter,
|
|
Args: []interface{}{startTime, endTime},
|
|
Order: []string{"created_at"},
|
|
},
|
|
prefs,
|
|
nil,
|
|
})
|
|
|
|
// Test reverse sorted accounts.
|
|
var expected []*AccountPreference
|
|
for i := len(prefs) - 1; i >= 0; i-- {
|
|
expected = append(expected, prefs[i])
|
|
}
|
|
prefTests = append(prefTests, accountTest{"Find all order by created_at desc",
|
|
AccountPreferenceFindRequest{
|
|
Where: createdFilter,
|
|
Args: []interface{}{startTime, endTime},
|
|
Order: []string{"created_at desc"},
|
|
},
|
|
expected,
|
|
nil,
|
|
})
|
|
|
|
// Test limit.
|
|
var limit uint = 2
|
|
prefTests = append(prefTests, accountTest{"Find limit",
|
|
AccountPreferenceFindRequest{
|
|
Where: createdFilter,
|
|
Args: []interface{}{startTime, endTime},
|
|
Order: []string{"created_at"},
|
|
Limit: &limit,
|
|
},
|
|
prefs[0:2],
|
|
nil,
|
|
})
|
|
|
|
// Test offset.
|
|
var offset uint = 1
|
|
prefTests = append(prefTests, accountTest{"Find limit, offset",
|
|
AccountPreferenceFindRequest{
|
|
Where: createdFilter,
|
|
Args: []interface{}{startTime, endTime},
|
|
Order: []string{"created_at"},
|
|
Limit: &limit,
|
|
Offset: &offset,
|
|
},
|
|
prefs[1:3],
|
|
nil,
|
|
})
|
|
|
|
// Test where filter.
|
|
whereParts := []string{}
|
|
whereArgs := []interface{}{startTime, endTime}
|
|
expected = []*AccountPreference{}
|
|
for i := 0; i < len(prefs); i++ {
|
|
if rand.Intn(100) < 50 {
|
|
continue
|
|
}
|
|
u := *prefs[i]
|
|
|
|
whereParts = append(whereParts, "name = ?")
|
|
whereArgs = append(whereArgs, u.Name)
|
|
expected = append(expected, &u)
|
|
}
|
|
|
|
prefTests = append(prefTests, accountTest{"Find where",
|
|
AccountPreferenceFindRequest{
|
|
Where: createdFilter + " AND (" + strings.Join(whereParts, " OR ") + ")",
|
|
Args: whereArgs,
|
|
Order: []string{"created_at"},
|
|
},
|
|
expected,
|
|
nil,
|
|
})
|
|
|
|
t.Log("Given the need to ensure find account preferences returns the expected results.")
|
|
{
|
|
for i, tt := range prefTests {
|
|
t.Logf("\tTest: %d\tWhen running test: %s", i, tt.name)
|
|
{
|
|
ctx := tests.Context()
|
|
|
|
res, err := Find(ctx, auth.Claims{}, test.MasterDB, tt.req)
|
|
if errors.Cause(err) != tt.error {
|
|
t.Logf("\t\tGot : %+v", err)
|
|
t.Logf("\t\tWant: %+v", tt.error)
|
|
t.Fatalf("\t%s\tFind failed.", tests.Failed)
|
|
} else if diff := cmp.Diff(res, tt.expected); diff != "" {
|
|
t.Logf("\t\tGot: %d items", len(res))
|
|
t.Logf("\t\tWant: %d items", len(tt.expected))
|
|
|
|
for _, u := range res {
|
|
t.Logf("\t\tGot: %s ID", u.Name)
|
|
}
|
|
for _, u := range tt.expected {
|
|
t.Logf("\t\tExpected: %s ID", u.Name)
|
|
}
|
|
|
|
t.Fatalf("\t%s\tExpected find result to match expected. Diff:\n%s", tests.Failed, diff)
|
|
}
|
|
t.Logf("\t%s\tFind ok.", tests.Success)
|
|
}
|
|
}
|
|
}
|
|
}
|