2025-08-21 20:57:55 +02:00
|
|
|
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
|
2025-10-03 21:23:08 +02:00
|
|
|
func TestHandlerGetAvailableSkillsForCreation(t *testing.T) {
|
2025-08-21 20:57:55 +02:00
|
|
|
// 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 {
|
2025-10-03 21:23:08 +02:00
|
|
|
if learnCost, exists := skillMap["leCost"]; exists {
|
2025-08-21 20:57:55 +02:00
|
|
|
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))
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|