Files
forge/models/user/email_address_test.go
zeekayandHanzo Dev aa87442fdd Record upstream authorship centrally in NOTICE instead of in every file header
Source files keep their SPDX-License-Identifier and Hanzo copyright line; the
Gitea, Gogs and LFS Test Server copyrights they used to repeat now live once in
NOTICE, alongside LICENSE. The header lint accepts any copyright line above the
SPDX line, so it no longer demands an upstream one.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-10 17:50:35 -07:00

208 lines
7.6 KiB
Go

// Copyright 2026 Hanzo AI, Inc. All rights reserved.
// SPDX-License-Identifier: MIT
package user_test
import (
"slices"
"testing"
"github.com/hanzoai/git/models/db"
"github.com/hanzoai/git/models/unittest"
user_model "github.com/hanzoai/git/models/user"
"github.com/hanzoai/git/modules/optional"
"github.com/stretchr/testify/assert"
)
func TestGetEmailAddresses(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
emails, _ := user_model.GetEmailAddresses(t.Context(), int64(1))
if assert.Len(t, emails, 3) {
assert.True(t, emails[0].IsPrimary)
assert.True(t, emails[2].IsActivated)
assert.False(t, emails[2].IsPrimary)
}
emails, _ = user_model.GetEmailAddresses(t.Context(), int64(2))
if assert.Len(t, emails, 2) {
assert.True(t, emails[0].IsPrimary)
assert.True(t, emails[0].IsActivated)
}
}
func TestIsEmailUsed(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
isExist, _ := user_model.IsEmailUsed(t.Context(), "")
assert.True(t, isExist)
isExist, _ = user_model.IsEmailUsed(t.Context(), "[email protected]")
assert.True(t, isExist)
isExist, _ = user_model.IsEmailUsed(t.Context(), "[email protected]")
assert.False(t, isExist)
}
func TestMakeEmailPrimary(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
err := user_model.MakeActiveEmailPrimary(t.Context(), 1, 9999999)
assert.Error(t, err)
assert.ErrorIs(t, err, user_model.ErrEmailAddressNotExist{})
email := unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{Email: "[email protected]"})
err = user_model.MakeActiveEmailPrimary(t.Context(), email.UID, email.ID)
assert.Error(t, err)
assert.ErrorIs(t, err, user_model.ErrEmailAddressNotExist{}) // inactive email is considered as not exist for "MakeActiveEmailPrimary"
email = unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{Email: "[email protected]"})
err = user_model.MakeActiveEmailPrimary(t.Context(), email.UID, email.ID)
assert.Error(t, err)
assert.True(t, user_model.IsErrUserNotExist(err))
email = unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{Email: "[email protected]"})
err = user_model.MakeActiveEmailPrimary(t.Context(), email.UID, email.ID)
assert.NoError(t, err)
user, _ := user_model.GetUserByID(t.Context(), int64(10))
assert.Equal(t, "[email protected]", user.Email)
}
func TestActivate(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
email := &user_model.EmailAddress{
ID: int64(1),
UID: int64(1),
Email: "[email protected]",
}
assert.NoError(t, user_model.ActivateEmail(t.Context(), email))
emails, _ := user_model.GetEmailAddresses(t.Context(), int64(1))
assert.Len(t, emails, 3)
assert.True(t, emails[0].IsActivated)
assert.True(t, emails[0].IsPrimary)
assert.False(t, emails[1].IsPrimary)
assert.True(t, emails[2].IsActivated)
assert.False(t, emails[2].IsPrimary)
}
func TestListEmails(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
// Must find all users and their emails
opts := &user_model.SearchEmailOptions{
ListOptions: db.ListOptions{
PageSize: 10000,
},
}
emails, count, err := user_model.SearchEmails(t.Context(), opts)
assert.NoError(t, err)
assert.Greater(t, count, int64(5))
contains := func(match func(s *user_model.SearchEmailResult) bool) bool {
return slices.ContainsFunc(emails, match)
}
assert.True(t, contains(func(s *user_model.SearchEmailResult) bool { return s.UID == 18 }))
// 'org3' is an organization
assert.False(t, contains(func(s *user_model.SearchEmailResult) bool { return s.UID == 3 }))
// Must find no records
opts = &user_model.SearchEmailOptions{Keyword: "NOTFOUND"}
emails, count, err = user_model.SearchEmails(t.Context(), opts)
assert.NoError(t, err)
assert.Equal(t, int64(0), count)
// Must find users 'user2', 'user28', etc.
opts = &user_model.SearchEmailOptions{Keyword: "user2"}
emails, count, err = user_model.SearchEmails(t.Context(), opts)
assert.NoError(t, err)
assert.NotEqual(t, int64(0), count)
assert.True(t, contains(func(s *user_model.SearchEmailResult) bool { return s.UID == 2 }))
assert.True(t, contains(func(s *user_model.SearchEmailResult) bool { return s.UID == 27 }))
// Must find only primary addresses (i.e. from the `user` table)
opts = &user_model.SearchEmailOptions{IsPrimary: optional.Some(true)}
emails, _, err = user_model.SearchEmails(t.Context(), opts)
assert.NoError(t, err)
assert.True(t, contains(func(s *user_model.SearchEmailResult) bool { return s.IsPrimary }))
assert.False(t, contains(func(s *user_model.SearchEmailResult) bool { return !s.IsPrimary }))
// Must find only inactive addresses (i.e. not validated)
opts = &user_model.SearchEmailOptions{IsActivated: optional.Some(false)}
emails, _, err = user_model.SearchEmails(t.Context(), opts)
assert.NoError(t, err)
assert.True(t, contains(func(s *user_model.SearchEmailResult) bool { return !s.IsActivated }))
assert.False(t, contains(func(s *user_model.SearchEmailResult) bool { return s.IsActivated }))
// Must find more than one page, but retrieve only one
opts = &user_model.SearchEmailOptions{
ListOptions: db.ListOptions{
PageSize: 5,
Page: 1,
},
}
emails, count, err = user_model.SearchEmails(t.Context(), opts)
assert.NoError(t, err)
assert.Len(t, emails, 5)
assert.Greater(t, count, int64(len(emails)))
}
func TestEmailAddressValidate(t *testing.T) {
kases := map[string]error{
"[email protected]": nil,
"[email protected]": nil,
"[email protected]": nil,
"[email protected]": nil,
"[email protected]": nil,
`[email protected]`: nil,
`[email protected]`: nil,
`first#[email protected]`: nil,
`[email protected]`: nil,
`first%[email protected]`: nil,
`first&[email protected]`: nil,
`first'[email protected]`: nil,
`first*[email protected]`: nil,
`[email protected]`: nil,
`first/[email protected]`: nil,
`[email protected]`: nil,
`[email protected]`: nil,
`first^[email protected]`: nil,
"first`[email protected]": nil,
`first{[email protected]`: nil,
`first|[email protected]`: nil,
`first}[email protected]`: nil,
`[email protected]`: nil,
`first;[email protected]`: user_model.ErrEmailCharIsNotSupported{`first;[email protected]`},
"[email protected]": user_model.ErrEmailInvalid{"[email protected]"},
"[email protected]": nil,
"#[email protected]": nil,
"[email protected]": nil,
"%[email protected]": nil,
"&[email protected]": nil,
"'[email protected]": nil,
"*[email protected]": nil,
"[email protected]": nil,
"[email protected]": user_model.ErrEmailInvalid{"[email protected]"},
"/[email protected]": nil,
"[email protected]": nil,
"[email protected]": nil,
"^[email protected]": nil,
"[email protected]": nil,
"`[email protected]": nil,
"{[email protected]": nil,
"|[email protected]": nil,
"}[email protected]": nil,
"[email protected]": nil,
";[email protected]": user_model.ErrEmailCharIsNotSupported{";[email protected]"},
"Foo <[email protected]>": user_model.ErrEmailCharIsNotSupported{"Foo <[email protected]>"},
string([]byte{0xE2, 0x84, 0xAA}): user_model.ErrEmailCharIsNotSupported{string([]byte{0xE2, 0x84, 0xAA})},
}
for kase, err := range kases {
t.Run(kase, func(t *testing.T) {
assert.Equal(t, err, user_model.ValidateEmail(kase))
})
}
}