Files
fm_be/internal/transport/http/handlers/takeover_handler_test.go
2026-07-16 22:16:45 +07:00

626 lines
21 KiB
Go

package handlers
import (
"context"
"fmt"
"strings"
"testing"
"time"
"github.com/gofiber/fiber/v2"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"wucher/internal/domain/base"
filemanager "wucher/internal/domain/file_manager"
"wucher/internal/domain/flight"
flightinspection "wucher/internal/domain/flight_inspection"
helicopterdomain "wucher/internal/domain/helicopter"
helicopterfile "wucher/internal/domain/helicopter_file"
reserveac "wucher/internal/domain/reserve_ac"
takeoverdomain "wucher/internal/domain/takeover"
mysqlrepo "wucher/internal/repository/mysql"
"wucher/internal/service"
"wucher/internal/shared/pkg/userctx"
"wucher/internal/shared/pkg/uuidv7"
responsedto "wucher/internal/transport/http/dto/response"
shareddto "wucher/internal/transport/http/dto/shared"
)
type takeoverTestUserNameGetter struct {
names map[string]string
}
func (g takeoverTestUserNameGetter) GetLastName(_ context.Context, userID []byte) (string, error) {
return "", nil
}
func (g takeoverTestUserNameGetter) GetShortName(_ context.Context, userID []byte) (string, error) {
if len(userID) == 0 {
return "", nil
}
if name, ok := g.names[idString(userID)]; ok {
return name, nil
}
return "", nil
}
func TestTakeoverHandler_buildTakeoverResponseAttributes_MapsReserveAcRelations(t *testing.T) {
h := &TakeoverHandler{}
takeoverID := uuidv7.MustBytes()
baseID := uuidv7.MustBytes()
flightID := uuidv7.MustBytes()
reserveID := uuidv7.MustBytes()
inspectionID := uuidv7.MustBytes()
helicopterID := uuidv7.MustBytes()
notes := " take-over notes "
inspectionSummary := &shareddto.ReserveAcInspectionSummary{ID: idString(inspectionID), InspectionDate: "2026-04-20"}
dutyResource := responsedto.DutyRosterResource{
Attributes: responsedto.DutyRosterAttributes{
ShiftTime: responsedto.DutyRosterShiftTime{ShiftStart: "06:00", ShiftEnd: "14:00"},
DutyDate: "2026-04-20",
Detail: responsedto.DutyRosterDetailsResponse{
Pilot: []responsedto.DutyRosterPilotCrew{{ID: idString(uuidv7.MustBytes()), Role: "pilot"}},
},
},
}
takeoverRow := &takeoverdomain.TakeoverAc{ID: takeoverID, BaseID: baseID, Notes: &notes}
createdBy := uuidv7.MustBytes()
updatedBy := uuidv7.MustBytes()
takeoverRow.CreatedBy = createdBy
takeoverRow.UpdatedBy = updatedBy
takeoverRow.CreatedAt = time.Date(2026, 3, 12, 3, 4, 5, 0, time.UTC)
takeoverRow.UpdatedAt = time.Date(2026, 7, 6, 8, 12, 18, 0, time.UTC)
flightRow := &flight.Flight{ID: flightID}
reserveRow := &reserveac.ReserveAc{
ID: reserveID,
InspectionID: inspectionID,
AircraftID: helicopterID,
Aircraft: &helicopterdomain.Helicopter{ID: helicopterID, Designation: "H145", Identifier: "D-HAAA", Type: "H145"},
}
attrs := h.buildTakeoverResponseAttributes(nil, takeoverRow, flightRow, reserveRow, inspectionSummary, dutyResource)
if got, want := attrs.ID, idString(takeoverID); got != want {
t.Fatalf("unexpected takeover id: got %s want %s", got, want)
}
if got, want := attrs.FlightID, idString(flightID); got != want {
t.Fatalf("unexpected flight id: got %s want %s", got, want)
}
if got, want := attrs.ReserveAcID, idString(reserveID); got != want {
t.Fatalf("unexpected reserve_ac_id: got %s want %s", got, want)
}
if got, want := attrs.FlightInspectionID, idString(inspectionID); got != want {
t.Fatalf("unexpected flight_inspection_id: got %s want %s", got, want)
}
if got, want := attrs.HelicopterID, idString(helicopterID); got != want {
t.Fatalf("unexpected helicopter_id: got %s want %s", got, want)
}
if attrs.Notes == nil || *attrs.Notes != notes {
t.Fatalf("unexpected notes: %#v", attrs.Notes)
}
if attrs.Inspection.ID != idString(inspectionID) {
t.Fatalf("unexpected inspection summary: %#v", attrs.Inspection)
}
if attrs.Base == nil || attrs.Base.ID != idString(baseID) {
t.Fatalf("unexpected base: %#v", attrs.Base)
}
if attrs.Base.ShiftTime.ShiftStart != "06:00" || attrs.Base.ShiftTime.ShiftEnd != "14:00" {
t.Fatalf("unexpected base shift time: %#v", attrs.Base.ShiftTime)
}
if attrs.CreatedBy != uuidStringOrEmpty(createdBy) {
t.Fatalf("unexpected created_by: %q", attrs.CreatedBy)
}
if attrs.UpdatedBy != uuidStringOrEmpty(updatedBy) {
t.Fatalf("unexpected updated_by: %q", attrs.UpdatedBy)
}
if attrs.CreatedAt != takeoverRow.CreatedAt.UTC().Format(time.RFC3339) {
t.Fatalf("unexpected created_at: %q", attrs.CreatedAt)
}
if attrs.UpdatedAt != takeoverRow.UpdatedAt.UTC().Format(time.RFC3339) {
t.Fatalf("unexpected updated_at: %q", attrs.UpdatedAt)
}
}
func TestFlightTakeoverStep_UsesUpdatedByShortName(t *testing.T) {
prev := userctx.GlobalUserNameGetter
updatedBy := uuidv7.MustBytes()
createdBy := uuidv7.MustBytes()
userctx.WireUserNameGetter(takeoverTestUserNameGetter{
names: map[string]string{
idString(updatedBy): "JON",
idString(createdBy): "GAN",
},
})
t.Cleanup(func() {
userctx.WireUserNameGetter(prev)
})
pilotID := uuidv7.MustBytes()
takeoverID := uuidv7.MustBytes()
baseID := uuidv7.MustBytes()
reserveID := uuidv7.MustBytes()
inspectionID := uuidv7.MustBytes()
helicopterID := uuidv7.MustBytes()
row := &flight.Flight{
ID: uuidv7.MustBytes(),
TakeoverAcID: takeoverID,
CreatedAt: time.Date(2026, 6, 18, 10, 0, 0, 0, time.UTC),
}
takeoverRow := &takeoverdomain.TakeoverAc{
ID: takeoverID,
BaseID: baseID,
CreatedBy: createdBy,
UpdatedBy: updatedBy,
ReserveAc: &reserveac.ReserveAc{
ID: reserveID,
AircraftID: helicopterID,
InspectionID: inspectionID,
Aircraft: &helicopterdomain.Helicopter{ID: helicopterID, Designation: "H145", Identifier: "D-HAAA", Type: "H145"},
Inspection: &flightinspection.FlightInspection{ID: inspectionID},
},
RosterCrews: []takeoverdomain.TakeoverRosterCrew{
{
RoleCode: "pilot",
UserID: pilotID,
NameLabel: "Pilot Full Name",
},
},
}
step := flightTakeoverStep(context.Background(), row, &takeoverServiceMock{
getByIDFn: func(context.Context, []byte) (*takeoverdomain.TakeoverAc, error) {
return takeoverRow, nil
},
})
if step.CompletedBy != "JON" {
t.Fatalf("expected updated_by short name, got %q", step.CompletedBy)
}
}
func TestTakeoverHandler_buildTakeoverResponseAttributes_MapsFiles(t *testing.T) {
h := &TakeoverHandler{}
takeoverID := uuidv7.MustBytes()
attachmentID := uuidv7.MustBytes()
fileID := uuidv7.MustBytes()
takeoverRow := &takeoverdomain.TakeoverAc{
ID: takeoverID,
Files: []takeoverdomain.TakeoverFile{
{
ID: uuidv7.MustBytes(),
TakeoverID: takeoverID,
FileAttachmentID: attachmentID,
FileAttachment: &filemanager.Attachment{
ID: attachmentID,
FileID: fileID,
File: &filemanager.File{
ID: fileID,
Name: "takeover-report.pdf",
ObjectKey: "files/2026/06/23/takeover-report.pdf",
Bucket: "bucket",
},
},
},
},
}
attrs := h.buildTakeoverResponseAttributes(nil, takeoverRow, &flight.Flight{ID: uuidv7.MustBytes()}, nil, nil, responsedto.DutyRosterResource{Attributes: responsedto.DutyRosterAttributes{Detail: responsedto.DutyRosterDetailsResponse{}}})
if len(attrs.Files) != 1 {
t.Fatalf("expected 1 takeover file, got %d", len(attrs.Files))
}
if attrs.Files[0].Attributes.FileAttachmentID != idString(attachmentID) {
t.Fatalf("unexpected attachment id: %#v", attrs.Files[0])
}
if attrs.Files[0].Attributes.FileName != "takeover-report.pdf" {
t.Fatalf("unexpected file name: %#v", attrs.Files[0])
}
}
func TestTakeoverHandler_buildTakeoverResponseAttributes_HidesTemplateDerivedFiles(t *testing.T) {
h := &TakeoverHandler{}
takeoverID := uuidv7.MustBytes()
regularAttID := uuidv7.MustBytes()
regularFileID := uuidv7.MustBytes()
templateAttID := uuidv7.MustBytes()
templateFileID := uuidv7.MustBytes()
takeoverRow := &takeoverdomain.TakeoverAc{
ID: takeoverID,
Files: []takeoverdomain.TakeoverFile{
{ // regular attached file — must stay visible
ID: uuidv7.MustBytes(),
TakeoverID: takeoverID,
FileAttachmentID: regularAttID,
FileAttachment: &filemanager.Attachment{
ID: regularAttID,
FileID: regularFileID,
File: &filemanager.File{
ID: regularFileID,
Name: "evidence.pdf",
ObjectKey: "files/evidence.pdf",
Bucket: "bucket",
},
},
},
{ // created from a template — must be hidden (kept in DB)
ID: uuidv7.MustBytes(),
TakeoverID: takeoverID,
FileAttachmentID: templateAttID,
FileAttachment: &filemanager.Attachment{
ID: templateAttID,
FileID: templateFileID,
File: &filemanager.File{
ID: templateFileID,
Name: "from-template.pdf",
ObjectKey: "files/from-template.pdf",
Bucket: "bucket",
TemplateUUID: uuidv7.MustBytes(),
},
},
},
},
}
attrs := h.buildTakeoverResponseAttributes(nil, takeoverRow, &flight.Flight{ID: uuidv7.MustBytes()}, nil, nil, responsedto.DutyRosterResource{Attributes: responsedto.DutyRosterAttributes{Detail: responsedto.DutyRosterDetailsResponse{}}})
if len(attrs.Files) != 1 {
t.Fatalf("expected only the regular file to be visible, got %d: %#v", len(attrs.Files), attrs.Files)
}
if attrs.Files[0].Attributes.FileName != "evidence.pdf" {
t.Fatalf("expected evidence.pdf visible, got %#v", attrs.Files[0])
}
}
func TestTakeoverHandler_ChecklistBuilderHidesTemplateDerivedFiles(t *testing.T) {
h := &TakeoverHandler{}
visibleID := uuidv7.MustBytes()
derivedID := uuidv7.MustBytes()
files := []helicopterfile.HelicopterFile{
{ID: visibleID},
{
ID: derivedID,
SourceFile: &filemanager.File{
TemplateUUID: uuidv7.MustBytes(),
},
},
}
out := h.buildChecklistSectionSummary(&fiber.Ctx{}, files, map[string]bool{idString(visibleID): true, idString(derivedID): true})
if out.Total != 1 || out.Completed != 1 || len(out.FileChecklist) != 1 || len(out.DoneFiles) != 1 {
t.Fatalf("unexpected filtered checklist summary: %#v", out)
}
if out.FileChecklist[0].HelicopterFileID != idString(visibleID) {
t.Fatalf("expected only visible file in checklist, got %#v", out.FileChecklist[0])
}
}
func TestTakeoverHandler_buildTakeoverResponseAttributes_LoadsFilesFromDBFallback(t *testing.T) {
db := openTakeoverHandlerTestDB(t)
if err := db.AutoMigrate(&filemanager.Folder{}, &filemanager.File{}, &filemanager.Attachment{}, &takeoverdomain.TakeoverAc{}, &takeoverdomain.TakeoverFile{}); err != nil {
t.Fatalf("auto migrate: %v", err)
}
rootFolderID := uuidv7.MustBytes()
if err := db.Create(&filemanager.Folder{
ID: rootFolderID,
Name: "takeovers",
NameNormalized: "takeovers",
Depth: 0,
PathCache: "/takeovers",
CreatedAt: time.Now().UTC(),
UpdatedAt: time.Now().UTC(),
}).Error; err != nil {
t.Fatalf("create folder: %v", err)
}
takeoverID := uuidv7.MustBytes()
fileID := uuidv7.MustBytes()
attachmentID := uuidv7.MustBytes()
if err := db.Create(&takeoverdomain.TakeoverAc{ID: takeoverID, CreatedAt: time.Now().UTC(), UpdatedAt: time.Now().UTC()}).Error; err != nil {
t.Fatalf("create takeover: %v", err)
}
if err := db.Create(&filemanager.File{
ID: fileID,
FolderID: rootFolderID,
Name: "takeover-report.pdf",
NameNormalized: "takeover-report-pdf",
SizeBytes: 1,
MimeType: "application/pdf",
Bucket: "bucket",
ObjectKey: "files/2026/06/23/takeover-report.pdf",
ChecksumSHA256: strings.Repeat("a", 64),
Status: filemanager.FileStatusReady,
LifecycleStatus: filemanager.FileLifecycleStatusActive,
CreatedAt: time.Now().UTC(),
UpdatedAt: time.Now().UTC(),
}).Error; err != nil {
t.Fatalf("create file: %v", err)
}
if err := db.Create(&filemanager.Attachment{
ID: attachmentID,
FileID: fileID,
RefType: "takeover",
RefID: idString(takeoverID),
CreatedAt: time.Now().UTC(),
UpdatedAt: time.Now().UTC(),
}).Error; err != nil {
t.Fatalf("create attachment: %v", err)
}
if err := db.Create(&takeoverdomain.TakeoverFile{
ID: uuidv7.MustBytes(),
TakeoverID: takeoverID,
FileAttachmentID: attachmentID,
}).Error; err != nil {
t.Fatalf("create takeover file: %v", err)
}
h := &TakeoverHandler{db: db}
attrs := h.buildTakeoverResponseAttributes(nil, &takeoverdomain.TakeoverAc{ID: takeoverID}, &flight.Flight{ID: uuidv7.MustBytes()}, nil, nil, responsedto.DutyRosterResource{Attributes: responsedto.DutyRosterAttributes{Detail: responsedto.DutyRosterDetailsResponse{}}})
if len(attrs.Files) != 1 {
t.Fatalf("expected 1 takeover file from db fallback, got %d", len(attrs.Files))
}
if attrs.Files[0].Attributes.FileAttachmentID != idString(attachmentID) {
t.Fatalf("unexpected file attachment id: %#v", attrs.Files[0])
}
if attrs.Files[0].Attributes.FileName != "takeover-report.pdf" {
t.Fatalf("unexpected file name from db fallback: %#v", attrs.Files[0])
}
}
func TestTakeoverHandler_buildTakeoverResponseAttributes_LoadsFilesFromAttachmentFallback(t *testing.T) {
db := openTakeoverHandlerTestDB(t)
if err := db.AutoMigrate(&filemanager.Folder{}, &filemanager.File{}, &filemanager.Attachment{}, &takeoverdomain.TakeoverAc{}); err != nil {
t.Fatalf("auto migrate: %v", err)
}
rootFolderID := uuidv7.MustBytes()
if err := db.Create(&filemanager.Folder{
ID: rootFolderID,
Name: "takeovers",
NameNormalized: "takeovers",
Depth: 0,
PathCache: "/takeovers",
CreatedAt: time.Now().UTC(),
UpdatedAt: time.Now().UTC(),
}).Error; err != nil {
t.Fatalf("create folder: %v", err)
}
takeoverID := uuidv7.MustBytes()
fileID := uuidv7.MustBytes()
attachmentID := uuidv7.MustBytes()
if err := db.Create(&takeoverdomain.TakeoverAc{ID: takeoverID, CreatedAt: time.Now().UTC(), UpdatedAt: time.Now().UTC()}).Error; err != nil {
t.Fatalf("create takeover: %v", err)
}
if err := db.Create(&filemanager.File{
ID: fileID,
FolderID: rootFolderID,
Name: "takeover-report.pdf",
NameNormalized: "takeover-report-pdf",
SizeBytes: 1,
MimeType: "application/pdf",
Bucket: "bucket",
ObjectKey: "files/2026/06/23/takeover-report.pdf",
ChecksumSHA256: strings.Repeat("b", 64),
Status: filemanager.FileStatusReady,
LifecycleStatus: filemanager.FileLifecycleStatusActive,
CreatedAt: time.Now().UTC(),
UpdatedAt: time.Now().UTC(),
}).Error; err != nil {
t.Fatalf("create file: %v", err)
}
if err := db.Create(&filemanager.Attachment{
ID: attachmentID,
FileID: fileID,
RefType: "takeover",
RefID: idString(takeoverID),
CreatedAt: time.Now().UTC(),
UpdatedAt: time.Now().UTC(),
}).Error; err != nil {
t.Fatalf("create attachment: %v", err)
}
h := &TakeoverHandler{db: db}
attrs := h.buildTakeoverResponseAttributes(nil, &takeoverdomain.TakeoverAc{ID: takeoverID}, &flight.Flight{ID: uuidv7.MustBytes()}, nil, nil, responsedto.DutyRosterResource{Attributes: responsedto.DutyRosterAttributes{Detail: responsedto.DutyRosterDetailsResponse{}}})
if len(attrs.Files) != 1 {
t.Fatalf("expected 1 takeover file from attachment fallback, got %d", len(attrs.Files))
}
if attrs.Files[0].Attributes.FileAttachmentID != idString(attachmentID) {
t.Fatalf("unexpected file attachment id: %#v", attrs.Files[0])
}
if attrs.Files[0].Attributes.FileName != "takeover-report.pdf" {
t.Fatalf("unexpected file name from attachment fallback: %#v", attrs.Files[0])
}
}
func TestTakeoverEditedTemplateFileBelongsToTakeover(t *testing.T) {
takeoverA := uuidv7.MustBytes()
takeoverB := uuidv7.MustBytes()
tests := []struct {
name string
row *filemanager.File
want bool
}{
{
name: "allows unattached draft",
row: &filemanager.File{},
want: true,
},
{
name: "allows matching takeover draft",
row: &filemanager.File{TakeoverID: append([]byte(nil), takeoverA...)},
want: true,
},
{
name: "rejects different takeover draft",
row: &filemanager.File{TakeoverID: append([]byte(nil), takeoverB...)},
want: false,
},
{
name: "rejects invalid stored takeover id",
row: &filemanager.File{TakeoverID: []byte("bad")},
want: false,
},
{
name: "rejects nil row",
row: nil,
want: false,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
if got := takeoverEditedTemplateFileBelongsToTakeover(tc.row, takeoverA); got != tc.want {
t.Fatalf("unexpected result: got %v want %v", got, tc.want)
}
})
}
}
func TestTakeoverHandler_buildTakeoverResponseAttributes_ResolvesShiftTimeFromDB(t *testing.T) {
db := openTakeoverHandlerTestDB(t)
now := time.Now().UTC()
category := &base.BaseCategory{
ID: uuidv7.MustBytes(),
Key: base.CategoryKeyHEMS,
Name: "HEMS",
CreatedAt: now,
UpdatedAt: now,
}
if err := db.Create(category).Error; err != nil {
t.Fatalf("create category: %v", err)
}
repo := mysqlrepo.NewBaseRepository(db)
baseID := uuidv7.MustBytes()
row := &base.Base{
ID: baseID,
BaseCategoryID: category.ID,
BaseName: "Lude",
BaseAbbreviation: "LOIG",
DefaultStartTimeType: base.ShiftTimeTypeBMCT,
DefaultEndTimeType: base.ShiftTimeTypeECET,
DefaultShiftStart: "06:00:00",
DefaultShiftEnd: "21:00:00",
OperationalShiftTimes: []base.BaseOperationalShiftTime{
{
DateStart: func() *time.Time { t := time.Date(2026, 6, 10, 0, 0, 0, 0, time.UTC); return &t }(),
DateEnd: func() *time.Time { t := time.Date(2026, 6, 10, 0, 0, 0, 0, time.UTC); return &t }(),
ShiftStart: "09:00:00",
ShiftEnd: "18:00:00",
},
},
CreatedAt: now,
UpdatedAt: now,
}
if err := repo.CreateBase(context.Background(), row, base.CategoryKeyHEMS); err != nil {
t.Fatalf("create base: %v", err)
}
h := &TakeoverHandler{
db: db,
base: service.NewBaseService(repo),
}
flightRow := &flight.Flight{
ID: uuidv7.MustBytes(),
Date: time.Date(2026, 6, 12, 0, 0, 0, 0, time.UTC),
}
takeoverRow := &takeoverdomain.TakeoverAc{
ID: uuidv7.MustBytes(),
BaseID: baseID,
Base: &base.Base{
ID: baseID,
BaseName: "Lude",
BaseAbbreviation: "LOIG",
},
}
dutyResource := responsedto.DutyRosterResource{
Attributes: responsedto.DutyRosterAttributes{
DutyDate: "2026-06-12",
Detail: responsedto.DutyRosterDetailsResponse{},
},
}
attrs := h.buildTakeoverResponseAttributes(nil, takeoverRow, flightRow, nil, nil, dutyResource)
resolved := h.resolveTakeoverShiftTime(nil, baseID, flightRow.Date)
if resolved.ShiftStart == "" || resolved.ShiftEnd == "" {
t.Fatalf("expected resolved shift time from takeover handler")
}
wantStart, wantEnd, err := service.ResolveBaseShiftRangeTx(db, baseID, flightRow.Date)
if err != nil {
t.Fatalf("resolve shift window: %v", err)
}
if attrs.Base == nil {
t.Fatalf("expected base in takeover response")
}
if attrs.Base.ShiftTime.ShiftStart != wantStart || attrs.Base.ShiftTime.ShiftEnd != wantEnd {
t.Fatalf("unexpected base shift time: got=%#v want=%s %s", attrs.Base.ShiftTime, wantStart, wantEnd)
}
if attrs.ShiftStart != wantStart || attrs.ShiftEnd != wantEnd {
t.Fatalf("unexpected top-level shift time: %#v %#v", attrs.ShiftStart, attrs.ShiftEnd)
}
}
func TestBuildDutyRosterResourceFromTakeoverRows_PreservesCalendarDutyDate(t *testing.T) {
jakarta := time.FixedZone("UTC+7", 7*60*60)
flightRow := &flight.Flight{
ID: uuidv7.MustBytes(),
TakeoverAcID: uuidv7.MustBytes(),
Date: time.Date(2026, time.June, 1, 0, 0, 0, 0, jakarta),
}
resource := buildDutyRosterResourceFromTakeoverRows(
responsedto.DutyRosterResource{
Attributes: responsedto.DutyRosterAttributes{
Detail: responsedto.DutyRosterDetailsResponse{},
},
},
nil,
flightRow,
)
if got, want := resource.Attributes.DutyDate, "2026-06-01"; got != want {
t.Fatalf("unexpected duty date: got %s want %s", got, want)
}
}
func TestCalendarDateString_PreservesCalendarDate(t *testing.T) {
jakarta := time.FixedZone("UTC+7", 7*60*60)
value := time.Date(2026, time.June, 1, 0, 0, 0, 0, jakarta)
if got, want := calendarDateString(value), "2026-06-01"; got != want {
t.Fatalf("unexpected calendar date string: got %s want %s", got, want)
}
}
func TestTakeoverHandler_resolveTakeoverReserveRow_PrefersPreloadedReserveAc(t *testing.T) {
h := &TakeoverHandler{}
expected := &reserveac.ReserveAc{ID: uuidv7.MustBytes(), InspectionID: uuidv7.MustBytes()}
takeoverRow := &takeoverdomain.TakeoverAc{ReserveAc: expected}
flightRow := &flight.Flight{ReserveAcID: uuidv7.MustBytes()}
got := h.resolveTakeoverReserveRow(nil, takeoverRow, flightRow)
if got != expected {
t.Fatalf("expected preloaded reserve_ac to win, got %#v", got)
}
}
func openTakeoverHandlerTestDB(t *testing.T) *gorm.DB {
t.Helper()
dsn := fmt.Sprintf("file:takeover_handler_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{}, &base.Base{}, &base.BaseOperationalShiftTime{}, &base.BaseContactRole{}); err != nil {
t.Fatalf("auto migrate: %v", err)
}
return db
}