042a1d4773
* introduced central package registry by package init function * dynamic registration of routes, model, migrations and initializers. * setting a docker compose project name to prevent shutdown of other containers with the same (composer)name * ai documentation * app template * Create tests for ALL API entpoints in ALL packages Based on current data. Ensure that all API endpoints used in frontend are tested. These tests are crucial for the next refactoring tasks. * adopting agent instructions for a more consistent coding style * added desired module layout and debugging information * Fix All Failing tests All failing tests are fixed now that makes the refactoring more easy since all tests must pass * restored routes for maintenance * added common translations * added new tests for API Endpoint * Merge branch 'separate_business_logic' * added lern and skill improvement cost editing * Set Docker image tag when building to prevent rebuild when nothing has changed * add and remove PP for Weaponskill fixed * add and remove PP for same named skills fixed * add new task
440 lines
11 KiB
Go
440 lines
11 KiB
Go
package character
|
|
|
|
import (
|
|
"bamort/database"
|
|
"bamort/bmrt/models"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestGetCharacterClassLearningPoints(t *testing.T) {
|
|
// Setup test database
|
|
database.SetupTestDB()
|
|
t.Cleanup(database.ResetTestDB)
|
|
|
|
// Migrate the new structures
|
|
if err := models.MigrateStructure(database.DB); err != nil {
|
|
t.Fatalf("Failed to migrate structures: %v", err)
|
|
}
|
|
|
|
/*
|
|
// Populate test data (character classes and learning points)
|
|
if err := models.PopulateClassLearningPointsData(); err != nil {
|
|
t.Logf("Warning: Failed to populate learning points data: %v", err)
|
|
// Continue anyway - some tests may still work
|
|
}
|
|
*/
|
|
|
|
// Setup Gin in test mode
|
|
gin.SetMode(gin.TestMode)
|
|
router := gin.New()
|
|
router.GET("/api/characters/classes/learning-points", GetCharacterClassLearningPoints)
|
|
|
|
tests := []struct {
|
|
name string
|
|
classParam string
|
|
standParam string
|
|
expectedStatus int
|
|
expectError bool
|
|
expectedClass string
|
|
checkWeapons bool
|
|
expectedWeapons int
|
|
checkSpells bool
|
|
expectedSpells int
|
|
}{
|
|
{
|
|
name: "Valid Spitzbube class Mittelschicht stand",
|
|
classParam: "Spitzbube",
|
|
standParam: "Mittelschicht",
|
|
expectedStatus: http.StatusOK,
|
|
expectError: false,
|
|
expectedClass: "Spitzbube",
|
|
checkWeapons: true,
|
|
expectedWeapons: 20,
|
|
checkSpells: true,
|
|
expectedSpells: 0,
|
|
},
|
|
{
|
|
name: "Valid Hexer class without stand",
|
|
classParam: "Hexer",
|
|
standParam: "",
|
|
expectedStatus: http.StatusOK,
|
|
expectError: false,
|
|
expectedClass: "Hexer",
|
|
checkWeapons: true,
|
|
expectedWeapons: 2,
|
|
checkSpells: true,
|
|
expectedSpells: 6,
|
|
},
|
|
{
|
|
name: "Valid Hexer class with Volk stand",
|
|
classParam: "Hexer",
|
|
standParam: "Volk",
|
|
expectedStatus: http.StatusOK,
|
|
expectError: false,
|
|
expectedClass: "Hexer",
|
|
checkWeapons: true,
|
|
expectedWeapons: 2,
|
|
checkSpells: true,
|
|
expectedSpells: 6,
|
|
},
|
|
{
|
|
name: "Valid Krieger class with Adel stand",
|
|
classParam: "Krieger",
|
|
standParam: "Adel",
|
|
expectedStatus: http.StatusOK,
|
|
expectError: false,
|
|
expectedClass: "Krieger",
|
|
checkWeapons: true,
|
|
expectedWeapons: 36,
|
|
checkSpells: false,
|
|
},
|
|
{
|
|
name: "Valid Magier class",
|
|
classParam: "Magier",
|
|
standParam: "",
|
|
expectedStatus: http.StatusOK,
|
|
expectError: false,
|
|
expectedClass: "Magier",
|
|
checkWeapons: true,
|
|
expectedWeapons: 2,
|
|
checkSpells: true,
|
|
expectedSpells: 7,
|
|
},
|
|
{
|
|
name: "Valid Spitzbube class",
|
|
classParam: "Spitzbube",
|
|
standParam: "",
|
|
expectedStatus: http.StatusOK,
|
|
expectError: false,
|
|
expectedClass: "Spitzbube",
|
|
checkWeapons: true,
|
|
expectedWeapons: 20,
|
|
checkSpells: false,
|
|
},
|
|
{
|
|
name: "Valid Waldläufer class",
|
|
classParam: "Waldläufer",
|
|
standParam: "",
|
|
expectedStatus: http.StatusOK,
|
|
expectError: false,
|
|
expectedClass: "Waldläufer",
|
|
checkWeapons: true,
|
|
expectedWeapons: 20,
|
|
checkSpells: false,
|
|
},
|
|
{
|
|
name: "Invalid class should return error",
|
|
classParam: "InvalidClass",
|
|
standParam: "",
|
|
expectedStatus: http.StatusNotFound,
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "Missing class parameter should return error",
|
|
classParam: "",
|
|
standParam: "",
|
|
expectedStatus: http.StatusBadRequest,
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "Valid class with invalid stand should still work",
|
|
classParam: "Hexer",
|
|
standParam: "InvalidStand",
|
|
expectedStatus: http.StatusOK,
|
|
expectError: false,
|
|
expectedClass: "Hexer",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Build request URL
|
|
url := "/api/characters/classes/learning-points"
|
|
if tt.classParam != "" || tt.standParam != "" {
|
|
url += "?"
|
|
if tt.classParam != "" {
|
|
url += "class=" + tt.classParam
|
|
}
|
|
if tt.standParam != "" {
|
|
if tt.classParam != "" {
|
|
url += "&"
|
|
}
|
|
url += "stand=" + tt.standParam
|
|
}
|
|
}
|
|
|
|
// Create request
|
|
req, err := http.NewRequest("GET", url, nil)
|
|
assert.NoError(t, err)
|
|
|
|
// Create response recorder
|
|
w := httptest.NewRecorder()
|
|
|
|
// Perform request
|
|
router.ServeHTTP(w, req)
|
|
|
|
// Check status code
|
|
assert.Equal(t, tt.expectedStatus, w.Code)
|
|
|
|
if tt.expectError {
|
|
// For error cases, check that we have an error message
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.Contains(t, response, "error")
|
|
} else {
|
|
// For success cases, check the response structure
|
|
var response LearningPointsData
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
|
|
// Check basic fields
|
|
assert.Equal(t, tt.expectedClass, response.ClassName)
|
|
assert.NotEmpty(t, response.ClassCode)
|
|
assert.NotNil(t, response.LearningPoints)
|
|
assert.NotNil(t, response.TypicalSkills)
|
|
|
|
// Check weapon points if specified (now in LearningPoints["Waffen"])
|
|
if tt.checkWeapons {
|
|
assert.Equal(t, tt.expectedWeapons, response.LearningPoints["Waffen"], "Weapon learning points should match")
|
|
}
|
|
|
|
// Check spell points if specified
|
|
if tt.checkSpells {
|
|
assert.Equal(t, tt.expectedSpells, response.SpellPoints)
|
|
}
|
|
|
|
// Check that learning points are not empty
|
|
assert.NotEmpty(t, response.LearningPoints)
|
|
|
|
// Check that typical skills are not empty
|
|
assert.NotEmpty(t, response.TypicalSkills)
|
|
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetLearningPointsForClass(t *testing.T) {
|
|
// Setup test database
|
|
database.SetupTestDB()
|
|
t.Cleanup(database.ResetTestDB)
|
|
|
|
// Migrate the new structures
|
|
if err := models.MigrateStructure(database.DB); err != nil {
|
|
t.Fatalf("Failed to migrate structures: %v", err)
|
|
}
|
|
|
|
/*
|
|
// Populate test data
|
|
if err := models.PopulateClassLearningPointsData(); err != nil {
|
|
t.Logf("Warning: Failed to populate learning points data: %v", err)
|
|
}
|
|
*/
|
|
|
|
tests := []struct {
|
|
name string
|
|
className string
|
|
stand string
|
|
expectError bool
|
|
expectedClass string
|
|
expectedCode string
|
|
checkPoints map[string]int
|
|
checkStand map[string]int
|
|
}{
|
|
{
|
|
name: "Hexer class data",
|
|
className: "Hexer",
|
|
stand: "",
|
|
expectError: false,
|
|
expectedClass: "Hexer",
|
|
expectedCode: "Hx",
|
|
checkPoints: map[string]int{
|
|
"Alltag": 3,
|
|
"Sozial": 2,
|
|
"Wissen": 2,
|
|
},
|
|
},
|
|
{
|
|
name: "Hexer with Volk stand",
|
|
className: "Hexer",
|
|
stand: "Volk",
|
|
expectError: false,
|
|
expectedClass: "Hexer",
|
|
expectedCode: "Hx",
|
|
checkPoints: map[string]int{
|
|
"Alltag": 5, // Base 3 + Volk bonus 2 = 5
|
|
"Sozial": 2,
|
|
"Wissen": 2,
|
|
},
|
|
checkStand: map[string]int{
|
|
"Alltag": 2,
|
|
},
|
|
},
|
|
{
|
|
name: "Krieger class data",
|
|
className: "Krieger",
|
|
stand: "",
|
|
expectError: false,
|
|
expectedClass: "Krieger",
|
|
expectedCode: "Kr",
|
|
checkPoints: map[string]int{
|
|
"Alltag": 2,
|
|
"Kampf": 3,
|
|
"Körper": 1,
|
|
},
|
|
},
|
|
{
|
|
name: "Krieger with Adel stand",
|
|
className: "Krieger",
|
|
stand: "Adel",
|
|
expectError: false,
|
|
expectedClass: "Krieger",
|
|
expectedCode: "Kr",
|
|
checkPoints: map[string]int{
|
|
"Alltag": 2,
|
|
"Kampf": 3,
|
|
"Körper": 1,
|
|
"Sozial": 2, // Stand bonus adds this new category
|
|
},
|
|
checkStand: map[string]int{
|
|
"Sozial": 2,
|
|
},
|
|
},
|
|
{
|
|
name: "Invalid class should return error",
|
|
className: "InvalidClass",
|
|
stand: "",
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
data, err := getLearningPointsForClass(tt.className, tt.stand)
|
|
|
|
if tt.expectError {
|
|
assert.Error(t, err)
|
|
assert.Nil(t, data)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, data)
|
|
|
|
// Check basic properties
|
|
assert.Equal(t, tt.expectedClass, data.ClassName)
|
|
assert.Equal(t, tt.expectedCode, data.ClassCode)
|
|
|
|
// Check learning points
|
|
if tt.checkPoints != nil {
|
|
for category, expectedPoints := range tt.checkPoints {
|
|
actualPoints, exists := data.LearningPoints[category]
|
|
assert.True(t, exists, "Category %s should exist", category)
|
|
assert.Equal(t, expectedPoints, actualPoints, "Points for category %s", category)
|
|
}
|
|
}
|
|
|
|
// Check that we have some typical skills
|
|
assert.NotEmpty(t, data.TypicalSkills)
|
|
|
|
// Validate typical skills structure
|
|
for _, skill := range data.TypicalSkills {
|
|
assert.NotEmpty(t, skill.Name)
|
|
assert.NotEmpty(t, skill.Attribute)
|
|
assert.GreaterOrEqual(t, skill.Bonus, 0)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetStandBonusPoints(t *testing.T) {
|
|
database.SetupTestDB()
|
|
defer database.ResetTestDB()
|
|
|
|
tests := []struct {
|
|
name string
|
|
stand string
|
|
expected map[string]int
|
|
}{
|
|
{
|
|
name: "Unfreie stand",
|
|
stand: "Unfreie",
|
|
expected: map[string]int{"Halbwelt": 2},
|
|
},
|
|
{
|
|
name: "Volk stand",
|
|
stand: "Volk",
|
|
expected: map[string]int{"Alltag": 2},
|
|
},
|
|
{
|
|
name: "Mittelschicht stand",
|
|
stand: "Mittelschicht",
|
|
expected: map[string]int{"Wissen": 2},
|
|
},
|
|
{
|
|
name: "Adel stand",
|
|
stand: "Adel",
|
|
expected: map[string]int{"Sozial": 2},
|
|
},
|
|
{
|
|
name: "Invalid stand",
|
|
stand: "Invalid",
|
|
expected: map[string]int{},
|
|
},
|
|
{
|
|
name: "Empty stand",
|
|
stand: "",
|
|
expected: map[string]int{},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := getStandBonusPoints(tt.stand)
|
|
assert.Equal(t, tt.expected, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
// Test all character classes to ensure they're properly defined
|
|
func TestAllCharacterClassesAreDefined(t *testing.T) {
|
|
// Setup test database
|
|
database.SetupTestDB()
|
|
t.Cleanup(database.ResetTestDB)
|
|
|
|
// Migrate the new structures
|
|
if err := models.MigrateStructure(database.DB); err != nil {
|
|
t.Fatalf("Failed to migrate structures: %v", err)
|
|
}
|
|
|
|
/*
|
|
// Populate test data
|
|
if err := models.PopulateClassLearningPointsData(); err != nil {
|
|
t.Logf("Warning: Failed to populate learning points data: %v", err)
|
|
}
|
|
*/
|
|
|
|
expectedClasses := []string{
|
|
"Assassine", "Barbar", "Glücksritter", "Händler", "Krieger", "Spitzbube", "Waldläufer",
|
|
"Barde", "Ordenskrieger", "Druide", "Hexer", "Magier", "Priester Beschützer", "Priester Streiter", "Schamane",
|
|
}
|
|
|
|
for _, className := range expectedClasses {
|
|
t.Run("Class_"+className, func(t *testing.T) {
|
|
data, err := getLearningPointsForClass(className, "")
|
|
assert.NoError(t, err, "Class %s should be defined", className)
|
|
assert.NotNil(t, data, "Class %s should return data", className)
|
|
assert.Equal(t, className, data.ClassName)
|
|
assert.NotEmpty(t, data.ClassCode)
|
|
assert.NotEmpty(t, data.LearningPoints)
|
|
assert.GreaterOrEqual(t, data.WeaponPoints, 0)
|
|
})
|
|
}
|
|
}
|