From 05867a93aad2fcb2cadb6910cf15443be9e6fcf0 Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 27 Jan 2026 08:19:18 +0100 Subject: [PATCH] added Tests for changing gsmaster.Believe --- backend/character/handlers.go | 2 +- backend/character/handlers_test.go | 82 ++++++++++++++++++++++++ backend/models/database.go | 22 ++++++- backend/models/model_game_system.go | 14 ++-- backend/models/model_game_system_test.go | 68 ++++++++++++++++++++ backend/models/model_gsmaster.go | 51 ++++++++++++--- 6 files changed, 223 insertions(+), 16 deletions(-) create mode 100644 backend/models/model_game_system_test.go diff --git a/backend/character/handlers.go b/backend/character/handlers.go index 0a38fc6..de599a2 100644 --- a/backend/character/handlers.go +++ b/backend/character/handlers.go @@ -3130,7 +3130,7 @@ func SearchBeliefs(c *gin.Context) { } // Get game system from query parameter, default to "midgard" - gameSystem := c.DefaultQuery("game_system", "midgard") + gameSystem := c.DefaultQuery("game_system", "") // Load beliefs from database believes, err := models.GetBelievesByActiveSources(gameSystem) diff --git a/backend/character/handlers_test.go b/backend/character/handlers_test.go index 4e3460b..7109ff4 100644 --- a/backend/character/handlers_test.go +++ b/backend/character/handlers_test.go @@ -1115,3 +1115,85 @@ func TestGetDatasheetOptions_CharacterNotFound(t *testing.T) { // Assert error response assert.Equal(t, http.StatusNotFound, w.Code) } + +func TestSearchBeliefs(t *testing.T) { + // Setup test environment + original := os.Getenv("ENVIRONMENT") + os.Setenv("ENVIRONMENT", "test") + t.Cleanup(func() { + if original != "" { + os.Setenv("ENVIRONMENT", original) + } else { + os.Unsetenv("ENVIRONMENT") + } + }) + + // Setup test database + database.SetupTestDB(true) + defer database.ResetTestDB() + + err := models.MigrateStructure() + assert.NoError(t, err) + + // Ensure game systems exist in the test DB + // Ensure game system rows exist (use INSERT OR IGNORE to avoid unique constraint errors) + database.DB.Exec("INSERT OR IGNORE INTO game_systems(code,name,description,is_active,created_at,modified_at) VALUES (?,?,?,?,strftime('%s','now'),strftime('%s','now'))", "M5", "M5", "", true) + //database.DB.Exec("INSERT OR IGNORE INTO game_systems(code,name,description,is_active,created_at,modified_at) VALUES (?,?,?,?,strftime('%s','now'),strftime('%s','now'))", "midgard", "midgard", "", true) + + // Create some test believes for midgard + b1 := &models.Believe{GameSystem: "midgard", Name: "TestFaithOne", SourceID: 1} + b2 := &models.Believe{GameSystem: "midgard", Name: "OtherFaith", SourceID: 1} + err = b1.Create() + assert.NoError(t, err) + err = b2.Create() + assert.NoError(t, err) + + gin.SetMode(gin.TestMode) + + tests := []struct { + name string + q string + gameSystem string + expectHits int + expectError bool + }{ + //{name: "GameSystem midgard", q: "Test", gameSystem: "midgard", expectHits: 1, expectError: false}, + //{name: "GameSystem M5", q: "Test", gameSystem: "M5", expectHits: 2, expectError: false}, + {name: "GameSystem XYZ", q: "Test", gameSystem: "XYZ", expectHits: 0, expectError: true}, + //{name: "GameSystem not set (default)", q: "Test", gameSystem: "", expectHits: 0, expectError: false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Build request URL with query params + url := "/api/characters/beliefs?q=" + tt.q + if tt.gameSystem != "" { + url = url + "&game_system=" + tt.gameSystem + } + + req, _ := http.NewRequest("GET", url, nil) + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Request = req + + SearchBeliefs(c) + + if tt.expectError { + assert.NotEqual(t, http.StatusOK, w.Code) + return + } else { + assert.Equal(t, http.StatusOK, w.Code) + } + + var resp map[string][]string + err := json.Unmarshal(w.Body.Bytes(), &resp) + assert.NoError(t, err) + beliefs, ok := resp["beliefs"] + assert.True(t, ok, "response should contain beliefs") + assert.IsType(t, []string{}, beliefs) + if tt.expectHits > 0 { + assert.Greater(t, len(beliefs), 0) + } + }) + } +} diff --git a/backend/models/database.go b/backend/models/database.go index caabb2b..d11a048 100644 --- a/backend/models/database.go +++ b/backend/models/database.go @@ -15,7 +15,11 @@ func MigrateStructure(db ...*gorm.DB) error { targetDB = database.DB } - err := gsMasterMigrateStructure(targetDB) + err := gameSystemMigrateStructure(targetDB) + if err != nil { + return err + } + err = gsMasterMigrateStructure(targetDB) if err != nil { return err } @@ -42,7 +46,23 @@ func MigrateStructure(db ...*gorm.DB) error { return nil } +func gameSystemMigrateStructure(db ...*gorm.DB) error { + // Use provided DB or default to database.DB + var targetDB *gorm.DB + if len(db) > 0 && db[0] != nil { + targetDB = db[0] + } else { + targetDB = database.DB + } + err := targetDB.AutoMigrate( + &GameSystem{}, + ) + if err != nil { + return err + } + return nil +} func gsMasterMigrateStructure(db ...*gorm.DB) error { // Use provided DB or default to database.DB var targetDB *gorm.DB diff --git a/backend/models/model_game_system.go b/backend/models/model_game_system.go index 1446d05..235b9c6 100644 --- a/backend/models/model_game_system.go +++ b/backend/models/model_game_system.go @@ -17,14 +17,20 @@ func (GameSystem) TableName() string { return "game_systems" } +func (gs *GameSystem) GetDefault() error { + return database.DB.First(gs, "code = ?", "M5").Error +} + func (gs *GameSystem) FirstByCode(code string) error { + if code == "" { + return gs.GetDefault() + } return database.DB.First(gs, "code = ?", code).Error } -func (gs *GameSystem) GetDefault() error { - return database.DB.First(gs, "is_active = ?", true).Error -} - func (gs *GameSystem) FirstByName(name string) error { + if name == "" { + return gs.GetDefault() + } return database.DB.First(gs, "name = ?", name).Error } diff --git a/backend/models/model_game_system_test.go b/backend/models/model_game_system_test.go new file mode 100644 index 0000000..2fb9f44 --- /dev/null +++ b/backend/models/model_game_system_test.go @@ -0,0 +1,68 @@ +package models + +import ( + "testing" + + "bamort/database" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGameSystem_Methods(t *testing.T) { + // Initialize test DB and migrations + database.SetupTestDB(true) + defer database.ResetTestDB() + + // Ensure default exists (use FirstOrCreate to avoid unique constraint) + defaultGS := GameSystem{} + database.DB.Where(GameSystem{Code: "M5"}).FirstOrCreate(&defaultGS, GameSystem{Code: "M5", Name: "M5", IsActive: true}) + + t.Run("FirstByCode returns matching record", func(t *testing.T) { + gs := GameSystem{Code: "TESTCODE", Name: "Test System", IsActive: true} + err := database.DB.Create(&gs).Error + require.NoError(t, err) + + var found GameSystem + err = found.FirstByCode("TESTCODE") + assert.NoError(t, err) + assert.Equal(t, "TESTCODE", found.Code) + assert.Equal(t, "Test System", found.Name) + }) + + t.Run("GetDefault returns M5", func(t *testing.T) { + var found GameSystem + err := found.GetDefault() + assert.NoError(t, err) + assert.Equal(t, "M5", found.Code) + }) + + t.Run("FirstByName behaviour", func(t *testing.T) { + // existing name + gs := GameSystem{Code: "CUSTOM", Name: "CustomSys", IsActive: true} + err := database.DB.Create(&gs).Error + require.NoError(t, err) + + var byName GameSystem + err = byName.FirstByName("CustomSys") + assert.NoError(t, err) + assert.Equal(t, "CustomSys", byName.Name) + + // empty name should fallback to default + byName = GameSystem{} + err = byName.FirstByName("") + assert.NoError(t, err) + assert.Equal(t, "M5", byName.Code) + + // empty name should fallback to default + byName = GameSystem{} + err = byName.FirstByName("midgard") + assert.NoError(t, err) + assert.Equal(t, "M5", byName.Code) + + // non-existent name should return an error + var notFound GameSystem + err = notFound.FirstByName("NoSuchSystem") + assert.Error(t, err) + }) +} diff --git a/backend/models/model_gsmaster.go b/backend/models/model_gsmaster.go index 09605f5..486e874 100644 --- a/backend/models/model_gsmaster.go +++ b/backend/models/model_gsmaster.go @@ -126,6 +126,7 @@ type Believe struct { Quelle string `json:"quelle"` // Deprecated: Für Rückwärtskompatibilität SourceID uint `gorm:"index" json:"source_id,omitempty"` // Verweis auf strukturierte Quelle PageNumber int `json:"page_number,omitempty"` // Seitenzahl im Quellenbuch + GameSystemId uint `json:"game_system_id,omitempty"` } // MiscLookup represents miscellaneous lookup values like gender, race, origin, etc. @@ -622,12 +623,16 @@ func (object *Believe) TableName() string { return dbPrefix + "_" + "believes" } -func (stamm *Believe) Create() error { - gameSystem := "midgard" - stamm.GameSystem = gameSystem +func (object *Believe) Create() error { + if object.GameSystemId == 0 { + gs := GameSystem{} + gs.FirstByName(object.GameSystem) + object.GameSystemId = gs.ID + object.GameSystem = gs.Name + } err := database.DB.Transaction(func(tx *gorm.DB) error { // Save the main character record - if err := tx.Create(&stamm).Error; err != nil { + if err := tx.Create(&object).Error; err != nil { return fmt.Errorf("failed to save Lookup: %w", err) } return nil @@ -637,8 +642,13 @@ func (stamm *Believe) Create() error { } func (object *Believe) FirstId(value uint) error { - gameSystem := "midgard" - err := database.DB.First(&object, "game_system=? AND id = ?", gameSystem, value).Error + if object.GameSystemId == 0 { + gs := GameSystem{} + gs.FirstByName(object.GameSystem) + object.GameSystemId = gs.ID + object.GameSystem = gs.Name + } + err := database.DB.First(&object, "(game_system=? OR game_system_id=?) AND id = ?", object.GameSystem, object.GameSystemId, value).Error if err != nil { // zauber found return err @@ -646,12 +656,17 @@ func (object *Believe) FirstId(value uint) error { return nil } -func (stamm *Believe) First(name string) error { +func (object *Believe) First(name string) error { if name == "" { return fmt.Errorf("name cannot be empty") } - gameSystem := "midgard" - err := database.DB.First(&stamm, "game_system=? AND name = ?", gameSystem, name).Error + if object.GameSystemId == 0 { + gs := GameSystem{} + gs.FirstByName(object.GameSystem) + object.GameSystemId = gs.ID + object.GameSystem = gs.Name + } + err := database.DB.First(&object, "(game_system=? OR game_system_id=?) AND name = ?", object.GameSystem, object.GameSystemId, name).Error if err != nil { // zauber found return err @@ -660,6 +675,12 @@ func (stamm *Believe) First(name string) error { } func (object *Believe) Save() error { + if object.GameSystemId == 0 { + gs := GameSystem{} + gs.FirstByName(object.GameSystem) + object.GameSystemId = gs.ID + object.GameSystem = gs.Name + } err := database.DB.Save(&object).Error if err != nil { // zauber found @@ -671,9 +692,19 @@ func (object *Believe) Save() error { // GetBelievesByActiveSources gibt Glaubensrichtungen nach aktiven Quellen zurück func GetBelievesByActiveSources(gameSystem string) ([]Believe, error) { var believes []Believe + gs := GameSystem{} + //if gameSystem="midgard" it should return recordset id 1 + gs.FirstByName(gameSystem) + if gs.ID == 0 { + gs.FirstByCode(gameSystem) + if gs.ID == 0 { + // return empty slice if no valid game system found + return believes, fmt.Errorf("No GameSystem ID or Name found for %s", gameSystem) + } + } err := database.DB. Joins("LEFT JOIN gsm_lit_sources ON gsm_believes.source_id = gsm_lit_sources.id"). - Where("gsm_believes.game_system = ? AND (gsm_lit_sources.is_active = ? OR gsm_believes.source_id IS NULL)", gameSystem, true). + Where("(gsm_believes.game_system = ? or gsm_believes.game_system_id=?) AND (gsm_lit_sources.is_active = ? OR gsm_believes.source_id IS NULL)", gs.Name, gs.ID, true). Order("gsm_believes.name ASC"). Find(&believes).Error return believes, err