214 lines
5.0 KiB
Go
214 lines
5.0 KiB
Go
package query
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
"net/url"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestParseList_defaults(t *testing.T) {
|
|
params, err := ParseList(url.Values{})
|
|
if err != nil {
|
|
t.Fatalf("ParseList() error = %v", err)
|
|
}
|
|
if params.Page != DefaultPage {
|
|
t.Fatalf("Page = %d, want %d", params.Page, DefaultPage)
|
|
}
|
|
if params.PageSize != DefaultPageSize {
|
|
t.Fatalf("PageSize = %d, want %d", params.PageSize, DefaultPageSize)
|
|
}
|
|
if params.Q != "" || params.Sort != "" {
|
|
t.Fatalf("Q/Sort should be empty, got q=%q sort=%q", params.Q, params.Sort)
|
|
}
|
|
if params.From != nil || params.To != nil {
|
|
t.Fatal("From/To should be nil")
|
|
}
|
|
if params.Offset() != 0 {
|
|
t.Fatalf("Offset() = %d, want 0", params.Offset())
|
|
}
|
|
if params.Limit() != DefaultPageSize {
|
|
t.Fatalf("Limit() = %d, want %d", params.Limit(), DefaultPageSize)
|
|
}
|
|
}
|
|
|
|
func TestParseList_valid(t *testing.T) {
|
|
values := url.Values{
|
|
"page": {"3"},
|
|
"page_size": {"25"},
|
|
"q": {" hello "},
|
|
"sort": {"-created_at"},
|
|
"from": {"2026-01-01"},
|
|
"to": {"2026-01-31"},
|
|
}
|
|
|
|
params, err := ParseList(values)
|
|
if err != nil {
|
|
t.Fatalf("ParseList() error = %v", err)
|
|
}
|
|
if params.Page != 3 || params.PageSize != 25 {
|
|
t.Fatalf("page/page_size = %d/%d", params.Page, params.PageSize)
|
|
}
|
|
if params.Q != "hello" || params.Sort != "-created_at" {
|
|
t.Fatalf("q/sort = %q/%q", params.Q, params.Sort)
|
|
}
|
|
if params.Offset() != 50 {
|
|
t.Fatalf("Offset() = %d, want 50", params.Offset())
|
|
}
|
|
if params.From == nil || params.To == nil {
|
|
t.Fatal("expected From/To to be set")
|
|
}
|
|
if params.From.Format(dateLayout) != "2026-01-01" {
|
|
t.Fatalf("From = %s", params.From.Format(time.RFC3339))
|
|
}
|
|
if params.To.Format(dateLayout) != "2026-01-31" {
|
|
t.Fatalf("To day = %s", params.To.Format(dateLayout))
|
|
}
|
|
}
|
|
|
|
func TestParseList_invalidPage(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
value string
|
|
}{
|
|
{"zero", "0"},
|
|
{"negative", "-1"},
|
|
{"non_numeric", "abc"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
_, err := ParseList(url.Values{"page": {tt.value}})
|
|
assertValidationError(t, err, "page")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseList_invalidPageSize(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
value string
|
|
}{
|
|
{"zero", "0"},
|
|
{"negative", "-5"},
|
|
{"too_large", "501"},
|
|
{"non_numeric", "large"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
_, err := ParseList(url.Values{"page_size": {tt.value}})
|
|
assertValidationError(t, err, "page_size")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseList_invalidDates(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
values url.Values
|
|
field string
|
|
}{
|
|
{
|
|
name: "invalid from format",
|
|
values: url.Values{"from": {"01-01-2026"}},
|
|
field: "from",
|
|
},
|
|
{
|
|
name: "invalid to format",
|
|
values: url.Values{"to": {"2026/01/31"}},
|
|
field: "to",
|
|
},
|
|
{
|
|
name: "from after to",
|
|
values: url.Values{"from": {"2026-02-01"}, "to": {"2026-01-01"}},
|
|
field: "from",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
_, err := ParseList(tt.values)
|
|
assertValidationError(t, err, tt.field)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseList_multipleErrors(t *testing.T) {
|
|
_, err := ParseList(url.Values{
|
|
"page": {"0"},
|
|
"page_size": {"501"},
|
|
"from": {"bad-date"},
|
|
})
|
|
var verr *ValidationError
|
|
if !errors.As(err, &verr) {
|
|
t.Fatalf("expected ValidationError, got %T", err)
|
|
}
|
|
if len(verr.Details) < 3 {
|
|
t.Fatalf("Details len = %d, want >= 3", len(verr.Details))
|
|
}
|
|
if verr.Code != "invalid_query_param" {
|
|
t.Fatalf("Code = %q", verr.Code)
|
|
}
|
|
}
|
|
|
|
func TestParseDate(t *testing.T) {
|
|
got, err := ParseDate("2026-05-22")
|
|
if err != nil {
|
|
t.Fatalf("ParseDate() error = %v", err)
|
|
}
|
|
if got.Location() != time.UTC {
|
|
t.Fatalf("location = %v, want UTC", got.Location())
|
|
}
|
|
if got.Format(dateLayout) != "2026-05-22" {
|
|
t.Fatalf("date = %s", got.Format(dateLayout))
|
|
}
|
|
|
|
if _, err := ParseDate("22-05-2026"); err == nil {
|
|
t.Fatal("expected error for invalid format")
|
|
}
|
|
}
|
|
|
|
func TestMeta(t *testing.T) {
|
|
params := ListParams{Page: 2, PageSize: 10}
|
|
total := int64(42)
|
|
meta := params.Meta(&total)
|
|
|
|
if meta.Page != 2 || meta.PageSize != 10 {
|
|
t.Fatalf("meta page/page_size = %d/%d", meta.Page, meta.PageSize)
|
|
}
|
|
if meta.Total == nil || *meta.Total != 42 {
|
|
t.Fatalf("meta total = %v", meta.Total)
|
|
}
|
|
}
|
|
|
|
func TestParseListRequest(t *testing.T) {
|
|
req, err := http.NewRequest(http.MethodGet, "/items?page=2&page_size=10&q=test", nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
params, err := ParseListRequest(req)
|
|
if err != nil {
|
|
t.Fatalf("ParseListRequest() error = %v", err)
|
|
}
|
|
if params.Page != 2 || params.PageSize != 10 || params.Q != "test" {
|
|
t.Fatalf("params = %+v", params)
|
|
}
|
|
}
|
|
|
|
func assertValidationError(t *testing.T, err error, field string) {
|
|
t.Helper()
|
|
var verr *ValidationError
|
|
if !errors.As(err, &verr) {
|
|
t.Fatalf("expected ValidationError, got %T: %v", err, err)
|
|
}
|
|
for _, detail := range verr.Details {
|
|
if detail.Field == field {
|
|
return
|
|
}
|
|
}
|
|
t.Fatalf("expected detail for field %q, got %+v", field, verr.Details)
|
|
}
|