From e9858cfed8e343dd3a3acc14da0c97e9ccb970ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 20 Jan 2023 10:59:06 +0100 Subject: [PATCH 01/10] User creation API: allow custom "created" timestamps Allow back-dating user creation via the `adminCreateUser` API operation. `CreateUserOption` now has an optional field `created_at`, which can contain a datetime-formatted string. If this field is present, the user's `created_unix` database field will be updated to its value. This is important for Blender's migration of users from Phabricator to Gitea. There are many users, and the creation timestamp of their account can give us some indication as to how long someone's been part of the community. The back-dating is done in a separate query that just updates the user's `created_unix` field. This was the easiest and cleanest way I could find, as in the initial `INSERT` query the field always is set to "now". --- models/user/user.go | 12 ++++++++++++ modules/structs/admin_user.go | 6 ++++++ routers/api/v1/admin/user.go | 11 +++++++++++ templates/swagger/v1_json.tmpl | 5 +++++ 4 files changed, 34 insertions(+) diff --git a/models/user/user.go b/models/user/user.go index a2c54a4429fd9..87cd48b7fde4a 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -953,6 +953,18 @@ func UpdateUser(ctx context.Context, u *User, changePrimaryEmail bool, cols ...s return err } +// UpdateUserCreated stores the user's `CreatedUnix` field in the database. +// This is intended to allow migration of users from another system while +// maintaining the user's creation timestamp. +func UpdateUserCreated(ctx context.Context, u *User) error { + if err := validateUser(u); err != nil { + return err + } + + _, err := db.Exec(ctx, "UPDATE `user` SET created_unix=? WHERE id=?", u.CreatedUnix, u.ID) + return err +} + // UpdateUserCols update user according special columns func UpdateUserCols(ctx context.Context, u *User, cols ...string) error { if err := validateUser(u); err != nil { diff --git a/modules/structs/admin_user.go b/modules/structs/admin_user.go index 0739653eea480..03184e045bd34 100644 --- a/modules/structs/admin_user.go +++ b/modules/structs/admin_user.go @@ -4,6 +4,8 @@ package structs +import "time" + // CreateUserOption create user options type CreateUserOption struct { SourceID int64 `json:"source_id"` @@ -20,6 +22,10 @@ type CreateUserOption struct { SendNotify bool `json:"send_notify"` Restricted *bool `json:"restricted"` Visibility string `json:"visibility" binding:"In(,public,limited,private)"` + + // For back-dating user creation. Useful when users are migrated from other systems. + // If nil, the user's creation timestamp will be set to "now". + Created *time.Time `json:"created_at"` } // EditUserOption edit user options diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index 6b48ce4a9d4d3..845c56b2387ae 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -20,6 +20,7 @@ import ( "code.gitea.io/gitea/modules/password" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/user" @@ -136,6 +137,16 @@ func CreateUser(ctx *context.APIContext) { } log.Trace("Account created by admin (%s): %s", ctx.Doer.Name, u.Name) + // Back-date the user creation. + if form.Created != nil { + u.CreatedUnix = timeutil.TimeStamp(form.Created.Unix()) + if err := user_model.UpdateUserCreated(ctx, u); err != nil { + ctx.Error(http.StatusInternalServerError, "UpdateUserCreated", err) + return + } + log.Trace("Account profile back-dated by admin (%s): %s", ctx.Doer.Name, u.Name) + } + // Send email notification. if form.SendNotify { mailer.SendRegisterNotifyMail(u) diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 76d02d825fd84..d9d9f493efa21 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -15622,6 +15622,11 @@ "password" ], "properties": { + "created_at": { + "type": "string", + "format": "date-time", + "x-go-name": "Created" + }, "email": { "type": "string", "format": "email", From a7633f36ca8db04531dc60e9c0b6245b0dfd5147 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 20 Jan 2023 11:09:26 +0100 Subject: [PATCH 02/10] Regenerate swagger & update field description Run `make generate-swagger` and update the comment describing the field so that they make sense from an API standpoint (and not just when reading the Go code). --- modules/structs/admin_user.go | 2 +- templates/swagger/v1_json.tmpl | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/structs/admin_user.go b/modules/structs/admin_user.go index 03184e045bd34..db0556f7e4c20 100644 --- a/modules/structs/admin_user.go +++ b/modules/structs/admin_user.go @@ -24,7 +24,7 @@ type CreateUserOption struct { Visibility string `json:"visibility" binding:"In(,public,limited,private)"` // For back-dating user creation. Useful when users are migrated from other systems. - // If nil, the user's creation timestamp will be set to "now". + // If omitted, the user's creation timestamp will be set to "now". Created *time.Time `json:"created_at"` } diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index d9d9f493efa21..72e36053d6a7d 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -15623,6 +15623,7 @@ ], "properties": { "created_at": { + "description": "For back-dating user creation. Useful when users are migrated from other systems.\nIf omitted, the user's creation timestamp will be set to \"now\".", "type": "string", "format": "date-time", "x-go-name": "Created" From 036b16db8b16492f8131e244a70f202fa1523b77 Mon Sep 17 00:00:00 2001 From: Sybren <122987084+drsybren@users.noreply.github.com> Date: Mon, 23 Jan 2023 13:47:44 +0100 Subject: [PATCH 03/10] Remove double space from SQL Thanks @yardenshoham for spotting this. Co-authored-by: Yarden Shoham --- models/user/user.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/user/user.go b/models/user/user.go index 87cd48b7fde4a..b4749e12c8f47 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -961,7 +961,7 @@ func UpdateUserCreated(ctx context.Context, u *User) error { return err } - _, err := db.Exec(ctx, "UPDATE `user` SET created_unix=? WHERE id=?", u.CreatedUnix, u.ID) + _, err := db.Exec(ctx, "UPDATE `user` SET created_unix=? WHERE id=?", u.CreatedUnix, u.ID) return err } From 4831ec60cdf9830d45fb9b8f15c0c371de2819e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 23 Jan 2023 14:00:54 +0100 Subject: [PATCH 04/10] Update comments, avoid mentioning back-dating. Since any timestamp is accepted by the system, avoid mentioning "back-dating" user creation. --- modules/structs/admin_user.go | 5 +++-- routers/api/v1/admin/user.go | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/modules/structs/admin_user.go b/modules/structs/admin_user.go index db0556f7e4c20..4d679c81d0017 100644 --- a/modules/structs/admin_user.go +++ b/modules/structs/admin_user.go @@ -23,8 +23,9 @@ type CreateUserOption struct { Restricted *bool `json:"restricted"` Visibility string `json:"visibility" binding:"In(,public,limited,private)"` - // For back-dating user creation. Useful when users are migrated from other systems. - // If omitted, the user's creation timestamp will be set to "now". + // For explicitly setting the user creation timestamp. Useful when users are + // migrated from other systems. When omitted, the user's creation timestamp + // will be set to "now". Created *time.Time `json:"created_at"` } diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index 845c56b2387ae..0578b20fc3f93 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -137,7 +137,9 @@ func CreateUser(ctx *context.APIContext) { } log.Trace("Account created by admin (%s): %s", ctx.Doer.Name, u.Name) - // Back-date the user creation. + // Update the user creation timestamp. This can only be done after the user + // record has been inserted into the database; the insert intself will always + // set the creation timestamp to "now". if form.Created != nil { u.CreatedUnix = timeutil.TimeStamp(form.Created.Unix()) if err := user_model.UpdateUserCreated(ctx, u); err != nil { From ce97999a281fe431c002c7f49c3ae1044ad2c2c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 23 Jan 2023 14:03:30 +0100 Subject: [PATCH 05/10] Run `make generate-swagger` --- templates/swagger/v1_json.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 72e36053d6a7d..8e447ca0dfbdf 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -15623,7 +15623,7 @@ ], "properties": { "created_at": { - "description": "For back-dating user creation. Useful when users are migrated from other systems.\nIf omitted, the user's creation timestamp will be set to \"now\".", + "description": "For explicitly setting the user creation timestamp. Useful when users are\nmigrated from other systems. When omitted, the user's creation timestamp\nwill be set to \"now\".", "type": "string", "format": "date-time", "x-go-name": "Created" From 256dcf56572d78118782e7a1b8b36b6fd7e3ece0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 23 Jan 2023 16:41:52 +0100 Subject: [PATCH 06/10] Use `NoAutoTime()` to allow manual changes to creation timestamp With this change to the "insert" query, it was possible to set the creation timestamp: ```diff @@ -740,11 +747,11 @@ func CreateUser(u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err e if err = DeleteUserRedirect(ctx, u.Name); err != nil { return err } - if err = db.Insert(ctx, u); err != nil { + if _, err = db.GetEngine(ctx).NoAutoTime().Insert(u); err != nil { return err } // insert email address if err := db.Insert(ctx, &EmailAddress{ ``` --- models/user/user.go | 21 ++++++++------------- routers/api/v1/admin/user.go | 19 +++++++------------ 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/models/user/user.go b/models/user/user.go index b4749e12c8f47..0ea54487ea64f 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -665,6 +665,13 @@ func CreateUser(u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err e u.IsRestricted = setting.Service.DefaultUserIsRestricted u.IsActive = !(setting.Service.RegisterEmailConfirm || setting.Service.RegisterManualConfirm) + if u.CreatedUnix == 0 { + u.CreatedUnix = timeutil.TimeStampNow() + } + if u.UpdatedUnix == 0 { + u.UpdatedUnix = u.CreatedUnix + } + // overwrite defaults if set if len(overwriteDefault) != 0 && overwriteDefault[0] != nil { overwrite := overwriteDefault[0] @@ -742,7 +749,7 @@ func CreateUser(u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err e return err } - if err = db.Insert(ctx, u); err != nil { + if _, err = db.GetEngine(ctx).NoAutoTime().Insert(u); err != nil { return err } @@ -953,18 +960,6 @@ func UpdateUser(ctx context.Context, u *User, changePrimaryEmail bool, cols ...s return err } -// UpdateUserCreated stores the user's `CreatedUnix` field in the database. -// This is intended to allow migration of users from another system while -// maintaining the user's creation timestamp. -func UpdateUserCreated(ctx context.Context, u *User) error { - if err := validateUser(u); err != nil { - return err - } - - _, err := db.Exec(ctx, "UPDATE `user` SET created_unix=? WHERE id=?", u.CreatedUnix, u.ID) - return err -} - // UpdateUserCols update user according special columns func UpdateUserCols(ctx context.Context, u *User, cols ...string) error { if err := validateUser(u); err != nil { diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index 0578b20fc3f93..ceb308328213c 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -121,6 +121,13 @@ func CreateUser(ctx *context.APIContext) { overwriteDefault.Visibility = &visibility } + // Update the user creation timestamp. This can only be done after the user + // record has been inserted into the database; the insert intself will always + // set the creation timestamp to "now". + if form.Created != nil { + u.CreatedUnix = timeutil.TimeStamp(form.Created.Unix()) + } + if err := user_model.CreateUser(u, overwriteDefault); err != nil { if user_model.IsErrUserAlreadyExist(err) || user_model.IsErrEmailAlreadyUsed(err) || @@ -137,18 +144,6 @@ func CreateUser(ctx *context.APIContext) { } log.Trace("Account created by admin (%s): %s", ctx.Doer.Name, u.Name) - // Update the user creation timestamp. This can only be done after the user - // record has been inserted into the database; the insert intself will always - // set the creation timestamp to "now". - if form.Created != nil { - u.CreatedUnix = timeutil.TimeStamp(form.Created.Unix()) - if err := user_model.UpdateUserCreated(ctx, u); err != nil { - ctx.Error(http.StatusInternalServerError, "UpdateUserCreated", err) - return - } - log.Trace("Account profile back-dated by admin (%s): %s", ctx.Doer.Name, u.Name) - } - // Send email notification. if form.SendNotify { mailer.SendRegisterNotifyMail(u) From 7e0b525e772b79645d41a62906a038f29e229a61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 24 Jan 2023 10:40:11 +0100 Subject: [PATCH 07/10] Detect whether `NoAutoTime()` should be used + a unittest Detect whether `NoAutoTime()` should be used based on `u.CreatedUnix`, and add two unittests that test the new functionality. --- models/user/user.go | 10 +++++++- models/user/user_test.go | 44 ++++++++++++++++++++++++++++++++++++ routers/api/v1/admin/user.go | 1 + 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/models/user/user.go b/models/user/user.go index 0ea54487ea64f..45b474be580a8 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -749,7 +749,15 @@ func CreateUser(u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err e return err } - if _, err = db.GetEngine(ctx).NoAutoTime().Insert(u); err != nil { + if u.CreatedUnix == 0 { + // Caller expects auto-time for creation & update timestamps. + err = db.Insert(ctx, u) + } else { + // Caller sets the timestamps themselves. They are responsible for ensuring + // both `CreatedUnix` and `UpdatedUnix` are set appropriately. + _, err = db.GetEngine(ctx).NoAutoTime().Insert(u) + } + if err != nil { return err } diff --git a/models/user/user_test.go b/models/user/user_test.go index 525da531f2399..9eea9b91cbf2f 100644 --- a/models/user/user_test.go +++ b/models/user/user_test.go @@ -4,6 +4,7 @@ package user_test import ( + "context" "math/rand" "strings" "testing" @@ -14,6 +15,7 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" "github.com/stretchr/testify/assert" @@ -252,6 +254,48 @@ func TestCreateUserEmailAlreadyUsed(t *testing.T) { assert.True(t, user_model.IsErrEmailAlreadyUsed(err)) } +func TestCreateUserCustomTimestamps(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + + // Add new user with a custom creation timestamp. + user.Name = "testuser" + user.LowerName = strings.ToLower(user.Name) + user.ID = 0 + user.Email = "unique@example.com" + user.CreatedUnix = 12345 // Long, long time ago... + err := user_model.CreateUser(user) + assert.NoError(t, err) + + fetched, err := user_model.GetUserByID(context.Background(), user.ID) + assert.NoError(t, err) + assert.Equal(t, timeutil.TimeStamp(12345), fetched.CreatedUnix) + assert.Equal(t, timeutil.TimeStamp(12345), fetched.UpdatedUnix) +} + +func TestCreateUserWithoutCustomTimestamps(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + + // Add new user with a custom creation timestamp. + user.Name = "testuser" + user.LowerName = strings.ToLower(user.Name) + user.ID = 0 + user.Email = "unique@example.com" + user.CreatedUnix = 0 + user.UpdatedUnix = 0 + err := user_model.CreateUser(user) + assert.NoError(t, err) + + fetched, err := user_model.GetUserByID(context.Background(), user.ID) + assert.NoError(t, err) + // 1674552894 is "now" at the moment of writing this code. + assert.Greater(t, fetched.CreatedUnix, timeutil.TimeStamp(1674552894)) + assert.Greater(t, fetched.UpdatedUnix, timeutil.TimeStamp(1674552894)) +} + func TestGetUserIDsByNames(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index ceb308328213c..b425752717b08 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -126,6 +126,7 @@ func CreateUser(ctx *context.APIContext) { // set the creation timestamp to "now". if form.Created != nil { u.CreatedUnix = timeutil.TimeStamp(form.Created.Unix()) + u.UpdatedUnix = u.CreatedUnix } if err := user_model.CreateUser(u, overwriteDefault); err != nil { From 6fd9f18750dc8a79eaf4a81fa38c67c83068a142 Mon Sep 17 00:00:00 2001 From: Sybren <122987084+drsybren@users.noreply.github.com> Date: Fri, 27 Jan 2023 11:29:42 +0100 Subject: [PATCH 08/10] Use "fake now" in unit test Suggestion by @wolfogre Co-authored-by: Jason Song --- models/user/user_test.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/models/user/user_test.go b/models/user/user_test.go index 9eea9b91cbf2f..f0c846b143dab 100644 --- a/models/user/user_test.go +++ b/models/user/user_test.go @@ -279,7 +279,11 @@ func TestCreateUserWithoutCustomTimestamps(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) - // Add new user with a custom creation timestamp. + fakeNow := timeutil.TimeStamp(1674552894) + timeutil.Set(fakeNow.AsTime()) + defer timeutil.Unset() + + // Add new user without a custom creation timestamp. user.Name = "testuser" user.LowerName = strings.ToLower(user.Name) user.ID = 0 @@ -291,9 +295,8 @@ func TestCreateUserWithoutCustomTimestamps(t *testing.T) { fetched, err := user_model.GetUserByID(context.Background(), user.ID) assert.NoError(t, err) - // 1674552894 is "now" at the moment of writing this code. - assert.Greater(t, fetched.CreatedUnix, timeutil.TimeStamp(1674552894)) - assert.Greater(t, fetched.UpdatedUnix, timeutil.TimeStamp(1674552894)) + assert.Equal(t, fetched.CreatedUnix, fakeNow) + assert.Equal(t, fetched.UpdatedUnix, fakeNow) } func TestGetUserIDsByNames(t *testing.T) { From f2bd162501a7dea756b284b41c511a24215dcb77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 10 Feb 2023 14:55:07 +0100 Subject: [PATCH 09/10] Handle reviewer feedback --- models/user/user.go | 8 ++++---- models/user/user_test.go | 26 +++++++++++++++++--------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/models/user/user.go b/models/user/user.go index f71e09804836a..e17b559128573 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -640,10 +640,8 @@ func CreateUser(u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err e u.IsRestricted = setting.Service.DefaultUserIsRestricted u.IsActive = !(setting.Service.RegisterEmailConfirm || setting.Service.RegisterManualConfirm) - if u.CreatedUnix == 0 { - u.CreatedUnix = timeutil.TimeStampNow() - } - if u.UpdatedUnix == 0 { + // Ensure consistency of the dates. + if u.UpdatedUnix < u.CreatedUnix { u.UpdatedUnix = u.CreatedUnix } @@ -725,9 +723,11 @@ func CreateUser(u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err e } if u.CreatedUnix == 0 { + fmt.Println("u.CreatedUnix == 0") // Caller expects auto-time for creation & update timestamps. err = db.Insert(ctx, u) } else { + fmt.Printf("u.CreatedUnix == %d\n", u.CreatedUnix) // Caller sets the timestamps themselves. They are responsible for ensuring // both `CreatedUnix` and `UpdatedUnix` are set appropriately. _, err = db.GetEngine(ctx).NoAutoTime().Insert(u) diff --git a/models/user/user_test.go b/models/user/user_test.go index f0c846b143dab..7a58d2f822cb8 100644 --- a/models/user/user_test.go +++ b/models/user/user_test.go @@ -8,6 +8,7 @@ import ( "math/rand" "strings" "testing" + "time" "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" @@ -260,18 +261,19 @@ func TestCreateUserCustomTimestamps(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // Add new user with a custom creation timestamp. + var creationTimestamp timeutil.TimeStamp = 12345 user.Name = "testuser" user.LowerName = strings.ToLower(user.Name) user.ID = 0 user.Email = "unique@example.com" - user.CreatedUnix = 12345 // Long, long time ago... + user.CreatedUnix = creationTimestamp err := user_model.CreateUser(user) assert.NoError(t, err) fetched, err := user_model.GetUserByID(context.Background(), user.ID) assert.NoError(t, err) - assert.Equal(t, timeutil.TimeStamp(12345), fetched.CreatedUnix) - assert.Equal(t, timeutil.TimeStamp(12345), fetched.UpdatedUnix) + assert.Equal(t, creationTimestamp, fetched.CreatedUnix) + assert.Equal(t, creationTimestamp, fetched.UpdatedUnix) } func TestCreateUserWithoutCustomTimestamps(t *testing.T) { @@ -279,12 +281,12 @@ func TestCreateUserWithoutCustomTimestamps(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) - fakeNow := timeutil.TimeStamp(1674552894) - timeutil.Set(fakeNow.AsTime()) - defer timeutil.Unset() + // There is no way to use a mocked time for the XORM auto-time functionality, + // so use the real clock to approximate the expected timestamp. + timestampStart := time.Now().Unix() // Add new user without a custom creation timestamp. - user.Name = "testuser" + user.Name = "Testuser" user.LowerName = strings.ToLower(user.Name) user.ID = 0 user.Email = "unique@example.com" @@ -293,10 +295,16 @@ func TestCreateUserWithoutCustomTimestamps(t *testing.T) { err := user_model.CreateUser(user) assert.NoError(t, err) + timestampEnd := time.Now().Unix() + fetched, err := user_model.GetUserByID(context.Background(), user.ID) assert.NoError(t, err) - assert.Equal(t, fetched.CreatedUnix, fakeNow) - assert.Equal(t, fetched.UpdatedUnix, fakeNow) + + assert.LessOrEqual(t, timestampStart, fetched.CreatedUnix) + assert.LessOrEqual(t, fetched.CreatedUnix, timestampEnd) + + assert.LessOrEqual(t, timestampStart, fetched.UpdatedUnix) + assert.LessOrEqual(t, fetched.UpdatedUnix, timestampEnd) } func TestGetUserIDsByNames(t *testing.T) { From 25b6aefd4b62d33f51085be780621fc4bac60c80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 14 Feb 2023 11:17:04 +0100 Subject: [PATCH 10/10] Remove accidental `fmt.Printf()` calls --- models/user/user.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/models/user/user.go b/models/user/user.go index e17b559128573..aff00be724a6b 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -723,11 +723,9 @@ func CreateUser(u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err e } if u.CreatedUnix == 0 { - fmt.Println("u.CreatedUnix == 0") // Caller expects auto-time for creation & update timestamps. err = db.Insert(ctx, u) } else { - fmt.Printf("u.CreatedUnix == %d\n", u.CreatedUnix) // Caller sets the timestamps themselves. They are responsible for ensuring // both `CreatedUnix` and `UpdatedUnix` are set appropriately. _, err = db.GetEngine(ctx).NoAutoTime().Insert(u)