376 lines
11 KiB
Go
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 }
|