feat: Guard search queries against common mistakes (#6404)

* feat: Error on excessive invalid search keys
* feat: Guard search queries against common mistakes
* Raise errors in FE on workspaces table
* All errors should be on newlines
This commit is contained in:
Steven Masley
2023-03-01 23:28:56 -06:00
committed by GitHub
parent 1724cbf872
commit 8cf292f50a
19 changed files with 805 additions and 635 deletions

View File

@ -5,10 +5,12 @@ import (
"net/http"
"net/url"
"testing"
"time"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
"github.com/coder/coder/coderd/database"
"github.com/coder/coder/coderd/httpapi"
)
@ -26,6 +28,68 @@ type queryParamTestCase[T any] struct {
func TestParseQueryParams(t *testing.T) {
t.Parallel()
t.Run("Enum", func(t *testing.T) {
t.Parallel()
expParams := []queryParamTestCase[database.ResourceType]{
{
QueryParam: "resource_type",
Value: string(database.ResourceTypeWorkspace),
Expected: database.ResourceTypeWorkspace,
},
{
QueryParam: "bad_type",
Value: "foo",
ExpectedErrorContains: "not a valid value",
},
}
parser := httpapi.NewQueryParamParser()
testQueryParams(t, expParams, parser, func(vals url.Values, def database.ResourceType, queryParam string) database.ResourceType {
return httpapi.ParseCustom(parser, vals, def, queryParam, httpapi.ParseEnum[database.ResourceType])
})
})
t.Run("EnumList", func(t *testing.T) {
t.Parallel()
expParams := []queryParamTestCase[[]database.ResourceType]{
{
QueryParam: "resource_type",
Value: fmt.Sprintf("%s,%s", database.ResourceTypeWorkspace, database.ResourceTypeApiKey),
Expected: []database.ResourceType{database.ResourceTypeWorkspace, database.ResourceTypeApiKey},
},
}
parser := httpapi.NewQueryParamParser()
testQueryParams(t, expParams, parser, func(vals url.Values, def []database.ResourceType, queryParam string) []database.ResourceType {
return httpapi.ParseCustomList(parser, vals, def, queryParam, httpapi.ParseEnum[database.ResourceType])
})
})
t.Run("Time", func(t *testing.T) {
t.Parallel()
const layout = "2006-01-02"
expParams := []queryParamTestCase[time.Time]{
{
QueryParam: "date",
Value: "2010-01-01",
Expected: must(time.Parse(layout, "2010-01-01")),
},
{
QueryParam: "bad_date",
Value: "2010",
ExpectedErrorContains: "must be a valid date format",
},
}
parser := httpapi.NewQueryParamParser()
testQueryParams(t, expParams, parser, func(vals url.Values, def time.Time, queryParam string) time.Time {
return parser.Time(vals, time.Time{}, queryParam, layout)
})
})
t.Run("UUID", func(t *testing.T) {
t.Parallel()
me := uuid.New()
@ -43,12 +107,12 @@ func TestParseQueryParams(t *testing.T) {
{
QueryParam: "invalid_id",
Value: "bogus",
ExpectedErrorContains: "must be a valid uuid",
ExpectedErrorContains: "invalid UUID length",
},
{
QueryParam: "long_id",
Value: "afe39fbf-0f52-4a62-b0cc-58670145d773-123",
ExpectedErrorContains: "must be a valid uuid",
ExpectedErrorContains: "invalid UUID length",
},
{
QueryParam: "no_value",
@ -187,8 +251,8 @@ func testQueryParams[T any](t *testing.T, testCases []queryParamTestCase[T], par
for _, c := range testCases {
// !! Do not run these in parallel !!
t.Run(c.QueryParam, func(t *testing.T) {
v := parse(v, c.Default, c.QueryParam)
require.Equal(t, c.Expected, v, fmt.Sprintf("param=%q value=%q", c.QueryParam, c.Value))
value := parse(v, c.Default, c.QueryParam)
require.Equal(t, c.Expected, value, fmt.Sprintf("param=%q value=%q", c.QueryParam, c.Value))
if c.ExpectedErrorContains != "" {
errors := parser.Errors
require.True(t, len(errors) > 0, "error exist")
@ -199,3 +263,10 @@ func testQueryParams[T any](t *testing.T, testCases []queryParamTestCase[T], par
})
}
}
func must[T any](value T, err error) T {
if err != nil {
panic(err)
}
return value
}