From 7096f30566ac65414c254bfdb37f5acfecaa014d Mon Sep 17 00:00:00 2001 From: Erik Date: Tue, 14 Jul 2026 23:53:38 +0200 Subject: [PATCH] feat(inventory-go): wire spell filters into /search/items; harden empty-input guards Co-Authored-By: Claude Sonnet 5 --- go-services/inventory-go/search.go | 3 +++ go-services/inventory-go/spell_filter.go | 9 ++++++--- go-services/inventory-go/spell_filter_test.go | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/go-services/inventory-go/search.go b/go-services/inventory-go/search.go index a5422e67..1090e8d7 100644 --- a/go-services/inventory-go/search.go +++ b/go-services/inventory-go/search.go @@ -302,6 +302,9 @@ func (s *Server) runSearch(ctx context.Context, q url.Values) (map[string]any, e } } + // --- spell filters: has_spell / spell_contains / legendary_cantrips --- + conds = append(conds, spellFilterConds(q, s.spells, ab)...) + where := "" if len(conds) > 0 { where = " WHERE " + strings.Join(conds, " AND ") diff --git a/go-services/inventory-go/spell_filter.go b/go-services/inventory-go/spell_filter.go index 8e132082..e78ac55f 100644 --- a/go-services/inventory-go/spell_filter.go +++ b/go-services/inventory-go/spell_filter.go @@ -53,6 +53,9 @@ func exactSpellID(spells map[int]map[string]any, q string) (int, bool) { best := -1 for id, sp := range spells { name, _ := sp["name"].(string) + if name == "" { + continue + } if strings.ToLower(name) == q && (best == -1 || id < best) { best = id } @@ -79,21 +82,21 @@ func spellFilterConds(q url.Values, spells map[int]map[string]any, ab *argBuilde var conds []string impossible := func() { conds = append(conds, "1 = 0") } - if v := q.Get("has_spell"); v != "" { + if v := strings.TrimSpace(q.Get("has_spell")); v != "" { if id, ok := exactSpellID(spells, v); ok { conds = append(conds, spellExists([]int{id}, ab)) } else { impossible() } } - if v := q.Get("spell_contains"); v != "" { + if v := strings.TrimSpace(q.Get("spell_contains")); v != "" { if ids := spellIDsContaining(spells, v); len(ids) > 0 { conds = append(conds, spellExists(ids, ab)) } else { impossible() } } - if v := q.Get("legendary_cantrips"); v != "" { + if v := strings.TrimSpace(q.Get("legendary_cantrips")); v != "" { for _, name := range splitNonEmpty(v) { if ids := spellIDsForCantrip(spells, name); len(ids) > 0 { conds = append(conds, spellExists(ids, ab)) diff --git a/go-services/inventory-go/spell_filter_test.go b/go-services/inventory-go/spell_filter_test.go index 4ecc4c1a..e0593959 100644 --- a/go-services/inventory-go/spell_filter_test.go +++ b/go-services/inventory-go/spell_filter_test.go @@ -112,3 +112,17 @@ func TestSpellFilterConds_Empty(t *testing.T) { t.Errorf("no params => nil conds, got %v", conds) } } + +func TestSpellFilterConds_WhitespaceParamsIgnored(t *testing.T) { + ab := &argBuilder{} + q := map[string][]string{"spell_contains": {" "}, "has_spell": {" "}, "legendary_cantrips": {" , "}} + if conds := spellFilterConds(q, testSpells, ab); conds != nil { + t.Errorf("whitespace params => nil conds, got %v", conds) + } +} + +func TestExactSpellID_EmptyNeverMatchesMalformed(t *testing.T) { + if _, ok := exactSpellID(testSpells, ""); ok { + t.Error("empty query must not match the malformed empty-name entry") + } +}