Add tests on authuser and authentication usecases
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
nemunaire 2025-05-22 15:11:32 +02:00
parent 8c25aed1d0
commit d6bc3b5b3f
8 changed files with 947 additions and 0 deletions

View file

@ -0,0 +1,238 @@
package usecase_test
import (
"testing"
"time"
"git.happydns.org/happyDomain/internal/storage/inmemory"
"git.happydns.org/happyDomain/internal/usecase"
userUC "git.happydns.org/happyDomain/internal/usecase/user"
"git.happydns.org/happyDomain/model"
)
type testUserInfo struct {
id happydns.Identifier
email string
newsletter bool
}
func (u testUserInfo) GetUserId() happydns.Identifier { return u.id }
func (u testUserInfo) GetEmail() string { return u.email }
func (u testUserInfo) JoinNewsletter() bool { return u.newsletter }
func Test_CompleteAuthentication(t *testing.T) {
mem, _ := inmemory.NewInMemoryStorage()
userUsecase := userUC.NewUserUsecases(mem, nil, nil, nil)
authenticationUsecase := usecase.NewAuthenticationUsecase(&happydns.Options{}, mem, userUsecase)
uinfo := testUserInfo{
id: happydns.Identifier([]byte("user-123")),
email: "john@example.com",
newsletter: false,
}
user, err := authenticationUsecase.CompleteAuthentication(uinfo)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if user.Email != "john@example.com" {
t.Errorf("expected email 'john@example.com', got %s", user.Email)
}
// Check the user is correctly stored in db
stored, err := mem.GetUser(happydns.Identifier([]byte("user-123")))
if err != nil {
t.Fatalf("expected stored user, got error: %v", err)
}
if stored.Email != "john@example.com" {
t.Errorf("expected stored email to be john@example.com, got %s", stored.Email)
}
}
type testNewsletterSubscription struct {
userSubscribed happydns.UserInfo
}
func (ds *testNewsletterSubscription) SubscribeToNewsletter(u happydns.UserInfo) error {
ds.userSubscribed = u
return nil
}
func Test_CompleteAuthentication_WithNewsletter(t *testing.T) {
mem, _ := inmemory.NewInMemoryStorage()
mockNewsletterSubscription := &testNewsletterSubscription{}
userUsecase := userUC.NewUserUsecases(mem, mockNewsletterSubscription, nil, nil)
authenticationUsecase := usecase.NewAuthenticationUsecase(&happydns.Options{}, mem, userUsecase)
uinfo := testUserInfo{
id: happydns.Identifier([]byte("user-123")),
email: "john@example.com",
newsletter: true,
}
_, err := authenticationUsecase.CompleteAuthentication(uinfo)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
// Check the user has been subscribed
if mockNewsletterSubscription.userSubscribed == nil || mockNewsletterSubscription.userSubscribed.GetEmail() != uinfo.GetEmail() {
t.Errorf("user not subscribed to newsletter after first login")
}
// Reset the subscription state
mockNewsletterSubscription.userSubscribed = nil
// Redo the authentication, now that the user is already registered
_, err = authenticationUsecase.CompleteAuthentication(uinfo)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if mockNewsletterSubscription.userSubscribed != nil {
t.Errorf("user has been re-subscribed to newsletter beyond first login")
}
}
func Test_AuthenticateUserWithPassword_WrongPassword(t *testing.T) {
mem, _ := inmemory.NewInMemoryStorage()
authUser := &happydns.UserAuth{
Email: "a@b.c",
}
err := authUser.DefinePassword("secure")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
err = mem.CreateAuthUser(authUser)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
userUsecase := userUC.NewUserUsecases(mem, nil, nil, nil)
authenticationUsecase := usecase.NewAuthenticationUsecase(&happydns.Options{}, mem, userUsecase)
_, err = authenticationUsecase.AuthenticateUserWithPassword(happydns.LoginRequest{
Email: "a@b.c",
Password: "wrong-password",
})
if err == nil || err.Error() != `tries to login as "a@b.c", but sent an invalid password` {
t.Errorf("unexpected error: %v", err)
}
}
func Test_AuthenticateUserWithPassword_WeakPassword(t *testing.T) {
mem, _ := inmemory.NewInMemoryStorage()
authUser := &happydns.UserAuth{
Email: "a@b.c",
}
err := authUser.DefinePassword("weak")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
err = mem.CreateAuthUser(authUser)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
userUsecase := userUC.NewUserUsecases(mem, nil, nil, nil)
authenticationUsecase := usecase.NewAuthenticationUsecase(&happydns.Options{}, mem, userUsecase)
_, err = authenticationUsecase.AuthenticateUserWithPassword(happydns.LoginRequest{
Email: "a@b.c",
Password: "weak",
})
if err == nil || err.Error() != `tries to login as "a@b.c", but sent an invalid password` {
t.Errorf("unexpected error: %v", err)
}
}
func Test_AuthenticateUserWithPassword_UnverifiedEmail(t *testing.T) {
mem, _ := inmemory.NewInMemoryStorage()
authUser := &happydns.UserAuth{
Email: "a@b.c",
}
err := authUser.DefinePassword("v3rySecure")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
err = mem.CreateAuthUser(authUser)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
userUsecase := userUC.NewUserUsecases(mem, nil, nil, nil)
authenticationUsecase := usecase.NewAuthenticationUsecase(&happydns.Options{}, mem, userUsecase)
_, err = authenticationUsecase.AuthenticateUserWithPassword(happydns.LoginRequest{
Email: "a@b.c",
Password: "v3rySecure",
})
if err == nil || err.Error() != `tries to login as "a@b.c", but has not verified email` {
t.Errorf("unexpected error: %v", err)
}
}
func Test_AuthenticateUserWithPassword_NoEmail(t *testing.T) {
mem, _ := inmemory.NewInMemoryStorage()
authUser := &happydns.UserAuth{
Email: "a@b.c",
}
err := authUser.DefinePassword("v3rySecure")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
err = mem.CreateAuthUser(authUser)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
userUsecase := userUC.NewUserUsecases(mem, nil, nil, nil)
authenticationUsecase := usecase.NewAuthenticationUsecase(&happydns.Options{NoMail: true}, mem, userUsecase)
_, err = authenticationUsecase.AuthenticateUserWithPassword(happydns.LoginRequest{
Email: "a@b.c",
Password: "v3rySecure",
})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
}
func Test_AuthenticateUserWithPassword(t *testing.T) {
mem, _ := inmemory.NewInMemoryStorage()
now := time.Now()
authUser := &happydns.UserAuth{
Email: "a@b.c",
EmailVerification: &now,
}
err := authUser.DefinePassword("v3rySecure")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
err = mem.CreateAuthUser(authUser)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
userUsecase := userUC.NewUserUsecases(mem, nil, nil, nil)
authenticationUsecase := usecase.NewAuthenticationUsecase(&happydns.Options{}, mem, userUsecase)
_, err = authenticationUsecase.AuthenticateUserWithPassword(happydns.LoginRequest{
Email: "a@b.c",
Password: "v3rySecure",
})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
}

