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
321 lines
9.1 KiB
Go
321 lines
9.1 KiB
Go
package character
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"bamort/database"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
// TestGetAllSkillsWithLearningCosts tests the helper function directly
|
|
func TestGetAllSkillsWithLearningCosts(t *testing.T) {
|
|
// Setup test database
|
|
database.SetupTestDB(true, true)
|
|
defer database.ResetTestDB()
|
|
|
|
tests := []struct {
|
|
name string
|
|
characterClass string
|
|
expectError bool
|
|
expectSkills bool
|
|
}{
|
|
{
|
|
name: "ValidCharacterClassAs",
|
|
characterClass: "As",
|
|
expectError: false,
|
|
expectSkills: true,
|
|
},
|
|
{
|
|
name: "ValidCharacterClassMagier",
|
|
characterClass: "Magier",
|
|
expectError: false,
|
|
expectSkills: true,
|
|
},
|
|
{
|
|
name: "EmptyCharacterClass",
|
|
characterClass: "",
|
|
expectError: false,
|
|
expectSkills: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
skillsByCategory, err := GetAllSkillsWithLearningCosts(tt.characterClass)
|
|
|
|
if tt.expectError {
|
|
assert.Error(t, err)
|
|
return
|
|
}
|
|
|
|
assert.NoError(t, err, "GetAllSkillsWithLearningCosts should not return error")
|
|
|
|
if tt.expectSkills {
|
|
assert.Greater(t, len(skillsByCategory), 0, "Should return at least some skill categories")
|
|
|
|
// Verify structure of returned data
|
|
for categoryName, skills := range skillsByCategory {
|
|
assert.NotEmpty(t, categoryName, "Category name should not be empty")
|
|
|
|
skillsArray := skills
|
|
assert.Greater(t, len(skillsArray), 0, "Category should have at least one skill")
|
|
|
|
// Check first skill structure
|
|
if len(skillsArray) > 0 {
|
|
skill := skillsArray[0]
|
|
assert.Contains(t, skill, "name", "Skill should have name")
|
|
assert.Contains(t, skill, "learnCost", "Skill should have learnCost")
|
|
|
|
name, nameOk := skill["name"].(string)
|
|
assert.True(t, nameOk, "Skill name should be string")
|
|
assert.NotEmpty(t, name, "Skill name should not be empty")
|
|
|
|
learnCost, costOk := skill["learnCost"].(int)
|
|
assert.True(t, costOk, "Learn cost should be int")
|
|
assert.Greater(t, learnCost, 0, "Learn cost should be positive")
|
|
assert.LessOrEqual(t, learnCost, 500, "Learn cost should be reasonable")
|
|
}
|
|
}
|
|
|
|
t.Logf("Character class %s: Found %d skill categories", tt.characterClass, len(skillsByCategory))
|
|
|
|
// Log some sample skills for verification
|
|
count := 0
|
|
for categoryName, skills := range skillsByCategory {
|
|
skillsArray := skills
|
|
for _, skill := range skillsArray {
|
|
skillMap := skill
|
|
t.Logf(" %s -> %s: %v LP", categoryName, skillMap["name"], skillMap["learnCost"])
|
|
count++
|
|
if count >= 5 { // Only log first 5 skills
|
|
break
|
|
}
|
|
}
|
|
if count >= 5 {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestGetAvailableSkillsForCreationHandler tests the HTTP handler directly
|
|
func TestHandlerGetAvailableSkillsForCreation(t *testing.T) {
|
|
// Setup test database
|
|
database.SetupTestDB(true, true)
|
|
defer database.ResetTestDB()
|
|
|
|
tests := []struct {
|
|
name string
|
|
requestBody interface{}
|
|
expectStatus int
|
|
expectError bool
|
|
validateFunc func(t *testing.T, response map[string]interface{})
|
|
}{
|
|
{
|
|
name: "ValidMagierRequest",
|
|
requestBody: gin.H{
|
|
"characterClass": "Magier",
|
|
},
|
|
expectStatus: http.StatusOK,
|
|
expectError: false,
|
|
validateFunc: func(t *testing.T, response map[string]interface{}) {
|
|
assert.Contains(t, response, "skills_by_category")
|
|
skillsByCategory := response["skills_by_category"].(map[string]interface{})
|
|
assert.Greater(t, len(skillsByCategory), 0, "Should have skill categories")
|
|
},
|
|
},
|
|
{
|
|
name: "ValidAsRequest",
|
|
requestBody: gin.H{
|
|
"characterClass": "As",
|
|
},
|
|
expectStatus: http.StatusOK,
|
|
expectError: false,
|
|
validateFunc: func(t *testing.T, response map[string]interface{}) {
|
|
assert.Contains(t, response, "skills_by_category")
|
|
skillsByCategory := response["skills_by_category"].(map[string]interface{})
|
|
assert.Greater(t, len(skillsByCategory), 0, "Should have skill categories")
|
|
|
|
// Verify that skills have reasonable learning costs
|
|
hasReasonableCosts := false
|
|
for _, skills := range skillsByCategory {
|
|
if skillsList, ok := skills.([]interface{}); ok {
|
|
for _, skill := range skillsList {
|
|
if skillMap, ok := skill.(map[string]interface{}); ok {
|
|
if learnCost, exists := skillMap["leCost"]; exists {
|
|
if cost, ok := learnCost.(float64); ok {
|
|
assert.Greater(t, cost, 0.0, "Learn cost should be positive")
|
|
assert.LessOrEqual(t, cost, 500.0, "Learn cost should be reasonable")
|
|
hasReasonableCosts = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
assert.True(t, hasReasonableCosts, "Should find skills with reasonable costs")
|
|
},
|
|
},
|
|
{
|
|
name: "EmptyCharacterClass",
|
|
requestBody: gin.H{
|
|
"characterClass": "",
|
|
},
|
|
expectStatus: http.StatusBadRequest,
|
|
expectError: true,
|
|
validateFunc: func(t *testing.T, response map[string]interface{}) {
|
|
assert.Contains(t, response, "error")
|
|
},
|
|
},
|
|
{
|
|
name: "MissingCharacterClass",
|
|
requestBody: gin.H{
|
|
"someOtherField": "value",
|
|
},
|
|
expectStatus: http.StatusBadRequest,
|
|
expectError: true,
|
|
validateFunc: func(t *testing.T, response map[string]interface{}) {
|
|
assert.Contains(t, response, "error")
|
|
},
|
|
},
|
|
{
|
|
name: "InvalidJSON",
|
|
requestBody: "invalid json string",
|
|
expectStatus: http.StatusBadRequest,
|
|
expectError: true,
|
|
validateFunc: func(t *testing.T, response map[string]interface{}) {
|
|
assert.Contains(t, response, "error")
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Prepare request body
|
|
var requestBody []byte
|
|
var err error
|
|
|
|
if str, ok := tt.requestBody.(string); ok {
|
|
requestBody = []byte(str)
|
|
} else {
|
|
requestBody, err = json.Marshal(tt.requestBody)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
// Create HTTP request
|
|
req, err := http.NewRequest("POST", "/api/characters/available-skills-creation", bytes.NewBuffer(requestBody))
|
|
assert.NoError(t, err)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
// Create response recorder
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
c.Request = req
|
|
|
|
// Call the handler
|
|
GetAvailableSkillsForCreation(c)
|
|
|
|
// Verify response status
|
|
assert.Equal(t, tt.expectStatus, w.Code, "HTTP status should match expected")
|
|
|
|
// Parse response body
|
|
var response map[string]interface{}
|
|
err = json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err, "Response should be valid JSON")
|
|
|
|
// Run custom validation if provided
|
|
if tt.validateFunc != nil {
|
|
tt.validateFunc(t, response)
|
|
}
|
|
|
|
// Log response for debugging
|
|
if !tt.expectError {
|
|
t.Logf("Response for %s: %v", tt.name, response)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// BenchmarkGetAllSkillsWithLearningCosts benchmarks the skills loading function
|
|
func BenchmarkGetAllSkillsWithLearningCosts(b *testing.B) {
|
|
// Setup test database
|
|
database.SetupTestDB(true, true)
|
|
defer database.ResetTestDB()
|
|
|
|
characterClass := "Magier"
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_, err := GetAllSkillsWithLearningCosts(characterClass)
|
|
if err != nil {
|
|
b.Fatalf("GetAllSkillsWithLearningCosts failed: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestSkillsCreationEndpointIntegration tests the full integration
|
|
func TestSkillsCreationEndpointIntegration(t *testing.T) {
|
|
// Setup test database
|
|
database.SetupTestDB(true, true)
|
|
defer database.ResetTestDB()
|
|
|
|
// Test different character classes
|
|
characterClasses := []string{"As", "Magier", "Krieger", "Spitzbube"}
|
|
|
|
for _, class := range characterClasses {
|
|
t.Run("CharacterClass_"+class, func(t *testing.T) {
|
|
// Create request
|
|
requestData := gin.H{
|
|
"characterClass": class,
|
|
}
|
|
requestBody, err := json.Marshal(requestData)
|
|
assert.NoError(t, err)
|
|
|
|
// Create HTTP request
|
|
req, err := http.NewRequest("POST", "/api/characters/available-skills-creation", bytes.NewBuffer(requestBody))
|
|
assert.NoError(t, err)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
// Create response recorder
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
c.Request = req
|
|
|
|
// Call the handler
|
|
GetAvailableSkillsForCreation(c)
|
|
|
|
// Verify response
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
|
|
var response map[string]interface{}
|
|
err = json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
|
|
// Verify structure
|
|
assert.Contains(t, response, "skills_by_category")
|
|
skillsByCategory := response["skills_by_category"].(map[string]interface{})
|
|
assert.Greater(t, len(skillsByCategory), 0, "Should have at least one skill category")
|
|
|
|
// Count total skills available
|
|
totalSkills := 0
|
|
for categoryName, skills := range skillsByCategory {
|
|
if skillsList, ok := skills.([]interface{}); ok {
|
|
totalSkills += len(skillsList)
|
|
t.Logf("Category %s has %d skills", categoryName, len(skillsList))
|
|
}
|
|
}
|
|
|
|
assert.Greater(t, totalSkills, 0, "Should have at least some skills available")
|
|
t.Logf("Character class %s: %d total skills in %d categories", class, totalSkills, len(skillsByCategory))
|
|
})
|
|
}
|
|
}
|