Artifact 51b8411cfd5195e4eb833a9d78bdebc18b41fbaf797e7b053b760219c36cbb50:
- File
internal/ops/codec_test.go
— part of check-in
[939f072f70]
at
2026-08-14 20:33:31
on branch trunk
— ops: form/URL-values codec + tag-driven validator
Field names from json tags; validate tags checked at Register time. (user: dmestas, size: 3022) [annotate] [blame] [check-ins using]
package ops import ( "net/url" "reflect" "strings" "testing" ) type formIn struct { Repo string `json:"repo"` Limit int `json:"limit"` After uint64 `json:"after"` Ratio float64 `json:"ratio"` Deleted bool `json:"deleted"` Tags []string `json:"tags"` Skip string `json:"-"` NoTag string } func TestDecodeValues(t *testing.T) { v := url.Values{ "repo": {"sqlite"}, "limit": {"50"}, "after": {"12345"}, "ratio": {"0.5"}, "deleted": {"true"}, "tags": {"a", "b"}, "NoTag": {"plain"}, } var in formIn if err := DecodeValues(v, &in); err != nil { t.Fatal(err) } want := formIn{ Repo: "sqlite", Limit: 50, After: 12345, Ratio: 0.5, Deleted: true, Tags: []string{"a", "b"}, NoTag: "plain", } if !reflect.DeepEqual(in, want) { t.Fatalf("got %+v, want %+v", in, want) } } func TestDecodeValuesMissingKeysLeaveZero(t *testing.T) { var in formIn if err := DecodeValues(url.Values{"repo": {"x"}}, &in); err != nil { t.Fatal(err) } if in.Repo != "x" || in.Limit != 0 || in.Tags != nil { t.Fatalf("got %+v", in) } } func TestDecodeValuesUnknownKeysIgnored(t *testing.T) { // HTML forms carry extras (CSRF tokens, submit buttons); never reject. var in formIn if err := DecodeValues(url.Values{"csrf": {"tok"}}, &in); err != nil { t.Fatal(err) } } func TestDecodeValuesBadNumber(t *testing.T) { var in formIn err := DecodeValues(url.Values{"limit": {"abc"}}, &in) if err == nil || !strings.Contains(err.Error(), "limit") { t.Fatalf("want error naming field limit, got %v", err) } } func TestDecodeValuesBadBool(t *testing.T) { var in formIn if err := DecodeValues(url.Values{"deleted": {"maybe"}}, &in); err == nil { t.Fatal("want bool parse error, got nil") } } func TestDecodeValuesSkipsDashTag(t *testing.T) { var in formIn if err := DecodeValues(url.Values{"Skip": {"x"}, "-": {"y"}}, &in); err != nil { t.Fatal(err) } if in.Skip != "" { t.Fatalf("json:\"-\" field decoded: %+v", in) } } func TestDecodeValuesRejectsNonPointerStruct(t *testing.T) { if err := DecodeValues(url.Values{}, formIn{}); err == nil { t.Fatal("want error for non-pointer dst, got nil") } x := 5 if err := DecodeValues(url.Values{}, &x); err == nil { t.Fatal("want error for non-struct dst, got nil") } } func TestDecodeValuesRejectsUnsupportedFieldType(t *testing.T) { type bad struct { Nested struct{ X int } `json:"nested"` } var b bad if err := DecodeValues(url.Values{"nested": {"{}"}}, &b); err == nil { t.Fatal("want unsupported-type error, got nil") } } func TestWildcards(t *testing.T) { for _, tc := range []struct { pattern string want []string }{ {"/api/v1/r/{repo}/timeline", []string{"repo"}}, {"/api/v1/r/{repo}/checkin/{hash}", []string{"repo", "hash"}}, {"/api/v1/version", nil}, {"/files/{path...}", []string{"path"}}, } { got := Wildcards(tc.pattern) if !reflect.DeepEqual(got, tc.want) { t.Errorf("Wildcards(%q) = %v, want %v", tc.pattern, got, tc.want) } } }