View file

@ -0,0 +1,122 @@
package authuser_test
import (
"errors"
"net/mail"
"net/url"
"strings"
"testing"
"git.happydns.org/happyDomain/internal/storage/inmemory"
"git.happydns.org/happyDomain/internal/usecase/authuser"
"git.happydns.org/happyDomain/model"
)
func TestGenAccountRecoveryHash(t *testing.T) {
recoveryKey := make([]byte, 64)
hash := authuser.GenAccountRecoveryHash(recoveryKey, false)
if hash == "" {
t.Error("Expected non-empty hash")
}
}
func TestCanRecoverAccount(t *testing.T) {
recoveryKey := make([]byte, 64)
user := &happydns.UserAuth{PasswordRecoveryKey: recoveryKey}
hash := authuser.GenAccountRecoveryHash(recoveryKey, false)
err := authuser.CanRecoverAccount(user, hash)
if err != nil {
t.Errorf("Expected no error, got %v", err)
}
}
type dummyMailer struct {
err error
}
func (m *dummyMailer) SendMail(to *mail.Address, subject, content string) (err error) {
return m.err
}
func TestGenerateLink(t *testing.T) {
store, _ := inmemory.NewInMemoryStorage()
config := &happydns.Options{ExternalURL: url.URL{Scheme: "http", Host: "example.com"}}
uc := authuser.NewRecoverAccountUsecase(store, nil, config, nil)
user := &happydns.UserAuth{Id: []byte("user1"), Email: "user@example.com"}
link, err := uc.GenerateLink(user)
if err != nil {
t.Errorf("Expected no error, got %v", err)
}
if link == "" {
t.Error("Expected non-empty link")
}
}
func TestSendLink(t *testing.T) {
store, _ := inmemory.NewInMemoryStorage()
mailer := &dummyMailer{}
config := &happydns.Options{ExternalURL: url.URL{Scheme: "http", Host: "example.com"}}
uc := authuser.NewRecoverAccountUsecase(store, mailer, config, nil)
user := &happydns.UserAuth{Id: []byte("user1"), Email: "user@example.com"}
err := uc.SendLink(user)
if err != nil {
t.Errorf("Expected no error, got %v", err)
}
}
func TestSendLink_Error(t *testing.T) {
store, _ := inmemory.NewInMemoryStorage()
mailer := &dummyMailer{err: errors.New("SMTP Error")}
config := &happydns.Options{ExternalURL: url.URL{Scheme: "http", Host: "example.com"}}
uc := authuser.NewRecoverAccountUsecase(store, mailer, config, nil)
user := &happydns.UserAuth{Id: []byte("user1"), Email: "user@example.com"}
err := uc.SendLink(user)
if err == nil || err.Error() != "SMTP Error" {
t.Errorf("Expected SMTP Error, got %v", err)
}
}
func TestResetPassword(t *testing.T) {
store, _ := inmemory.NewInMemoryStorage()
mailer := &dummyMailer{}
config := &happydns.Options{ExternalURL: url.URL{Scheme: "http", Host: "example.com"}}
changePassword := authuser.NewChangePasswordUsecase(store, authuser.NewCheckPasswordConstraintsUsecase())
uc := authuser.NewRecoverAccountUsecase(store, mailer, config, changePassword)
user := &happydns.UserAuth{Id: []byte("user1"), Email: "user@example.com", PasswordRecoveryKey: make([]byte, 64)}
err := uc.ResetPassword(user, happydns.AccountRecoveryForm{
Key: authuser.GenAccountRecoveryHash(user.PasswordRecoveryKey, false),
Password: "StrongPassword123!",
})
if err != nil {
t.Errorf("Expected no error, got %v", err)
}
// Previous recovery hash should work too
err = uc.ResetPassword(user, happydns.AccountRecoveryForm{
Key: authuser.GenAccountRecoveryHash(user.PasswordRecoveryKey, true),
Password: "StrongPassword123!",
})
if err != nil {
t.Errorf("Expected no error, got %v", err)
}
// Invalid key should not work
otherKey := make([]byte, 64)
otherKey[1] = byte('a')
err = uc.ResetPassword(user, happydns.AccountRecoveryForm{
Key: authuser.GenAccountRecoveryHash(otherKey, true),
Password: "StrongPassword123!",
})
if err == nil || !strings.HasPrefix(err.Error(), "The account recovery link you follow is invalid or has expired (it is valid during ") {
t.Errorf("Expected invalid recovery link, got %v", err)
}
}

