Files
bamort/backend/bmrt/character/practice_points_additional_test.go
T
Bardioc26 042a1d4773 Learncost frontend (#42)
* 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
2026-05-01 18:15:31 +02:00

394 lines
11 KiB
Go

package character
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"os"
"testing"
"bamort/bmrt/models"
"bamort/database"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func setupPPTestEnv(t *testing.T) {
t.Helper()
original := os.Getenv("ENVIRONMENT")
os.Setenv("ENVIRONMENT", "test")
t.Cleanup(func() {
if original != "" {
os.Setenv("ENVIRONMENT", original)
} else {
os.Unsetenv("ENVIRONMENT")
}
})
database.SetupTestDB(true, true)
t.Cleanup(database.ResetTestDB)
require.NoError(t, models.MigrateStructure())
gin.SetMode(gin.TestMode)
}
func createCharWithSkillPP(t *testing.T, skillName string, ppAmount int) *models.Char {
t.Helper()
char := &models.Char{
BamortBase: models.BamortBase{Name: "PP Test Char"},
UserID: 4,
Rasse: "Mensch",
Typ: "Kr",
}
require.NoError(t, database.DB.Create(char).Error)
skill := &models.SkFertigkeit{
BamortCharTrait: models.BamortCharTrait{
BamortBase: models.BamortBase{Name: skillName},
CharacterID: char.ID,
UserID: 4,
},
Fertigkeitswert: 5,
Pp: ppAmount,
Improvable: true,
}
require.NoError(t, database.DB.Create(skill).Error)
return char
}
func TestGetPracticePoints(t *testing.T) {
setupPPTestEnv(t)
char := createCharWithSkillPP(t, "Athletik", 3)
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Set("userID", uint(4))
c.Params = gin.Params{{Key: "id", Value: fmt.Sprintf("%d", char.ID)}}
c.Request = httptest.NewRequest(http.MethodGet, "/", nil)
GetPracticePoints(c)
assert.Equal(t, http.StatusOK, w.Code)
var resp []PracticePointResponse
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
require.Len(t, resp, 1)
assert.Equal(t, "Athletik", resp[0].SkillName)
assert.Equal(t, 3, resp[0].Amount)
}
func TestGetPracticePointsEmpty(t *testing.T) {
setupPPTestEnv(t)
// A character with zero PP skills
char := createCharWithSkillPP(t, "Athletik", 0)
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Set("userID", uint(4))
c.Params = gin.Params{{Key: "id", Value: fmt.Sprintf("%d", char.ID)}}
c.Request = httptest.NewRequest(http.MethodGet, "/", nil)
GetPracticePoints(c)
assert.Equal(t, http.StatusOK, w.Code)
// Should return null or empty
body := w.Body.String()
assert.True(t, body == "null" || body == "[]", "Empty PP list should return null or empty array, got: "+body)
}
func TestGetPracticePointsCharNotFound(t *testing.T) {
setupPPTestEnv(t)
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Set("userID", uint(4))
c.Params = gin.Params{{Key: "id", Value: "99999"}}
c.Request = httptest.NewRequest(http.MethodGet, "/", nil)
GetPracticePoints(c)
assert.Equal(t, http.StatusNotFound, w.Code)
}
func TestUpdatePracticePoints(t *testing.T) {
setupPPTestEnv(t)
char := createCharWithSkillPP(t, "Athletik", 1)
updates := []PracticePointResponse{
{SkillName: "Athletik", Amount: 5},
}
body, _ := json.Marshal(updates)
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Set("userID", uint(4))
c.Params = gin.Params{{Key: "id", Value: fmt.Sprintf("%d", char.ID)}}
c.Request = httptest.NewRequest(http.MethodPut, "/", bytes.NewBuffer(body))
c.Request.Header.Set("Content-Type", "application/json")
UpdatePracticePoints(c)
assert.Equal(t, http.StatusOK, w.Code)
}
func TestUpdatePracticePointsNotOwner(t *testing.T) {
setupPPTestEnv(t)
char := createCharWithSkillPP(t, "Athletik", 1)
updates := []PracticePointResponse{{SkillName: "Athletik", Amount: 5}}
body, _ := json.Marshal(updates)
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Set("userID", uint(99))
c.Params = gin.Params{{Key: "id", Value: fmt.Sprintf("%d", char.ID)}}
c.Request = httptest.NewRequest(http.MethodPut, "/", bytes.NewBuffer(body))
c.Request.Header.Set("Content-Type", "application/json")
UpdatePracticePoints(c)
assert.Equal(t, http.StatusForbidden, w.Code)
}
func TestAddPracticePoint(t *testing.T) {
setupPPTestEnv(t)
char := createCharWithSkillPP(t, "Athletik", 0)
reqBody := map[string]interface{}{
"skill_name": "Athletik",
"amount": 1,
}
body, _ := json.Marshal(reqBody)
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Set("userID", uint(4))
c.Params = gin.Params{{Key: "id", Value: fmt.Sprintf("%d", char.ID)}}
c.Request = httptest.NewRequest(http.MethodPost, "/", bytes.NewBuffer(body))
c.Request.Header.Set("Content-Type", "application/json")
AddPracticePoint(c)
assert.Equal(t, http.StatusOK, w.Code)
var resp PracticePointActionResponse
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
assert.True(t, resp.Success)
assert.Equal(t, "Athletik", resp.TargetSkill)
}
func TestAddPracticePointSkillNotFound(t *testing.T) {
setupPPTestEnv(t)
char := createCharWithSkillPP(t, "Athletik", 0)
reqBody := map[string]interface{}{
"skill_name": "Zauberei",
"amount": 1,
}
body, _ := json.Marshal(reqBody)
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Set("userID", uint(4))
c.Params = gin.Params{{Key: "id", Value: fmt.Sprintf("%d", char.ID)}}
c.Request = httptest.NewRequest(http.MethodPost, "/", bytes.NewBuffer(body))
c.Request.Header.Set("Content-Type", "application/json")
AddPracticePoint(c)
assert.Equal(t, http.StatusBadRequest, w.Code)
}
func TestUsePracticePoint(t *testing.T) {
setupPPTestEnv(t)
char := createCharWithSkillPP(t, "Athletik", 3)
reqBody := map[string]interface{}{
"skill_name": "Athletik",
"amount": 1,
}
body, _ := json.Marshal(reqBody)
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Set("userID", uint(4))
c.Params = gin.Params{{Key: "id", Value: fmt.Sprintf("%d", char.ID)}}
c.Request = httptest.NewRequest(http.MethodPost, "/", bytes.NewBuffer(body))
c.Request.Header.Set("Content-Type", "application/json")
UsePracticePoint(c)
assert.Equal(t, http.StatusOK, w.Code)
var resp PracticePointActionResponse
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
assert.True(t, resp.Success)
}
func TestUsePracticePointInsufficientPP(t *testing.T) {
setupPPTestEnv(t)
char := createCharWithSkillPP(t, "Athletik", 0)
reqBody := map[string]interface{}{
"skill_name": "Athletik",
"amount": 5,
}
body, _ := json.Marshal(reqBody)
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Set("userID", uint(4))
c.Params = gin.Params{{Key: "id", Value: fmt.Sprintf("%d", char.ID)}}
c.Request = httptest.NewRequest(http.MethodPost, "/", bytes.NewBuffer(body))
c.Request.Header.Set("Content-Type", "application/json")
UsePracticePoint(c)
assert.Equal(t, http.StatusBadRequest, w.Code)
}
// createCharWithTwoSameNameSkills creates a character with two "Sprache" skills
// distinguished only by their Beschreibung ("Elfisch" and "Zwergisch").
func createCharWithTwoSameNameSkills(t *testing.T) (*models.Char, *models.SkFertigkeit, *models.SkFertigkeit) {
t.Helper()
char := &models.Char{
BamortBase: models.BamortBase{Name: "Duplicate Skill Char"},
UserID: 4,
Rasse: "Mensch",
Typ: "Kr",
}
require.NoError(t, database.DB.Create(char).Error)
skill1 := &models.SkFertigkeit{
BamortCharTrait: models.BamortCharTrait{
BamortBase: models.BamortBase{Name: "Sprache"},
CharacterID: char.ID,
UserID: 4,
},
Beschreibung: "Elfisch",
Fertigkeitswert: 5,
Pp: 0,
Improvable: true,
}
require.NoError(t, database.DB.Create(skill1).Error)
skill2 := &models.SkFertigkeit{
BamortCharTrait: models.BamortCharTrait{
BamortBase: models.BamortBase{Name: "Sprache"},
CharacterID: char.ID,
UserID: 4,
},
Beschreibung: "Zwergisch",
Fertigkeitswert: 3,
Pp: 0,
Improvable: true,
}
require.NoError(t, database.DB.Create(skill2).Error)
return char, skill1, skill2
}
func TestAddPracticePoint_DuplicateSkillName_WithDescription(t *testing.T) {
setupPPTestEnv(t)
char, skill1, skill2 := createCharWithTwoSameNameSkills(t)
_ = skill1
reqBody := map[string]interface{}{
"skill_name": "Sprache",
"skill_description": "Zwergisch",
"amount": 1,
}
body, _ := json.Marshal(reqBody)
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Set("userID", uint(4))
c.Params = gin.Params{{Key: "id", Value: fmt.Sprintf("%d", char.ID)}}
c.Request = httptest.NewRequest(http.MethodPost, "/", bytes.NewBuffer(body))
c.Request.Header.Set("Content-Type", "application/json")
AddPracticePoint(c)
require.Equal(t, http.StatusOK, w.Code)
// Verify only the Zwergisch skill got the PP
var updatedSkill2 models.SkFertigkeit
require.NoError(t, database.DB.First(&updatedSkill2, skill2.ID).Error)
assert.Equal(t, 1, updatedSkill2.Pp, "Zwergisch should have 1 PP")
var updatedSkill1 models.SkFertigkeit
require.NoError(t, database.DB.First(&updatedSkill1, skill1.ID).Error)
assert.Equal(t, 0, updatedSkill1.Pp, "Elfisch should still have 0 PP")
}
func TestUsePracticePoint_DuplicateSkillName_WithDescription(t *testing.T) {
setupPPTestEnv(t)
char, skill1, _ := createCharWithTwoSameNameSkills(t)
// Give only the Elfisch skill some PP
require.NoError(t, database.DB.Model(skill1).Update("pp", 3).Error)
reqBody := map[string]interface{}{
"skill_name": "Sprache",
"skill_description": "Elfisch",
"amount": 1,
}
body, _ := json.Marshal(reqBody)
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Set("userID", uint(4))
c.Params = gin.Params{{Key: "id", Value: fmt.Sprintf("%d", char.ID)}}
c.Request = httptest.NewRequest(http.MethodPost, "/", bytes.NewBuffer(body))
c.Request.Header.Set("Content-Type", "application/json")
UsePracticePoint(c)
require.Equal(t, http.StatusOK, w.Code)
var updatedSkill1 models.SkFertigkeit
require.NoError(t, database.DB.First(&updatedSkill1, skill1.ID).Error)
assert.Equal(t, 2, updatedSkill1.Pp, "Elfisch should have 2 PP after using 1")
}
func TestGetPracticePoints_IncludesDescription(t *testing.T) {
setupPPTestEnv(t)
char, _, _ := createCharWithTwoSameNameSkills(t)
// Give both skills some PP
require.NoError(t, database.DB.Model(&models.SkFertigkeit{}).
Where("character_id = ? AND beschreibung = ?", char.ID, "Elfisch").
Update("pp", 2).Error)
require.NoError(t, database.DB.Model(&models.SkFertigkeit{}).
Where("character_id = ? AND beschreibung = ?", char.ID, "Zwergisch").
Update("pp", 1).Error)
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Set("userID", uint(4))
c.Params = gin.Params{{Key: "id", Value: fmt.Sprintf("%d", char.ID)}}
c.Request = httptest.NewRequest(http.MethodGet, "/", nil)
GetPracticePoints(c)
assert.Equal(t, http.StatusOK, w.Code)
var resp []PracticePointResponse
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
require.Len(t, resp, 2)
// Both entries should have SkillDescription set
for _, pp := range resp {
assert.NotEmpty(t, pp.SkillDescription, "SkillDescription must be set for disambiguation")
}
}