Files
bamort/backend/bmrt/gsmaster/handlers_http_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

1214 lines
38 KiB
Go

package gsmaster
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"bamort/database"
"bamort/bmrt/models"
"bamort/user"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// ---- Test Setup ----
func setupHandlerTest(t *testing.T) {
t.Helper()
setupTestEnvironment(t)
database.SetupTestDB(true)
t.Cleanup(database.ResetTestDB)
gin.SetMode(gin.TestMode)
}
// makeRouter builds a Gin router with gsmaster routes registered under /api.
// If role is non-empty, a middleware injects a user with that role before the routes.
// An empty role means no user is injected (simulates unauthenticated request).
func makeRouter(role string) *gin.Engine {
r := gin.New()
if role != "" {
r.Use(func(c *gin.Context) {
u := &user.User{UserID: 1, Role: role}
c.Set("user", u)
c.Next()
})
}
RegisterRoutes(r.Group("/api"))
return r
}
// perform executes an HTTP request against the router and returns the recorder.
func perform(router *gin.Engine, method, path string, body interface{}) *httptest.ResponseRecorder {
var bodyBytes []byte
if body != nil {
bodyBytes, _ = json.Marshal(body)
}
req := httptest.NewRequest(method, path, bytes.NewBuffer(bodyBytes))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
return w
}
// directContext creates a gin context wired to a ResponseRecorder and sets up
// an optional param and optional request body.
func directContext(method, path string, body interface{}) (*gin.Context, *httptest.ResponseRecorder) {
var bodyBytes []byte
if body != nil {
bodyBytes, _ = json.Marshal(body)
}
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Request = httptest.NewRequest(method, path, bytes.NewBuffer(bodyBytes))
c.Request.Header.Set("Content-Type", "application/json")
return c, w
}
// ---- Data helpers ----
func seedSkill(t *testing.T) models.Skill {
t.Helper()
skill := models.Skill{
Name: "Test Skill HTTP",
GameSystemId: 1,
Initialwert: 5,
Improvable: true,
Bonuseigenschaft: "st",
}
require.NoError(t, database.DB.Create(&skill).Error)
return skill
}
func seedWeaponSkill(t *testing.T) models.WeaponSkill {
t.Helper()
ws := models.WeaponSkill{Skill: models.Skill{
Name: "Test WeaponSkill HTTP",
GameSystemId: 1,
Initialwert: 5,
Improvable: true,
}}
require.NoError(t, database.DB.Create(&ws).Error)
return ws
}
func seedSpell(t *testing.T) models.Spell {
t.Helper()
spell := models.Spell{
Name: "Test Spell HTTP",
GameSystemId: 1,
Stufe: 1,
AP: "2",
Art: "Gestenzauber",
Zauberdauer: "10 sec",
}
require.NoError(t, database.DB.Create(&spell).Error)
return spell
}
func seedEquipment(t *testing.T) models.Equipment {
t.Helper()
eq := models.Equipment{
Name: "Test Equipment HTTP",
GameSystem: "midgard",
GameSystemId: 1,
Gewicht: 0.5,
Wert: 5.0,
}
require.NoError(t, database.DB.Create(&eq).Error)
return eq
}
func seedWeapon(t *testing.T) models.Weapon {
t.Helper()
w := models.Weapon{
Equipment: models.Equipment{
Name: "Test Weapon HTTP",
GameSystem: "midgard",
GameSystemId: 1,
},
Damage: "1W6",
}
require.NoError(t, database.DB.Create(&w).Error)
return w
}
// ---- GET /api/maintenance ----
func TestGetMasterData(t *testing.T) {
setupHandlerTest(t)
router := makeRouter("")
t.Run("returns 200 with all master data keys", func(t *testing.T) {
w := perform(router, http.MethodGet, "/api/maintenance?game_system_id=1", nil)
assert.Equal(t, http.StatusOK, w.Code)
var resp map[string]interface{}
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
assert.Contains(t, resp, "skills")
assert.Contains(t, resp, "weaponskills")
assert.Contains(t, resp, "spells")
assert.Contains(t, resp, "equipment")
assert.Contains(t, resp, "weapons")
assert.Contains(t, resp, "sources")
assert.Contains(t, resp, "skillcategories")
assert.Contains(t, resp, "spellcategories")
})
}
// ---- GET /api/maintenance/skills ----
func TestGetMDSkills(t *testing.T) {
setupHandlerTest(t)
router := makeRouter("")
t.Run("returns 200 with skill list", func(t *testing.T) {
seedSkill(t)
w := perform(router, http.MethodGet, "/api/maintenance/skills", nil)
assert.Equal(t, http.StatusOK, w.Code)
var resp map[string]interface{}
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
assert.Contains(t, resp, "skills")
assert.Contains(t, resp, "weaponskills")
assert.Contains(t, resp, "skillcategories")
})
}
// ---- GET /api/maintenance/skills/:id ----
func TestGetMDSkill(t *testing.T) {
setupHandlerTest(t)
t.Run("happy path returns 200 with skill", func(t *testing.T) {
skill := seedSkill(t)
c, w := directContext(http.MethodGet, fmt.Sprintf("/api/maintenance/skills/%d", skill.ID), nil)
c.Params = gin.Params{{Key: "id", Value: fmt.Sprintf("%d", skill.ID)}}
GetMDSkill(c)
assert.Equal(t, http.StatusOK, w.Code)
var resp models.Skill
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
assert.Equal(t, skill.ID, resp.ID)
assert.Equal(t, "Test Skill HTTP", resp.Name)
})
t.Run("not found returns 404", func(t *testing.T) {
c, w := directContext(http.MethodGet, "/api/maintenance/skills/99999", nil)
c.Params = gin.Params{{Key: "id", Value: "99999"}}
GetMDSkill(c)
assert.Equal(t, http.StatusNotFound, w.Code)
})
t.Run("invalid id returns 400", func(t *testing.T) {
c, w := directContext(http.MethodGet, "/api/maintenance/skills/abc", nil)
c.Params = gin.Params{{Key: "id", Value: "abc"}}
GetMDSkill(c)
assert.Equal(t, http.StatusBadRequest, w.Code)
})
}
// ---- GET /api/maintenance/skills-enhanced ----
func TestGetEnhancedMDSkills(t *testing.T) {
setupHandlerTest(t)
router := makeRouter("")
t.Run("returns 200 with skills, sources, categories, difficulties", func(t *testing.T) {
w := perform(router, http.MethodGet, "/api/maintenance/skills-enhanced", nil)
assert.Equal(t, http.StatusOK, w.Code)
var resp map[string]interface{}
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
assert.Contains(t, resp, "skills")
assert.Contains(t, resp, "sources")
assert.Contains(t, resp, "categories")
assert.Contains(t, resp, "difficulties")
})
}
// ---- GET /api/maintenance/skills-enhanced/:id ----
func TestGetEnhancedMDSkill(t *testing.T) {
setupHandlerTest(t)
t.Run("happy path returns 200 with skill", func(t *testing.T) {
skill := seedSkill(t)
c, w := directContext(http.MethodGet, fmt.Sprintf("/api/maintenance/skills-enhanced/%d", skill.ID), nil)
c.Params = gin.Params{{Key: "id", Value: fmt.Sprintf("%d", skill.ID)}}
GetEnhancedMDSkill(c)
assert.Equal(t, http.StatusOK, w.Code)
var resp SkillWithCategories
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
assert.Equal(t, skill.ID, resp.ID)
})
t.Run("not found returns 404", func(t *testing.T) {
c, w := directContext(http.MethodGet, "/api/maintenance/skills-enhanced/99999", nil)
c.Params = gin.Params{{Key: "id", Value: "99999"}}
GetEnhancedMDSkill(c)
assert.Equal(t, http.StatusNotFound, w.Code)
})
t.Run("bad id returns 400", func(t *testing.T) {
c, w := directContext(http.MethodGet, "/api/maintenance/skills-enhanced/abc", nil)
c.Params = gin.Params{{Key: "id", Value: "abc"}}
GetEnhancedMDSkill(c)
assert.Equal(t, http.StatusBadRequest, w.Code)
})
}
// ---- POST /api/maintenance/skills (protected) ----
func TestAddSkill(t *testing.T) {
setupHandlerTest(t)
newSkill := map[string]interface{}{
"name": "New Skill",
"game_system_id": 1,
"initialwert": 5,
"improvable": true,
"bonuseigenschaft": "st",
}
t.Run("maintainer creates skill returns 201", func(t *testing.T) {
router := makeRouter(user.RoleMaintainer)
w := perform(router, http.MethodPost, "/api/maintenance/skills", newSkill)
assert.Equal(t, http.StatusCreated, w.Code)
var resp models.Skill
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
assert.Equal(t, "New Skill", resp.Name)
assert.NotZero(t, resp.ID)
})
t.Run("bad JSON returns 400", func(t *testing.T) {
c, w := directContext(http.MethodPost, "/api/maintenance/skills", nil)
c.Request = httptest.NewRequest(http.MethodPost, "/api/maintenance/skills", bytes.NewBufferString("{bad json"))
c.Request.Header.Set("Content-Type", "application/json")
AddSkill(c)
assert.Equal(t, http.StatusBadRequest, w.Code)
})
t.Run("no auth returns 401", func(t *testing.T) {
router := makeRouter("") // no user injected
w := perform(router, http.MethodPost, "/api/maintenance/skills", newSkill)
assert.Equal(t, http.StatusUnauthorized, w.Code)
})
t.Run("standard user returns 403", func(t *testing.T) {
router := makeRouter(user.RoleStandardUser)
w := perform(router, http.MethodPost, "/api/maintenance/skills", newSkill)
assert.Equal(t, http.StatusForbidden, w.Code)
})
}
// ---- PUT /api/maintenance/skills/:id (protected) ----
func TestUpdateMDSkill(t *testing.T) {
setupHandlerTest(t)
skill := seedSkill(t)
updated := map[string]interface{}{
"name": "Updated Skill",
"initialwert": 10,
"improvable": true,
"game_system_id": 1,
}
t.Run("maintainer updates skill returns 200", func(t *testing.T) {
router := makeRouter(user.RoleMaintainer)
w := perform(router, http.MethodPut, fmt.Sprintf("/api/maintenance/skills/%d", skill.ID), updated)
assert.Equal(t, http.StatusOK, w.Code)
})
t.Run("not found returns 404", func(t *testing.T) {
c, w := directContext(http.MethodPut, "/api/maintenance/skills/99999", updated)
c.Params = gin.Params{{Key: "id", Value: "99999"}}
UpdateMDSkill(c)
assert.Equal(t, http.StatusNotFound, w.Code)
})
t.Run("no auth returns 401", func(t *testing.T) {
router := makeRouter("")
w := perform(router, http.MethodPut, fmt.Sprintf("/api/maintenance/skills/%d", skill.ID), updated)
assert.Equal(t, http.StatusUnauthorized, w.Code)
})
t.Run("standard user returns 403", func(t *testing.T) {
router := makeRouter(user.RoleStandardUser)
w := perform(router, http.MethodPut, fmt.Sprintf("/api/maintenance/skills/%d", skill.ID), updated)
assert.Equal(t, http.StatusForbidden, w.Code)
})
}
// ---- DELETE /api/maintenance/skills/:id (protected) ----
func TestDeleteMDSkill(t *testing.T) {
setupHandlerTest(t)
t.Run("maintainer deletes skill returns 204", func(t *testing.T) {
skill := seedSkill(t)
router := makeRouter(user.RoleMaintainer)
w := perform(router, http.MethodDelete, fmt.Sprintf("/api/maintenance/skills/%d", skill.ID), nil)
assert.Equal(t, http.StatusNoContent, w.Code)
})
t.Run("not found returns 404", func(t *testing.T) {
c, w := directContext(http.MethodDelete, "/api/maintenance/skills/99999", nil)
c.Params = gin.Params{{Key: "id", Value: "99999"}}
DeleteMDSkill(c)
assert.Equal(t, http.StatusNotFound, w.Code)
})
t.Run("no auth returns 401", func(t *testing.T) {
router := makeRouter("")
w := perform(router, http.MethodDelete, "/api/maintenance/skills/1", nil)
assert.Equal(t, http.StatusUnauthorized, w.Code)
})
}
// ---- POST /api/maintenance/skills-enhanced (protected) ----
func TestCreateEnhancedMDSkill(t *testing.T) {
setupHandlerTest(t)
req := SkillUpdateRequest{
Skill: models.Skill{
Name: "Enhanced New Skill",
GameSystemId: 1,
Initialwert: 8,
Improvable: true,
Bonuseigenschaft: "In",
},
CategoryDifficulties: []CategoryDifficultyPair{},
}
t.Run("maintainer creates enhanced skill returns 201", func(t *testing.T) {
router := makeRouter(user.RoleMaintainer)
w := perform(router, http.MethodPost, "/api/maintenance/skills-enhanced", req)
assert.Equal(t, http.StatusCreated, w.Code)
var resp SkillWithCategories
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
assert.Equal(t, "Enhanced New Skill", resp.Name)
assert.NotZero(t, resp.ID)
})
t.Run("bad JSON returns 400", func(t *testing.T) {
c, w := directContext(http.MethodPost, "/api/maintenance/skills-enhanced", nil)
c.Request = httptest.NewRequest(http.MethodPost, "/api/maintenance/skills-enhanced", bytes.NewBufferString("{bad json"))
c.Request.Header.Set("Content-Type", "application/json")
CreateEnhancedMDSkill(c)
assert.Equal(t, http.StatusBadRequest, w.Code)
})
t.Run("no auth returns 401", func(t *testing.T) {
router := makeRouter("")
w := perform(router, http.MethodPost, "/api/maintenance/skills-enhanced", req)
assert.Equal(t, http.StatusUnauthorized, w.Code)
})
t.Run("standard user returns 403", func(t *testing.T) {
router := makeRouter(user.RoleStandardUser)
w := perform(router, http.MethodPost, "/api/maintenance/skills-enhanced", req)
assert.Equal(t, http.StatusForbidden, w.Code)
})
}
// ---- PUT /api/maintenance/skills-enhanced/:id (protected) ----
func TestUpdateEnhancedMDSkill(t *testing.T) {
setupHandlerTest(t)
skill := seedSkill(t)
req := SkillUpdateRequest{
Skill: models.Skill{
Name: "Enhanced Updated Skill",
GameSystemId: 1,
Initialwert: 12,
Improvable: true,
},
CategoryDifficulties: []CategoryDifficultyPair{},
}
t.Run("maintainer updates enhanced skill returns 200", func(t *testing.T) {
router := makeRouter(user.RoleMaintainer)
w := perform(router, http.MethodPut, fmt.Sprintf("/api/maintenance/skills-enhanced/%d", skill.ID), req)
assert.Equal(t, http.StatusOK, w.Code)
var resp SkillWithCategories
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
assert.Equal(t, "Enhanced Updated Skill", resp.Name)
})
t.Run("not found returns 404", func(t *testing.T) {
c, w := directContext(http.MethodPut, "/api/maintenance/skills-enhanced/99999", req)
c.Params = gin.Params{{Key: "id", Value: "99999"}}
UpdateEnhancedMDSkill(c)
assert.Equal(t, http.StatusInternalServerError, w.Code) // fails on update of non-existent
})
t.Run("no auth returns 401", func(t *testing.T) {
router := makeRouter("")
w := perform(router, http.MethodPut, fmt.Sprintf("/api/maintenance/skills-enhanced/%d", skill.ID), req)
assert.Equal(t, http.StatusUnauthorized, w.Code)
})
}
// ---- WeaponSkill endpoints ----
func TestGetMDWeaponSkills(t *testing.T) {
setupHandlerTest(t)
router := makeRouter("")
t.Run("returns 200 with weaponskills list", func(t *testing.T) {
seedWeaponSkill(t)
w := perform(router, http.MethodGet, "/api/maintenance/weaponskills", nil)
assert.Equal(t, http.StatusOK, w.Code)
var resp map[string]interface{}
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
assert.Contains(t, resp, "weaponskills")
})
}
func TestGetMDWeaponSkill(t *testing.T) {
setupHandlerTest(t)
t.Run("happy path returns 200", func(t *testing.T) {
ws := seedWeaponSkill(t)
c, w := directContext(http.MethodGet, fmt.Sprintf("/api/maintenance/weaponskills/%d", ws.ID), nil)
c.Params = gin.Params{{Key: "id", Value: fmt.Sprintf("%d", ws.ID)}}
GetMDWeaponSkill(c)
assert.Equal(t, http.StatusOK, w.Code)
})
t.Run("not found returns 404", func(t *testing.T) {
c, w := directContext(http.MethodGet, "/api/maintenance/weaponskills/99999", nil)
c.Params = gin.Params{{Key: "id", Value: "99999"}}
GetMDWeaponSkill(c)
assert.Equal(t, http.StatusNotFound, w.Code)
})
t.Run("invalid id returns 400", func(t *testing.T) {
c, w := directContext(http.MethodGet, "/api/maintenance/weaponskills/abc", nil)
c.Params = gin.Params{{Key: "id", Value: "abc"}}
GetMDWeaponSkill(c)
assert.Equal(t, http.StatusBadRequest, w.Code)
})
}
func TestGetEnhancedMDWeaponSkills(t *testing.T) {
setupHandlerTest(t)
router := makeRouter("")
t.Run("returns 200 with weaponskills, sources, difficulties", func(t *testing.T) {
w := perform(router, http.MethodGet, "/api/maintenance/weaponskills-enhanced", nil)
assert.Equal(t, http.StatusOK, w.Code)
var resp map[string]interface{}
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
assert.Contains(t, resp, "weaponskills")
assert.Contains(t, resp, "sources")
assert.Contains(t, resp, "difficulties")
})
}
func TestGetEnhancedMDWeaponSkill(t *testing.T) {
setupHandlerTest(t)
t.Run("happy path returns 200", func(t *testing.T) {
ws := seedWeaponSkill(t)
c, w := directContext(http.MethodGet, fmt.Sprintf("/api/maintenance/weaponskills-enhanced/%d", ws.ID), nil)
c.Params = gin.Params{{Key: "id", Value: fmt.Sprintf("%d", ws.ID)}}
GetEnhancedMDWeaponSkill(c)
assert.Equal(t, http.StatusOK, w.Code)
})
t.Run("not found returns 404", func(t *testing.T) {
c, w := directContext(http.MethodGet, "/api/maintenance/weaponskills-enhanced/99999", nil)
c.Params = gin.Params{{Key: "id", Value: "99999"}}
GetEnhancedMDWeaponSkill(c)
assert.Equal(t, http.StatusNotFound, w.Code)
})
}
func TestAddWeaponSkill(t *testing.T) {
setupHandlerTest(t)
newWS := map[string]interface{}{
"name": "New WeaponSkill",
"initialwert": 5,
"improvable": true,
"game_system_id": 1,
}
t.Run("maintainer creates weaponskill returns 201", func(t *testing.T) {
router := makeRouter(user.RoleMaintainer)
w := perform(router, http.MethodPost, "/api/maintenance/weaponskills", newWS)
assert.Equal(t, http.StatusCreated, w.Code)
})
t.Run("no auth returns 401", func(t *testing.T) {
router := makeRouter("")
w := perform(router, http.MethodPost, "/api/maintenance/weaponskills", newWS)
assert.Equal(t, http.StatusUnauthorized, w.Code)
})
t.Run("standard user returns 403", func(t *testing.T) {
router := makeRouter(user.RoleStandardUser)
w := perform(router, http.MethodPost, "/api/maintenance/weaponskills", newWS)
assert.Equal(t, http.StatusForbidden, w.Code)
})
}
func TestUpdateMDWeaponSkill(t *testing.T) {
setupHandlerTest(t)
ws := seedWeaponSkill(t)
updated := map[string]interface{}{
"name": "Updated WeaponSkill",
"initialwert": 7,
"improvable": true,
}
t.Run("maintainer updates weaponskill returns 200", func(t *testing.T) {
router := makeRouter(user.RoleMaintainer)
w := perform(router, http.MethodPut, fmt.Sprintf("/api/maintenance/weaponskills/%d", ws.ID), updated)
assert.Equal(t, http.StatusOK, w.Code)
})
t.Run("not found returns 404", func(t *testing.T) {
c, w := directContext(http.MethodPut, "/api/maintenance/weaponskills/99999", updated)
c.Params = gin.Params{{Key: "id", Value: "99999"}}
UpdateMDWeaponSkill(c)
assert.Equal(t, http.StatusNotFound, w.Code)
})
t.Run("no auth returns 401", func(t *testing.T) {
router := makeRouter("")
w := perform(router, http.MethodPut, fmt.Sprintf("/api/maintenance/weaponskills/%d", ws.ID), updated)
assert.Equal(t, http.StatusUnauthorized, w.Code)
})
}
func TestDeleteMDWeaponSkill(t *testing.T) {
setupHandlerTest(t)
t.Run("maintainer deletes weaponskill returns 204", func(t *testing.T) {
ws := seedWeaponSkill(t)
router := makeRouter(user.RoleMaintainer)
w := perform(router, http.MethodDelete, fmt.Sprintf("/api/maintenance/weaponskills/%d", ws.ID), nil)
assert.Equal(t, http.StatusNoContent, w.Code)
})
t.Run("not found returns 404", func(t *testing.T) {
c, w := directContext(http.MethodDelete, "/api/maintenance/weaponskills/99999", nil)
c.Params = gin.Params{{Key: "id", Value: "99999"}}
DeleteMDWeaponSkill(c)
assert.Equal(t, http.StatusNotFound, w.Code)
})
t.Run("no auth returns 401", func(t *testing.T) {
router := makeRouter("")
w := perform(router, http.MethodDelete, "/api/maintenance/weaponskills/1", nil)
assert.Equal(t, http.StatusUnauthorized, w.Code)
})
}
func TestUpdateEnhancedMDWeaponSkill(t *testing.T) {
setupHandlerTest(t)
ws := seedWeaponSkill(t)
req := map[string]interface{}{
"name": "Enhanced Updated WS",
"initialwert": 8,
"improvable": true,
}
t.Run("maintainer updates enhanced weaponskill returns 200", func(t *testing.T) {
router := makeRouter(user.RoleMaintainer)
w := perform(router, http.MethodPut, fmt.Sprintf("/api/maintenance/weaponskills-enhanced/%d", ws.ID), req)
assert.Equal(t, http.StatusOK, w.Code)
})
t.Run("not found returns 404", func(t *testing.T) {
c, w := directContext(http.MethodPut, "/api/maintenance/weaponskills-enhanced/99999", req)
c.Params = gin.Params{{Key: "id", Value: "99999"}}
UpdateEnhancedMDWeaponSkill(c)
assert.Equal(t, http.StatusNotFound, w.Code)
})
t.Run("no auth returns 401", func(t *testing.T) {
router := makeRouter("")
w := perform(router, http.MethodPut, fmt.Sprintf("/api/maintenance/weaponskills-enhanced/%d", ws.ID), req)
assert.Equal(t, http.StatusUnauthorized, w.Code)
})
}
// ---- Spell endpoints ----
func TestGetMDSpells(t *testing.T) {
setupHandlerTest(t)
router := makeRouter("")
t.Run("returns 200 with spells list", func(t *testing.T) {
seedSpell(t)
w := perform(router, http.MethodGet, "/api/maintenance/spells", nil)
assert.Equal(t, http.StatusOK, w.Code)
var resp []models.Spell
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
assert.GreaterOrEqual(t, len(resp), 1)
})
}
func TestGetMDSpell(t *testing.T) {
setupHandlerTest(t)
t.Run("happy path returns 200", func(t *testing.T) {
spell := seedSpell(t)
c, w := directContext(http.MethodGet, fmt.Sprintf("/api/maintenance/spells/%d", spell.ID), nil)
c.Params = gin.Params{{Key: "id", Value: fmt.Sprintf("%d", spell.ID)}}
GetMDSpell(c)
assert.Equal(t, http.StatusOK, w.Code)
var resp models.Spell
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
assert.Equal(t, spell.ID, resp.ID)
assert.Equal(t, "Test Spell HTTP", resp.Name)
})
t.Run("not found returns 404", func(t *testing.T) {
c, w := directContext(http.MethodGet, "/api/maintenance/spells/99999", nil)
c.Params = gin.Params{{Key: "id", Value: "99999"}}
GetMDSpell(c)
assert.Equal(t, http.StatusNotFound, w.Code)
})
}
func TestGetEnhancedMDSpells(t *testing.T) {
setupHandlerTest(t)
router := makeRouter("")
t.Run("returns 200 with spells, sources, categories", func(t *testing.T) {
w := perform(router, http.MethodGet, "/api/maintenance/spells-enhanced", nil)
assert.Equal(t, http.StatusOK, w.Code)
var resp map[string]interface{}
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
assert.Contains(t, resp, "spells")
assert.Contains(t, resp, "sources")
assert.Contains(t, resp, "categories")
})
}
func TestGetEnhancedMDSpell(t *testing.T) {
setupHandlerTest(t)
t.Run("happy path returns 200", func(t *testing.T) {
spell := seedSpell(t)
c, w := directContext(http.MethodGet, fmt.Sprintf("/api/maintenance/spells-enhanced/%d", spell.ID), nil)
c.Params = gin.Params{{Key: "id", Value: fmt.Sprintf("%d", spell.ID)}}
GetEnhancedMDSpell(c)
assert.Equal(t, http.StatusOK, w.Code)
})
t.Run("not found returns 404", func(t *testing.T) {
c, w := directContext(http.MethodGet, "/api/maintenance/spells-enhanced/99999", nil)
c.Params = gin.Params{{Key: "id", Value: "99999"}}
GetEnhancedMDSpell(c)
assert.Equal(t, http.StatusNotFound, w.Code)
})
}
func TestAddSpell(t *testing.T) {
setupHandlerTest(t)
newSpell := map[string]interface{}{
"name": "New Spell",
"game_system_id": 1,
"level": 1,
"ap": "3",
}
t.Run("maintainer creates spell returns 201", func(t *testing.T) {
router := makeRouter(user.RoleMaintainer)
w := perform(router, http.MethodPost, "/api/maintenance/spells", newSpell)
assert.Equal(t, http.StatusCreated, w.Code)
var resp models.Spell
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
assert.Equal(t, "New Spell", resp.Name)
assert.NotZero(t, resp.ID)
})
t.Run("no auth returns 401", func(t *testing.T) {
router := makeRouter("")
w := perform(router, http.MethodPost, "/api/maintenance/spells", newSpell)
assert.Equal(t, http.StatusUnauthorized, w.Code)
})
t.Run("standard user returns 403", func(t *testing.T) {
router := makeRouter(user.RoleStandardUser)
w := perform(router, http.MethodPost, "/api/maintenance/spells", newSpell)
assert.Equal(t, http.StatusForbidden, w.Code)
})
}
func TestUpdateMDSpell(t *testing.T) {
setupHandlerTest(t)
spell := seedSpell(t)
updated := map[string]interface{}{
"name": "Updated Spell",
"level": 2,
}
t.Run("maintainer updates spell returns 200", func(t *testing.T) {
router := makeRouter(user.RoleMaintainer)
w := perform(router, http.MethodPut, fmt.Sprintf("/api/maintenance/spells/%d", spell.ID), updated)
assert.Equal(t, http.StatusOK, w.Code)
})
t.Run("not found returns 404", func(t *testing.T) {
c, w := directContext(http.MethodPut, "/api/maintenance/spells/99999", updated)
c.Params = gin.Params{{Key: "id", Value: "99999"}}
UpdateMDSpell(c)
assert.Equal(t, http.StatusNotFound, w.Code)
})
t.Run("no auth returns 401", func(t *testing.T) {
router := makeRouter("")
w := perform(router, http.MethodPut, fmt.Sprintf("/api/maintenance/spells/%d", spell.ID), updated)
assert.Equal(t, http.StatusUnauthorized, w.Code)
})
}
func TestUpdateEnhancedMDSpell(t *testing.T) {
setupHandlerTest(t)
spell := seedSpell(t)
req := SpellUpdateRequest{
Spell: models.Spell{
Name: "Enhanced Updated Spell",
Stufe: 3,
},
}
t.Run("maintainer updates enhanced spell returns 200", func(t *testing.T) {
router := makeRouter(user.RoleMaintainer)
w := perform(router, http.MethodPut, fmt.Sprintf("/api/maintenance/spells-enhanced/%d", spell.ID), req)
assert.Equal(t, http.StatusOK, w.Code)
})
t.Run("not found returns 500", func(t *testing.T) {
// UpdateEnhancedMDSpell does a silent no-op update then fails to fetch → 500
c, w := directContext(http.MethodPut, "/api/maintenance/spells-enhanced/99999", req)
c.Params = gin.Params{{Key: "id", Value: "99999"}}
UpdateEnhancedMDSpell(c)
assert.Equal(t, http.StatusInternalServerError, w.Code)
})
t.Run("no auth returns 401", func(t *testing.T) {
router := makeRouter("")
w := perform(router, http.MethodPut, fmt.Sprintf("/api/maintenance/spells-enhanced/%d", spell.ID), req)
assert.Equal(t, http.StatusUnauthorized, w.Code)
})
}
func TestDeleteMDSpell(t *testing.T) {
setupHandlerTest(t)
t.Run("maintainer deletes spell returns 204", func(t *testing.T) {
spell := seedSpell(t)
router := makeRouter(user.RoleMaintainer)
w := perform(router, http.MethodDelete, fmt.Sprintf("/api/maintenance/spells/%d", spell.ID), nil)
assert.Equal(t, http.StatusNoContent, w.Code)
})
t.Run("not found returns 404", func(t *testing.T) {
c, w := directContext(http.MethodDelete, "/api/maintenance/spells/99999", nil)
c.Params = gin.Params{{Key: "id", Value: "99999"}}
DeleteMDSpell(c)
assert.Equal(t, http.StatusNotFound, w.Code)
})
t.Run("no auth returns 401", func(t *testing.T) {
router := makeRouter("")
w := perform(router, http.MethodDelete, "/api/maintenance/spells/1", nil)
assert.Equal(t, http.StatusUnauthorized, w.Code)
})
}
// ---- Equipment endpoints (require game_system_id) ----
func TestGetMDEquipments(t *testing.T) {
setupHandlerTest(t)
router := makeRouter("")
t.Run("returns 200 with equipment list", func(t *testing.T) {
seedEquipment(t)
w := perform(router, http.MethodGet, "/api/maintenance/equipment?game_system_id=1", nil)
assert.Equal(t, http.StatusOK, w.Code)
var resp []models.Equipment
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
assert.GreaterOrEqual(t, len(resp), 1)
})
t.Run("invalid game_system_id returns 400", func(t *testing.T) {
w := perform(router, http.MethodGet, "/api/maintenance/equipment?game_system_id=abc", nil)
assert.Equal(t, http.StatusBadRequest, w.Code)
})
}
func TestGetMDEquipment(t *testing.T) {
setupHandlerTest(t)
router := makeRouter("")
t.Run("happy path returns 200", func(t *testing.T) {
eq := seedEquipment(t)
w := perform(router, http.MethodGet, fmt.Sprintf("/api/maintenance/equipment/%d?game_system_id=1", eq.ID), nil)
assert.Equal(t, http.StatusOK, w.Code)
var resp models.Equipment
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
assert.Equal(t, eq.ID, resp.ID)
assert.Equal(t, "Test Equipment HTTP", resp.Name)
})
t.Run("not found returns 404", func(t *testing.T) {
w := perform(router, http.MethodGet, "/api/maintenance/equipment/99999?game_system_id=1", nil)
assert.Equal(t, http.StatusNotFound, w.Code)
})
}
func TestGetEnhancedMDEquipment(t *testing.T) {
setupHandlerTest(t)
router := makeRouter("")
t.Run("returns 200 with equipment and sources", func(t *testing.T) {
w := perform(router, http.MethodGet, "/api/maintenance/equipment-enhanced?game_system_id=1", nil)
assert.Equal(t, http.StatusOK, w.Code)
var resp map[string]interface{}
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
assert.Contains(t, resp, "equipment")
assert.Contains(t, resp, "sources")
})
}
func TestGetEnhancedMDEquipmentItem(t *testing.T) {
setupHandlerTest(t)
router := makeRouter("")
t.Run("happy path returns 200", func(t *testing.T) {
eq := seedEquipment(t)
w := perform(router, http.MethodGet, fmt.Sprintf("/api/maintenance/equipment-enhanced/%d?game_system_id=1", eq.ID), nil)
assert.Equal(t, http.StatusOK, w.Code)
})
t.Run("not found returns 404", func(t *testing.T) {
w := perform(router, http.MethodGet, "/api/maintenance/equipment-enhanced/99999?game_system_id=1", nil)
assert.Equal(t, http.StatusNotFound, w.Code)
})
}
func TestAddEquipment(t *testing.T) {
setupHandlerTest(t)
newEq := map[string]interface{}{
"name": "New Equipment",
"gewicht": 1.0,
"wert": 10.0,
}
t.Run("maintainer creates equipment returns 201", func(t *testing.T) {
router := makeRouter(user.RoleMaintainer)
w := perform(router, http.MethodPost, "/api/maintenance/equipment?game_system_id=1", newEq)
assert.Equal(t, http.StatusCreated, w.Code)
var resp models.Equipment
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
assert.Equal(t, "New Equipment", resp.Name)
assert.NotZero(t, resp.ID)
})
t.Run("no auth returns 401", func(t *testing.T) {
router := makeRouter("")
w := perform(router, http.MethodPost, "/api/maintenance/equipment?game_system_id=1", newEq)
assert.Equal(t, http.StatusUnauthorized, w.Code)
})
t.Run("standard user returns 403", func(t *testing.T) {
router := makeRouter(user.RoleStandardUser)
w := perform(router, http.MethodPost, "/api/maintenance/equipment?game_system_id=1", newEq)
assert.Equal(t, http.StatusForbidden, w.Code)
})
}
func TestUpdateMDEquipment(t *testing.T) {
setupHandlerTest(t)
eq := seedEquipment(t)
updated := map[string]interface{}{
"name": "Updated Equipment",
"gewicht": 2.0,
"wert": 20.0,
}
t.Run("maintainer updates equipment returns 200", func(t *testing.T) {
router := makeRouter(user.RoleMaintainer)
w := perform(router, http.MethodPut, fmt.Sprintf("/api/maintenance/equipment/%d?game_system_id=1", eq.ID), updated)
assert.Equal(t, http.StatusOK, w.Code)
})
t.Run("not found returns 404", func(t *testing.T) {
router := makeRouter(user.RoleMaintainer)
w := perform(router, http.MethodPut, "/api/maintenance/equipment/99999?game_system_id=1", updated)
assert.Equal(t, http.StatusNotFound, w.Code)
})
t.Run("no auth returns 401", func(t *testing.T) {
router := makeRouter("")
w := perform(router, http.MethodPut, fmt.Sprintf("/api/maintenance/equipment/%d?game_system_id=1", eq.ID), updated)
assert.Equal(t, http.StatusUnauthorized, w.Code)
})
}
func TestUpdateEnhancedMDEquipmentItem(t *testing.T) {
setupHandlerTest(t)
eq := seedEquipment(t)
req := EquipmentUpdateRequest{
Equipment: models.Equipment{
Name: "Enhanced Updated Equipment",
Gewicht: 3.0,
Wert: 30.0,
},
}
t.Run("maintainer updates enhanced equipment returns 200", func(t *testing.T) {
router := makeRouter(user.RoleMaintainer)
w := perform(router, http.MethodPut, fmt.Sprintf("/api/maintenance/equipment-enhanced/%d?game_system_id=1", eq.ID), req)
assert.Equal(t, http.StatusOK, w.Code)
})
t.Run("not found returns 500", func(t *testing.T) {
// UpdateEnhancedMDEquipmentItem does a silent no-op update then fails to fetch → 500
router := makeRouter(user.RoleMaintainer)
w := perform(router, http.MethodPut, "/api/maintenance/equipment-enhanced/99999?game_system_id=1", req)
assert.Equal(t, http.StatusInternalServerError, w.Code)
})
t.Run("no auth returns 401", func(t *testing.T) {
router := makeRouter("")
w := perform(router, http.MethodPut, fmt.Sprintf("/api/maintenance/equipment-enhanced/%d?game_system_id=1", eq.ID), req)
assert.Equal(t, http.StatusUnauthorized, w.Code)
})
}
func TestDeleteMDEquipment(t *testing.T) {
setupHandlerTest(t)
t.Run("maintainer deletes equipment returns 204", func(t *testing.T) {
eq := seedEquipment(t)
router := makeRouter(user.RoleMaintainer)
w := perform(router, http.MethodDelete, fmt.Sprintf("/api/maintenance/equipment/%d?game_system_id=1", eq.ID), nil)
assert.Equal(t, http.StatusNoContent, w.Code)
})
t.Run("no auth returns 401", func(t *testing.T) {
router := makeRouter("")
w := perform(router, http.MethodDelete, "/api/maintenance/equipment/1?game_system_id=1", nil)
assert.Equal(t, http.StatusUnauthorized, w.Code)
})
}
// ---- Weapon endpoints (require game_system_id) ----
func TestGetMDWeapons(t *testing.T) {
setupHandlerTest(t)
router := makeRouter("")
t.Run("returns 200 with weapons list", func(t *testing.T) {
seedWeapon(t)
w := perform(router, http.MethodGet, "/api/maintenance/weapons?game_system_id=1", nil)
assert.Equal(t, http.StatusOK, w.Code)
var resp []models.Weapon
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
assert.GreaterOrEqual(t, len(resp), 1)
})
t.Run("invalid game_system_id returns 400", func(t *testing.T) {
w := perform(router, http.MethodGet, "/api/maintenance/weapons?game_system_id=abc", nil)
assert.Equal(t, http.StatusBadRequest, w.Code)
})
}
func TestGetMDWeapon(t *testing.T) {
setupHandlerTest(t)
router := makeRouter("")
t.Run("happy path returns 200", func(t *testing.T) {
weapon := seedWeapon(t)
w := perform(router, http.MethodGet, fmt.Sprintf("/api/maintenance/weapons/%d?game_system_id=1", weapon.ID), nil)
assert.Equal(t, http.StatusOK, w.Code)
var resp models.Weapon
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
assert.Equal(t, weapon.ID, resp.ID)
assert.Equal(t, "Test Weapon HTTP", resp.Name)
})
t.Run("not found returns 404", func(t *testing.T) {
w := perform(router, http.MethodGet, "/api/maintenance/weapons/99999?game_system_id=1", nil)
assert.Equal(t, http.StatusNotFound, w.Code)
})
}
func TestGetEnhancedMDWeapons(t *testing.T) {
setupHandlerTest(t)
router := makeRouter("")
t.Run("returns 200 with weapons and sources", func(t *testing.T) {
w := perform(router, http.MethodGet, "/api/maintenance/weapons-enhanced?game_system_id=1", nil)
assert.Equal(t, http.StatusOK, w.Code)
var resp map[string]interface{}
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
assert.Contains(t, resp, "weapons")
assert.Contains(t, resp, "sources")
})
}
func TestGetEnhancedMDWeapon(t *testing.T) {
setupHandlerTest(t)
router := makeRouter("")
t.Run("happy path returns 200", func(t *testing.T) {
weapon := seedWeapon(t)
w := perform(router, http.MethodGet, fmt.Sprintf("/api/maintenance/weapons-enhanced/%d?game_system_id=1", weapon.ID), nil)
assert.Equal(t, http.StatusOK, w.Code)
})
t.Run("not found returns 404", func(t *testing.T) {
w := perform(router, http.MethodGet, "/api/maintenance/weapons-enhanced/99999?game_system_id=1", nil)
assert.Equal(t, http.StatusNotFound, w.Code)
})
}
func TestAddWeapon(t *testing.T) {
setupHandlerTest(t)
newWeapon := map[string]interface{}{
"name": "New Weapon",
"damage": "1W8",
}
t.Run("maintainer creates weapon returns 201", func(t *testing.T) {
router := makeRouter(user.RoleMaintainer)
w := perform(router, http.MethodPost, "/api/maintenance/weapons?game_system_id=1", newWeapon)
assert.Equal(t, http.StatusCreated, w.Code)
var resp models.Weapon
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
assert.Equal(t, "New Weapon", resp.Name)
assert.NotZero(t, resp.ID)
})
t.Run("no auth returns 401", func(t *testing.T) {
router := makeRouter("")
w := perform(router, http.MethodPost, "/api/maintenance/weapons?game_system_id=1", newWeapon)
assert.Equal(t, http.StatusUnauthorized, w.Code)
})
t.Run("standard user returns 403", func(t *testing.T) {
router := makeRouter(user.RoleStandardUser)
w := perform(router, http.MethodPost, "/api/maintenance/weapons?game_system_id=1", newWeapon)
assert.Equal(t, http.StatusForbidden, w.Code)
})
}
func TestUpdateMDWeapon(t *testing.T) {
setupHandlerTest(t)
weapon := seedWeapon(t)
updated := map[string]interface{}{
"name": "Updated Weapon",
"damage": "2W6",
}
t.Run("maintainer updates weapon returns 200", func(t *testing.T) {
router := makeRouter(user.RoleMaintainer)
w := perform(router, http.MethodPut, fmt.Sprintf("/api/maintenance/weapons/%d?game_system_id=1", weapon.ID), updated)
assert.Equal(t, http.StatusOK, w.Code)
})
t.Run("not found returns 404", func(t *testing.T) {
router := makeRouter(user.RoleMaintainer)
w := perform(router, http.MethodPut, "/api/maintenance/weapons/99999?game_system_id=1", updated)
assert.Equal(t, http.StatusNotFound, w.Code)
})
t.Run("no auth returns 401", func(t *testing.T) {
router := makeRouter("")
w := perform(router, http.MethodPut, fmt.Sprintf("/api/maintenance/weapons/%d?game_system_id=1", weapon.ID), updated)
assert.Equal(t, http.StatusUnauthorized, w.Code)
})
}
func TestUpdateEnhancedMDWeapon(t *testing.T) {
setupHandlerTest(t)
weapon := seedWeapon(t)
req := map[string]interface{}{
"name": "Enhanced Updated Weapon",
"damage": "3W4",
}
t.Run("maintainer updates enhanced weapon returns 200", func(t *testing.T) {
router := makeRouter(user.RoleMaintainer)
w := perform(router, http.MethodPut, fmt.Sprintf("/api/maintenance/weapons-enhanced/%d?game_system_id=1", weapon.ID), req)
assert.Equal(t, http.StatusOK, w.Code)
})
t.Run("not found returns 404", func(t *testing.T) {
router := makeRouter(user.RoleMaintainer)
w := perform(router, http.MethodPut, "/api/maintenance/weapons-enhanced/99999?game_system_id=1", req)
assert.Equal(t, http.StatusNotFound, w.Code)
})
t.Run("no auth returns 401", func(t *testing.T) {
router := makeRouter("")
w := perform(router, http.MethodPut, fmt.Sprintf("/api/maintenance/weapons-enhanced/%d?game_system_id=1", weapon.ID), req)
assert.Equal(t, http.StatusUnauthorized, w.Code)
})
}
func TestDeleteMDWeapon(t *testing.T) {
setupHandlerTest(t)
t.Run("maintainer deletes weapon returns 204", func(t *testing.T) {
weapon := seedWeapon(t)
router := makeRouter(user.RoleMaintainer)
w := perform(router, http.MethodDelete, fmt.Sprintf("/api/maintenance/weapons/%d?game_system_id=1", weapon.ID), nil)
assert.Equal(t, http.StatusNoContent, w.Code)
})
t.Run("no auth returns 401", func(t *testing.T) {
router := makeRouter("")
w := perform(router, http.MethodDelete, "/api/maintenance/weapons/1?game_system_id=1", nil)
assert.Equal(t, http.StatusUnauthorized, w.Code)
})
}