View file

@ -0,0 +1,51 @@
// This file is part of the happyDomain (R) project.
// Copyright (c) 2020-2025 happyDomain
// Authors: Pierre-Olivier Mercier, et al.
//
// This program is offered under a commercial and under the AGPL license.
// For commercial licensing, contact us at <contact@happydomain.org>.
//
// For AGPL licensing:
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package authuser_test
import (
"testing"
"git.happydns.org/happyDomain/internal/usecase/authuser"
"git.happydns.org/happyDomain/model"
)
func TestCanRegisterUsecase_IsOpened(t *testing.T) {
// Test case where registration is enabled
t.Run("Registration is enabled", func(t *testing.T) {
cfg := &happydns.Options{DisableRegistration: false}
uc := authuser.NewCanRegisterUsecase(cfg)
if !uc.IsOpened() {
t.Errorf("Expected registration to be enabled")
}
})
// Test case where registration is disabled
t.Run("Registration is disabled", func(t *testing.T) {
cfg := &happydns.Options{DisableRegistration: true}
uc := authuser.NewCanRegisterUsecase(cfg)
if uc.IsOpened() {
t.Errorf("Expected registration to be disabled")
}
})
}

View file

@ -0,0 +1,125 @@
// This file is part of the happyDomain (R) project.
// Copyright (c) 2020-2025 happyDomain
// Authors: Pierre-Olivier Mercier, et al.
//
// This program is offered under a commercial and under the AGPL license.
// For commercial licensing, contact us at <contact@happydomain.org>.
//
// For AGPL licensing:
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package authuser_test
import (
"testing"
"git.happydns.org/happyDomain/internal/storage/inmemory"
"git.happydns.org/happyDomain/internal/usecase/authuser"
"git.happydns.org/happyDomain/model"
)
func TestChangePasswordUsecase_Change(t *testing.T) {
// Setup in-memory storage
storage, _ := inmemory.NewInMemoryStorage()
user := &happydns.UserAuth{
Email: "test@example.com",
}
user.DefinePassword("oldpassword")
// Create a user in the storage
err := storage.CreateAuthUser(user)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
// Setup the usecase
checkPasswordConstraints := authuser.NewCheckPasswordConstraintsUsecase()
uc := authuser.NewChangePasswordUsecase(storage, checkPasswordConstraints)
// Test changing password
newPassword := "newPa$$w0rd"
err = uc.Change(user, newPassword)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
// Verify the password was changed
updatedUser, err := storage.GetAuthUser(user.Id)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
if !updatedUser.CheckPassword(newPassword) {
t.Error("Expected password to be updated")
}
}
func TestChangePasswordUsecase_CheckNewPassword(t *testing.T) {
// Setup in-memory storage
storage, _ := inmemory.NewInMemoryStorage()
user := &happydns.UserAuth{
Email: "test@example.com",
}
user.DefinePassword("oldpassword")
// Create a user in the storage
err := storage.CreateAuthUser(user)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
// Setup the usecase
checkPasswordConstraints := authuser.NewCheckPasswordConstraintsUsecase()
uc := authuser.NewChangePasswordUsecase(storage, checkPasswordConstraints)
// Test checking new password with correct current password
form := happydns.ChangePasswordForm{
Current: "oldpassword",
Password: "newPa$$w0rd",
PasswordConfirm: "newPa$$w0rd",
}
err = uc.CheckNewPassword(user, form)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
// Test checking new password with incorrect current password
form.Current = "wrongpassword"
err = uc.CheckNewPassword(user, form)
if err == nil {
t.Error("Expected error for incorrect current password")
}
}
func TestChangePasswordUsecase_CheckResetPassword(t *testing.T) {
// Setup the usecase
checkPasswordConstraints := authuser.NewCheckPasswordConstraintsUsecase()
uc := authuser.NewChangePasswordUsecase(nil, checkPasswordConstraints)
// Test checking reset password with matching passwords
form := happydns.ChangePasswordForm{
Password: "newPa$$w0rd",
PasswordConfirm: "newPa$$w0rd",
}
err := uc.CheckResetPassword(&happydns.UserAuth{}, form)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
// Test checking reset password with non-matching passwords
form.PasswordConfirm = "differentpassword"
err = uc.CheckResetPassword(&happydns.UserAuth{}, form)
if err == nil {
t.Error("Expected error for non-matching passwords")
}
}

