package msgdata import ( "encoding/base64" "fmt" "testing" "github.com/stretchr/testify/assert" "edge-infra.dev/pkg/sds/emergencyaccess/apierror" "edge-infra.dev/pkg/sds/emergencyaccess/eaconst" ) const ( notAType = eaconst.RequestType("not-a-type") ) var ( defaultV2_0CommandRequest = 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), }, } defaultV2_0ExecutableRequest = v2_0ExecutableRequest{ ReqData: v2Executable{ Executable: executable{ Name: "myScript", Contents: "", }, Args: []string{"hello", "world"}, }, ReqAttr: map[string]string{ eaconst.VersionKey: string(eaconst.MessageVersion2_0), eaconst.RequestTypeKey: string(eaconst.Executable), }, } ) func TestNewV2_0Request(t *testing.T) { t.Parallel() tests := map[string]struct { payload string requestType eaconst.RequestType expected Request errAssert assert.ErrorAssertionFunc }{ "Command": { payload: "echo hello world", 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, }, "Executable": { payload: "./myScript hello world", 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, }, "Can't Parse Payload": { errAssert: APIError(apierror.ErrInvalidCommand, "payload cannot be empty"), }, "Invalid Command": { payload: "\"\" echo hello world", errAssert: APIError(apierror.ErrInvalidCommand, "command is empty", "No command identified"), }, "Invalid Executable Name": { payload: "./\"\" hello world", errAssert: APIError(apierror.ErrInvalidCommand, "executable name is empty", "No executable name identified"), }, } for name, tc := range tests { tc := tc t.Run(name, func(t *testing.T) { t.Parallel() actual, err := NewV2_0Request(tc.payload) tc.errAssert(t, err) assert.Equal(t, tc.expected, actual) }) } } func TestV2_0RequestData(t *testing.T) { t.Parallel() tests := map[string]struct { req Request expected string }{ "Command": { req: defaultV2_0CommandRequest, expected: `{ "command": "echo", "args": ["hello", "world"] }`, }, "Executable": { req: &defaultV2_0ExecutableRequest, expected: `{ "executable": { "name": "myScript", "contents": "" }, "args": ["hello", "world"] }`, }, } for name, tc := range tests { tc := tc t.Run(name, func(t *testing.T) { t.Parallel() data, err := tc.req.Data() assert.NoError(t, err) assert.JSONEq(t, tc.expected, string(data)) }) } } func TestV2_0CommandRequestAttributes(t *testing.T) { t.Parallel() req := defaultV2_0CommandRequest assert.Equal(t, req.ReqAttr, req.Attributes()) } func TestV2_0ExecutableRequestAttributes(t *testing.T) { t.Parallel() req := defaultV2_0ExecutableRequest assert.Equal(t, req.ReqAttr, req.Attributes()) } func TestV2_0CommandRequestAddAttribute(t *testing.T) { t.Parallel() req := v2_0CommandRequest{ ReqAttr: deepCopyMap(defaultV2_0CommandRequest.ReqAttr), ReqData: defaultV2_0CommandRequest.ReqData, } attr := req.Attributes() req.AddAttribute(defaultKey, defaultVal) assert.NotEqual(t, attr, req.Attributes()) attr[defaultKey] = defaultVal assert.Equal(t, attr, req.Attributes()) // Test Attributes do not get updated with a changed value req.AddAttribute(defaultKey, updatedVal) assert.Equal(t, attr, req.Attributes()) attr[defaultKey] = updatedVal assert.NotEqual(t, attr, req.Attributes()) } func TestV2_0ExectuableRequestAddAttribute(t *testing.T) { t.Parallel() req := v2_0ExecutableRequest{ ReqAttr: deepCopyMap(defaultV2_0ExecutableRequest.ReqAttr), ReqData: defaultV2_0ExecutableRequest.ReqData, } attr := req.Attributes() req.AddAttribute(defaultKey, defaultVal) assert.NotEqual(t, attr, req.Attributes()) attr[defaultKey] = defaultVal assert.Equal(t, attr, req.Attributes()) // Test Attributes do not get updated with a changed value req.AddAttribute(defaultKey, updatedVal) assert.Equal(t, attr, req.Attributes()) attr[defaultKey] = updatedVal assert.NotEqual(t, attr, req.Attributes()) } func TestV2_0CommandRequestCommandToBeAuthorized(t *testing.T) { t.Parallel() req := defaultV2_0CommandRequest assert.Equal(t, req.ReqData.Command, req.CommandToBeAuthorized()) } func TestV2_0ExecutableRequestCommandToBeAuthorized(t *testing.T) { t.Parallel() req := defaultV2_0ExecutableRequest assert.Equal(t, req.ReqData.Executable.Name, req.CommandToBeAuthorized()) } func TestV2_0CommandRequestType(t *testing.T) { t.Parallel() req := defaultV2_0CommandRequest assert.Equal(t, eaconst.Command, req.RequestType()) } func TestV2_0ExecutableRequestType(t *testing.T) { t.Parallel() req := defaultV2_0ExecutableRequest assert.Equal(t, eaconst.Executable, req.RequestType()) } func TestV2_0CommandRequestValidate(t *testing.T) { t.Parallel() tests := map[string]struct { req v2_0CommandRequest errAssert assert.ErrorAssertionFunc }{ "Valid": { req: v2_0CommandRequest{ ReqData: v2Command{ Command: "echo hello world", }, ReqAttr: map[string]string{ eaconst.VersionKey: string(eaconst.MessageVersion2_0), eaconst.RequestTypeKey: string(eaconst.Command), }, }, errAssert: assert.NoError, }, "All Invalid": { errAssert: APIError( apierror.ErrInvalidCommand, "command is empty\nversion attribute \"\" should be \"2.0\"\ntype attribute \"\" should be \"command\"", "No command identified", ), }, } for name, tc := range tests { tc := tc t.Run(name, func(t *testing.T) { t.Parallel() tc.errAssert(t, tc.req.validate()) }) } } func TestV2_0ExecutableRequestValidate(t *testing.T) { t.Parallel() tests := map[string]struct { req v2_0ExecutableRequest errAssert assert.ErrorAssertionFunc }{ "Valid": { req: v2_0ExecutableRequest{ ReqData: v2Executable{ Executable: executable{ Name: "myScript", }, }, ReqAttr: map[string]string{ eaconst.VersionKey: string(eaconst.MessageVersion2_0), eaconst.RequestTypeKey: string(eaconst.Executable), }, }, errAssert: assert.NoError, }, "Valid With Contents": { req: v2_0ExecutableRequest{ ReqData: v2Executable{ Executable: executable{ Name: "myScript", Contents: base64.StdEncoding.EncodeToString([]byte("some contents")), }, }, ReqAttr: map[string]string{ eaconst.VersionKey: string(eaconst.MessageVersion2_0), eaconst.RequestTypeKey: string(eaconst.Executable), }, }, errAssert: assert.NoError, }, "All Invalid": { errAssert: APIError( apierror.ErrInvalidCommand, "executable name is empty\nversion attribute \"\" should be \"2.0\"\ntype attribute \"\" should be \"executable\"", "No executable name identified", ), }, } for name, tc := range tests { tc := tc t.Run(name, func(t *testing.T) { t.Parallel() tc.errAssert(t, tc.req.validate()) }) } } func TestV2_0ArtifactorWriteContents(t *testing.T) { t.Parallel() contents := "" // Take a copy of the default executable test - this avoids issues where we // modify the default value used by other tests v2Request := defaultV2_0ExecutableRequest tests := map[string]struct { req Request expOk bool expData string }{ "Command": { req: defaultV2_0CommandRequest, expOk: false, }, "Executable": { req: &v2Request, expOk: true, expData: fmt.Sprintf(`{ "executable": { "name": "myScript", "contents": "%s" }, "args": ["hello", "world"] }`, contents), }, } for name, tc := range tests { tc := tc t.Run(name, func(t *testing.T) { t.Parallel() artifactor, ok := tc.req.(Artifactor) assert.Equal(t, tc.expOk, ok) if ok { artifactor.WriteContents(contents) data, err := tc.req.Data() assert.NoError(t, err) assert.JSONEq(t, tc.expData, string(data)) } }) } }