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

376 lines
11 KiB
Go

package mysql
import (
"context"
"fmt"
"strings"
"testing"
"time"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"wucher/internal/domain/base"
filemanager "wucher/internal/domain/file_manager"
"wucher/internal/service"
"wucher/internal/shared/pkg/uuidv7"
)
func openBaseTestDB(t *testing.T) *gorm.DB {
t.Helper()
dsn := fmt.Sprintf("file:base_test_%d?mode=memory&cache=shared", time.Now().UnixNano())
db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{
NowFunc: func() time.Time { return time.Now().UTC() },
})
if err != nil {
t.Fatalf("open sqlite: %v", err)
}
if err := db.AutoMigrate(&base.BaseCategory{}, &filemanager.Folder{}, &filemanager.File{}, &filemanager.Attachment{}, &base.Base{}, &base.BaseContactRole{}); err != nil {
t.Fatalf("auto migrate: %v", err)
}
if err := db.AutoMigrate(&base.BaseOperationalShiftTime{}); err != nil {
t.Fatalf("auto migrate base operational shift time: %v", err)
}
return db
}
func TestBaseRepositoryUpdateBase_ReplacesFotoAttachmentID(t *testing.T) {
db := openBaseTestDB(t)
repo := NewBaseRepository(db)
now := time.Now().UTC()
category := &base.BaseCategory{
ID: uuidv7.MustBytes(),
Key: base.CategoryKeyRegular,
Name: "Regular",
CreatedAt: now,
UpdatedAt: now,
}
if err := db.Create(category).Error; err != nil {
t.Fatalf("create category: %v", err)
}
folder := &filemanager.Folder{
ID: uuidv7.MustBytes(),
Name: "photos",
NameNormalized: "photos",
Depth: 0,
PathCache: "/photos",
NameSlot: "live",
CreatedAt: now,
UpdatedAt: now,
}
if err := db.Create(folder).Error; err != nil {
t.Fatalf("create folder: %v", err)
}
makeFile := func(name, key string) *filemanager.File {
return &filemanager.File{
ID: uuidv7.MustBytes(),
FolderID: folder.ID,
Name: name,
NameNormalized: strings.ToLower(name),
Extension: "webp",
SizeBytes: 1234,
MimeType: "image/webp",
Bucket: "bucket",
ObjectKey: key,
Status: filemanager.FileStatusReady,
NameSlot: "live",
CreatedAt: now,
UpdatedAt: now,
}
}
oldFile := makeFile("old.webp", "objects/old.webp")
newFile := makeFile("new.webp", "objects/new.webp")
if err := db.Create(oldFile).Error; err != nil {
t.Fatalf("create old file: %v", err)
}
if err := db.Create(newFile).Error; err != nil {
t.Fatalf("create new file: %v", err)
}
oldAttachment := &filemanager.Attachment{
ID: uuidv7.MustBytes(),
FileID: oldFile.ID,
RefType: "base_photo",
RefID: "base-1",
IsPrimary: true,
CreatedAt: now,
UpdatedAt: now,
}
newAttachment := &filemanager.Attachment{
ID: uuidv7.MustBytes(),
FileID: newFile.ID,
RefType: "base_photo",
RefID: "base-1",
IsPrimary: true,
CreatedAt: now,
UpdatedAt: now,
}
if err := db.Create(oldAttachment).Error; err != nil {
t.Fatalf("create old attachment: %v", err)
}
if err := db.Create(newAttachment).Error; err != nil {
t.Fatalf("create new attachment: %v", err)
}
row := &base.Base{
ID: uuidv7.MustBytes(),
BaseCategoryID: category.ID,
BaseName: "Base Old",
FotoAttachmentID: oldAttachment.ID,
FotoAttachment: oldAttachment,
CreatedAt: now,
UpdatedAt: now,
}
if err := db.Create(row).Error; err != nil {
t.Fatalf("seed create base: %v", err)
}
row.FotoAttachmentID = newAttachment.ID
row.FotoAttachment = oldAttachment
if err := repo.UpdateBase(context.Background(), row, base.CategoryKeyRegular); err != nil {
t.Fatalf("update base: %v", err)
}
loaded, err := repo.GetBaseByID(context.Background(), row.ID, base.CategoryKeyRegular)
if err != nil {
t.Fatalf("get by id after update: %v", err)
}
if loaded == nil {
t.Fatalf("expected updated row")
}
if string(loaded.FotoAttachmentID) != string(newAttachment.ID) {
t.Fatalf("expected foto_attachment_id replaced")
}
}
func TestBaseRepositoryListBases_DefaultOrderActiveSortkey(t *testing.T) {
db := openBaseTestDB(t)
repo := NewBaseRepository(db)
now := time.Now().UTC()
category := &base.BaseCategory{
ID: uuidv7.MustBytes(),
Key: base.CategoryKeyRegular,
Name: "Regular",
CreatedAt: now,
UpdatedAt: now,
}
if err := db.Create(category).Error; err != nil {
t.Fatalf("create category: %v", err)
}
seed := []base.Base{
{ID: uuidv7.MustBytes(), BaseName: "Gamma", IsActive: true},
{ID: uuidv7.MustBytes(), BaseName: "Beta", SortKey: intPtrBaseRepo(0), IsActive: true},
{ID: uuidv7.MustBytes(), BaseName: "Charlie", SortKey: intPtrBaseRepo(2), IsActive: true},
{ID: uuidv7.MustBytes(), BaseName: "Alpha", SortKey: intPtrBaseRepo(1), IsActive: true},
{ID: uuidv7.MustBytes(), BaseName: "Zulu", IsActive: false},
{ID: uuidv7.MustBytes(), BaseName: "Bravo", SortKey: intPtrBaseRepo(9), IsActive: false},
}
for i := range seed {
if err := repo.CreateBase(context.Background(), &seed[i], base.CategoryKeyRegular); err != nil {
t.Fatalf("seed create: %v", err)
}
}
rows, total, err := repo.ListBases(context.Background(), "", "", 0, 0, "", nil)
if err != nil {
t.Fatalf("list bases: %v", err)
}
if total != 6 || len(rows) != 6 {
t.Fatalf("unexpected total/len total=%d len=%d", total, len(rows))
}
gotOrder := []string{rows[0].BaseName, rows[1].BaseName, rows[2].BaseName, rows[3].BaseName, rows[4].BaseName, rows[5].BaseName}
wantOrder := []string{"Beta", "Alpha", "Charlie", "Gamma", "Bravo", "Zulu"}
for i := range wantOrder {
if gotOrder[i] != wantOrder[i] {
t.Fatalf("unexpected default order: got=%v want=%v", gotOrder, wantOrder)
}
}
}
func TestBaseRepositoryCreateAndGetOperationalShiftTimes(t *testing.T) {
db := openBaseTestDB(t)
repo := NewBaseRepository(db)
now := time.Now().UTC()
category := &base.BaseCategory{
ID: uuidv7.MustBytes(),
Key: base.CategoryKeyRegular,
Name: "Regular",
CreatedAt: now,
UpdatedAt: now,
}
if err := db.Create(category).Error; err != nil {
t.Fatalf("create category: %v", err)
}
start := time.Date(1900, 1, 1, 9, 0, 0, 0, time.UTC)
end := time.Date(1900, 1, 1, 18, 0, 0, 0, time.UTC)
date := time.Date(2026, 4, 12, 0, 0, 0, 0, time.UTC)
row := &base.Base{
ID: uuidv7.MustBytes(),
BaseCategoryID: category.ID,
BaseName: "Base Ops",
OperationalShiftTimes: []base.BaseOperationalShiftTime{
{
DateStart: &date,
DateEnd: &date,
StartTimeType: base.ShiftTimeTypeFixed,
EndTimeType: base.ShiftTimeTypeFixed,
ShiftStart: start.Format("15:04:05"),
ShiftEnd: end.Format("15:04:05"),
},
},
CreatedAt: now,
UpdatedAt: now,
}
if err := repo.CreateBase(context.Background(), row, base.CategoryKeyRegular); err != nil {
t.Fatalf("create base: %v", err)
}
loaded, err := repo.GetBaseByID(context.Background(), row.ID, base.CategoryKeyRegular)
if err != nil {
t.Fatalf("get base: %v", err)
}
if loaded == nil || len(loaded.OperationalShiftTimes) != 1 {
t.Fatalf("expected operational shift time to preload")
}
if got := loaded.OperationalShiftTimes[0].DateStart.Format("2006-01-02"); got != "2026-04-12" {
t.Fatalf("unexpected date start: %s", got)
}
if got := loaded.OperationalShiftTimes[0].DateEnd.Format("2006-01-02"); got != "2026-04-12" {
t.Fatalf("unexpected date end: %s", got)
}
if got := loaded.OperationalShiftTimes[0].ShiftStart; got != "09:00:00" {
t.Fatalf("unexpected shift start: %#v", got)
}
if got := loaded.OperationalShiftTimes[0].StartTimeType; got != base.ShiftTimeTypeFixed {
t.Fatalf("unexpected start_time_type: %#v", got)
}
}
func TestBaseRepositoryCreateOperationalShiftTimes_AllowsNilShiftForNonFixedTypes(t *testing.T) {
db := openBaseTestDB(t)
repo := NewBaseRepository(db)
now := time.Now().UTC()
category := &base.BaseCategory{
ID: uuidv7.MustBytes(),
Key: base.CategoryKeyRegular,
Name: "Regular",
CreatedAt: now,
UpdatedAt: now,
}
if err := db.Create(category).Error; err != nil {
t.Fatalf("create category: %v", err)
}
date := time.Date(2026, 4, 12, 0, 0, 0, 0, time.UTC)
row := &base.Base{
ID: uuidv7.MustBytes(),
BaseCategoryID: category.ID,
BaseName: "Base Ops Empty Shift",
OperationalShiftTimes: []base.BaseOperationalShiftTime{
{
DateStart: &date,
DateEnd: &date,
StartTimeType: base.ShiftTimeTypeBMCT,
EndTimeType: base.ShiftTimeTypeECET,
},
},
CreatedAt: now,
UpdatedAt: now,
}
if err := repo.CreateBase(context.Background(), row, base.CategoryKeyRegular); err != nil {
t.Fatalf("create base: %v", err)
}
loaded, err := repo.GetBaseByID(context.Background(), row.ID, base.CategoryKeyRegular)
if err != nil {
t.Fatalf("get base: %v", err)
}
if loaded == nil || len(loaded.OperationalShiftTimes) != 1 {
t.Fatalf("expected operational shift time to preload")
}
if got := loaded.OperationalShiftTimes[0].StartTimeType; got != base.ShiftTimeTypeBMCT {
t.Fatalf("unexpected start_time_type: %#v", got)
}
if got := loaded.OperationalShiftTimes[0].EndTimeType; got != base.ShiftTimeTypeECET {
t.Fatalf("unexpected end_time_type: %#v", got)
}
if got := loaded.OperationalShiftTimes[0].ShiftStart; got != "" {
t.Fatalf("expected empty shift start for non-fixed type, got %#v", got)
}
if got := loaded.OperationalShiftTimes[0].ShiftEnd; got != "" {
t.Fatalf("expected empty shift end for non-fixed type, got %#v", got)
}
}
func TestBaseServiceCreateDetailed_PersistsFixedOperationalShiftTimes(t *testing.T) {
db := openBaseTestDB(t)
repo := NewBaseRepository(db)
svc := service.NewBaseService(repo)
now := time.Now().UTC()
category := &base.BaseCategory{
ID: uuidv7.MustBytes(),
Key: base.CategoryKeyRegular,
Name: "Regular",
CreatedAt: now,
UpdatedAt: now,
}
if err := db.Create(category).Error; err != nil {
t.Fatalf("create category: %v", err)
}
start := time.Date(2000, 1, 1, 3, 0, 0, 0, time.UTC)
end := time.Date(2000, 1, 1, 5, 0, 0, 0, time.UTC)
date := time.Date(2026, 6, 15, 0, 0, 0, 0, time.UTC)
res, err := svc.CreateDetailed(context.Background(), service.BaseCreateInput{
ID: uuidv7.MustBytes(),
CategoryType: base.CategoryKeyRegular,
BaseName: "Base Ops Fixed",
Latitude: 47.3769,
Longitude: 8.5417,
DefaultStartTimeType: base.ShiftTimeTypeFixed,
DefaultEndTimeType: base.ShiftTimeTypeFixed,
DefaultShiftStart: &start,
DefaultShiftEnd: &end,
OperationalShiftTimes: []service.BaseOperationalShiftTimeInput{
{
DateStart: date,
DateEnd: date,
StartTimeType: base.ShiftTimeTypeFixed,
EndTimeType: base.ShiftTimeTypeFixed,
ShiftStart: &start,
ShiftEnd: &end,
},
},
})
if err != nil {
t.Fatalf("create detailed: %v", err)
}
if res == nil || res.Row == nil {
t.Fatalf("expected create detailed result row")
}
loaded, err := repo.GetBaseByID(context.Background(), res.Row.ID, base.CategoryKeyRegular)
if err != nil {
t.Fatalf("get base: %v", err)
}
if loaded == nil || len(loaded.OperationalShiftTimes) != 1 {
t.Fatalf("expected operational shift time to preload")
}
if got := loaded.OperationalShiftTimes[0].ShiftStart; got != "03:00:00" {
t.Fatalf("unexpected shift start stored: %#v", got)
}
if got := loaded.OperationalShiftTimes[0].ShiftEnd; got != "05:00:00" {
t.Fatalf("unexpected shift end stored: %#v", got)
}
}
func intPtrBaseRepo(v int) *int { return &v }