Artifact d7be882e6355c616cf009308c9a706c68c758d1eb4aef63ee065458ffd31195a:
- File
internal/ops/validate_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: 3015) [annotate] [blame] [check-ins using]
package ops import ( "context" "strings" "testing" ) type valIn struct { Repo string `json:"repo" validate:"required,max=64"` Limit int `json:"limit" validate:"min=1,max=500"` Sort string `json:"sort" validate:"oneof=asc|desc"` Tags []string `json:"tags" validate:"max=10"` Note string `json:"note"` } func TestValidateOK(t *testing.T) { in := valIn{Repo: "sqlite", Limit: 50, Sort: "asc", Note: "anything"} if err := Validate(in); err != nil { t.Fatal(err) } } func TestValidateRequired(t *testing.T) { err := Validate(valIn{Limit: 1, Sort: "asc"}) if err == nil || !strings.Contains(err.Error(), "repo") || !strings.Contains(err.Error(), "required") { t.Fatalf("want required error naming repo, got %v", err) } } func TestValidateNumericBounds(t *testing.T) { if err := Validate(valIn{Repo: "r", Limit: 0, Sort: "asc"}); err == nil { t.Fatal("want min violation for limit=0, got nil") } if err := Validate(valIn{Repo: "r", Limit: 501, Sort: "asc"}); err == nil { t.Fatal("want max violation for limit=501, got nil") } } func TestValidateStringAndSliceLength(t *testing.T) { if err := Validate(valIn{Repo: strings.Repeat("x", 65), Limit: 1, Sort: "asc"}); err == nil { t.Fatal("want max-length violation for repo, got nil") } long := make([]string, 11) if err := Validate(valIn{Repo: "r", Limit: 1, Sort: "asc", Tags: long}); err == nil { t.Fatal("want max-length violation for tags, got nil") } } func TestValidateOneof(t *testing.T) { if err := Validate(valIn{Repo: "r", Limit: 1, Sort: "sideways"}); err == nil { t.Fatal("want oneof violation, got nil") } // Empty non-required oneof field passes: oneof only constrains set values. if err := Validate(valIn{Repo: "r", Limit: 1}); err != nil { t.Fatal(err) } } func TestValidateOptionalFieldAdmitsZero(t *testing.T) { // Pin the semantics: min/max apply unconditionally. An optional field // is one without `required` whose bounds admit the zero value. type opt struct { N int `json:"n" validate:"max=5"` } if err := Validate(opt{}); err != nil { t.Fatal(err) } } func TestRegisterRejectsBadValidateTag(t *testing.T) { type badTag struct { X int `json:"x" validate:"min=notanumber"` } r := NewRegistry() err := Register(r, Op[badTag, echoOut]{ Name: "bad.tag", Kind: Query, Route: "GET /bad", Run: func(_ context.Context, _ badTag) (echoOut, error) { return echoOut{}, nil }, }) if err == nil || !strings.Contains(err.Error(), "validate") { t.Fatalf("want validate-tag error at register time, got %v", err) } } func TestRegisterRejectsUnknownRule(t *testing.T) { type badRule struct { X string `json:"x" validate:"shiny"` } r := NewRegistry() err := Register(r, Op[badRule, echoOut]{ Name: "bad.rule", Kind: Query, Route: "GET /badrule", Run: func(_ context.Context, _ badRule) (echoOut, error) { return echoOut{}, nil }, }) if err == nil || !strings.Contains(err.Error(), "shiny") { t.Fatalf("want unknown-rule error, got %v", err) } }