diff --git a/internal/reqdata/set_test.go b/internal/reqdata/set_test.go index 0805b84..8458afa 100644 --- a/internal/reqdata/set_test.go +++ b/internal/reqdata/set_test.go @@ -1,6 +1,7 @@ package reqdata import ( + "errors" "fmt" "net/http" "net/http/httptest" @@ -8,154 +9,256 @@ import ( "reflect" "strings" "testing" + + "git.xdrm.io/go/aicra/internal/config" ) -func TestEmptyStore(t *testing.T) { - store := New(nil, nil) +func getEmptyService() *config.Service { + return &config.Service{} +} - if store.URI == nil { - t.Errorf("store 'URI' list should be initialized") - t.Fail() - } - if len(store.URI) != 0 { - t.Errorf("store 'URI' list should be empty") - t.Fail() +func getServiceWithURI(capturingBraces ...string) *config.Service { + service := &config.Service{ + Input: make(map[string]*config.Parameter), } - if store.Get == nil { - t.Errorf("store 'Get' map should be initialized") - t.Fail() + index := 0 + + for _, capture := range capturingBraces { + if len(capture) == 0 { + index++ + continue + } + + id := fmt.Sprintf("{%s}", capture) + service.Input[id] = &config.Parameter{ + Rename: capture, + Validator: func(value interface{}) (interface{}, bool) { return value, true }, + } + + service.Captures = append(service.Captures, &config.BraceCapture{ + Name: capture, + Index: index, + Ref: service.Input[id], + }) + index++ } - if store.Form == nil { - t.Errorf("store 'Form' map should be initialized") - t.Fail() + + return service +} +func getServiceWithQuery(params ...string) *config.Service { + service := &config.Service{ + Input: make(map[string]*config.Parameter), + Query: make(map[string]*config.Parameter), } - if store.Set == nil { - t.Errorf("store 'Set' map should be initialized") - t.Fail() + + for _, name := range params { + id := fmt.Sprintf("GET@%s", name) + service.Input[id] = &config.Parameter{ + Rename: name, + Validator: func(value interface{}) (interface{}, bool) { return value, true }, + } + + service.Query[name] = service.Input[id] } + + return service +} +func getServiceWithForm(params ...string) *config.Service { + service := &config.Service{ + Input: make(map[string]*config.Parameter), + Form: make(map[string]*config.Parameter), + } + + for _, name := range params { + service.Input[name] = &config.Parameter{ + Rename: name, + Validator: func(value interface{}) (interface{}, bool) { return value, true }, + } + + service.Form[name] = service.Input[name] + } + + return service } func TestStoreWithUri(t *testing.T) { - urilist := []string{"abc", "def"} - store := New(urilist, nil) - - if len(store.URI) != len(urilist) { - t.Errorf("store 'Set' should contain %d elements (got %d)", len(urilist), len(store.URI)) - t.Fail() - } - if len(store.Set) != len(urilist) { - t.Errorf("store 'Set' should contain %d elements (got %d)", len(urilist), len(store.Set)) - t.Fail() + tests := []struct { + ServiceParams []string + URI string + Err error + }{ + { + []string{}, + "/non-captured/uri", + nil, + }, + { + []string{"missing"}, + "/", + ErrMissingURIParameter, + }, + { + []string{"gotit", "missing"}, + "/gotme", + ErrMissingURIParameter, + }, + { + []string{"gotit", "gotittoo"}, + "/gotme/andme", + nil, + }, + { + []string{"gotit", "gotittoo"}, + "/gotme/andme/ignored", + nil, + }, + { + []string{"first", "", "second"}, + "/gotme/ignored/gotmetoo", + nil, + }, + { + []string{"first", "", "second"}, + "/gotme/ignored", + ErrMissingURIParameter, + }, } - for i, value := range urilist { + for i, test := range tests { + t.Run(fmt.Sprintf("test.%d", i), func(t *testing.T) { + service := getServiceWithURI(test.ServiceParams...) + store := New(service) - t.Run(fmt.Sprintf("URL#%d='%s'", i, value), func(t *testing.T) { - key := fmt.Sprintf("URL#%d", i) - element, isset := store.Set[key] - - if !isset { - t.Errorf("store should contain element with key '%s'", key) - t.Failed() + req := httptest.NewRequest(http.MethodGet, "http://host.com"+test.URI, nil) + err := store.ExtractURI(req) + if err != nil { + if test.Err != nil { + if !errors.Is(err, test.Err) { + t.Errorf("expected error <%s>, got <%s>", test.Err, err) + t.FailNow() + } + return + } + t.Errorf("unexpected error <%s>", err) + t.FailNow() } - if element.Value != value { - t.Errorf("store[%s] should return '%s' (got '%s')", key, value, element.Value) - t.Failed() + if len(store.Data) != len(service.Input) { + t.Errorf("store should contain %d elements, got %d", len(service.Input), len(store.Data)) + t.Fail() } + }) - } } -func TestStoreWithGet(t *testing.T) { - tests := []struct { - Query string +func TestExtractQuery(t *testing.T) { - InvalidNames []string - ParamNames []string - ParamValues [][]string + tests := []struct { + ServiceParam []string + Query string + Err error + + ParamNames []string + ParamValues [][]string }{ { + ServiceParam: []string{}, Query: "", - InvalidNames: []string{}, - ParamNames: []string{}, - ParamValues: [][]string{}, + Err: nil, + ParamNames: nil, + ParamValues: nil, }, { + ServiceParam: []string{"missing"}, + Query: "", + Err: ErrMissingRequiredParam, + ParamNames: nil, + ParamValues: nil, + }, + { + ServiceParam: []string{"a"}, Query: "a", - InvalidNames: []string{}, + Err: nil, ParamNames: []string{"a"}, ParamValues: [][]string{[]string{""}}, }, { + ServiceParam: []string{"a"}, Query: "a&b", - InvalidNames: []string{}, + Err: nil, + ParamNames: []string{"a"}, + ParamValues: [][]string{[]string{""}}, + }, + { + ServiceParam: []string{"a", "missing"}, + Query: "a&b", + Err: ErrMissingRequiredParam, + ParamNames: nil, + ParamValues: nil, + }, + { + ServiceParam: []string{"a", "b"}, + Query: "a&b", + Err: nil, ParamNames: []string{"a", "b"}, ParamValues: [][]string{[]string{""}, []string{""}}, }, { + ServiceParam: []string{"a"}, + Err: nil, Query: "a=", - InvalidNames: []string{}, ParamNames: []string{"a"}, ParamValues: [][]string{[]string{""}}, }, { + ServiceParam: []string{"a", "b"}, + Err: nil, Query: "a=&b=x", - InvalidNames: []string{}, ParamNames: []string{"a", "b"}, ParamValues: [][]string{[]string{""}, []string{"x"}}, }, { + ServiceParam: []string{"a", "c"}, + Err: nil, Query: "a=b&c=d", - InvalidNames: []string{}, ParamNames: []string{"a", "c"}, ParamValues: [][]string{[]string{"b"}, []string{"d"}}, }, { + ServiceParam: []string{"a", "c"}, + Err: nil, Query: "a=b&c=d&a=x", - InvalidNames: []string{}, ParamNames: []string{"a", "c"}, ParamValues: [][]string{[]string{"b", "x"}, []string{"d"}}, }, - { - Query: "a=b&_invalid=x", - InvalidNames: []string{"_invalid"}, - ParamNames: []string{"a", "_invalid"}, - ParamValues: [][]string{[]string{"b"}, []string{""}}, - }, - { - Query: "a=b&invalid_=x", - InvalidNames: []string{"invalid_"}, - ParamNames: []string{"a", "invalid_"}, - ParamValues: [][]string{[]string{"b"}, []string{""}}, - }, - { - Query: "a=b&GET@injection=x", - InvalidNames: []string{"GET@injection"}, - ParamNames: []string{"a", "GET@injection"}, - ParamValues: [][]string{[]string{"b"}, []string{""}}, - }, - { // not really useful as all after '#' should be ignored by http clients - Query: "a=b&URL#injection=x", - InvalidNames: []string{"URL#injection"}, - ParamNames: []string{"a", "URL#injection"}, - ParamValues: [][]string{[]string{"b"}, []string{""}}, - }, } for i, test := range tests { t.Run(fmt.Sprintf("request.%d", i), func(t *testing.T) { + store := New(getServiceWithQuery(test.ServiceParam...)) + req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("http://host.com?%s", test.Query), nil) - store := New(nil, req) + err := store.ExtractQuery(req) + if err != nil { + if test.Err != nil { + if !errors.Is(err, test.Err) { + t.Errorf("expected error <%s>, got <%s>", test.Err, err) + t.FailNow() + } + return + } + t.Errorf("unexpected error <%s>", err) + t.FailNow() + } if test.ParamNames == nil || test.ParamValues == nil { - if len(store.Set) != 0 { - t.Errorf("expected no GET parameters and got %d", len(store.Get)) - t.Failed() + if len(store.Data) != 0 { + t.Errorf("expected no GET parameters and got %d", len(store.Data)) + t.FailNow() } // no param to check @@ -164,47 +267,28 @@ func TestStoreWithGet(t *testing.T) { if len(test.ParamNames) != len(test.ParamValues) { t.Errorf("invalid test: names and values differ in size (%d vs %d)", len(test.ParamNames), len(test.ParamValues)) - t.Failed() + t.FailNow() } for pi, pName := range test.ParamNames { - key := fmt.Sprintf("GET@%s", pName) values := test.ParamValues[pi] - isNameValid := true - for _, invalid := range test.InvalidNames { - if pName == invalid { - isNameValid = false - } - } - - t.Run(key, func(t *testing.T) { - - param, isset := store.Set[key] + t.Run(pName, func(t *testing.T) { + param, isset := store.Data[pName] if !isset { - if isNameValid { - t.Errorf("store should contain element with key '%s'", key) - t.Failed() - } - return - } - - // if should be invalid - if isset && !isNameValid { - t.Errorf("store should NOT contain element with key '%s' (invalid name)", key) - t.Failed() + t.Errorf("param does not exist") + t.FailNow() } cast, canCast := param.Value.([]string) - if !canCast { t.Errorf("should return a []string (got '%v')", cast) - t.Failed() + t.FailNow() } if len(cast) != len(values) { t.Errorf("should return %d string(s) (got '%d')", len(values), len(cast)) - t.Failed() + t.FailNow() } for vi, value := range values { @@ -212,7 +296,7 @@ func TestStoreWithGet(t *testing.T) { t.Run(fmt.Sprintf("value.%d", vi), func(t *testing.T) { if value != cast[vi] { t.Errorf("should return '%s' (got '%s')", value, cast[vi]) - t.Failed() + t.FailNow() } }) } @@ -239,85 +323,92 @@ func TestStoreWithUrlEncodedFormParseError(t *testing.T) { req.PostForm = nil // defer req.Body.Close() - store := New(nil, req) - if len(store.Form) > 0 { - t.Errorf("expected malformed urlencoded to have failed being parsed (got %d elements)", len(store.Form)) + store := New(nil) + err := store.ExtractForm(req) + if err == nil { + t.Errorf("expected malformed urlencoded to have FailNow being parsed (got %d elements)", len(store.Data)) t.FailNow() + } } -func TestStoreWithUrlEncodedForm(t *testing.T) { +func TestExtractFormUrlEncoded(t *testing.T) { tests := []struct { - URLEncoded string + ServiceParams []string + URLEncoded string + Err error - InvalidNames []string - ParamNames []string - ParamValues [][]string + ParamNames []string + ParamValues [][]string }{ { - URLEncoded: "", - InvalidNames: []string{}, - ParamNames: []string{}, - ParamValues: [][]string{}, + ServiceParams: []string{}, + URLEncoded: "", + Err: nil, + ParamNames: nil, + ParamValues: nil, }, { - URLEncoded: "a", - InvalidNames: []string{}, - ParamNames: []string{"a"}, - ParamValues: [][]string{[]string{""}}, + ServiceParams: []string{"missing"}, + URLEncoded: "", + Err: ErrMissingRequiredParam, + ParamNames: nil, + ParamValues: nil, }, { - URLEncoded: "a&b", - InvalidNames: []string{}, - ParamNames: []string{"a", "b"}, - ParamValues: [][]string{[]string{""}, []string{""}}, + ServiceParams: []string{"a"}, + URLEncoded: "a", + Err: nil, + ParamNames: []string{"a"}, + ParamValues: [][]string{[]string{""}}, }, { - URLEncoded: "a=", - InvalidNames: []string{}, - ParamNames: []string{"a"}, - ParamValues: [][]string{[]string{""}}, + ServiceParams: []string{"a"}, + URLEncoded: "a&b", + Err: nil, + ParamNames: []string{"a"}, + ParamValues: [][]string{[]string{""}}, }, { - URLEncoded: "a=&b=x", - InvalidNames: []string{}, - ParamNames: []string{"a", "b"}, - ParamValues: [][]string{[]string{""}, []string{"x"}}, + ServiceParams: []string{"a", "missing"}, + URLEncoded: "a&b", + Err: ErrMissingRequiredParam, + ParamNames: nil, + ParamValues: nil, }, { - URLEncoded: "a=b&c=d", - InvalidNames: []string{}, - ParamNames: []string{"a", "c"}, - ParamValues: [][]string{[]string{"b"}, []string{"d"}}, + ServiceParams: []string{"a", "b"}, + URLEncoded: "a&b", + Err: nil, + ParamNames: []string{"a", "b"}, + ParamValues: [][]string{[]string{""}, []string{""}}, }, { - URLEncoded: "a=b&c=d&a=x", - InvalidNames: []string{}, - ParamNames: []string{"a", "c"}, - ParamValues: [][]string{[]string{"b", "x"}, []string{"d"}}, + ServiceParams: []string{"a"}, + Err: nil, + URLEncoded: "a=", + ParamNames: []string{"a"}, + ParamValues: [][]string{[]string{""}}, }, { - URLEncoded: "a=b&_invalid=x", - InvalidNames: []string{"_invalid"}, - ParamNames: []string{"a", "_invalid"}, - ParamValues: [][]string{[]string{"b"}, []string{""}}, + ServiceParams: []string{"a", "b"}, + Err: nil, + URLEncoded: "a=&b=x", + ParamNames: []string{"a", "b"}, + ParamValues: [][]string{[]string{""}, []string{"x"}}, }, { - URLEncoded: "a=b&invalid_=x", - InvalidNames: []string{"invalid_"}, - ParamNames: []string{"a", "invalid_"}, - ParamValues: [][]string{[]string{"b"}, []string{""}}, + ServiceParams: []string{"a", "c"}, + Err: nil, + URLEncoded: "a=b&c=d", + ParamNames: []string{"a", "c"}, + ParamValues: [][]string{[]string{"b"}, []string{"d"}}, }, { - URLEncoded: "a=b&GET@injection=x", - InvalidNames: []string{"GET@injection"}, - ParamNames: []string{"a", "GET@injection"}, - ParamValues: [][]string{[]string{"b"}, []string{""}}, - }, - { - URLEncoded: "a=b&URL#injection=x", - InvalidNames: []string{"URL#injection"}, - ParamNames: []string{"a", "URL#injection"}, - ParamValues: [][]string{[]string{"b"}, []string{""}}, + ServiceParams: []string{"a", "c"}, + Err: nil, + URLEncoded: "a=b&c=d&a=x", + ParamNames: []string{"a", "c"}, + ParamValues: [][]string{[]string{"b", "x"}, []string{"d"}}, }, } @@ -327,12 +418,25 @@ func TestStoreWithUrlEncodedForm(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "http://host.com", body) req.Header.Add("Content-Type", "application/x-www-form-urlencoded") defer req.Body.Close() - store := New(nil, req) + + store := New(getServiceWithForm(test.ServiceParams...)) + err := store.ExtractForm(req) + if err != nil { + if test.Err != nil { + if !errors.Is(err, test.Err) { + t.Errorf("expected error <%s>, got <%s>", test.Err, err) + t.FailNow() + } + return + } + t.Errorf("unexpected error <%s>", err) + t.FailNow() + } if test.ParamNames == nil || test.ParamValues == nil { - if len(store.Set) != 0 { - t.Errorf("expected no FORM parameters and got %d", len(store.Get)) - t.Failed() + if len(store.Data) != 0 { + t.Errorf("expected no GET parameters and got %d", len(store.Data)) + t.FailNow() } // no param to check @@ -341,47 +445,28 @@ func TestStoreWithUrlEncodedForm(t *testing.T) { if len(test.ParamNames) != len(test.ParamValues) { t.Errorf("invalid test: names and values differ in size (%d vs %d)", len(test.ParamNames), len(test.ParamValues)) - t.Failed() + t.FailNow() } - for pi, pName := range test.ParamNames { - key := pName + for pi, key := range test.ParamNames { values := test.ParamValues[pi] - isNameValid := true - for _, invalid := range test.InvalidNames { - if pName == invalid { - isNameValid = false - } - } - t.Run(key, func(t *testing.T) { - - param, isset := store.Set[key] + param, isset := store.Data[key] if !isset { - if isNameValid { - t.Errorf("store should contain element with key '%s'", key) - t.Failed() - } - return - } - - // if should be invalid - if isset && !isNameValid { - t.Errorf("store should NOT contain element with key '%s' (invalid name)", key) - t.Failed() + t.Errorf("param does not exist") + t.FailNow() } cast, canCast := param.Value.([]string) - if !canCast { t.Errorf("should return a []string (got '%v')", cast) - t.Failed() + t.FailNow() } if len(cast) != len(values) { t.Errorf("should return %d string(s) (got '%d')", len(values), len(cast)) - t.Failed() + t.FailNow() } for vi, value := range values { @@ -389,7 +474,7 @@ func TestStoreWithUrlEncodedForm(t *testing.T) { t.Run(fmt.Sprintf("value.%d", vi), func(t *testing.T) { if value != cast[vi] { t.Errorf("should return '%s' (got '%s')", value, cast[vi]) - t.Failed() + t.FailNow() } }) } @@ -403,109 +488,98 @@ func TestStoreWithUrlEncodedForm(t *testing.T) { func TestJsonParameters(t *testing.T) { tests := []struct { - RawJson string + ServiceParams []string + Raw string + Err error - InvalidNames []string - ParamNames []string - ParamValues []interface{} + ParamNames []string + ParamValues []interface{} }{ // no need to fully check json because it is parsed with the standard library { - RawJson: "", - InvalidNames: []string{}, - ParamNames: []string{}, - ParamValues: []interface{}{}, + ServiceParams: []string{}, + Raw: "", + Err: nil, + ParamNames: []string{}, + ParamValues: []interface{}{}, }, { - RawJson: "{}", - InvalidNames: []string{}, - ParamNames: []string{}, - ParamValues: []interface{}{}, + ServiceParams: []string{}, + Raw: "{}", + Err: nil, + ParamNames: []string{}, + ParamValues: []interface{}{}, }, { - RawJson: "{ \"a\": \"b\" }", - InvalidNames: []string{}, - ParamNames: []string{"a"}, - ParamValues: []interface{}{"b"}, + ServiceParams: []string{}, + Raw: `{ "a": "b" }`, + Err: nil, + ParamNames: []string{}, + ParamValues: []interface{}{}, }, { - RawJson: "{ \"a\": \"b\", \"c\": \"d\" }", - InvalidNames: []string{}, - ParamNames: []string{"a", "c"}, - ParamValues: []interface{}{"b", "d"}, + ServiceParams: []string{"a"}, + Raw: `{ "a": "b" }`, + Err: nil, + ParamNames: []string{"a"}, + ParamValues: []interface{}{"b"}, }, { - RawJson: "{ \"_invalid\": \"x\" }", - InvalidNames: []string{"_invalid"}, - ParamNames: []string{"_invalid"}, - ParamValues: []interface{}{nil}, + ServiceParams: []string{"a"}, + Raw: `{ "a": "b", "ignored": "d" }`, + Err: nil, + ParamNames: []string{"a"}, + ParamValues: []interface{}{"b"}, }, { - RawJson: "{ \"a\": \"b\", \"_invalid\": \"x\" }", - InvalidNames: []string{"_invalid"}, - ParamNames: []string{"a", "_invalid"}, - ParamValues: []interface{}{"b", nil}, - }, - - { - RawJson: "{ \"invalid_\": \"x\" }", - InvalidNames: []string{"invalid_"}, - ParamNames: []string{"invalid_"}, - ParamValues: []interface{}{nil}, + ServiceParams: []string{"a", "c"}, + Raw: `{ "a": "b", "c": "d" }`, + Err: nil, + ParamNames: []string{"a", "c"}, + ParamValues: []interface{}{"b", "d"}, }, { - RawJson: "{ \"a\": \"b\", \"invalid_\": \"x\" }", - InvalidNames: []string{"invalid_"}, - ParamNames: []string{"a", "invalid_"}, - ParamValues: []interface{}{"b", nil}, - }, - - { - RawJson: "{ \"GET@injection\": \"x\" }", - InvalidNames: []string{"GET@injection"}, - ParamNames: []string{"GET@injection"}, - ParamValues: []interface{}{nil}, - }, - { - RawJson: "{ \"a\": \"b\", \"GET@injection\": \"x\" }", - InvalidNames: []string{"GET@injection"}, - ParamNames: []string{"a", "GET@injection"}, - ParamValues: []interface{}{"b", nil}, - }, - - { - RawJson: "{ \"URL#injection\": \"x\" }", - InvalidNames: []string{"URL#injection"}, - ParamNames: []string{"URL#injection"}, - ParamValues: []interface{}{nil}, - }, - { - RawJson: "{ \"a\": \"b\", \"URL#injection\": \"x\" }", - InvalidNames: []string{"URL#injection"}, - ParamNames: []string{"a", "URL#injection"}, - ParamValues: []interface{}{"b", nil}, + ServiceParams: []string{"a"}, + Raw: `{ "a": null }`, + Err: nil, + ParamNames: []string{"a"}, + ParamValues: []interface{}{nil}, }, // json parse error { - RawJson: "{ \"a\": \"b\", }", - InvalidNames: []string{}, - ParamNames: []string{}, - ParamValues: []interface{}{}, + ServiceParams: []string{}, + Raw: `{ "a": "b", }`, + Err: ErrInvalidJSON, + ParamNames: []string{}, + ParamValues: []interface{}{}, }, } for i, test := range tests { t.Run(fmt.Sprintf("request.%d", i), func(t *testing.T) { - body := strings.NewReader(test.RawJson) + body := strings.NewReader(test.Raw) req := httptest.NewRequest(http.MethodPost, "http://host.com", body) req.Header.Add("Content-Type", "application/json") defer req.Body.Close() - store := New(nil, req) + store := New(getServiceWithForm(test.ServiceParams...)) + + err := store.ExtractForm(req) + if err != nil { + if test.Err != nil { + if !errors.Is(err, test.Err) { + t.Errorf("expected error <%s>, got <%s>", test.Err, err) + t.FailNow() + } + return + } + t.Errorf("unexpected error <%s>", err) + t.FailNow() + } if test.ParamNames == nil || test.ParamValues == nil { - if len(store.Set) != 0 { - t.Errorf("expected no JSON parameters and got %d", len(store.Get)) - t.Failed() + if len(store.Data) != 0 { + t.Errorf("expected no JSON parameters and got %d", len(store.Data)) + t.FailNow() } // no param to check @@ -514,37 +588,22 @@ func TestJsonParameters(t *testing.T) { if len(test.ParamNames) != len(test.ParamValues) { t.Errorf("invalid test: names and values differ in size (%d vs %d)", len(test.ParamNames), len(test.ParamValues)) - t.Failed() + t.FailNow() } for pi, pName := range test.ParamNames { key := pName value := test.ParamValues[pi] - isNameValid := true - for _, invalid := range test.InvalidNames { - if pName == invalid { - isNameValid = false - } - } - t.Run(key, func(t *testing.T) { - param, isset := store.Set[key] + param, isset := store.Data[key] if !isset { - if isNameValid { - t.Errorf("store should contain element with key '%s'", key) - t.Failed() - } + t.Errorf("store should contain element with key '%s'", key) + t.FailNow() return } - // if should be invalid - if isset && !isNameValid { - t.Errorf("store should NOT contain element with key '%s' (invalid name)", key) - t.Failed() - } - valueType := reflect.TypeOf(value) paramValue := param.Value @@ -552,12 +611,12 @@ func TestJsonParameters(t *testing.T) { if valueType != paramValueType { t.Errorf("should be of type %v (got '%v')", valueType, paramValueType) - t.Failed() + t.FailNow() } if paramValue != value { t.Errorf("should return %v (got '%v')", value, paramValue) - t.Failed() + t.FailNow() } }) @@ -570,44 +629,59 @@ func TestJsonParameters(t *testing.T) { func TestMultipartParameters(t *testing.T) { tests := []struct { - RawMultipart string + ServiceParams []string + RawMultipart string + Err error - InvalidNames []string - ParamNames []string - ParamValues []interface{} + ParamNames []string + ParamValues []interface{} }{ // no need to fully check json because it is parsed with the standard library { - RawMultipart: ``, - InvalidNames: []string{}, - ParamNames: []string{}, - ParamValues: []interface{}{}, + ServiceParams: []string{}, + RawMultipart: ``, + Err: nil, + ParamNames: []string{}, + ParamValues: []interface{}{}, }, { + ServiceParams: []string{}, RawMultipart: `--xxx `, - InvalidNames: []string{}, - ParamNames: []string{}, - ParamValues: []interface{}{}, + Err: ErrInvalidMultipart, + ParamNames: []string{}, + ParamValues: []interface{}{}, }, { + ServiceParams: []string{}, RawMultipart: `--xxx --xxx--`, - InvalidNames: []string{}, - ParamNames: []string{}, - ParamValues: []interface{}{}, + Err: ErrInvalidMultipart, + ParamNames: []string{}, + ParamValues: []interface{}{}, }, { + ServiceParams: []string{}, RawMultipart: `--xxx Content-Disposition: form-data; name="a" b --xxx--`, - InvalidNames: []string{}, - ParamNames: []string{"a"}, - ParamValues: []interface{}{"b"}, + ParamNames: []string{}, + ParamValues: []interface{}{}, }, { + ServiceParams: []string{"a"}, + RawMultipart: `--xxx +Content-Disposition: form-data; name="a" + +b +--xxx--`, + ParamNames: []string{"a"}, + ParamValues: []interface{}{"b"}, + }, + { + ServiceParams: []string{"a", "c"}, RawMultipart: `--xxx Content-Disposition: form-data; name="a" @@ -617,115 +691,24 @@ Content-Disposition: form-data; name="c" d --xxx--`, - InvalidNames: []string{}, - ParamNames: []string{"a", "c"}, - ParamValues: []interface{}{"b", "d"}, - }, - { - RawMultipart: `--xxx -Content-Disposition: form-data; name="_invalid" - -x ---xxx--`, - InvalidNames: []string{"_invalid"}, - ParamNames: []string{"_invalid"}, - ParamValues: []interface{}{nil}, + Err: nil, + ParamNames: []string{"a", "c"}, + ParamValues: []interface{}{"b", "d"}, }, { + ServiceParams: []string{"a"}, RawMultipart: `--xxx Content-Disposition: form-data; name="a" b --xxx -Content-Disposition: form-data; name="_invalid" +Content-Disposition: form-data; name="ignored" x --xxx--`, - InvalidNames: []string{"_invalid"}, - ParamNames: []string{"a", "_invalid"}, - ParamValues: []interface{}{"b", nil}, - }, - - { - RawMultipart: `--xxx -Content-Disposition: form-data; name="invalid_" - -x ---xxx--`, - InvalidNames: []string{"invalid_"}, - ParamNames: []string{"invalid_"}, - ParamValues: []interface{}{nil}, - }, - { - RawMultipart: `--xxx -Content-Disposition: form-data; name="a" - -b ---xxx -Content-Disposition: form-data; name="invalid_" - -x ---xxx--`, - InvalidNames: []string{"invalid_"}, - ParamNames: []string{"a", "invalid_"}, - ParamValues: []interface{}{"b", nil}, - }, - - { - RawMultipart: `--xxx -Content-Disposition: form-data; name="GET@injection" - -x ---xxx--`, - InvalidNames: []string{"GET@injection"}, - ParamNames: []string{"GET@injection"}, - ParamValues: []interface{}{nil}, - }, - { - RawMultipart: `--xxx -Content-Disposition: form-data; name="a" - -b ---xxx -Content-Disposition: form-data; name="GET@injection" - -x ---xxx--`, - InvalidNames: []string{"GET@injection"}, - ParamNames: []string{"a", "GET@injection"}, - ParamValues: []interface{}{"b", nil}, - }, - - { - RawMultipart: `--xxx -Content-Disposition: form-data; name="URL#injection" - -x ---xxx--`, - InvalidNames: []string{"URL#injection"}, - ParamNames: []string{"URL#injection"}, - ParamValues: []interface{}{nil}, - }, - { - RawMultipart: `--xxx -Content-Disposition: form-data; name="a" - -b ---xxx -Content-Disposition: form-data; name="URL#injection" - -x ---xxx--`, - InvalidNames: []string{"URL#injection"}, - ParamNames: []string{"a", "URL#injection"}, - ParamValues: []interface{}{"b", nil}, - }, - // json parse error - { - RawMultipart: "{ \"a\": \"b\", }", - InvalidNames: []string{}, - ParamNames: []string{}, - ParamValues: []interface{}{}, + Err: nil, + ParamNames: []string{"a"}, + ParamValues: []interface{}{"b"}, }, } @@ -735,12 +718,25 @@ x req := httptest.NewRequest(http.MethodPost, "http://host.com", body) req.Header.Add("Content-Type", "multipart/form-data; boundary=xxx") defer req.Body.Close() - store := New(nil, req) + store := New(getServiceWithForm(test.ServiceParams...)) + + err := store.ExtractForm(req) + if err != nil { + if test.Err != nil { + if !errors.Is(err, test.Err) { + t.Errorf("expected error <%s>, got <%s>", test.Err, err) + t.FailNow() + } + return + } + t.Errorf("unexpected error <%s>", err) + t.FailNow() + } if test.ParamNames == nil || test.ParamValues == nil { - if len(store.Set) != 0 { - t.Errorf("expected no JSON parameters and got %d", len(store.Get)) - t.Failed() + if len(store.Data) != 0 { + t.Errorf("expected no JSON parameters and got %d", len(store.Data)) + t.FailNow() } // no param to check @@ -749,37 +745,21 @@ x if len(test.ParamNames) != len(test.ParamValues) { t.Errorf("invalid test: names and values differ in size (%d vs %d)", len(test.ParamNames), len(test.ParamValues)) - t.Failed() + t.FailNow() } - for pi, pName := range test.ParamNames { - key := pName + for pi, key := range test.ParamNames { value := test.ParamValues[pi] - isNameValid := true - for _, invalid := range test.InvalidNames { - if pName == invalid { - isNameValid = false - } - } - t.Run(key, func(t *testing.T) { - param, isset := store.Set[key] + param, isset := store.Data[key] if !isset { - if isNameValid { - t.Errorf("store should contain element with key '%s'", key) - t.Failed() - } + t.Errorf("store should contain element with key '%s'", key) + t.FailNow() return } - // if should be invalid - if isset && !isNameValid { - t.Errorf("store should NOT contain element with key '%s' (invalid name)", key) - t.Failed() - } - valueType := reflect.TypeOf(value) paramValue := param.Value @@ -787,12 +767,12 @@ x if valueType != paramValueType { t.Errorf("should be of type %v (got '%v')", valueType, paramValueType) - t.Failed() + t.FailNow() } if paramValue != value { t.Errorf("should return %v (got '%v')", value, paramValue) - t.Failed() + t.FailNow() } })