mirror of
https://github.com/pocketbase/pocketbase.git
synced 2024-12-03 19:26:50 +02:00
331 lines
7.3 KiB
Go
331 lines
7.3 KiB
Go
package rest_test
|
|
|
|
import (
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/labstack/echo/v5"
|
|
"github.com/pocketbase/pocketbase/tools/rest"
|
|
"github.com/pocketbase/pocketbase/tools/search"
|
|
)
|
|
|
|
func TestSerialize(t *testing.T) {
|
|
scenarios := []struct {
|
|
name string
|
|
serializer rest.Serializer
|
|
statusCode int
|
|
data any
|
|
query string
|
|
expected string
|
|
}{
|
|
{
|
|
"empty query",
|
|
rest.Serializer{},
|
|
200,
|
|
map[string]any{"a": 1, "b": 2, "c": "test"},
|
|
"",
|
|
`{"a":1,"b":2,"c":"test"}`,
|
|
},
|
|
{
|
|
"empty fields",
|
|
rest.Serializer{},
|
|
200,
|
|
map[string]any{"a": 1, "b": 2, "c": "test"},
|
|
"fields=",
|
|
`{"a":1,"b":2,"c":"test"}`,
|
|
},
|
|
{
|
|
"missing fields",
|
|
rest.Serializer{},
|
|
200,
|
|
map[string]any{"a": 1, "b": 2, "c": "test"},
|
|
"fields=missing",
|
|
`{}`,
|
|
},
|
|
{
|
|
"non map response",
|
|
rest.Serializer{},
|
|
200,
|
|
"test",
|
|
"fields=a,b,test",
|
|
`"test"`,
|
|
},
|
|
{
|
|
"non slice of map response",
|
|
rest.Serializer{},
|
|
200,
|
|
[]any{"a", "b", "test"},
|
|
"fields=a,test",
|
|
`["a","b","test"]`,
|
|
},
|
|
{
|
|
"map with no matching field",
|
|
rest.Serializer{},
|
|
200,
|
|
map[string]any{"a": 1, "b": 2, "c": "test"},
|
|
"fields=missing", // test individual fields trim
|
|
`{}`,
|
|
},
|
|
{
|
|
">299 response",
|
|
rest.Serializer{},
|
|
300,
|
|
map[string]any{"a": 1, "b": 2, "c": "test"},
|
|
"fields=missing",
|
|
`{"a":1,"b":2,"c":"test"}`,
|
|
},
|
|
{
|
|
"<200 response",
|
|
rest.Serializer{},
|
|
199,
|
|
map[string]any{"a": 1, "b": 2, "c": "test"},
|
|
"fields=missing",
|
|
`{"a":1,"b":2,"c":"test"}`,
|
|
},
|
|
{
|
|
"map with existing and missing fields",
|
|
rest.Serializer{},
|
|
200,
|
|
map[string]any{"a": 1, "b": 2, "c": "test"},
|
|
"fields=a, c ,missing", // test individual fields trim
|
|
`{"a":1,"c":"test"}`,
|
|
},
|
|
{
|
|
"custom fields param",
|
|
rest.Serializer{FieldsParam: "custom"},
|
|
200,
|
|
map[string]any{"a": 1, "b": 2, "c": "test"},
|
|
"custom=a, c ,missing", // test individual fields trim
|
|
`{"a":1,"c":"test"}`,
|
|
},
|
|
{
|
|
"slice of maps with existing and missing fields",
|
|
rest.Serializer{},
|
|
200,
|
|
[]any{
|
|
map[string]any{"a": 11, "b": 11, "c": "test1"},
|
|
map[string]any{"a": 22, "b": 22, "c": "test2"},
|
|
},
|
|
"fields=a, c ,missing", // test individual fields trim
|
|
`[{"a":11,"c":"test1"},{"a":22,"c":"test2"}]`,
|
|
},
|
|
{
|
|
"nested fields with mixed map and any slices",
|
|
rest.Serializer{},
|
|
200,
|
|
map[string]any{
|
|
"a": 1,
|
|
"b": 2,
|
|
"c": "test",
|
|
"anySlice": []any{
|
|
map[string]any{
|
|
"A": []int{1, 2, 3},
|
|
"B": []any{"1", "2", 3},
|
|
"C": "test",
|
|
"D": map[string]any{
|
|
"DA": 1,
|
|
"DB": 2,
|
|
},
|
|
},
|
|
map[string]any{
|
|
"A": "test",
|
|
},
|
|
},
|
|
"mapSlice": []map[string]any{
|
|
{
|
|
"A": []int{1, 2, 3},
|
|
"B": []any{"1", "2", 3},
|
|
"C": "test",
|
|
"D": []any{
|
|
map[string]any{"DA": 1},
|
|
},
|
|
},
|
|
{
|
|
"B": []any{"1", "2", 3},
|
|
"D": []any{
|
|
map[string]any{"DA": 2},
|
|
map[string]any{"DA": 3},
|
|
map[string]any{"DB": 4}, // will result to empty since there is no DA
|
|
},
|
|
},
|
|
},
|
|
"fullMap": []map[string]any{
|
|
{
|
|
"A": []int{1, 2, 3},
|
|
"B": []any{"1", "2", 3},
|
|
"C": "test",
|
|
},
|
|
{
|
|
"B": []any{"1", "2", 3},
|
|
"D": []any{
|
|
map[string]any{"DA": 2},
|
|
map[string]any{"DA": 3}, // will result to empty since there is no DA
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"fields=a, c, anySlice.A, mapSlice.C, mapSlice.D.DA, anySlice.D,fullMap",
|
|
`{"a":1,"anySlice":[{"A":[1,2,3],"D":{"DA":1,"DB":2}},{"A":"test"}],"c":"test","fullMap":[{"A":[1,2,3],"B":["1","2",3],"C":"test"},{"B":["1","2",3],"D":[{"DA":2},{"DA":3}]}],"mapSlice":[{"C":"test","D":[{"DA":1}]},{"D":[{"DA":2},{"DA":3},{}]}]}`,
|
|
},
|
|
{
|
|
"SearchResult",
|
|
rest.Serializer{},
|
|
200,
|
|
search.Result{
|
|
Page: 1,
|
|
PerPage: 10,
|
|
TotalItems: 20,
|
|
TotalPages: 30,
|
|
Items: []any{
|
|
map[string]any{"a": 11, "b": 11, "c": "test1"},
|
|
map[string]any{"a": 22, "b": 22, "c": "test2"},
|
|
},
|
|
},
|
|
"fields=a,c,missing",
|
|
`{"items":[{"a":11,"c":"test1"},{"a":22,"c":"test2"}],"page":1,"perPage":10,"totalItems":20,"totalPages":30}`,
|
|
},
|
|
{
|
|
"*SearchResult",
|
|
rest.Serializer{},
|
|
200,
|
|
&search.Result{
|
|
Page: 1,
|
|
PerPage: 10,
|
|
TotalItems: 20,
|
|
TotalPages: 30,
|
|
Items: []any{
|
|
map[string]any{"a": 11, "b": 11, "c": "test1"},
|
|
map[string]any{"a": 22, "b": 22, "c": "test2"},
|
|
},
|
|
},
|
|
"fields=a,c",
|
|
`{"items":[{"a":11,"c":"test1"},{"a":22,"c":"test2"}],"page":1,"perPage":10,"totalItems":20,"totalPages":30}`,
|
|
},
|
|
{
|
|
"root wildcard",
|
|
rest.Serializer{},
|
|
200,
|
|
&search.Result{
|
|
Page: 1,
|
|
PerPage: 10,
|
|
TotalItems: 20,
|
|
TotalPages: 30,
|
|
Items: []any{
|
|
map[string]any{"a": 11, "b": 11, "c": "test1"},
|
|
map[string]any{"a": 22, "b": 22, "c": "test2"},
|
|
},
|
|
},
|
|
"fields=*",
|
|
`{"items":[{"a":11,"b":11,"c":"test1"},{"a":22,"b":22,"c":"test2"}],"page":1,"perPage":10,"totalItems":20,"totalPages":30}`,
|
|
},
|
|
{
|
|
"root wildcard with nested exception",
|
|
rest.Serializer{},
|
|
200,
|
|
map[string]any{
|
|
"id": "123",
|
|
"title": "lorem",
|
|
"rel": map[string]any{
|
|
"id": "456",
|
|
"title": "rel_title",
|
|
},
|
|
},
|
|
"fields=*,rel.id",
|
|
`{"id":"123","rel":{"id":"456"},"title":"lorem"}`,
|
|
},
|
|
{
|
|
"sub wildcard",
|
|
rest.Serializer{},
|
|
200,
|
|
map[string]any{
|
|
"id": "123",
|
|
"title": "lorem",
|
|
"rel": map[string]any{
|
|
"id": "456",
|
|
"title": "rel_title",
|
|
"sub": map[string]any{
|
|
"id": "789",
|
|
"title": "sub_title",
|
|
},
|
|
},
|
|
},
|
|
"fields=id,rel.*",
|
|
`{"id":"123","rel":{"id":"456","sub":{"id":"789","title":"sub_title"},"title":"rel_title"}}`,
|
|
},
|
|
{
|
|
"sub wildcard with nested exception",
|
|
rest.Serializer{},
|
|
200,
|
|
map[string]any{
|
|
"id": "123",
|
|
"title": "lorem",
|
|
"rel": map[string]any{
|
|
"id": "456",
|
|
"title": "rel_title",
|
|
"sub": map[string]any{
|
|
"id": "789",
|
|
"title": "sub_title",
|
|
},
|
|
},
|
|
},
|
|
"fields=id,rel.*,rel.sub.id",
|
|
`{"id":"123","rel":{"id":"456","sub":{"id":"789"},"title":"rel_title"}}`,
|
|
},
|
|
{
|
|
"invalid excerpt modifier",
|
|
rest.Serializer{},
|
|
400,
|
|
map[string]any{"a": 1, "b": 2, "c": "test"},
|
|
"fields=*:excerpt",
|
|
`{"a":1,"b":2,"c":"test"}`,
|
|
},
|
|
{
|
|
"valid excerpt modifier",
|
|
rest.Serializer{},
|
|
200,
|
|
map[string]any{
|
|
"id": "123",
|
|
"title": "lorem",
|
|
"rel": map[string]any{
|
|
"id": "456",
|
|
"title": "<p>rel_title</p>",
|
|
"sub": map[string]any{
|
|
"id": "789",
|
|
"title": "sub_title",
|
|
},
|
|
},
|
|
},
|
|
"fields=*:excerpt(2),rel.title:excerpt(3, true)",
|
|
`{"id":"12","rel":{"title":"rel..."},"title":"lo"}`,
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.name, func(t *testing.T) {
|
|
req := httptest.NewRequest(http.MethodPost, "/", nil)
|
|
req.URL.RawQuery = s.query
|
|
rec := httptest.NewRecorder()
|
|
|
|
e := echo.New()
|
|
c := e.NewContext(req, rec)
|
|
c.Response().Status = s.statusCode
|
|
|
|
if err := s.serializer.Serialize(c, s.data, ""); err != nil {
|
|
t.Fatalf("Serialize failure: %v", err)
|
|
}
|
|
|
|
rawBody, err := io.ReadAll(rec.Result().Body)
|
|
if err != nil {
|
|
t.Fatalf("Failed to read request body: %v", err)
|
|
}
|
|
|
|
if v := strings.TrimSpace(string(rawBody)); v != s.expected {
|
|
t.Fatalf("Expected body\n%v \ngot: \n%v", s.expected, v)
|
|
}
|
|
})
|
|
}
|
|
}
|