user accounts

This commit is contained in:
2026-09-01 13:53:22 +05:30
parent 6b27448ff9
commit 1e3fe88e87
2 changed files with 75 additions and 0 deletions

View File

@@ -4,6 +4,7 @@ import (
"testing" "testing"
"nearle/models" "nearle/models"
"nearle/repositories"
) )
/* /*
@@ -62,3 +63,71 @@ func TestNoEmailLeavesTheSignInNameEmpty(t *testing.T) {
t.Errorf("authname = %q, want empty", ready.Authname) t.Errorf("authname = %q, want empty", ready.Authname)
} }
} }
/*
The wiring, not the function.
PrepareNewAccount was written, tested and committed — and never called from
`users/create`. The unit tests above all passed, because they exercise the
function directly; nothing asserted that the creation path actually used it. So
four deployments shipped a fix that was not reachable, and the bug it fixes was
still reproducible in production every time.
These call the SERVICE and inspect what reaches the repository, which is the
only thing that can tell the two apart.
*/
type recordingUserRepo struct {
repositories.UserRepository
created models.User
}
func (r *recordingUserRepo) CreateUser(user models.User) (int, error) {
r.created = user
return 4242, nil
}
func (r *recordingUserRepo) GetUserById(uid int) (models.UserInfo, error) {
return models.UserInfo{Userid: uid}, nil
}
func TestCreateUserActuallyPreparesTheAccount(t *testing.T) {
repo := &recordingUserRepo{}
if _, err := NewUserService(repo).CreateUser(models.User{
Email: "thiruomart@gmail.com",
}); err != nil {
t.Fatalf("CreateUser: %v", err)
}
if repo.created.Authname != "thiruomart@gmail.com" {
t.Errorf("the account reached the repository with authname %q — PrepareNewAccount is not wired in",
repo.created.Authname)
}
if repo.created.Configid != ConsoleConfigID {
t.Errorf("the account reached the repository with configid %d, want %d",
repo.created.Configid, ConsoleConfigID)
}
}
type recordingTenantRepo struct {
repositories.TenantRepository
created models.User
}
func (r *recordingTenantRepo) CreateStaff(user models.User) error {
r.created = user
return nil
}
// The other creation path. Both make back-office accounts, so both have to
// prepare them — and only one of them did.
func TestCreateStaffActuallyPreparesTheAccount(t *testing.T) {
repo := &recordingTenantRepo{}
if err := NewTenantService(repo).CreateStaff(models.User{Email: "suriya@example.com"}); err != nil {
t.Fatalf("CreateStaff: %v", err)
}
if repo.created.Authname != "suriya@example.com" || repo.created.Configid != ConsoleConfigID {
t.Errorf("CreateStaff did not prepare the account: authname=%q configid=%d",
repo.created.Authname, repo.created.Configid)
}
}

View File

@@ -167,6 +167,12 @@ func (s *userService) AppLogin(user models.User) (models.TenantUserInfo, fiber.M
} }
func (s *userService) CreateUser(user models.User) (models.UserInfo, error) { func (s *userService) CreateUser(user models.User) (models.UserInfo, error) {
// Without an authname and a console configid the account is created,
// listed, and then refused at the login screen: `weblogin` matches
// `WHERE authname = ? AND configid = ?` and never looks at the email
// column. See PrepareNewAccount.
user = PrepareNewAccount(user)
// Call repository to create user // Call repository to create user
userid, err := s.repo.CreateUser(user) userid, err := s.repo.CreateUser(user)
if err != nil { if err != nil {