View file

@ -0,0 +1,144 @@
package authuser_test
import (
"errors"
"testing"
"git.happydns.org/happyDomain/internal/mailer"
"git.happydns.org/happyDomain/internal/storage/inmemory"
"git.happydns.org/happyDomain/internal/usecase/authuser"
"git.happydns.org/happyDomain/model"
)
type dummyEmailValidation struct {
err error
}
func (d *dummyEmailValidation) GenerateLink(u *happydns.UserAuth) string {
return ""
}
func (d *dummyEmailValidation) SendLink(u *happydns.UserAuth) error {
return d.err
}
func (d *dummyEmailValidation) Validate(user *happydns.UserAuth, form happydns.AddressValidationForm) error {
return d.err
}
func TestCreateAuthUser_Success(t *testing.T) {
store, _ := inmemory.NewInMemoryStorage()
pwChecker := authuser.NewCheckPasswordConstraintsUsecase()
emailValidation := &dummyEmailValidation{}
usecase := authuser.NewCreateAuthUserUsecase(store, &mailer.Mailer{}, pwChecker, emailValidation)
reg := happydns.UserRegistration{
Email: "test@example.com",
Password: "StrongPassword123!",
Newsletter: true,
}
user, err := usecase.Create(reg)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
if user.Email != reg.Email {
t.Errorf("expected email %s, got %s", reg.Email, user.Email)
}
if user.Password == nil {
t.Errorf("expected defined password, got %s", user.Password)
}
if !user.AllowCommercials {
t.Error("expected user to have AllowCommercials = true")
}
}
func TestCreateAuthUser_InvalidEmail(t *testing.T) {
store, _ := inmemory.NewInMemoryStorage()
pwChecker := authuser.NewCheckPasswordConstraintsUsecase()
usecase := authuser.NewCreateAuthUserUsecase(store, &mailer.Mailer{}, pwChecker, &dummyEmailValidation{})
reg := happydns.UserRegistration{
Email: "bademail",
Password: "StrongPassword123!",
}
_, err := usecase.Create(reg)
if err == nil || err.Error() != "the given email is invalid" {
t.Errorf("expected validation error for email, got: %v", err)
}
}
func TestCreateAuthUser_WeakPassword(t *testing.T) {
store, _ := inmemory.NewInMemoryStorage()
pwChecker := authuser.NewCheckPasswordConstraintsUsecase()
usecase := authuser.NewCreateAuthUserUsecase(store, &mailer.Mailer{}, pwChecker, &dummyEmailValidation{})
reg := happydns.UserRegistration{
Email: "test@example.com",
Password: "123",
}
_, err := usecase.Create(reg)
if err == nil || err.Error() != "password must be at least 8 characters long" {
t.Errorf("expected password constraint error, got: %v", err)
}
reg.Password = "Secur3$"
_, err = usecase.Create(reg)
if err == nil || err.Error() != "password must be at least 8 characters long" {
t.Errorf("expected password constraint error, got: %v", err)
}
reg.Password = "secure123"
_, err = usecase.Create(reg)
if err == nil || err.Error() != "Password must contain upper case letters." {
t.Errorf("expected password constraint error, got: %v", err)
}
reg.Password = "Secure123"
_, err = usecase.Create(reg)
if err == nil || err.Error() != "Password must be longer or contain symbols." {
t.Errorf("expected password constraint error, got: %v", err)
}
}
func TestCreateAuthUser_EmailAlreadyUsed(t *testing.T) {
store, _ := inmemory.NewInMemoryStorage()
pwChecker := authuser.NewCheckPasswordConstraintsUsecase()
usecase := authuser.NewCreateAuthUserUsecase(store, &mailer.Mailer{}, pwChecker, &dummyEmailValidation{})
// Create a user first
reg := happydns.UserRegistration{
Email: "used@example.com",
Password: "StrongPassword123!",
}
_, err := usecase.Create(reg)
if err != nil {
t.Fatalf("setup user creation failed: %v", err)
}
// Try creating again with the same email
_, err = usecase.Create(reg)
if err == nil || err.Error() != "an account already exists with the given address. Try logging in." {
t.Errorf("expected duplicate email error, got: %v", err)
}
}
func TestCreateAuthUser_EmailValidationFails(t *testing.T) {
store, _ := inmemory.NewInMemoryStorage()
pwChecker := authuser.NewCheckPasswordConstraintsUsecase()
emailValidation := &dummyEmailValidation{err: errors.New("SMTP error")}
usecase := authuser.NewCreateAuthUserUsecase(store, &mailer.Mailer{}, pwChecker, emailValidation)
reg := happydns.UserRegistration{
Email: "fail@example.com",
Password: "StrongPassword123!",
}
_, err := usecase.Create(reg)
if err == nil || err.Error() != "unable to send validation email: SMTP error" {
t.Errorf("expected internal error for email sending, got: %v", err)
}
}

