Add error and test for invalid filter values
continuous-integration/drone/pr Build is failing Details

This commit is contained in:
kolaente 2020-06-27 13:44:52 +02:00
parent 139d2a4723
commit d4e0dac28b
Signed by: konrad
GPG Key ID: F40E70337AB24C9B
4 changed files with 180 additions and 120 deletions

View File

@ -78,6 +78,7 @@ This document describes the different errors Vikunja can return.
| 4016 | 403 | Invalid task field. |
| 4017 | 403 | Invalid task filter comparator. |
| 4018 | 403 | Invalid task filter concatinator. |
| 4019 | 403 | Invalid task filter value. |
### Namespace

View File

@ -177,71 +177,85 @@ func TestTaskCollection(t *testing.T) {
assert.NotContains(t, rec.Body.String(), `{"id":6,"title":"task #6 lower due date","description":"","done":false,"due_date":1543616724,"reminder_dates":null,"repeat_after":0,"repeat_from_current_date":false,"priority":0,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"username":"","email":"","created":0,"updated":0}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"due_date":1543636724,"reminder_dates":null,"repeat_after":0,"repeat_from_current_date":false,"priority":0,"start_date":0,"end_date":0,"assignees":null,"labels":null,"created":1543626724,"updated":1543626724,"created_by":{"id":0,"username":"","email":"","created":0,"updated":0}}]`)
})
})
t.Run("Date range", func(t *testing.T) {
t.Run("start and end date", func(t *testing.T) {
rec, err := testHandler.testReadAllWithUser(
url.Values{
"filter_by": []string{"start_date", "end_date", "due_date"},
"filter_value": []string{"2018-12-11T03:46:40+00:00", "2018-12-13T11:20:01+00:00", "2018-11-29T14:00:00+00:00"},
"filter_comparator": []string{"greater", "less", "greater"},
},
urlParams,
)
assert.NoError(t, err)
assert.NotContains(t, rec.Body.String(), `task #1`)
assert.NotContains(t, rec.Body.String(), `task #2`)
assert.NotContains(t, rec.Body.String(), `task #3`)
assert.NotContains(t, rec.Body.String(), `task #4`)
assert.Contains(t, rec.Body.String(), `task #5`)
assert.Contains(t, rec.Body.String(), `task #6`)
assert.Contains(t, rec.Body.String(), `task #7`)
assert.Contains(t, rec.Body.String(), `task #8`)
assert.Contains(t, rec.Body.String(), `task #9`)
assert.NotContains(t, rec.Body.String(), `task #10`)
assert.NotContains(t, rec.Body.String(), `task #11`)
assert.NotContains(t, rec.Body.String(), `task #12`)
assert.NotContains(t, rec.Body.String(), `task #13`)
assert.NotContains(t, rec.Body.String(), `task #14`)
t.Run("Filter", func(t *testing.T) {
t.Run("Date range", func(t *testing.T) {
t.Run("start and end date", func(t *testing.T) {
rec, err := testHandler.testReadAllWithUser(
url.Values{
"filter_by": []string{"start_date", "end_date", "due_date"},
"filter_value": []string{"2018-12-11T03:46:40+00:00", "2018-12-13T11:20:01+00:00", "2018-11-29T14:00:00+00:00"},
"filter_comparator": []string{"greater", "less", "greater"},
},
urlParams,
)
assert.NoError(t, err)
assert.NotContains(t, rec.Body.String(), `task #1`)
assert.NotContains(t, rec.Body.String(), `task #2`)
assert.NotContains(t, rec.Body.String(), `task #3`)
assert.NotContains(t, rec.Body.String(), `task #4`)
assert.Contains(t, rec.Body.String(), `task #5`)
assert.Contains(t, rec.Body.String(), `task #6`)
assert.Contains(t, rec.Body.String(), `task #7`)
assert.Contains(t, rec.Body.String(), `task #8`)
assert.Contains(t, rec.Body.String(), `task #9`)
assert.NotContains(t, rec.Body.String(), `task #10`)
assert.NotContains(t, rec.Body.String(), `task #11`)
assert.NotContains(t, rec.Body.String(), `task #12`)
assert.NotContains(t, rec.Body.String(), `task #13`)
assert.NotContains(t, rec.Body.String(), `task #14`)
})
t.Run("start date only", func(t *testing.T) {
rec, err := testHandler.testReadAllWithUser(
url.Values{
"filter_by": []string{"start_date"},
"filter_value": []string{"2018-10-20T01:46:40+00:00"},
"filter_comparator": []string{"greater"},
},
urlParams,
)
assert.NoError(t, err)
assert.NotContains(t, rec.Body.String(), `task #1`)
assert.NotContains(t, rec.Body.String(), `task #2`)
assert.NotContains(t, rec.Body.String(), `task #3`)
assert.NotContains(t, rec.Body.String(), `task #4`)
assert.NotContains(t, rec.Body.String(), `task #5`)
assert.NotContains(t, rec.Body.String(), `task #6`)
assert.Contains(t, rec.Body.String(), `task #7`)
assert.NotContains(t, rec.Body.String(), `task #8`)
assert.Contains(t, rec.Body.String(), `task #9`)
assert.NotContains(t, rec.Body.String(), `task #10`)
assert.NotContains(t, rec.Body.String(), `task #11`)
assert.NotContains(t, rec.Body.String(), `task #12`)
assert.NotContains(t, rec.Body.String(), `task #13`)
assert.NotContains(t, rec.Body.String(), `task #14`)
})
t.Run("end date only", func(t *testing.T) {
rec, err := testHandler.testReadAllWithUser(
url.Values{
"filter_by": []string{"end_date"},
"filter_value": []string{"2018-12-13T11:20:01+00:00"},
"filter_comparator": []string{"greater"},
},
urlParams,
)
assert.NoError(t, err)
// If no start date but an end date is specified, this should be null
// since we don't have any tasks in the fixtures with an end date >
// the current date.
assert.Equal(t, "[]\n", rec.Body.String())
})
})
t.Run("start date only", func(t *testing.T) {
rec, err := testHandler.testReadAllWithUser(
t.Run("invalid date", func(t *testing.T) {
_, err := testHandler.testReadAllWithUser(
url.Values{
"filter_by": []string{"start_date"},
"filter_value": []string{"2018-10-20T01:46:40+00:00"},
"filter_by": []string{"due_date"},
"filter_value": []string{"1540000000"},
"filter_comparator": []string{"greater"},
},
urlParams,
nil,
)
assert.NoError(t, err)
assert.NotContains(t, rec.Body.String(), `task #1`)
assert.NotContains(t, rec.Body.String(), `task #2`)
assert.NotContains(t, rec.Body.String(), `task #3`)
assert.NotContains(t, rec.Body.String(), `task #4`)
assert.NotContains(t, rec.Body.String(), `task #5`)
assert.NotContains(t, rec.Body.String(), `task #6`)
assert.Contains(t, rec.Body.String(), `task #7`)
assert.NotContains(t, rec.Body.String(), `task #8`)
assert.Contains(t, rec.Body.String(), `task #9`)
assert.NotContains(t, rec.Body.String(), `task #10`)
assert.NotContains(t, rec.Body.String(), `task #11`)
assert.NotContains(t, rec.Body.String(), `task #12`)
assert.NotContains(t, rec.Body.String(), `task #13`)
assert.NotContains(t, rec.Body.String(), `task #14`)
})
t.Run("end date only", func(t *testing.T) {
rec, err := testHandler.testReadAllWithUser(
url.Values{
"filter_by": []string{"end_date"},
"filter_value": []string{"2018-12-13T11:20:01+00:00"},
"filter_comparator": []string{"greater"},
},
urlParams,
)
assert.NoError(t, err)
// If no start date but an end date is specified, this should be null
// since we don't have any tasks in the fixtures with an end date >
// the current date.
assert.Equal(t, "[]\n", rec.Body.String())
assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeInvalidTaskFilterValue)
})
})
})
@ -342,71 +356,85 @@ func TestTaskCollection(t *testing.T) {
assert.NotContains(t, rec.Body.String(), `{"id":6,"title":"task #6 lower due date","description":"","done":false,"due_date":1543616724,"reminder_dates":null,"repeat_after":0,"repeat_from_current_date":false,"priority":0,"start_date":0,"end_date":0,"assignees":null,"labels":null,"hex_color":"","created":1543626724,"updated":1543626724,"created_by":{"id":0,"username":"","email":"","created":0,"updated":0}},{"id":5,"title":"task #5 higher due date","description":"","done":false,"due_date":1543636724,"reminder_dates":null,"repeat_after":0,"repeat_from_current_date":false,"priority":0,"start_date":0,"end_date":0,"assignees":null,"labels":null,"created":1543626724,"updated":1543626724,"created_by":{"id":0,"username":"","email":"","created":0,"updated":0}}]`)
})
})
t.Run("Date range", func(t *testing.T) {
t.Run("start and end date", func(t *testing.T) {
rec, err := testHandler.testReadAllWithUser(
url.Values{
"filter_by": []string{"start_date", "end_date", "due_date"},
"filter_value": []string{"2018-12-11T03:46:40+00:00", "2018-12-13T11:20:01+00:00", "2018-11-29T14:00:00+00:00"},
"filter_comparator": []string{"greater", "less", "greater"},
},
nil,
)
assert.NoError(t, err)
assert.NotContains(t, rec.Body.String(), `task #1`)
assert.NotContains(t, rec.Body.String(), `task #2`)
assert.NotContains(t, rec.Body.String(), `task #3`)
assert.NotContains(t, rec.Body.String(), `task #4`)
assert.Contains(t, rec.Body.String(), `task #5`)
assert.Contains(t, rec.Body.String(), `task #6`)
assert.Contains(t, rec.Body.String(), `task #7`)
assert.Contains(t, rec.Body.String(), `task #8`)
assert.Contains(t, rec.Body.String(), `task #9`)
assert.NotContains(t, rec.Body.String(), `task #10`)
assert.NotContains(t, rec.Body.String(), `task #11`)
assert.NotContains(t, rec.Body.String(), `task #12`)
assert.NotContains(t, rec.Body.String(), `task #13`)
assert.NotContains(t, rec.Body.String(), `task #14`)
t.Run("Filter", func(t *testing.T) {
t.Run("Date range", func(t *testing.T) {
t.Run("start and end date", func(t *testing.T) {
rec, err := testHandler.testReadAllWithUser(
url.Values{
"filter_by": []string{"start_date", "end_date", "due_date"},
"filter_value": []string{"2018-12-11T03:46:40+00:00", "2018-12-13T11:20:01+00:00", "2018-11-29T14:00:00+00:00"},
"filter_comparator": []string{"greater", "less", "greater"},
},
nil,
)
assert.NoError(t, err)
assert.NotContains(t, rec.Body.String(), `task #1`)
assert.NotContains(t, rec.Body.String(), `task #2`)
assert.NotContains(t, rec.Body.String(), `task #3`)
assert.NotContains(t, rec.Body.String(), `task #4`)
assert.Contains(t, rec.Body.String(), `task #5`)
assert.Contains(t, rec.Body.String(), `task #6`)
assert.Contains(t, rec.Body.String(), `task #7`)
assert.Contains(t, rec.Body.String(), `task #8`)
assert.Contains(t, rec.Body.String(), `task #9`)
assert.NotContains(t, rec.Body.String(), `task #10`)
assert.NotContains(t, rec.Body.String(), `task #11`)
assert.NotContains(t, rec.Body.String(), `task #12`)
assert.NotContains(t, rec.Body.String(), `task #13`)
assert.NotContains(t, rec.Body.String(), `task #14`)
})
t.Run("start date only", func(t *testing.T) {
rec, err := testHandler.testReadAllWithUser(
url.Values{
"filter_by": []string{"start_date"},
"filter_value": []string{"2018-10-20T01:46:40+00:00"},
"filter_comparator": []string{"greater"},
},
nil,
)
assert.NoError(t, err)
assert.NotContains(t, rec.Body.String(), `task #1`)
assert.NotContains(t, rec.Body.String(), `task #2`)
assert.NotContains(t, rec.Body.String(), `task #3`)
assert.NotContains(t, rec.Body.String(), `task #4`)
assert.NotContains(t, rec.Body.String(), `task #5`)
assert.NotContains(t, rec.Body.String(), `task #6`)
assert.Contains(t, rec.Body.String(), `task #7`)
assert.NotContains(t, rec.Body.String(), `task #8`)
assert.Contains(t, rec.Body.String(), `task #9`)
assert.NotContains(t, rec.Body.String(), `task #10`)
assert.NotContains(t, rec.Body.String(), `task #11`)
assert.NotContains(t, rec.Body.String(), `task #12`)
assert.NotContains(t, rec.Body.String(), `task #13`)
assert.NotContains(t, rec.Body.String(), `task #14`)
})
t.Run("end date only", func(t *testing.T) {
rec, err := testHandler.testReadAllWithUser(
url.Values{
"filter_by": []string{"end_date"},
"filter_value": []string{"2018-12-13T11:20:01+00:00"},
"filter_comparator": []string{"greater"},
},
nil,
)
assert.NoError(t, err)
// If no start date but an end date is specified, this should be null
// since we don't have any tasks in the fixtures with an end date >
// the current date.
assert.Equal(t, "[]\n", rec.Body.String())
})
})
t.Run("start date only", func(t *testing.T) {
rec, err := testHandler.testReadAllWithUser(
t.Run("invalid date", func(t *testing.T) {
_, err := testHandler.testReadAllWithUser(
url.Values{
"filter_by": []string{"start_date"},
"filter_value": []string{"2018-10-20T01:46:40+00:00"},
"filter_by": []string{"due_date"},
"filter_value": []string{"1540000000"},
"filter_comparator": []string{"greater"},
},
nil,
)
assert.NoError(t, err)
assert.NotContains(t, rec.Body.String(), `task #1`)
assert.NotContains(t, rec.Body.String(), `task #2`)
assert.NotContains(t, rec.Body.String(), `task #3`)
assert.NotContains(t, rec.Body.String(), `task #4`)
assert.NotContains(t, rec.Body.String(), `task #5`)
assert.NotContains(t, rec.Body.String(), `task #6`)
assert.Contains(t, rec.Body.String(), `task #7`)
assert.NotContains(t, rec.Body.String(), `task #8`)
assert.Contains(t, rec.Body.String(), `task #9`)
assert.NotContains(t, rec.Body.String(), `task #10`)
assert.NotContains(t, rec.Body.String(), `task #11`)
assert.NotContains(t, rec.Body.String(), `task #12`)
assert.NotContains(t, rec.Body.String(), `task #13`)
assert.NotContains(t, rec.Body.String(), `task #14`)
})
t.Run("end date only", func(t *testing.T) {
rec, err := testHandler.testReadAllWithUser(
url.Values{
"filter_by": []string{"end_date"},
"filter_value": []string{"2018-12-13T11:20:01+00:00"},
"filter_comparator": []string{"greater"},
},
nil,
)
assert.NoError(t, err)
// If no start date but an end date is specified, this should be null
// since we don't have any tasks in the fixtures with an end date >
// the current date.
assert.Equal(t, "[]\n", rec.Body.String())
assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeInvalidTaskFilterValue)
})
})
})

View File

@ -734,6 +734,34 @@ func (err ErrInvalidTaskFilterConcatinator) HTTPError() web.HTTPError {
}
}
// ErrInvalidTaskFilterValue represents an error where the provided task filter value is invalid
type ErrInvalidTaskFilterValue struct {
Value string
Field string
}
// IsErrInvalidTaskFilterValue checks if an error is ErrInvalidTaskFilterValue.
func IsErrInvalidTaskFilterValue(err error) bool {
_, ok := err.(ErrInvalidTaskFilterValue)
return ok
}
func (err ErrInvalidTaskFilterValue) Error() string {
return fmt.Sprintf("Task filter value is invalid [Value: %s, Field: %s]", err.Value, err.Field)
}
// ErrCodeInvalidTaskFilterValue holds the unique world-error code of this error
const ErrCodeInvalidTaskFilterValue = 4019
// HTTPError holds the http error description
func (err ErrInvalidTaskFilterValue) HTTPError() web.HTTPError {
return web.HTTPError{
HTTPCode: http.StatusBadRequest,
Code: ErrCodeInvalidTaskFilterValue,
Message: fmt.Sprintf("The task filter value '%s' for field '%s' is invalid.", err.Value, err.Field),
}
}
// =================
// Namespace errors
// =================

View File

@ -89,7 +89,10 @@ func getTaskFiltersByCollections(c *TaskCollection) (filters []*taskFilter, err
if len(c.FilterValue) > i {
filter.value, err = getNativeValueForTaskField(filter.field, c.FilterValue[i])
if err != nil {
return
return nil, ErrInvalidTaskFilterValue{
Value: filter.field,
Field: c.FilterValue[i],
}
}
}