Files
fm_be/internal/repository/mysql/user_repo_test.go
2026-07-16 22:16:45 +07:00

206 lines
6.0 KiB
Go

package mysql
import (
"context"
"testing"
"wucher/internal/domain/auth"
"wucher/internal/domain/user"
"wucher/internal/shared/pkg/appctx"
"wucher/internal/shared/pkg/uuidv7"
)
func setupUserRepoFixture(t *testing.T) (*UserRepository, context.Context, []byte) {
t.Helper()
db := openDBTestSQLite(t)
if err := AutoMigrate(db); err != nil {
t.Fatalf("auto migrate: %v", err)
}
repo := NewUserRepository(db)
roleID := uuidv7.MustBytes()
if err := db.Create(&auth.Role{
ID: roleID,
Code: "admin",
Name: "admin",
Description: "admin role",
}).Error; err != nil {
t.Fatalf("seed role: %v", err)
}
return repo, context.Background(), roleID
}
func TestUserRepository_CRUDAndLookupBranches(t *testing.T) {
repo, baseCtx, roleID := setupUserRepoFixture(t)
actorID := uuidv7.MustBytes()
ctx := appctx.WithUserID(baseCtx, actorID)
u := &user.User{
Email: "john@example.com",
Username: "john",
FirstName: "John",
LastName: "Doe",
Timezone: "UTC",
RoleID: roleID,
}
if err := repo.Create(ctx, u); err != nil {
t.Fatalf("create: %v", err)
}
if len(u.CreatedBy) != 16 || len(u.UpdatedBy) != 16 {
t.Fatalf("expected created_by and updated_by set by actor")
}
byEmail, err := repo.GetByEmail(ctx, u.Email)
if err != nil || byEmail == nil {
t.Fatalf("get by email failed: user=%v err=%v", byEmail, err)
}
byUsername, err := repo.GetByUsername(ctx, u.Username)
if err != nil || byUsername == nil {
t.Fatalf("get by username failed: user=%v err=%v", byUsername, err)
}
lastName, err := repo.GetLastNameByID(ctx, u.ID)
if err != nil || lastName != "Doe" {
t.Fatalf("get last name failed: lastName=%q err=%v", lastName, err)
}
// Update branch: RoleIDs nil should skip role-replacement path.
byEmail.FirstName = "Johnny"
byEmail.RoleIDs = nil
if err := repo.Update(ctx, byEmail); err != nil {
t.Fatalf("update (roleIDs nil): %v", err)
}
// Update branch: RoleIDs provided should execute role-replacement path.
byEmail.LastName = "Doe-Updated"
byEmail.RoleIDs = [][]byte{roleID}
if err := repo.Update(ctx, byEmail); err != nil {
t.Fatalf("update (roleIDs set): %v", err)
}
// List branches: with/without filter, sort, and limit.
list, total, err := repo.List(ctx, "john", "users.email ASC", 1, 0)
if err != nil {
t.Fatalf("list with filter: %v", err)
}
if total < 1 || len(list) != 1 {
t.Fatalf("expected one filtered result, total=%d len=%d", total, len(list))
}
list, total, err = repo.List(ctx, "", "", 0, 0)
if err != nil {
t.Fatalf("list without limit: %v", err)
}
if total < 1 || len(list) < 1 {
t.Fatalf("expected at least one user, total=%d len=%d", total, len(list))
}
// Not found branches.
if got, err := repo.GetByID(ctx, uuidv7.MustBytes()); err != nil || got != nil {
t.Fatalf("expected not found by id => nil,nil; got=%v err=%v", got, err)
}
if got, err := repo.GetByEmail(ctx, "missing@example.com"); err != nil || got != nil {
t.Fatalf("expected not found by email => nil,nil; got=%v err=%v", got, err)
}
if got, err := repo.GetByUsername(ctx, "missing-user"); err != nil || got != nil {
t.Fatalf("expected not found by username => nil,nil; got=%v err=%v", got, err)
}
if got, err := repo.GetLastNameByID(ctx, uuidv7.MustBytes()); err != nil || got != "" {
t.Fatalf("expected not found last_name => empty,nil; got=%q err=%v", got, err)
}
if err := repo.Delete(ctx, u.ID); err != nil {
t.Fatalf("delete: %v", err)
}
// Delete branch with invalid uuid bytes (skip attachment cleanup query branch).
if err := repo.Delete(ctx, []byte("short")); err != nil {
t.Fatalf("delete short id should be noop-success, err=%v", err)
}
}
func TestUserRepository_ErrorBranchesWhenDatabaseClosed(t *testing.T) {
db := openDBTestSQLite(t)
if err := AutoMigrate(db); err != nil {
t.Fatalf("auto migrate: %v", err)
}
repo := NewUserRepository(db)
sqlDB, err := db.DB()
if err != nil {
t.Fatalf("db.DB: %v", err)
}
if err := sqlDB.Close(); err != nil {
t.Fatalf("close db: %v", err)
}
ctx := context.Background()
roleID := uuidv7.MustBytes()
userID := uuidv7.MustBytes()
u := &user.User{
ID: userID,
Email: "closed@example.com",
Username: "closed",
FirstName: "Closed",
LastName: "DB",
Timezone: "UTC",
RoleID: roleID,
}
if err := repo.Create(ctx, u); err == nil {
t.Fatalf("expected create error on closed db")
}
if err := repo.Update(ctx, u); err == nil {
t.Fatalf("expected update error on closed db")
}
if err := repo.Delete(ctx, userID); err == nil {
t.Fatalf("expected delete error on closed db")
}
if _, err := repo.GetByID(ctx, userID); err == nil {
t.Fatalf("expected get by id error on closed db")
}
if _, err := repo.GetByEmail(ctx, u.Email); err == nil {
t.Fatalf("expected get by email error on closed db")
}
if _, err := repo.GetByUsername(ctx, u.Username); err == nil {
t.Fatalf("expected get by username error on closed db")
}
if _, err := repo.GetLastNameByID(ctx, userID); err == nil {
t.Fatalf("expected get last name error on closed db")
}
if _, _, err := repo.List(ctx, "", "", 10, 0); err == nil {
t.Fatalf("expected list error on closed db")
}
}
func TestUserRepository_CreateDuplicateAndDeleteAttachmentCleanupError(t *testing.T) {
repo, ctx, roleID := setupUserRepoFixture(t)
u := &user.User{
Email: "dup@example.com",
Username: "dup-user",
FirstName: "Dup",
LastName: "User",
Timezone: "UTC",
RoleID: roleID,
}
if err := repo.Create(ctx, u); err != nil {
t.Fatalf("create first user: %v", err)
}
dup := &user.User{
Email: "dup@example.com",
Username: "dup-user-2",
FirstName: "Dup2",
LastName: "User2",
Timezone: "UTC",
RoleID: roleID,
}
if err := repo.Create(ctx, dup); err == nil {
t.Fatalf("expected duplicate-email create error")
}
// Force attachment cleanup statement to fail so delete returns error branch.
if err := repo.db.Exec("DROP TABLE attachments").Error; err != nil {
t.Fatalf("drop attachments table: %v", err)
}
if err := repo.Delete(ctx, u.ID); err == nil {
t.Fatalf("expected delete error when attachments table missing")
}
}