Repository: ogen-go/ogen
Pull request #784 (2023-02-10 07:47:28)

chore(uri): make `HasParam` errors more consistent


Patch:
diff --git a/uri/cookie_decoder.go b/uri/cookie_decoder.go
index c98dc382..8d8d0b1f 100644
--- a/uri/cookie_decoder.go
+++ b/uri/cookie_decoder.go
@@ -2,6 +2,8 @@ package uri
 
 import (
 	"net/http"
+
+	"github.com/go-faster/errors"
 )
 
 type CookieDecoder struct {
@@ -21,6 +23,9 @@ type CookieParameterDecodingConfig struct {
 
 func (d *CookieDecoder) HasParam(cfg CookieParameterDecodingConfig) error {
 	_, err := d.req.Cookie(cfg.Name)
+	if err == http.ErrNoCookie {
+		return errors.Errorf("cookie parameter %q not set", cfg.Name)
+	}
 	return err
 }
 
diff --git a/uri/cookie_param_decoder.go b/uri/cookie_param_decoder.go
index ff00e7de..a166aed0 100644
--- a/uri/cookie_param_decoder.go
+++ b/uri/cookie_param_decoder.go
@@ -15,7 +15,11 @@ type cookieParamDecoder struct {
 
 func (d *cookieParamDecoder) DecodeValue() (string, error) {
 	c, err := d.req.Cookie(d.paramName)
-	if err != nil {
+	switch err {
+	case nil:
+	case http.ErrNoCookie:
+		return "", errors.Errorf("cookie parameter %q not set", d.paramName)
+	default:
 		return "", errors.Wrapf(err, "get cookie %q", d.paramName)
 	}
 
diff --git a/uri/query_decoder.go b/uri/query_decoder.go
index 54329cd4..09b57e67 100644
--- a/uri/query_decoder.go
+++ b/uri/query_decoder.go
@@ -45,7 +45,7 @@ func (d *QueryDecoder) HasParam(cfg QueryParameterDecodingConfig) error {
 
 				_, ok := d.values[qparam]
 				if !ok && field.Required {
-					return errors.Errorf("parameter %q not set", qparam)
+					return errors.Errorf("query parameter %q not set", qparam)
 				}
 
 				found = true
@@ -60,7 +60,7 @@ func (d *QueryDecoder) HasParam(cfg QueryParameterDecodingConfig) error {
 	}
 
 	if _, ok := d.values[cfg.Name]; !ok {
-		return errors.Errorf("parameter %q not set", cfg.Name)
+		return errors.Errorf("query parameter %q not set", cfg.Name)
 	}
 	return nil
 }


Test patch:
diff --git a/uri/cookie_decoder_test.go b/uri/cookie_decoder_test.go
new file mode 100644
index 00000000..58f73cf8
--- /dev/null
+++ b/uri/cookie_decoder_test.go
@@ -0,0 +1,47 @@
+package uri
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestCookieDecoder_HasParam(t *testing.T) {
+	tests := []struct {
+		Input      http.Header
+		CookieName string
+		WantErr    string
+	}{
+		{
+			Input:      http.Header{},
+			CookieName: "foo",
+			WantErr:    "cookie parameter \"foo\" not set",
+		},
+		{
+			Input: http.Header{
+				"Cookie": []string{"foo=bar"},
+			},
+			CookieName: "foo",
+		},
+	}
+	for i, tt := range tests {
+		tt := tt
+		t.Run(fmt.Sprintf("Test%d", i+1), func(t *testing.T) {
+			req := &http.Request{
+				Header: tt.Input,
+			}
+			d := NewCookieDecoder(req)
+
+			err := d.HasParam(CookieParameterDecodingConfig{
+				Name: tt.CookieName,
+			})
+			if tt.WantErr != "" {
+				require.EqualError(t, err, tt.WantErr)
+				return
+			}
+			require.NoError(t, err)
+		})
+	}
+}
diff --git a/uri/header_decoder_test.go b/uri/header_decoder_test.go
new file mode 100644
index 00000000..d3198470
--- /dev/null
+++ b/uri/header_decoder_test.go
@@ -0,0 +1,44 @@
+package uri
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestHeaderDecoder_HasParam(t *testing.T) {
+	tests := []struct {
+		Input      http.Header
+		HeaderName string
+		WantErr    string
+	}{
+		{
+			Input:      http.Header{},
+			HeaderName: "X-Foo",
+			WantErr:    "header parameter \"X-Foo\" not set",
+		},
+		{
+			Input: http.Header{
+				"X-Foo": []string{"bar"},
+			},
+			HeaderName: "X-Foo",
+		},
+	}
+	for i, tt := range tests {
+		tt := tt
+		t.Run(fmt.Sprintf("Test%d", i+1), func(t *testing.T) {
+			d := NewHeaderDecoder(tt.Input)
+
+			err := d.HasParam(HeaderParameterDecodingConfig{
+				Name: tt.HeaderName,
+			})
+			if tt.WantErr != "" {
+				require.EqualError(t, err, tt.WantErr)
+				return
+			}
+			require.NoError(t, err)
+		})
+	}
+}
diff --git a/uri/query_decoder_test.go b/uri/query_decoder_test.go
new file mode 100644
index 00000000..57e606ef
--- /dev/null
+++ b/uri/query_decoder_test.go
@@ -0,0 +1,124 @@
+package uri
+
+import (
+	"fmt"
+	"net/url"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestQueryDecoder_HasParam(t *testing.T) {
+	tests := []struct {
+		Input   url.Values
+		Cfg     QueryParameterDecodingConfig
+		WantErr string
+	}{
+		// QueryStyleDeepObject + Explode
+		{
+			Input: url.Values{},
+			Cfg: QueryParameterDecodingConfig{
+				Name:    "object",
+				Style:   QueryStyleDeepObject,
+				Explode: true,
+				Fields: []QueryParameterObjectField{
+					{"foo", true},
+				},
+			},
+			WantErr: "query parameter \"object[foo]\" not set",
+		},
+		{
+			Input: url.Values{},
+			Cfg: QueryParameterDecodingConfig{
+				Name:    "object",
+				Style:   QueryStyleDeepObject,
+				Explode: true,
+				Fields: []QueryParameterObjectField{
+					{"foo", false},
+				},
+			},
+		},
+		{
+			Input: url.Values{
+				"object[foo]": []string{"bar"},
+			},
+			Cfg: QueryParameterDecodingConfig{
+				Name:    "object",
+				Style:   QueryStyleDeepObject,
+				Explode: true,
+				Fields: []QueryParameterObjectField{
+					{"foo", true},
+				},
+			},
+		},
+
+		// QueryStyleForm + Explode
+		{
+			Input: url.Values{},
+			Cfg: QueryParameterDecodingConfig{
+				Name:    "foo",
+				Style:   QueryStyleForm,
+				Explode: true,
+				Fields: []QueryParameterObjectField{
+					{"foo", true},
+				},
+			},
+			WantErr: "query parameter \"foo\" not set",
+		},
+		{
+			Input: url.Values{},
+			Cfg: QueryParameterDecodingConfig{
+				Name:    "foo",
+				Style:   QueryStyleForm,
+				Explode: true,
+				Fields: []QueryParameterObjectField{
+					{"foo", false},
+				},
+			},
+		},
+		{
+			Input: url.Values{
+				"foo": []string{"bar"},
+			},
+			Cfg: QueryParameterDecodingConfig{
+				Name:    "foo",
+				Style:   QueryStyleForm,
+				Explode: true,
+				Fields: []QueryParameterObjectField{
+					{"foo", true},
+				},
+			},
+		},
+
+		// Other
+		{
+			Cfg: QueryParameterDecodingConfig{
+				Name:  "foo",
+				Style: QueryStyleForm,
+			},
+			WantErr: "query parameter \"foo\" not set",
+		},
+		{
+			Input: url.Values{
+				"foo": []string{"bar"},
+			},
+			Cfg: QueryParameterDecodingConfig{
+				Name:  "foo",
+				Style: QueryStyleForm,
+			},
+		},
+	}
+	for i, tt := range tests {
+		tt := tt
+		t.Run(fmt.Sprintf("Test%d", i+1), func(t *testing.T) {
+			d := NewQueryDecoder(tt.Input)
+
+			err := d.HasParam(tt.Cfg)
+			if tt.WantErr != "" {
+				require.EqualError(t, err, tt.WantErr)
+				return
+			}
+			require.NoError(t, err)
+		})
+	}
+}