Compare commits
No commits in common. "0126a551b3858d1e97159c7e8761f56263994bbc" and "a173ab6c4c8ebee102658b34ae1fa26caf80fac1" have entirely different histories.
0126a551b3
...
a173ab6c4c
|
@ -16,14 +16,6 @@ func (err Error) Wrap(e error) *WrapError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WrapString returns a new error which wraps a new error created from a string.
|
|
||||||
func (err Error) WrapString(e string) *WrapError {
|
|
||||||
return &WrapError{
|
|
||||||
base: err,
|
|
||||||
wrap: Error(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WrapError is way to wrap errors recursively.
|
// WrapError is way to wrap errors recursively.
|
||||||
type WrapError struct {
|
type WrapError struct {
|
||||||
base error
|
base error
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
package cerr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestConstError(t *testing.T) {
|
|
||||||
const cerr1 = Error("some-string")
|
|
||||||
const cerr2 = Error("some-other-string")
|
|
||||||
const cerr3 = Error("some-string") // same const value as @cerr1
|
|
||||||
|
|
||||||
if cerr1.Error() == cerr2.Error() {
|
|
||||||
t.Errorf("cerr1 should not be equal to cerr2 ('%s', '%s')", cerr1.Error(), cerr2.Error())
|
|
||||||
}
|
|
||||||
if cerr2.Error() == cerr3.Error() {
|
|
||||||
t.Errorf("cerr2 should not be equal to cerr3 ('%s', '%s')", cerr2.Error(), cerr3.Error())
|
|
||||||
}
|
|
||||||
if cerr1.Error() != cerr3.Error() {
|
|
||||||
t.Errorf("cerr1 should be equal to cerr3 ('%s', '%s')", cerr1.Error(), cerr3.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWrappedConstError(t *testing.T) {
|
|
||||||
const parent = Error("file error")
|
|
||||||
|
|
||||||
const readErrorConst = Error("cannot read file")
|
|
||||||
var wrappedReadError = parent.Wrap(readErrorConst)
|
|
||||||
|
|
||||||
expectedWrappedReadError := fmt.Sprintf("%s: %s", parent.Error(), readErrorConst.Error())
|
|
||||||
if wrappedReadError.Error() != expectedWrappedReadError {
|
|
||||||
t.Errorf("expected '%s' (got '%s')", wrappedReadError.Error(), expectedWrappedReadError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func TestWrappedStandardError(t *testing.T) {
|
|
||||||
const parent = Error("file error")
|
|
||||||
|
|
||||||
var writeErrorStandard error = errors.New("cannot write file")
|
|
||||||
var wrappedWriteError = parent.Wrap(writeErrorStandard)
|
|
||||||
|
|
||||||
expectedWrappedWriteError := fmt.Sprintf("%s: %s", parent.Error(), writeErrorStandard.Error())
|
|
||||||
if wrappedWriteError.Error() != expectedWrappedWriteError {
|
|
||||||
t.Errorf("expected '%s' (got '%s')", wrappedWriteError.Error(), expectedWrappedWriteError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func TestWrappedStringError(t *testing.T) {
|
|
||||||
const parent = Error("file error")
|
|
||||||
|
|
||||||
var closeErrorString string = "cannot close file"
|
|
||||||
var wrappedCloseError = parent.WrapString(closeErrorString)
|
|
||||||
|
|
||||||
expectedWrappedCloseError := fmt.Sprintf("%s: %s", parent.Error(), closeErrorString)
|
|
||||||
if wrappedCloseError.Error() != expectedWrappedCloseError {
|
|
||||||
t.Errorf("expected '%s' (got '%s')", wrappedCloseError.Error(), expectedWrappedCloseError)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,636 +0,0 @@
|
||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestLegalServiceName(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
Raw string
|
|
||||||
Error error
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
`{
|
|
||||||
"/": {
|
|
||||||
"invalid/service-name": {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
ErrFormat.Wrap(ErrIllegalServiceName.WrapString("invalid/service-name")),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`{
|
|
||||||
"/": {
|
|
||||||
"invalid/service/name": {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
ErrFormat.Wrap(ErrIllegalServiceName.WrapString("invalid/service/name")),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`{
|
|
||||||
"/": {
|
|
||||||
"invalid-service-name": {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
ErrFormat.Wrap(ErrIllegalServiceName.WrapString("invalid-service-name")),
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
`{
|
|
||||||
"/": {
|
|
||||||
"valid.service_name": {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, test := range tests {
|
|
||||||
|
|
||||||
t.Run(fmt.Sprintf("service.%d", i), func(t *testing.T) {
|
|
||||||
_, err := Parse(strings.NewReader(test.Raw))
|
|
||||||
|
|
||||||
if err == nil && test.Error != nil {
|
|
||||||
t.Errorf("expected an error: '%s'", test.Error.Error())
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
if err != nil && test.Error == nil {
|
|
||||||
t.Errorf("unexpected error: '%s'", err.Error())
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil && test.Error != nil {
|
|
||||||
if err.Error() != test.Error.Error() {
|
|
||||||
t.Errorf("expected the error '%s' (got '%s')", test.Error.Error(), err.Error())
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func TestAvailableMethods(t *testing.T) {
|
|
||||||
reader := strings.NewReader(`{
|
|
||||||
"GET": { "info": "info" },
|
|
||||||
"POST": { "info": "info" },
|
|
||||||
"PUT": { "info": "info" },
|
|
||||||
"DELETE": { "info": "info" }
|
|
||||||
}`)
|
|
||||||
srv, err := Parse(reader)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error (got '%s')", err)
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
if srv.Method(http.MethodGet) == nil {
|
|
||||||
t.Errorf("expected method GET to be available")
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
if srv.Method(http.MethodPost) == nil {
|
|
||||||
t.Errorf("expected method POST to be available")
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
if srv.Method(http.MethodPut) == nil {
|
|
||||||
t.Errorf("expected method PUT to be available")
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
if srv.Method(http.MethodDelete) == nil {
|
|
||||||
t.Errorf("expected method DELETE to be available")
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
if srv.Method(http.MethodPatch) != nil {
|
|
||||||
t.Errorf("expected method PATH to be UNavailable")
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func TestParseEmpty(t *testing.T) {
|
|
||||||
reader := strings.NewReader(`{}`)
|
|
||||||
_, err := Parse(reader)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error (got '%s')", err)
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func TestParseJsonError(t *testing.T) {
|
|
||||||
reader := strings.NewReader(`{
|
|
||||||
"GET": {
|
|
||||||
"info": "info
|
|
||||||
},
|
|
||||||
}`) // trailing ',' is invalid JSON
|
|
||||||
_, err := Parse(reader)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("expected error")
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseMissingMethodDescription(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
Raw string
|
|
||||||
Error error
|
|
||||||
}{
|
|
||||||
{ // missing description
|
|
||||||
`{
|
|
||||||
"GET": {
|
|
||||||
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
ErrFormat.Wrap(ErrMissingMethodDesc.WrapString("GET /")),
|
|
||||||
},
|
|
||||||
{ // missing description
|
|
||||||
`{
|
|
||||||
"/": {
|
|
||||||
"subservice": {
|
|
||||||
"GET": {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
ErrFormat.Wrap(ErrMissingMethodDesc.WrapString("GET subservice")),
|
|
||||||
},
|
|
||||||
{ // empty description
|
|
||||||
`{
|
|
||||||
"GET": {
|
|
||||||
"info": ""
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
ErrFormat.Wrap(ErrMissingMethodDesc.WrapString("GET /")),
|
|
||||||
},
|
|
||||||
{ // valid description
|
|
||||||
`{
|
|
||||||
"GET": {
|
|
||||||
"info": "a"
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
{ // valid description
|
|
||||||
`{
|
|
||||||
"GET": {
|
|
||||||
"info": "some description"
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, test := range tests {
|
|
||||||
|
|
||||||
t.Run(fmt.Sprintf("method.%d", i), func(t *testing.T) {
|
|
||||||
_, err := Parse(strings.NewReader(test.Raw))
|
|
||||||
|
|
||||||
if err == nil && test.Error != nil {
|
|
||||||
t.Errorf("expected an error: '%s'", test.Error.Error())
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
if err != nil && test.Error == nil {
|
|
||||||
t.Errorf("unexpected error: '%s'", err.Error())
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil && test.Error != nil {
|
|
||||||
if err.Error() != test.Error.Error() {
|
|
||||||
t.Errorf("expected the error '%s' (got '%s')", test.Error.Error(), err.Error())
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParamEmptyRenameNoRename(t *testing.T) {
|
|
||||||
reader := strings.NewReader(`{
|
|
||||||
"GET": {
|
|
||||||
"info": "info",
|
|
||||||
"in": {
|
|
||||||
"original": { "info": "valid-desc", "type": "valid-type", "name": "" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`)
|
|
||||||
srv, err := Parse(reader)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error: '%s'", err)
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
method := srv.Method(http.MethodGet)
|
|
||||||
if method == nil {
|
|
||||||
t.Errorf("expected GET method not to be nil")
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
for _, param := range method.Parameters {
|
|
||||||
|
|
||||||
if param.Rename != "original" {
|
|
||||||
t.Errorf("expected the parameter 'original' not to be renamed to '%s'", param.Rename)
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
func TestOptionalParam(t *testing.T) {
|
|
||||||
reader := strings.NewReader(`{
|
|
||||||
"GET": {
|
|
||||||
"info": "info",
|
|
||||||
"in": {
|
|
||||||
"optional": { "info": "valid-desc", "type": "?optional-type" },
|
|
||||||
"required": { "info": "valid-desc", "type": "required-type" },
|
|
||||||
"required2": { "info": "valid-desc", "type": "a" },
|
|
||||||
"optional2": { "info": "valid-desc", "type": "?a" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`)
|
|
||||||
srv, err := Parse(reader)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error: '%s'", err)
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
method := srv.Method(http.MethodGet)
|
|
||||||
if method == nil {
|
|
||||||
t.Errorf("expected GET method not to be nil")
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
for pName, param := range method.Parameters {
|
|
||||||
|
|
||||||
if pName == "optional" || pName == "optional2" {
|
|
||||||
if !param.Optional {
|
|
||||||
t.Errorf("expected parameter '%s' to be optional", pName)
|
|
||||||
t.Failed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if pName == "required" || pName == "required2" {
|
|
||||||
if param.Optional {
|
|
||||||
t.Errorf("expected parameter '%s' to be required", pName)
|
|
||||||
t.Failed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
func TestParseParameters(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
Raw string
|
|
||||||
Error error
|
|
||||||
ErrorAlternative error
|
|
||||||
}{
|
|
||||||
{ // invalid param name prefix
|
|
||||||
`{
|
|
||||||
"GET": {
|
|
||||||
"info": "info",
|
|
||||||
"in": {
|
|
||||||
"_param1": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
ErrFormat.Wrap(ErrIllegalParamName.WrapString("GET / {_param1}")),
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
{ // invalid param name suffix
|
|
||||||
`{
|
|
||||||
"GET": {
|
|
||||||
"info": "info",
|
|
||||||
"in": {
|
|
||||||
"param1_": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
ErrFormat.Wrap(ErrIllegalParamName.WrapString("GET / {param1_}")),
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
|
|
||||||
{ // missing param description
|
|
||||||
`{
|
|
||||||
"GET": {
|
|
||||||
"info": "info",
|
|
||||||
"in": {
|
|
||||||
"param1": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
ErrFormat.Wrap(ErrMissingParamDesc.WrapString("GET / {param1}")),
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
{ // empty param description
|
|
||||||
`{
|
|
||||||
"GET": {
|
|
||||||
"info": "info",
|
|
||||||
"in": {
|
|
||||||
"param1": {
|
|
||||||
"info": ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
ErrFormat.Wrap(ErrMissingParamDesc.WrapString("GET / {param1}")),
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
|
|
||||||
{ // missing param type
|
|
||||||
`{
|
|
||||||
"GET": {
|
|
||||||
"info": "info",
|
|
||||||
"in": {
|
|
||||||
"param1": {
|
|
||||||
"info": "valid"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
ErrFormat.Wrap(ErrMissingParamType.WrapString("GET / {param1}")),
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
{ // empty param type
|
|
||||||
`{
|
|
||||||
"GET": {
|
|
||||||
"info": "info",
|
|
||||||
"in": {
|
|
||||||
"param1": {
|
|
||||||
"info": "valid",
|
|
||||||
"type": ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
ErrFormat.Wrap(ErrMissingParamType.WrapString("GET / {param1}")),
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
{ // invalid type (optional mark only)
|
|
||||||
`{
|
|
||||||
"GET": {
|
|
||||||
"info": "info",
|
|
||||||
"in": {
|
|
||||||
"param1": {
|
|
||||||
"info": "valid",
|
|
||||||
"type": "?"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
|
|
||||||
ErrFormat.Wrap(ErrMissingParamType.WrapString("GET / {param1}")),
|
|
||||||
ErrFormat.Wrap(ErrMissingParamType.WrapString("GET / {param1}")),
|
|
||||||
},
|
|
||||||
{ // valid description + valid type
|
|
||||||
`{
|
|
||||||
"GET": {
|
|
||||||
"info": "info",
|
|
||||||
"in": {
|
|
||||||
"param1": {
|
|
||||||
"info": "valid",
|
|
||||||
"type": "a"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
nil,
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
{ // valid description + valid OPTIONAL type
|
|
||||||
`{
|
|
||||||
"GET": {
|
|
||||||
"info": "info",
|
|
||||||
"in": {
|
|
||||||
"param1": {
|
|
||||||
"info": "valid",
|
|
||||||
"type": "?valid"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
nil,
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
|
|
||||||
{ // name conflict with rename
|
|
||||||
`{
|
|
||||||
"GET": {
|
|
||||||
"info": "info",
|
|
||||||
"in": {
|
|
||||||
"param1": { "info": "valid", "type": "valid" },
|
|
||||||
"param2": { "info": "valid", "type": "valid", "name": "param1" }
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
// 2 possible errors as map order is not deterministic
|
|
||||||
ErrFormat.Wrap(ErrParamNameConflict.WrapString("GET / {param1}")),
|
|
||||||
ErrFormat.Wrap(ErrParamNameConflict.WrapString("GET / {param2}")),
|
|
||||||
},
|
|
||||||
{ // rename conflict with name
|
|
||||||
`{
|
|
||||||
"GET": {
|
|
||||||
"info": "info",
|
|
||||||
"in": {
|
|
||||||
"param1": { "info": "valid", "type": "valid", "name": "param2" },
|
|
||||||
"param2": { "info": "valid", "type": "valid" }
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
// 2 possible errors as map order is not deterministic
|
|
||||||
ErrFormat.Wrap(ErrParamNameConflict.WrapString("GET / {param1}")),
|
|
||||||
ErrFormat.Wrap(ErrParamNameConflict.WrapString("GET / {param2}")),
|
|
||||||
},
|
|
||||||
{ // rename conflict with rename
|
|
||||||
`{
|
|
||||||
"GET": {
|
|
||||||
"info": "info",
|
|
||||||
"in": {
|
|
||||||
"param1": { "info": "valid", "type": "valid", "name": "conflict" },
|
|
||||||
"param2": { "info": "valid", "type": "valid", "name": "conflict" }
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
// 2 possible errors as map order is not deterministic
|
|
||||||
ErrFormat.Wrap(ErrParamNameConflict.WrapString("GET / {param1}")),
|
|
||||||
ErrFormat.Wrap(ErrParamNameConflict.WrapString("GET / {param2}")),
|
|
||||||
},
|
|
||||||
|
|
||||||
{ // both renamed with no conflict
|
|
||||||
`{
|
|
||||||
"GET": {
|
|
||||||
"info": "info",
|
|
||||||
"in": {
|
|
||||||
"param1": { "info": "valid", "type": "valid", "name": "freename" },
|
|
||||||
"param2": { "info": "valid", "type": "valid", "name": "freename2" }
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
nil,
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, test := range tests {
|
|
||||||
|
|
||||||
t.Run(fmt.Sprintf("method.%d", i), func(t *testing.T) {
|
|
||||||
_, err := Parse(strings.NewReader(test.Raw))
|
|
||||||
|
|
||||||
if err == nil && test.Error != nil {
|
|
||||||
t.Errorf("expected an error: '%s'", test.Error.Error())
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
if err != nil && test.Error == nil {
|
|
||||||
t.Errorf("unexpected error: '%s'", err.Error())
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil && test.Error != nil {
|
|
||||||
if err.Error() != test.Error.Error() && err.Error() != test.ErrorAlternative.Error() {
|
|
||||||
t.Errorf("got the error: '%s'", err.Error())
|
|
||||||
t.Errorf("expected error (alternative 1): '%s'", test.Error.Error())
|
|
||||||
t.Errorf("expected error (alternative 2): '%s'", test.ErrorAlternative.Error())
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBrowseSimple(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
Raw string
|
|
||||||
Path []string
|
|
||||||
BrowseDepth int
|
|
||||||
ValidDepth bool
|
|
||||||
}{
|
|
||||||
{ // false positive -1
|
|
||||||
`{
|
|
||||||
"/" : {
|
|
||||||
"parent": {
|
|
||||||
"/": {
|
|
||||||
"subdir": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
[]string{"parent", "subdir"},
|
|
||||||
1,
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{ // false positive +1
|
|
||||||
`{
|
|
||||||
"/" : {
|
|
||||||
"parent": {
|
|
||||||
"/": {
|
|
||||||
"subdir": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
[]string{"parent", "subdir"},
|
|
||||||
3,
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
`{
|
|
||||||
"/" : {
|
|
||||||
"parent": {
|
|
||||||
"/": {
|
|
||||||
"subdir": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
[]string{"parent", "subdir"},
|
|
||||||
2,
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{ // unknown path
|
|
||||||
`{
|
|
||||||
"/" : {
|
|
||||||
"parent": {
|
|
||||||
"/": {
|
|
||||||
"subdir": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
[]string{"x", "y"},
|
|
||||||
2,
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{ // unknown path
|
|
||||||
`{
|
|
||||||
"/" : {
|
|
||||||
"parent": {
|
|
||||||
"/": {
|
|
||||||
"subdir": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
[]string{"parent", "y"},
|
|
||||||
1,
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{ // Warning: this case is important to understand the precedence of service paths over
|
|
||||||
// the value of some variables. Here if we send a string parameter in the GET method that
|
|
||||||
// unfortunately is equal to 'subdir', it will call the sub-service /parent/subdir' instead
|
|
||||||
// of the service /parent with its parameter set to the value 'subdir'.
|
|
||||||
`{
|
|
||||||
"/" : {
|
|
||||||
"parent": {
|
|
||||||
"/": {
|
|
||||||
"subdir": {}
|
|
||||||
},
|
|
||||||
"GET": {
|
|
||||||
"info": "valid-desc",
|
|
||||||
"in": {
|
|
||||||
"some-value": {
|
|
||||||
"info": "valid-desc",
|
|
||||||
"type": "valid-type"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
[]string{"parent", "subdir"},
|
|
||||||
2,
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, test := range tests {
|
|
||||||
|
|
||||||
t.Run(fmt.Sprintf("method.%d", i), func(t *testing.T) {
|
|
||||||
srv, err := Parse(strings.NewReader(test.Raw))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error: '%s'", err)
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
_, depth := srv.Browse(test.Path)
|
|
||||||
if test.ValidDepth {
|
|
||||||
if depth != test.BrowseDepth {
|
|
||||||
t.Errorf("expected a depth of %d (got %d)", test.BrowseDepth, depth)
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if depth == test.BrowseDepth {
|
|
||||||
t.Errorf("expected a depth NOT %d (got %d)", test.BrowseDepth, depth)
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
package config
|
|
||||||
|
|
||||||
import "git.xdrm.io/go/aicra/internal/cerr"
|
|
||||||
|
|
||||||
// ErrRead - a problem ocurred when trying to read the configuration file
|
|
||||||
const ErrRead = cerr.Error("cannot read config")
|
|
||||||
|
|
||||||
// ErrFormat - a invalid format has been detected
|
|
||||||
const ErrFormat = cerr.Error("invalid config format")
|
|
||||||
|
|
||||||
// ErrIllegalServiceName - an illegal character has been found in a service name
|
|
||||||
const ErrIllegalServiceName = cerr.Error("service must not contain any slash '/' nor '-' symbols")
|
|
||||||
|
|
||||||
// ErrMissingMethodDesc - a method is missing its description
|
|
||||||
const ErrMissingMethodDesc = cerr.Error("missing method description")
|
|
||||||
|
|
||||||
// ErrMissingParamDesc - a parameter is missing its description
|
|
||||||
const ErrMissingParamDesc = cerr.Error("missing parameter description")
|
|
||||||
|
|
||||||
// ErrIllegalParamName - a parameter has an illegal name
|
|
||||||
const ErrIllegalParamName = cerr.Error("illegal parameter name (must not begin/end with '_')")
|
|
||||||
|
|
||||||
// ErrMissingParamType - a parameter has an illegal type
|
|
||||||
const ErrMissingParamType = cerr.Error("missing parameter type")
|
|
||||||
|
|
||||||
// ErrParamNameConflict - a parameter has a conflict with its name/rename field
|
|
||||||
const ErrParamNameConflict = cerr.Error("name conflict for parameter")
|
|
|
@ -1,6 +1,7 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -9,7 +10,7 @@ func (methodDef *Method) checkAndFormat(servicePath string, httpMethod string) e
|
||||||
|
|
||||||
// 1. fail on missing description
|
// 1. fail on missing description
|
||||||
if len(methodDef.Description) < 1 {
|
if len(methodDef.Description) < 1 {
|
||||||
return ErrMissingMethodDesc.WrapString(httpMethod + " " + servicePath)
|
return fmt.Errorf("missing %s.%s description", servicePath, httpMethod)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. stop if no parameter
|
// 2. stop if no parameter
|
||||||
|
@ -21,16 +22,16 @@ func (methodDef *Method) checkAndFormat(servicePath string, httpMethod string) e
|
||||||
// 3. for each parameter
|
// 3. for each parameter
|
||||||
for pName, pData := range methodDef.Parameters {
|
for pName, pData := range methodDef.Parameters {
|
||||||
|
|
||||||
// 3.1. check name
|
// check name
|
||||||
if strings.Trim(pName, "_") != pName {
|
if strings.Trim(pName, "_") != pName {
|
||||||
return ErrIllegalParamName.WrapString(httpMethod + " " + servicePath + " {" + pName + "}")
|
return fmt.Errorf("invalid name '%s' must not begin/end with '_'", pName)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(pData.Rename) < 1 {
|
if len(pData.Rename) < 1 {
|
||||||
pData.Rename = pName
|
pData.Rename = pName
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3.2. Check for name/rename conflict
|
// 5. Check for name/rename conflict
|
||||||
for paramName, param := range methodDef.Parameters {
|
for paramName, param := range methodDef.Parameters {
|
||||||
|
|
||||||
// ignore self
|
// ignore self
|
||||||
|
@ -38,26 +39,39 @@ func (methodDef *Method) checkAndFormat(servicePath string, httpMethod string) e
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3.2.1. Same rename field
|
// 1. Same rename field
|
||||||
// 3.2.2. Not-renamed field matches a renamed field
|
if pData.Rename == param.Rename {
|
||||||
// 3.2.3. Renamed field matches name
|
return fmt.Errorf("rename conflict for %s.%s parameter '%s'", servicePath, httpMethod, pData.Rename)
|
||||||
if pData.Rename == param.Rename || pName == param.Rename || pData.Rename == paramName {
|
}
|
||||||
return ErrParamNameConflict.WrapString(httpMethod + " " + servicePath + " {" + pName + "}")
|
|
||||||
|
// 2. Not-renamed field matches a renamed field
|
||||||
|
if pName == param.Rename {
|
||||||
|
return fmt.Errorf("name conflict for %s.%s parameter '%s'", servicePath, httpMethod, pName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Renamed field matches name
|
||||||
|
if pData.Rename == paramName {
|
||||||
|
return fmt.Errorf("name conflict for %s.%s parameter '%s'", servicePath, httpMethod, pName)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3.3. Fail on missing description
|
// 6. Manage invalid type
|
||||||
|
if len(pData.Type) < 1 {
|
||||||
|
return fmt.Errorf("invalid type for %s.%s parameter '%s'", servicePath, httpMethod, pName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Fail on missing description
|
||||||
if len(pData.Description) < 1 {
|
if len(pData.Description) < 1 {
|
||||||
return ErrMissingParamDesc.WrapString(httpMethod + " " + servicePath + " {" + pName + "}")
|
return fmt.Errorf("missing description for %s.%s parameter '%s'", servicePath, httpMethod, pName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3.4. Manage invalid type
|
// 8. Fail on missing type
|
||||||
if len(pData.Type) < 1 || pData.Type == "?" {
|
if len(pData.Type) < 1 {
|
||||||
return ErrMissingParamType.WrapString(httpMethod + " " + servicePath + " {" + pName + "}")
|
return fmt.Errorf("missing type for %s.%s parameter '%s'", servicePath, httpMethod, pName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3.5. Set optional + type
|
// 9. Set optional + type
|
||||||
if pData.Type[0] == '?' {
|
if pData.Type[0] == '?' {
|
||||||
pData.Optional = true
|
pData.Optional = true
|
||||||
pData.Type = pData.Type[1:]
|
pData.Type = pData.Type[1:]
|
||||||
|
@ -67,3 +81,13 @@ func (methodDef *Method) checkAndFormat(servicePath string, httpMethod string) e
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// scopeHasPermission returns whether the permission fulfills a given scope
|
||||||
|
func scopeHasPermission(permission string, scope []string) bool {
|
||||||
|
for _, s := range scope {
|
||||||
|
if permission == s {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
|
@ -2,11 +2,20 @@ package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"git.xdrm.io/go/aicra/internal/cerr"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ErrRead - a problem ocurred when trying to read the configuration file
|
||||||
|
const ErrRead = cerr.Error("cannot read config")
|
||||||
|
|
||||||
|
// ErrFormat - a invalid format has been detected
|
||||||
|
const ErrFormat = cerr.Error("invalid config format")
|
||||||
|
|
||||||
// Parse builds a service from a json reader and checks for most format errors.
|
// Parse builds a service from a json reader and checks for most format errors.
|
||||||
func Parse(r io.Reader) (*Service, error) {
|
func Parse(r io.Reader) (*Service, error) {
|
||||||
receiver := &Service{}
|
receiver := &Service{}
|
||||||
|
@ -78,20 +87,20 @@ func (svc *Service) checkAndFormat(servicePath string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. stop if no child */
|
// 1. stop if no child */
|
||||||
if svc.Children == nil || len(svc.Children) < 1 {
|
if svc.Children == nil || len(svc.Children) < 1 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. for each service */
|
// 2. for each service */
|
||||||
for childService, ctl := range svc.Children {
|
for childService, ctl := range svc.Children {
|
||||||
|
|
||||||
// 3.1. invalid name */
|
// 3. invalid name */
|
||||||
if strings.ContainsAny(childService, "/-") {
|
if strings.ContainsAny(childService, "/-") {
|
||||||
return ErrIllegalServiceName.WrapString(childService)
|
return fmt.Errorf("service '%s' must not contain any slash '/' nor '-' symbols", childService)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3.2. check recursively */
|
// 4. check recursively */
|
||||||
err := ctl.checkAndFormat(childService)
|
err := ctl.checkAndFormat(childService)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
Loading…
Reference in New Issue