package data import ( "context" "testing" "time" "kra/internal/biz" "kra/internal/conf" ) func newMenuTestData(t *testing.T) *Data { t.Helper() db, err := openWithDriver("sqlite", "file:"+t.Name()+"?mode=memory&cache=shared") if err != nil { t.Fatal(err) } if err = db.AutoMigrate(&menuPO{}, &menuParameterPO{}, &menuButtonPO{}, &authorityButtonPO{}, &authorityPO{}, &authorityMenuPO{}); err != nil { t.Fatal(err) } data := &Data{ gormDB: newReloadableDB(db, nil), runtime: conf.NewRuntime(&conf.Data{Database: &conf.Data_Database{Driver: "sqlite"}}, &conf.AdminBackend{}), } t.Cleanup(func() { data.gormDB.close() }) return data } func TestMenuCreatePreservesSubmittedTimestamps(t *testing.T) { data := newMenuTestData(t) ctx := context.Background() createdAt := time.Date(2025, time.January, 2, 3, 4, 5, 0, time.UTC) updatedAt := time.Date(2025, time.February, 3, 4, 5, 6, 0, time.UTC) menu := &biz.Menu{CreatedAt: createdAt, UpdatedAt: updatedAt, Name: "created", Path: "created", Component: "view/created.vue", Title: "Created"} if err := (&menuRepo{data: data}).Create(ctx, menu); err != nil { t.Fatal(err) } var stored menuPO if err := data.gormDB.WithContext(ctx).First(&stored, menu.ID).Error; err != nil { t.Fatal(err) } if !stored.CreatedAt.Equal(createdAt) || !stored.UpdatedAt.Equal(updatedAt) { t.Fatalf("stored timestamps = (%v, %v), want (%v, %v)", stored.CreatedAt, stored.UpdatedAt, createdAt, updatedAt) } } func TestSetAuthorityMenusRejectsMissingAuthority(t *testing.T) { data := newMenuTestData(t) err := (&menuRepo{data: data}).SetAuthorityMenus(context.Background(), 999, nil) if err == nil { t.Fatal("SetAuthorityMenus missing authority succeeded") } } func TestSetMenuRolesPreservesDuplicateAuthorityIDs(t *testing.T) { data := newMenuTestData(t) ctx := context.Background() if err := (&menuRepo{data: data}).SetMenuRoles(ctx, 10, []uint{888, 888}); err != nil { t.Fatal(err) } var count int64 if err := data.gormDB.WithContext(ctx).Model(&authorityMenuPO{}). Where("sys_base_menu_id = ? AND sys_authority_authority_id = ?", 10, 888). Count(&count).Error; err != nil { t.Fatal(err) } if count != 2 { t.Fatalf("authority-menu rows = %d, want duplicate role IDs preserved", count) } } func TestMenuUpdateRebuildsRelationsWithoutChangingRequestedIdentity(t *testing.T) { data := newMenuTestData(t) ctx := context.Background() createdAt := time.Date(2025, time.March, 4, 5, 6, 7, 0, time.UTC) updatedAt := time.Date(2025, time.April, 5, 6, 7, 8, 0, time.UTC) if err := data.gormDB.WithContext(ctx).Create(&menuPO{ID: 10, Name: "before", Path: "before", Component: "view/before.vue", Title: "Before"}).Error; err != nil { t.Fatal(err) } if err := data.gormDB.WithContext(ctx).Create(&menuParameterPO{ID: 21, CreatedAt: createdAt, UpdatedAt: updatedAt, MenuID: 10, Type: "query", Key: "old", Value: "old"}).Error; err != nil { t.Fatal(err) } if err := data.gormDB.WithContext(ctx).Create(&menuButtonPO{ID: 31, CreatedAt: createdAt, UpdatedAt: updatedAt, MenuID: 10, Name: "edit", Description: "old"}).Error; err != nil { t.Fatal(err) } if err := data.gormDB.WithContext(ctx).Create(&authorityButtonPO{AuthorityID: 888, MenuID: 10, ButtonID: 31}).Error; err != nil { t.Fatal(err) } repo := &menuRepo{data: data} err := repo.Update(ctx, &biz.Menu{ ID: 10, Name: "after", Path: "after", Component: "view/after.vue", Title: "After", Parameters: []*biz.MenuParameter{{ ID: 21, CreatedAt: createdAt, UpdatedAt: updatedAt, Type: "params", Key: "new", Value: "value", }}, Buttons: []*biz.MenuButton{{ ID: 31, CreatedAt: createdAt, UpdatedAt: updatedAt, Name: "edit", Description: "new", }}, }) if err != nil { t.Fatal(err) } var parameter menuParameterPO if err = data.gormDB.WithContext(ctx).First(¶meter, 21).Error; err != nil { t.Fatal(err) } if parameter.MenuID != 10 || parameter.Type != "params" || parameter.Key != "new" || parameter.Value != "value" { t.Fatalf("parameter = %+v", parameter) } if !parameter.CreatedAt.Equal(createdAt) || !parameter.UpdatedAt.Equal(updatedAt) { t.Fatalf("parameter timestamps = (%v, %v), want (%v, %v)", parameter.CreatedAt, parameter.UpdatedAt, createdAt, updatedAt) } var button menuButtonPO if err = data.gormDB.WithContext(ctx).First(&button, 31).Error; err != nil { t.Fatal(err) } if button.MenuID != 10 || button.Name != "edit" || button.Description != "new" { t.Fatalf("button = %+v", button) } if !button.CreatedAt.Equal(createdAt) || !button.UpdatedAt.Equal(updatedAt) { t.Fatalf("button timestamps = (%v, %v), want (%v, %v)", button.CreatedAt, button.UpdatedAt, createdAt, updatedAt) } var links int64 if err = data.gormDB.WithContext(ctx).Model(&authorityButtonPO{}). Where("authority_id = ? AND sys_menu_id = ? AND sys_base_menu_btn_id = ?", 888, 10, 31). Count(&links).Error; err != nil { t.Fatal(err) } if links != 1 { t.Fatalf("authority-button links = %d, want 1", links) } }