View file

@ -0,0 +1,101 @@
// This file is part of the happyDomain (R) project.
// Copyright (c) 2020-2025 happyDomain
// Authors: Pierre-Olivier Mercier, et al.
//
// This program is offered under a commercial and under the AGPL license.
// For commercial licensing, contact us at <contact@happydomain.org>.
//
// For AGPL licensing:
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package authuser_test
import (
"fmt"
"testing"
"git.happydns.org/happyDomain/internal/storage/inmemory"
"git.happydns.org/happyDomain/internal/usecase/authuser"
"git.happydns.org/happyDomain/model"
)
// MockCloseUserSessionsUsecase is a mock implementation of CloseUserSessionsUsecase.
type MockCloseUserSessionsUsecase struct {
CloseAllFunc func(user happydns.UserInfo) error
}
func (m *MockCloseUserSessionsUsecase) CloseAll(user happydns.UserInfo) error {
return m.CloseAllFunc(user)
}
func (m *MockCloseUserSessionsUsecase) ByID(userID happydns.Identifier) error {
return m.CloseAll(&happydns.UserAuth{Id: userID})
}
func TestDeleteAuthUserUsecase_Delete(t *testing.T) {
// Create an in-memory storage
store, _ := inmemory.NewInMemoryStorage()
// Create a mock for CloseUserSessionsUsecase
mockCloseUserSessions := &MockCloseUserSessionsUsecase{
CloseAllFunc: func(user happydns.UserInfo) error {
return nil
},
}
// Create an instance of DeleteAuthUserUsecase
uc := authuser.NewDeleteAuthUserUsecase(store, mockCloseUserSessions)
// Create a test user
user := &happydns.UserAuth{
Email: "test@example.com",
}
user.DefinePassword("test-password")
// Add the user to the storage
err := store.CreateAuthUser(user)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
// Test case 1: Invalid password
err = uc.Delete(user, "wrong-password")
if err == nil || err.Error() != "invalid current password" {
t.Errorf("Expected error 'invalid current password', got %v", err)
}
// Test case 2: Error in closing sessions
mockCloseUserSessions.CloseAllFunc = func(user happydns.UserInfo) error {
return fmt.Errorf("error closing sessions")
}
err = uc.Delete(user, "test-password")
if err == nil || err.Error() != "unable to delete user sessions: error closing sessions" {
t.Errorf("Expected error 'unable to delete user sessions: error closing sessions', got %v", err)
}
// Test case 3: Bad password when deleting user
err = uc.Delete(user, "bad-password")
if err == nil || err.Error() != "invalid current password" {
t.Errorf("Expected error 'invalid current password', got %v", err)
}
// Test case 4: Successful deletion
mockCloseUserSessions.CloseAllFunc = func(user happydns.UserInfo) error {
return nil
}
err = uc.Delete(user, "test-password")
if err != nil {
t.Errorf("Expected no error, got %v", err)
}
}

