package main import ( "net/url" "testing" ) func TestScopeQ(t *testing.T) { cases := []struct { name string q string base string want string }{ {"empty q gains scope", "", "dtoro", `path:"dtoro/*"`}, {"plain search gains scope", "label:dog", "dtoro", `label:dog path:"dtoro/*"`}, {"inside path kept", `path:"dtoro/2024/*" label:dog`, "dtoro", `path:"dtoro/2024/*" label:dog`}, {"exact base kept", `path:"dtoro"`, "dtoro", `path:"dtoro"`}, {"outside path replaced", `path:"muli/*"`, "dtoro", `path:"dtoro/*"`}, {"bare term outside replaced", `path:muli/x label:dog`, "dtoro", `label:dog path:"dtoro/*"`}, {"pipe alternative escaping", `path:"dtoro/*|muli/*"`, "dtoro", `path:"dtoro/*"`}, {"pipe all inside kept", `path:"dtoro/a|dtoro/b/*"`, "dtoro", `path:"dtoro/a|dtoro/b/*"`}, {"sibling prefix rejected", `path:"dtoro2/*"`, "dtoro", `path:"dtoro/*"`}, {"bare wildcard on base rejected", `path:dtoro*`, "dtoro", `path:"dtoro/*"`}, {"mixed valid+invalid terms collapse", `path:"dtoro/a" path:"muli/b"`, "dtoro", `path:"dtoro/*"`}, {"case-insensitive filter name", `PATH:"muli/*"`, "dtoro", `path:"dtoro/*"`}, {"nested base", `path:"family/alice/x"`, "family/alice", `path:"family/alice/x"`}, {"nested base parent escape", `path:"family/*"`, "family/alice", `path:"family/alice/*"`}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { if got := scopeQ(tc.q, tc.base); got != tc.want { t.Errorf("scopeQ(%q, %q) = %q, want %q", tc.q, tc.base, got, tc.want) } }) } } func TestScopeSearchValues(t *testing.T) { v := url.Values{} v.Set("count", "60") v.Set("path", "muli/*") got := scopeSearchValues(v, "dtoro") if got.Get("path") != "" { t.Errorf("outside path param should be dropped, got %q", got.Get("path")) } if got.Get("q") != `path:"dtoro/*"` { t.Errorf("q should carry the scope, got %q", got.Get("q")) } if got.Get("count") != "60" { t.Errorf("unrelated params must survive, got count=%q", got.Get("count")) } v2 := url.Values{} v2.Set("path", "dtoro/2024") got2 := scopeSearchValues(v2, "dtoro") if got2.Get("path") != "dtoro/2024" { t.Errorf("inside path param should be kept, got %q", got2.Get("path")) } } func TestPathValueAllowed(t *testing.T) { if pathValueAllowed(`"muli/*"`, "dtoro") { t.Error("outside value must be rejected") } if !pathValueAllowed(`"dtoro/Photos/2024"`, "dtoro") { t.Error("inside value must be allowed") } if pathValueAllowed("dtoro/a|muli/b", "dtoro") { t.Error("any escaping pipe alternative must reject the whole value") } }