package msgdata import ( "testing" "github.com/stretchr/testify/assert" "edge-infra.dev/pkg/sds/emergencyaccess/apierror" "edge-infra.dev/pkg/sds/emergencyaccess/eaconst" ) type helper interface { Helper() } func EqualError(message string) assert.ErrorAssertionFunc { return func(t assert.TestingT, err error, i ...interface{}) bool { if help, ok := t.(helper); ok { help.Helper() } return assert.EqualError(t, err, message, i...) } } // assert.ErrorAssertionFunc that asserts the error is an api error with the given // code, and contains the given message in the error string func APIError(code apierror.ErrorCode, message string, userError ...string) assert.ErrorAssertionFunc { return func(tt assert.TestingT, err error, i ...interface{}) bool { if help, ok := tt.(helper); ok { help.Helper() } if !assert.ErrorContains(tt, err, message, i...) { return false } if !assert.Implements(tt, (*apierror.APIError)(nil), err, i...) { return false } e := err.(apierror.APIError) assert.Equal(tt, userError, e.UserError()) return assert.Equal(tt, code, e.Code(), i...) } } func TestNewRequest(t *testing.T) { t.Parallel() tests := map[string]struct { data []byte attributes map[string]string expected Request errAssert assert.ErrorAssertionFunc }{ "1.0: Success": { data: []byte(`{ "command": "echo hello world" }`), attributes: map[string]string{ eaconst.VersionKey: string(eaconst.MessageVersion1_0), eaconst.RequestTypeKey: string(eaconst.Command), }, expected: v1_0Request{ ReqData: v1Data{ Command: "echo hello world", }, ReqAttr: map[string]string{ eaconst.VersionKey: string(eaconst.MessageVersion1_0), eaconst.RequestTypeKey: string(eaconst.Command), }, command: "echo", }, errAssert: assert.NoError, }, "1.0: Bad Data": { data: []byte(`{ "command": "echo hel`), attributes: map[string]string{ eaconst.VersionKey: string(eaconst.MessageVersion1_0), eaconst.RequestTypeKey: string(eaconst.Command), }, errAssert: EqualError(`failed to unmarshal: unexpected end of JSON input`), }, "1.0: No Command": { data: []byte(`{ "something": "something" }`), attributes: map[string]string{ eaconst.VersionKey: string(eaconst.MessageVersion1_0), eaconst.RequestTypeKey: string(eaconst.Command), }, errAssert: APIError(apierror.ErrInvalidCommand, `payload cannot be empty`), }, "1.0: No Command Identified": { data: []byte(`{ "command": "\"\" echo hello there" }`), attributes: map[string]string{ eaconst.VersionKey: string(eaconst.MessageVersion1_0), eaconst.RequestTypeKey: string(eaconst.Command), }, errAssert: APIError(apierror.ErrInvalidCommand, "command is empty", `No command identified`), }, "2.0 Command: Success": { data: []byte(`{ "command": "echo", "args": ["hello", "world"] }`), attributes: map[string]string{ eaconst.VersionKey: string(eaconst.MessageVersion2_0), eaconst.RequestTypeKey: string(eaconst.Command), }, expected: v2_0CommandRequest{ ReqData: v2Command{ Command: "echo", Args: []string{"hello", "world"}, }, ReqAttr: map[string]string{ eaconst.VersionKey: string(eaconst.MessageVersion2_0), eaconst.RequestTypeKey: string(eaconst.Command), }, }, errAssert: assert.NoError, }, "2.0 Command: Bad Data": { data: []byte(`{ "command": "ech`), attributes: map[string]string{ eaconst.VersionKey: string(eaconst.MessageVersion2_0), eaconst.RequestTypeKey: string(eaconst.Command), }, errAssert: EqualError(`failed to unmarshal data: unexpected end of JSON input`), }, "2.0 Command: Invalid": { data: []byte(`{ "args": ["hello", "world"] }`), attributes: map[string]string{ eaconst.VersionKey: string(eaconst.MessageVersion2_0), eaconst.RequestTypeKey: string(eaconst.Command), }, errAssert: APIError(apierror.ErrInvalidCommand, "command is empty", `No command identified`), }, "2.0 Executable: Success": { data: []byte(`{ "executable": { "name": "myScript" }, "args": ["hello", "world"] }`), attributes: map[string]string{ eaconst.VersionKey: string(eaconst.MessageVersion2_0), eaconst.RequestTypeKey: string(eaconst.Executable), }, expected: &v2_0ExecutableRequest{ ReqData: v2Executable{ Executable: executable{ Name: "myScript", }, Args: []string{"hello", "world"}, }, ReqAttr: map[string]string{ eaconst.VersionKey: string(eaconst.MessageVersion2_0), eaconst.RequestTypeKey: string(eaconst.Executable), }, }, errAssert: assert.NoError, }, "2.0 Executable: Bad Data": { data: []byte(`{ "executable": {`), attributes: map[string]string{ eaconst.VersionKey: string(eaconst.MessageVersion2_0), eaconst.RequestTypeKey: string(eaconst.Executable), }, errAssert: EqualError(`failed to unmarshal data: unexpected end of JSON input`), }, "2.0 Executable: Invalid": { data: []byte(`{ "args": ["hello", "world"] }`), attributes: map[string]string{ eaconst.VersionKey: string(eaconst.MessageVersion2_0), eaconst.RequestTypeKey: string(eaconst.Executable), }, errAssert: APIError(apierror.ErrInvalidCommand, "executable name is empty", `No executable name identified`), }, "2.0: Unsupported Type": { attributes: map[string]string{ eaconst.VersionKey: string(eaconst.MessageVersion2_0), eaconst.RequestTypeKey: string(notAType), }, errAssert: EqualError(`received version 2.0 message with unsupported request type "not-a-type"`), }, "No Message Version": { attributes: map[string]string{ eaconst.RequestTypeKey: string(eaconst.Command), }, errAssert: EqualError(`failed to find version attribute`), }, "No Request Type": { attributes: map[string]string{ eaconst.VersionKey: string(eaconst.MessageVersion1_0), }, errAssert: EqualError(`failed to find requestType attribute`), }, "Unsupported Message Version": { attributes: map[string]string{ eaconst.VersionKey: "0.1", eaconst.RequestTypeKey: string(eaconst.Command), }, errAssert: EqualError(`received unsupported request message version "0.1"`), }, } for name, tc := range tests { tc := tc t.Run(name, func(t *testing.T) { t.Parallel() req, err := NewRequest(tc.data, tc.attributes) tc.errAssert(t, err) assert.Equal(t, tc.expected, req) }) } } func TestParsePayload(t *testing.T) { t.Parallel() tests := map[string]struct { input string expName string expArgs []string errAssert assert.ErrorAssertionFunc }{ "Only command": { input: "echo", expName: "echo", expArgs: []string{}, errAssert: assert.NoError, }, "Multiple words": { input: "echo hello there", expName: "echo", expArgs: []string{"hello", "there"}, errAssert: assert.NoError, }, "Command with space": { input: "\"echo hello\"", expName: "echo hello", expArgs: []string{}, errAssert: assert.NoError, }, "Command with space 2": { input: "echo\\ hello", expName: "echo hello", expArgs: []string{}, errAssert: assert.NoError, }, "Full Path": { input: "/bin/mybinary hello", expName: "/bin/mybinary", expArgs: []string{"hello"}, errAssert: assert.NoError, }, "Relative path": { input: "./mybinary hello", expName: "./mybinary", expArgs: []string{"hello"}, errAssert: assert.NoError, }, "Env Var": { input: "a=b c", expName: "a=b", expArgs: []string{"c"}, errAssert: assert.NoError, }, "Env Var with underscore": { input: "_a=b c", expName: "_a=b", expArgs: []string{"c"}, errAssert: assert.NoError, }, "Env Var with number": { input: "a3=b c", expName: "a3=b", expArgs: []string{"c"}, errAssert: assert.NoError, }, "Not a var number": { input: "1a=b c", expName: "1a=b", expArgs: []string{"c"}, errAssert: assert.NoError, }, "Not a var special character": { input: "a=f|3 echo 8", expName: "a=f|3", expArgs: []string{"echo", "8"}, errAssert: assert.NoError, }, "2 Env Var": { input: "_a=b KA=3 c", expName: "_a=b", expArgs: []string{"KA=3", "c"}, errAssert: assert.NoError, }, "Leading space": { input: " _a=b KA=3 c", expName: "_a=b", expArgs: []string{"KA=3", "c"}, errAssert: assert.NoError, }, "Trailing space": { input: " _a=b KA=3 c ", expName: "_a=b", expArgs: []string{"KA=3", "c"}, errAssert: assert.NoError, }, "No command": { input: " KA=3 ", expName: "KA=3", expArgs: []string{}, errAssert: assert.NoError, }, "Unicode number": { input: "aⅧ=3 echo 8", expName: "aⅧ=3", expArgs: []string{"echo", "8"}, errAssert: assert.NoError, }, "Unicode digit": { input: "a৩=3 echo 8", expName: "a৩=3", expArgs: []string{"echo", "8"}, errAssert: assert.NoError, }, "Unicode letter": { input: "aḸ=3 echo 8", expName: "aḸ=3", expArgs: []string{"echo", "8"}, errAssert: assert.NoError, }, "Empty Payload": { errAssert: EqualError("payload cannot be empty"), }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { t.Parallel() name, args, err := parsePayload(tc.input) tc.errAssert(t, err) assert.Equal(t, tc.expName, name) assert.Equal(t, tc.expArgs, args) }) } }