View file

@ -0,0 +1,95 @@
package authuser_test
import (
"errors"
"net/url"
"strings"
"testing"
"time"
"git.happydns.org/happyDomain/internal/storage/inmemory"
"git.happydns.org/happyDomain/internal/usecase/authuser"
"git.happydns.org/happyDomain/model"
)
func TestGenEmailValidationHash(t *testing.T) {
hash := authuser.GenRegistrationHash(&happydns.UserAuth{CreatedAt: time.Now()}, false)
if hash == "" {
t.Error("Expected non-empty hash")
}
}
func TestGenerateRegistrationLink(t *testing.T) {
store, _ := inmemory.NewInMemoryStorage()
config := &happydns.Options{ExternalURL: url.URL{Scheme: "http", Host: "example.com"}}
uc := authuser.NewEmailValidationUsecase(store, nil, config)
user := &happydns.UserAuth{Id: []byte("user1"), Email: "user@example.com"}
link := uc.GenerateLink(user)
if link == "" {
t.Error("Expected non-empty link")
}
}
func TestSendRegistrationLink(t *testing.T) {
store, _ := inmemory.NewInMemoryStorage()
mailer := &dummyMailer{}
config := &happydns.Options{ExternalURL: url.URL{Scheme: "http", Host: "example.com"}}
uc := authuser.NewEmailValidationUsecase(store, mailer, config)
user := &happydns.UserAuth{Id: []byte("user1"), Email: "user@example.com"}
err := uc.SendLink(user)
if err != nil {
t.Errorf("Expected no error, got %v", err)
}
}
func TestSendRegistrationLink_Error(t *testing.T) {
store, _ := inmemory.NewInMemoryStorage()
mailer := &dummyMailer{err: errors.New("SMTP Error")}
config := &happydns.Options{ExternalURL: url.URL{Scheme: "http", Host: "example.com"}}
uc := authuser.NewEmailValidationUsecase(store, mailer, config)
user := &happydns.UserAuth{Id: []byte("user1"), Email: "user@example.com"}
err := uc.SendLink(user)
if err == nil || err.Error() != "SMTP Error" {
t.Errorf("Expected SMTP Error, got %v", err)
}
}
func TestValidateEmail(t *testing.T) {
store, _ := inmemory.NewInMemoryStorage()
mailer := &dummyMailer{}
config := &happydns.Options{ExternalURL: url.URL{Scheme: "http", Host: "example.com"}}
uc := authuser.NewEmailValidationUsecase(store, mailer, config)
user := &happydns.UserAuth{Id: []byte("user1"), Email: "user@example.com", PasswordRecoveryKey: make([]byte, 64)}
err := uc.Validate(user, happydns.AddressValidationForm{
Key: authuser.GenRegistrationHash(user, false),
})
if err != nil {
t.Errorf("Expected no error, got %v", err)
}
// Previous recovery hash should work too
err = uc.Validate(user, happydns.AddressValidationForm{
Key: authuser.GenRegistrationHash(user, true),
})
if err != nil {
t.Errorf("Expected no error, got %v", err)
}
// Non-matching user creation time should not work
user2 := *user
user2.CreatedAt = time.Now()
err = uc.Validate(user, happydns.AddressValidationForm{
Key: authuser.GenRegistrationHash(&user2, false),
})
if err == nil || !strings.HasPrefix(err.Error(), "bad email validation key: the validation address link you follow is invalid or has expired (it is valid during ") {
t.Errorf("Expected invalid validation link, got %v", err)
}
}

