You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by oc...@apache.org on 2023/04/10 16:47:52 UTC

[trafficcontrol] branch master updated: Add unit test for user folder in Traffic Ops (#7395)

This is an automated email from the ASF dual-hosted git repository.

ocket8888 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git


The following commit(s) were added to refs/heads/master by this push:
     new 096ef2a8f8 Add unit test for user folder in Traffic Ops (#7395)
096ef2a8f8 is described below

commit 096ef2a8f8f5bbe81529adc071faa7ad2f69e7fd
Author: Steve Hamrick <sh...@gmail.com>
AuthorDate: Mon Apr 10 10:47:46 2023 -0600

    Add unit test for user folder in Traffic Ops (#7395)
    
    * Increase unit test coverage in user/
    
    * Code review fixes
    
    * Fix tests
    
    * Still check for nil
    
    * Don't export struct
    
    * Code review feedback
    
    * Missed a file
    
    * Missed some fmts
---
 lib/go-tc/users.go                                 |   7 +-
 traffic_ops/traffic_ops_golang/test/helpers.go     |  49 ++-
 .../traffic_ops_golang/test/helpers_test.go        |  58 +++-
 .../traffic_ops_golang/user/current_test.go        | 259 +++++++++++++++
 traffic_ops/traffic_ops_golang/user/user_test.go   | 347 +++++++++++++++++++++
 5 files changed, 709 insertions(+), 11 deletions(-)

diff --git a/lib/go-tc/users.go b/lib/go-tc/users.go
index e06179fa23..6965521002 100644
--- a/lib/go-tc/users.go
+++ b/lib/go-tc/users.go
@@ -127,7 +127,7 @@ type UserToken struct {
 	Token string `json:"t"`
 }
 
-// commonUserFields is unexported, but its contents are still visible when it is embedded
+// commonUserFields contents are still visible when it is embedded
 // LastUpdated is a new field for some structs.
 type commonUserFields struct {
 	AddressLine1    *string    `json:"addressLine1" db:"address_line1"`
@@ -471,11 +471,6 @@ func (u *CurrentUserUpdateRequestUser) UnmarshalAndValidate(user *User) error {
 	return util.JoinErrs(errs)
 }
 
-// ------------------- Response structs -------------------- //
-//  Response structs should only be used in the client       //
-//  The client's use of these will eventually be deprecated  //
-// --------------------------------------------------------- //
-
 // UsersResponse can hold a Traffic Ops API response to a request to get a list of users.
 type UsersResponse struct {
 	Response []User `json:"response"`
diff --git a/traffic_ops/traffic_ops_golang/test/helpers.go b/traffic_ops/traffic_ops_golang/test/helpers.go
index 50c17fcf97..98e9835f1e 100644
--- a/traffic_ops/traffic_ops_golang/test/helpers.go
+++ b/traffic_ops/traffic_ops_golang/test/helpers.go
@@ -26,16 +26,57 @@ import (
 	"strings"
 )
 
-// Extract the tag annotations from a struct into a string array
+// ColsFromStructByTag extracts the tag annotations from a struct into a string array.
 func ColsFromStructByTag(tagName string, thing interface{}) []string {
-	cols := []string{}
+	return ColsFromStructByTagExclude(tagName, thing, nil)
+}
+
+// InsertAtStr inserts insertMap (string to insert at -> []insert names) into cols non-destructively.
+func InsertAtStr(cols []string, insertMap map[string][]string) []string {
+	if insertMap == nil {
+		return cols
+	}
+	if cols == nil {
+		return nil
+	}
+
+	colLen := len(cols)
+	insertLen := 0
+	for _, val := range insertMap {
+		insertLen += len(val)
+	}
+	newColumns := make([]string, colLen+insertLen)
+	oldIndex := 0
+	for newIndex := 0; newIndex < len(newColumns); newIndex++ {
+		newColumns[newIndex] = (cols)[oldIndex]
+		if inserts, ok := insertMap[newColumns[newIndex]]; ok {
+			for j, insert := range inserts {
+				newColumns[newIndex+j+1] = insert
+			}
+			newIndex += len(inserts)
+		}
+		oldIndex++
+	}
+	return newColumns
+}
+
+// ColsFromStructByTagExclude extracts the tag annotations from a struct into a string array except for excludedColumns.
+func ColsFromStructByTagExclude(tagName string, thing interface{}, excludeColumns []string) []string {
+	var cols []string
+	var excludeMap map[string]bool
+	if excludeColumns != nil {
+		excludeMap = make(map[string]bool, len(excludeColumns))
+		for _, col := range excludeColumns {
+			excludeMap[col] = true
+		}
+	}
 	t := reflect.TypeOf(thing)
 	for i := 0; i < t.NumField(); i++ {
 		field := t.Field(i)
-		if (strings.Compare(tagName, "db") == 0) && (tagName != "") {
+		if tagName != "" {
 			// Get the field tag value
 			tag := field.Tag.Get(tagName)
-			if tag != "" {
+			if _, ok := excludeMap[tag]; !ok && tag != "" {
 				cols = append(cols, tag)
 			}
 		}
diff --git a/traffic_ops/traffic_ops_golang/test/helpers_test.go b/traffic_ops/traffic_ops_golang/test/helpers_test.go
index d86256f094..958575d67a 100644
--- a/traffic_ops/traffic_ops_golang/test/helpers_test.go
+++ b/traffic_ops/traffic_ops_golang/test/helpers_test.go
@@ -19,10 +19,14 @@ package test
  * under the License.
  */
 
-import "testing"
+import (
+	"reflect"
+	"testing"
+)
 
 type thingy struct {
 	Cat string `json:"cat" db:"cat"`
+	Mat bool   `json:"mat"`
 	Hat int    `json:"hat" db:"hat"`
 }
 
@@ -40,3 +44,55 @@ func TestColsFromStructByTag(t *testing.T) {
 		}
 	}
 }
+
+func TestColsFromStructByTagExclude(t *testing.T) {
+	res := ColsFromStructByTagExclude("db", thingy{}, []string{"cat"})
+	cols := []string{"hat"}
+
+	if len(res) != len(cols) {
+		t.Errorf("Expected %d columns, got %d", len(cols), len(res))
+	}
+	for i, c := range cols {
+		if c != res[i] {
+			t.Errorf("Expected %s, got %s", c, res[i])
+		}
+	}
+}
+
+func TestInsertAtStr(t *testing.T) {
+	cols := []string{"zero", "two", "four"}
+	expectedCols := []string{"zero", "one", "two", "three", "four", "five", "six"}
+	colMap := map[string][]string{
+		"zero": {"one"},
+		"two":  {"three"},
+		"four": {"five", "six"},
+	}
+
+	newCols := InsertAtStr(cols, colMap)
+	if newCols == nil {
+		t.Fatal("expected new columns to be not nil")
+	}
+
+	if len(newCols) != len(expectedCols) {
+		t.Fatalf("expected same amount of columns got %d and %d", len(newCols), len(expectedCols))
+	}
+
+	for i, _ := range expectedCols {
+		if (newCols)[i] != expectedCols[i] {
+			t.Fatalf("expected col %v to be the same, got %s and %s", i, newCols[i], expectedCols[i])
+		}
+	}
+
+	newCols = InsertAtStr(nil, colMap)
+	if newCols != nil {
+		t.Fatal("expected no new columns when an argument is nil")
+	}
+	newCols = InsertAtStr(cols, nil)
+	if !reflect.DeepEqual(newCols, cols) {
+		t.Fatal("expected the same columns when insertMap is nil")
+	}
+	newCols = InsertAtStr(nil, nil)
+	if newCols != nil {
+		t.Fatal("expected no new columns when both arguments are nil")
+	}
+}
diff --git a/traffic_ops/traffic_ops_golang/user/current_test.go b/traffic_ops/traffic_ops_golang/user/current_test.go
new file mode 100644
index 0000000000..0a92391643
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/user/current_test.go
@@ -0,0 +1,259 @@
+package user
+
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import (
+	"context"
+	"errors"
+	"testing"
+	"time"
+
+	"github.com/apache/trafficcontrol/lib/go-tc"
+	"github.com/apache/trafficcontrol/lib/go-util"
+	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/test"
+
+	"github.com/jmoiron/sqlx"
+	"gopkg.in/DATA-DOG/go-sqlmock.v1"
+)
+
+var (
+	legacyUser = defineUserCurrent()
+)
+
+func defineUserCurrent() *tc.UserCurrent {
+	u := tc.UserCurrent{
+		UserName:  util.Ptr("testy"),
+		LocalUser: util.Ptr(true),
+		RoleName:  util.Ptr("role"),
+	}
+	u.AddressLine1 = util.Ptr("line1")
+	u.AddressLine2 = util.Ptr("line2")
+	u.City = util.Ptr("city")
+	u.Company = util.Ptr("company")
+	u.Country = util.Ptr("country")
+	u.Email = util.Ptr("test@email.com")
+	u.FullName = util.Ptr("Testy Mctestface")
+	u.GID = nil
+	u.ID = util.Ptr(1)
+	u.LastUpdated = nil
+	u.PhoneNumber = util.Ptr("999-999-9999")
+	u.PostalCode = util.Ptr("11111-1111")
+	u.PublicSSHKey = nil
+	u.Role = util.Ptr(1)
+	u.StateOrProvince = util.Ptr("state")
+	u.Tenant = nil
+	u.TenantID = util.Ptr(0)
+	u.Token = nil
+	u.UID = nil
+
+	return &u
+}
+func addUserRow(rows *sqlmock.Rows, users ...*tc.UserV4) {
+	if rows == nil {
+		return
+	}
+	for _, addUser := range users {
+		if addUser == nil {
+			continue
+		}
+		rows.AddRow(
+			user.AddressLine1,
+			user.AddressLine2,
+			user.ChangeLogCount,
+			user.City,
+			user.Company,
+			user.Country,
+			user.Email,
+			user.FullName,
+			user.GID,
+			user.ID,
+			user.LastAuthenticated,
+			user.LastUpdated,
+			user.NewUser,
+			user.PhoneNumber,
+			user.PostalCode,
+			user.PublicSSHKey,
+			user.RegistrationSent,
+			user.Role,
+			user.StateOrProvince,
+			user.Tenant,
+			user.TenantID,
+			user.UCDN,
+			user.UID,
+			user.Username,
+		)
+	}
+}
+
+func addLegacyUserRow(rows *sqlmock.Rows, users ...*tc.UserCurrent) {
+	if rows == nil {
+		return
+	}
+	for _, addUser := range users {
+		if addUser == nil {
+			continue
+		}
+		rows.AddRow(
+			legacyUser.AddressLine1,
+			legacyUser.AddressLine2,
+			legacyUser.City,
+			legacyUser.Company,
+			legacyUser.Country,
+			legacyUser.Email,
+			legacyUser.FullName,
+			legacyUser.GID,
+			legacyUser.ID,
+			legacyUser.LastUpdated,
+			legacyUser.LocalUser,
+			legacyUser.NewUser,
+			legacyUser.PhoneNumber,
+			legacyUser.PostalCode,
+			legacyUser.PublicSSHKey,
+			legacyUser.Role,
+			legacyUser.RoleName,
+			legacyUser.StateOrProvince,
+			legacyUser.Tenant,
+			legacyUser.TenantID,
+			legacyUser.UID,
+			legacyUser.UserName,
+		)
+	}
+}
+
+func TestUser(t *testing.T) {
+	mockDB, mock, err := sqlmock.New()
+	if err != nil {
+		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
+	}
+	defer mockDB.Close()
+
+	db := sqlx.NewDb(mockDB, "sqlmock")
+	defer db.Close()
+
+	cols := test.ColsFromStructByTagExclude("db", tc.UserV4{}, []string{"local_passwd", "token"})
+	cols = test.InsertAtStr(cols, map[string][]string{
+		"full_name": {
+			"gid",
+		},
+		"state_or_province": {
+			"tenant",
+		},
+		"tenant_id": {
+			"ucdn",
+			"uid",
+		},
+	})
+	getUserRows := sqlmock.NewRows(cols)
+	addUserRow(getUserRows, user)
+	mock.ExpectBegin()
+	mock.ExpectQuery("SELECT .*").WillReturnError(errors.New("bad"))
+
+	dbCtx, cancelTx := context.WithTimeout(context.TODO(), 10*time.Second)
+	defer cancelTx()
+	tx, err := db.BeginTxx(dbCtx, nil)
+	if err != nil {
+		t.Fatalf("creating transaction: %v", err)
+	}
+
+	_, err = getUser(tx.Tx, *user.ID)
+	if err == nil {
+		t.Fatal("expected error but got none")
+	}
+
+	mock.ExpectQuery("SELECT .*").WillReturnRows(getUserRows)
+
+	newUser, err := getUser(tx.Tx, *user.ID)
+	if err != nil {
+		t.Fatalf("unable to get user: %s", err)
+	}
+
+	newUser.LocalPassword = user.LocalPassword
+	if newUser != *user {
+		t.Fatal("returned user is not the same as db user")
+	}
+
+	updateUserRows := sqlmock.NewRows(cols)
+	addUserRow(updateUserRows, user)
+	mock.ExpectQuery("UPDATE .* RETURNING .*").WillReturnRows(updateUserRows)
+	mock.ExpectExec("UPDATE .*").WithArgs(*user.LocalPassword, *user.ID).WillReturnResult(sqlmock.NewResult(int64(*user.ID), 1))
+
+	err = updateUser(user, tx.Tx, true)
+	if err != nil {
+		t.Fatalf("unable to update user: %s", err)
+	}
+}
+
+func TestLegacyUser(t *testing.T) {
+	mockDB, mock, err := sqlmock.New()
+	if err != nil {
+		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
+	}
+	defer mockDB.Close()
+
+	db := sqlx.NewDb(mockDB, "sqlmock")
+	defer db.Close()
+
+	cols := []string{
+		"address_line1",
+		"address_line2",
+		"city",
+		"company",
+		"country",
+		"email",
+		"full_name",
+		"gid",
+		"id",
+		"new_user",
+		"phone_number",
+		"postal_code",
+		"public_ssh_key",
+		"role",
+		"role_name",
+		"state_or_province",
+		"tenant",
+		"tenant_id",
+		"uid",
+		"username",
+		"last_updated",
+		"local_passwd",
+	}
+	getUserRows := sqlmock.NewRows(cols)
+	addLegacyUserRow(getUserRows, legacyUser)
+	mock.ExpectBegin()
+	mock.ExpectQuery("SELECT .*").WillReturnError(errors.New("bad"))
+
+	dbCtx, cancelTx := context.WithTimeout(context.TODO(), 10*time.Second)
+	defer cancelTx()
+	tx, err := db.BeginTxx(dbCtx, nil)
+	if err != nil {
+		t.Fatalf("creating transaction: %v", err)
+	}
+
+	_, err = getLegacyUser(tx.Tx, *legacyUser.ID)
+	if err == nil {
+		t.Fatal("expected error but got none")
+	}
+
+	mock.ExpectQuery("SELECT .*").WillReturnRows(getUserRows)
+
+	newUser, err := getLegacyUser(tx.Tx, *legacyUser.ID)
+	if err != nil {
+		t.Fatalf("unable to get user: %s", err)
+	}
+
+	if newUser != *legacyUser {
+		t.Fatal("returned user is not the same as db user")
+	}
+}
diff --git a/traffic_ops/traffic_ops_golang/user/user_test.go b/traffic_ops/traffic_ops_golang/user/user_test.go
new file mode 100644
index 0000000000..af7368719c
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/user/user_test.go
@@ -0,0 +1,347 @@
+package user
+
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import (
+	"testing"
+	"time"
+
+	"github.com/apache/trafficcontrol/lib/go-tc"
+	"github.com/apache/trafficcontrol/lib/go-util"
+)
+
+var (
+	user = &tc.UserV4{
+		AddressLine1:      util.Ptr("line1"),
+		AddressLine2:      util.Ptr("line2"),
+		ChangeLogCount:    8,
+		City:              util.Ptr("city"),
+		Company:           util.Ptr("company"),
+		Country:           util.Ptr("country"),
+		Email:             util.Ptr("test@email.com"),
+		FullName:          util.Ptr("Testy Mctestface"),
+		GID:               nil,
+		ID:                util.Ptr(1),
+		LastAuthenticated: util.Ptr(time.Now()),
+		LastUpdated:       time.Now(),
+		LocalPassword:     util.Ptr("pw"),
+		NewUser:           false,
+		PhoneNumber:       util.Ptr("999-999-9999"),
+		PostalCode:        util.Ptr("11111-1111"),
+		PublicSSHKey:      nil,
+		RegistrationSent:  util.Ptr(time.Now()),
+		Role:              "role",
+		StateOrProvince:   util.Ptr("state"),
+		Tenant:            nil,
+		TenantID:          0,
+		Token:             nil,
+		UCDN:              "",
+		UID:               nil,
+		Username:          "testy",
+	}
+	oldUser = defineUser()
+)
+
+func defineUser() tc.User {
+	u := tc.User{
+		Username:             util.Ptr(user.Username),
+		RegistrationSent:     nil,
+		LocalPassword:        user.LocalPassword,
+		ConfirmLocalPassword: user.LocalPassword,
+		RoleName:             util.Ptr(user.Role),
+	}
+	u.AddressLine1 = user.AddressLine1
+	u.AddressLine2 = user.AddressLine2
+	u.City = user.City
+	u.Company = user.Company
+	u.Country = user.Country
+	u.Email = user.Email
+	u.FullName = user.FullName
+	u.GID = user.GID
+	u.ID = user.ID
+	u.LastUpdated = tc.TimeNoModFromTime(user.LastUpdated)
+	u.NewUser = util.Ptr(user.NewUser)
+	u.PhoneNumber = user.PhoneNumber
+	u.PostalCode = user.PostalCode
+	u.PublicSSHKey = user.PublicSSHKey
+	u.Role = nil
+	u.StateOrProvince = user.StateOrProvince
+	u.Tenant = user.Tenant
+	u.TenantID = util.Ptr(user.TenantID)
+	u.Token = user.Token
+	u.UID = user.UID
+	return u
+}
+
+func TestDowngrade(t *testing.T) {
+	old := user.Downgrade()
+
+	if *old.FullName != *oldUser.FullName {
+		t.Fatalf("expected FullName to be equal, got %s and %s", *old.FullName, *oldUser.FullName)
+	}
+	if *old.NewUser != *oldUser.NewUser {
+		t.Fatalf("expected NewUser to be equal, got %t and %t", *old.NewUser, *oldUser.NewUser)
+	}
+	if *old.RoleName != *oldUser.RoleName {
+		t.Fatalf("expected RoleName to be equal, got %s and %s", *old.RoleName, *oldUser.RoleName)
+	}
+	if *old.TenantID != *oldUser.TenantID {
+		t.Fatalf("expected TenantID to be equal, got %d and %d", *old.TenantID, *oldUser.TenantID)
+	}
+	if *old.Username != *oldUser.Username {
+		t.Fatalf("expected Username to be equal, got %s and %s", *old.Username, *oldUser.Username)
+	}
+	if *old.AddressLine1 != *oldUser.AddressLine1 {
+		t.Fatalf("expected AddressLine1 to be equal, got %s and %s", *old.AddressLine1, *oldUser.AddressLine1)
+	}
+	if *old.AddressLine2 != *oldUser.AddressLine2 {
+		t.Fatalf("expected AddressLine2 to be equal, got %s and %s", *old.AddressLine2, *oldUser.AddressLine2)
+	}
+	if *old.City != *oldUser.City {
+		t.Fatalf("expected City to be equal, got %s and %s", *old.City, *oldUser.City)
+	}
+	if *old.Company != *oldUser.Company {
+		t.Fatalf("expected Company to be equal, got %s and %s", *old.Company, *oldUser.Company)
+	}
+	if old.ConfirmLocalPassword != nil {
+		t.Fatalf("expected ConfirmLocalPassword to be nil, got %s", *old.ConfirmLocalPassword)
+	}
+	if *old.Country != *oldUser.Country {
+		t.Fatalf("expected Country to be equal, got %s and %s", *old.Country, *oldUser.Country)
+	}
+	if *old.Email != *oldUser.Email {
+		t.Fatalf("expected Email to be equal, got %s and %s", *old.Email, *oldUser.Email)
+	}
+	if old.GID != nil {
+		t.Fatalf("expected GID to be null, got %d", *old.GID)
+	}
+	if *old.ID != *oldUser.ID {
+		t.Fatalf("expected ID to be equal, got %d and %d", *old.ID, *oldUser.ID)
+	}
+	if old.LocalPassword != nil {
+		t.Fatalf("expected LocalPassword to be nil, got %s", *old.LocalPassword)
+	}
+	if *old.PhoneNumber != *oldUser.PhoneNumber {
+		t.Fatalf("expected PhoneNumber to be equal, got %s and %s", *old.PhoneNumber, *oldUser.PhoneNumber)
+	}
+	if old.PublicSSHKey != nil {
+		t.Fatalf("expected PublicSSHKey to be null, got %s", *old.PublicSSHKey)
+	}
+	if *old.StateOrProvince != *oldUser.StateOrProvince {
+		t.Fatalf("expected StateOrProvince to be equal, got %s and %s", *old.StateOrProvince, *oldUser.StateOrProvince)
+	}
+	if old.Tenant != nil {
+		t.Fatalf("expected Tenant to be null, got %s", *old.Tenant)
+	}
+	if old.Token != nil {
+		t.Fatalf("expected Token to be null, got %s", *old.Token)
+	}
+	if old.UID != nil {
+		t.Fatalf("expected UID to be null, got %d", *old.UID)
+	}
+}
+
+func TestUpgrade(t *testing.T) {
+	user.LocalPassword = util.Ptr("pw")
+	newUser := oldUser.Upgrade()
+
+	if *user.FullName != *newUser.FullName {
+		t.Fatalf("expected FullName to be equal, got %s and %s", *user.FullName, *newUser.FullName)
+	}
+	if user.NewUser != newUser.NewUser {
+		t.Fatalf("expected NewUser to be equal, got %t and %t", user.NewUser, newUser.NewUser)
+	}
+	if user.Role != newUser.Role {
+		t.Fatalf("expected Role to be equal, got %s and %s", user.Role, newUser.Role)
+	}
+	if user.TenantID != newUser.TenantID {
+		t.Fatalf("expected TenantID to be equal, got %d and %d", user.TenantID, newUser.TenantID)
+	}
+	if user.Username != newUser.Username {
+		t.Fatalf("expected Username to be equal, got %s and %s", user.Username, newUser.Username)
+	}
+	if *user.AddressLine1 != *newUser.AddressLine1 {
+		t.Fatalf("expected AddressLine1 to be equal, got %s and %s", *user.AddressLine1, *newUser.AddressLine1)
+	}
+	if *user.AddressLine2 != *newUser.AddressLine2 {
+		t.Fatalf("expected AddressLine2 to be equal, got %s and %s", *user.AddressLine2, *newUser.AddressLine2)
+	}
+	if *user.City != *newUser.City {
+		t.Fatalf("expected City to be equal, got %s and %s", *user.City, *newUser.City)
+	}
+	if *user.Company != *newUser.Company {
+		t.Fatalf("expected Company to be equal, got %s and %s", *user.Company, *newUser.Company)
+	}
+	if *user.Country != *newUser.Country {
+		t.Fatalf("expected Country to be equal, got %s and %s", *user.Country, *newUser.Country)
+	}
+	if *user.Email != *newUser.Email {
+		t.Fatalf("expected Email to be equal, got %s and %s", *user.Email, *newUser.Email)
+	}
+	if user.GID != nil {
+		t.Fatalf("expected GID to be null, got %d", *user.GID)
+	}
+	if *user.ID != *newUser.ID {
+		t.Fatalf("expected ID to be equal, got %d and %d", *user.ID, *newUser.ID)
+	}
+	if *user.LocalPassword != *newUser.LocalPassword {
+		t.Fatalf("expected LocalPassword to be equal, got %s and %s", *user.LocalPassword, *newUser.LocalPassword)
+	}
+	if *user.PhoneNumber != *newUser.PhoneNumber {
+		t.Fatalf("expected PhoneNumber to be equal, got %s and %s", *user.PhoneNumber, *newUser.PhoneNumber)
+	}
+	if user.PublicSSHKey != nil {
+		t.Fatalf("expected PublicSSHKey to be null, got %s", *user.PublicSSHKey)
+	}
+	if *user.StateOrProvince != *newUser.StateOrProvince {
+		t.Fatalf("expected StateOrProvince to be equal, got %s and %s", *user.StateOrProvince, *newUser.StateOrProvince)
+	}
+	if user.Tenant != nil {
+		t.Fatalf("expected Tenant to be null, got %s", *user.Tenant)
+	}
+	if user.Token != nil {
+		t.Fatalf("expected Token to be null, got %s", *user.Token)
+	}
+	if user.UID != nil {
+		t.Fatalf("expected UID to be null, got %d", *user.UID)
+	}
+}
+
+func TestUpgradeCurrent(t *testing.T) {
+	newUser := legacyUser.Upgrade(user.RegistrationSent, user.LastAuthenticated, user.UCDN, user.ChangeLogCount)
+
+	if *user.FullName != *newUser.FullName {
+		t.Fatalf("expected FullName to be equal, got %s and %s", *user.FullName, *newUser.FullName)
+	}
+	if user.NewUser != newUser.NewUser {
+		t.Fatalf("expected NewUser to be equal, got %t and %t", user.NewUser, newUser.NewUser)
+	}
+	if user.Role != newUser.Role {
+		t.Fatalf("expected Role to be equal, got %s and %s", user.Role, newUser.Role)
+	}
+	if user.TenantID != newUser.TenantID {
+		t.Fatalf("expected TenantID to be equal, got %d and %d", user.TenantID, newUser.TenantID)
+	}
+	if user.Username != newUser.Username {
+		t.Fatalf("expected Username to be equal, got %s and %s", user.Username, newUser.Username)
+	}
+	if *user.AddressLine1 != *newUser.AddressLine1 {
+		t.Fatalf("expected AddressLine1 to be equal, got %s and %s", *user.AddressLine1, *newUser.AddressLine1)
+	}
+	if *user.AddressLine2 != *newUser.AddressLine2 {
+		t.Fatalf("expected AddressLine2 to be equal, got %s and %s", *user.AddressLine2, *newUser.AddressLine2)
+	}
+	if *user.City != *newUser.City {
+		t.Fatalf("expected City to be equal, got %s and %s", *user.City, *newUser.City)
+	}
+	if *user.Company != *newUser.Company {
+		t.Fatalf("expected Company to be equal, got %s and %s", *user.Company, *newUser.Company)
+	}
+	if *user.Country != *newUser.Country {
+		t.Fatalf("expected Country to be equal, got %s and %s", *user.Country, *newUser.Country)
+	}
+	if *user.Email != *newUser.Email {
+		t.Fatalf("expected Email to be equal, got %s and %s", *user.Email, *newUser.Email)
+	}
+	if user.GID != nil {
+		t.Fatalf("expected GID to be null, got %d", *user.GID)
+	}
+	if *user.ID != *newUser.ID {
+		t.Fatalf("expected ID to be equal, got %d and %d", *user.ID, *newUser.ID)
+	}
+	if newUser.LocalPassword != nil {
+		t.Fatalf("expected LocalPassword to be nil, got %s", *newUser.LocalPassword)
+	}
+	if *user.PhoneNumber != *newUser.PhoneNumber {
+		t.Fatalf("expected PhoneNumber to be equal, got %s and %s", *user.PhoneNumber, *newUser.PhoneNumber)
+	}
+	if user.PublicSSHKey != nil {
+		t.Fatalf("expected PublicSSHKey to be null, got %s", *user.PublicSSHKey)
+	}
+	if *user.StateOrProvince != *newUser.StateOrProvince {
+		t.Fatalf("expected StateOrProvince to be equal, got %s and %s", *user.StateOrProvince, *newUser.StateOrProvince)
+	}
+	if user.Tenant != nil {
+		t.Fatalf("expected Tenant to be null, got %s", *user.Tenant)
+	}
+	if user.Token != nil {
+		t.Fatalf("expected Token to be null, got %s", *user.Token)
+	}
+	if user.UID != nil {
+		t.Fatalf("expected UID to be null, got %d", *user.UID)
+	}
+}
+
+func TestDowngradeCurrent(t *testing.T) {
+	old := user.ToLegacyCurrentUser(*legacyUser.Role, *legacyUser.LocalUser)
+
+	if *old.FullName != *oldUser.FullName {
+		t.Fatalf("expected FullName to be equal, got %s and %s", *old.FullName, *oldUser.FullName)
+	}
+	if *old.NewUser != *oldUser.NewUser {
+		t.Fatalf("expected NewUser to be equal, got %t and %t", *old.NewUser, *oldUser.NewUser)
+	}
+	if *old.RoleName != *oldUser.RoleName {
+		t.Fatalf("expected RoleName to be equal, got %s and %s", *old.RoleName, *oldUser.RoleName)
+	}
+	if *old.TenantID != *oldUser.TenantID {
+		t.Fatalf("expected TenantID to be equal, got %d and %d", *old.TenantID, *oldUser.TenantID)
+	}
+	if *old.UserName != *oldUser.Username {
+		t.Fatalf("expected Username to be equal, got %s and %s", *old.UserName, *oldUser.Username)
+	}
+	if *old.AddressLine1 != *oldUser.AddressLine1 {
+		t.Fatalf("expected AddressLine1 to be equal, got %s and %s", *old.AddressLine1, *oldUser.AddressLine1)
+	}
+	if *old.AddressLine2 != *oldUser.AddressLine2 {
+		t.Fatalf("expected AddressLine2 to be equal, got %s and %s", *old.AddressLine2, *oldUser.AddressLine2)
+	}
+	if *old.City != *oldUser.City {
+		t.Fatalf("expected City to be equal, got %s and %s", *old.City, *oldUser.City)
+	}
+	if *old.Company != *oldUser.Company {
+		t.Fatalf("expected Company to be equal, got %s and %s", *old.Company, *oldUser.Company)
+	}
+	if *old.Country != *oldUser.Country {
+		t.Fatalf("expected Country to be equal, got %s and %s", *old.Country, *oldUser.Country)
+	}
+	if *old.Email != *oldUser.Email {
+		t.Fatalf("expected Email to be equal, got %s and %s", *old.Email, *oldUser.Email)
+	}
+	if old.GID != nil {
+		t.Fatalf("expected GID to be null, got %d", *old.GID)
+	}
+	if *old.ID != *oldUser.ID {
+		t.Fatalf("expected ID to be equal, got %d and %d", *old.ID, *oldUser.ID)
+	}
+	if *old.PhoneNumber != *oldUser.PhoneNumber {
+		t.Fatalf("expected PhoneNumber to be equal, got %s and %s", *old.PhoneNumber, *oldUser.PhoneNumber)
+	}
+	if old.PublicSSHKey != nil {
+		t.Fatalf("expected PublicSSHKey to be null, got %s", *old.PublicSSHKey)
+	}
+	if *old.StateOrProvince != *oldUser.StateOrProvince {
+		t.Fatalf("expected StateOrProvince to be equal, got %s and %s", *old.StateOrProvince, *oldUser.StateOrProvince)
+	}
+	if old.Tenant != nil {
+		t.Fatalf("expected Tenant to be null, got %s", *old.Tenant)
+	}
+	if old.Token != nil {
+		t.Fatalf("expected Token to be null, got %s", *old.Token)
+	}
+	if old.UID != nil {
+		t.Fatalf("expected UID to be null, got %d", *old.UID)
+	}
+}