View file

@ -0,0 +1,71 @@
package authuser_test
import (
"testing"
"time"
"git.happydns.org/happyDomain/internal/storage/inmemory"
"git.happydns.org/happyDomain/internal/usecase/authuser"
"git.happydns.org/happyDomain/model"
)
func TestGetAuthUserUsecase(t *testing.T) {
memStore, err := inmemory.NewInMemoryStorage()
if err != nil {
t.Fatalf("Failed to create in-memory storage: %v", err)
}
now := time.Now()
user := &happydns.UserAuth{
Email: "test@example.com",
EmailVerification: &now,
CreatedAt: now,
LastLoggedIn: &now,
Password: []byte("fakehash"),
}
// Add new user in memory (and assign an ID)
err = memStore.CreateAuthUser(user)
if err != nil {
t.Fatalf("Failed to create auth user: %v", err)
}
if user.Id == nil {
t.Fatalf("Expected non-nil user ID, got %s", user.Id)
}
uc := authuser.NewGetAuthUserUsecase(memStore)
t.Run("ByID returns the correct user", func(t *testing.T) {
got, err := uc.ByID(user.Id)
if err != nil {
t.Errorf("Expected no error, got %v", err)
}
if got.Email != "test@example.com" {
t.Errorf("Expected email 'test@example.com', got %s", got.Email)
}
})
t.Run("ByEmail returns the correct user", func(t *testing.T) {
got, err := uc.ByEmail("test@example.com")
if err != nil {
t.Errorf("Expected no error, got %v", err)
}
if !got.Id.Equals(user.Id) {
t.Errorf("Expected ID '%s', got %s", user.Id, got.Id)
}
})
t.Run("ByID returns error for unknown ID", func(t *testing.T) {
_, err := uc.ByID([]byte("unknown-id"))
if err == nil {
t.Error("Expected error for unknown ID, got nil")
}
})
t.Run("ByEmail returns error for unknown email", func(t *testing.T) {
_, err := uc.ByEmail("unknown@example.com")
if err == nil {
t.Error("Expected error for unknown email, got nil")
}
})
}