1 package msgdata
2
3 import (
4 "encoding/base64"
5 "fmt"
6 "testing"
7
8 "github.com/stretchr/testify/assert"
9
10 "edge-infra.dev/pkg/sds/emergencyaccess/apierror"
11 "edge-infra.dev/pkg/sds/emergencyaccess/eaconst"
12 )
13
14 const (
15 notAType = eaconst.RequestType("not-a-type")
16 )
17
18 var (
19 defaultV2_0CommandRequest = v2_0CommandRequest{
20 ReqData: v2Command{
21 Command: "echo",
22 Args: []string{"hello", "world"},
23 },
24 ReqAttr: map[string]string{
25 eaconst.VersionKey: string(eaconst.MessageVersion2_0),
26 eaconst.RequestTypeKey: string(eaconst.Command),
27 },
28 }
29 defaultV2_0ExecutableRequest = v2_0ExecutableRequest{
30 ReqData: v2Executable{
31 Executable: executable{
32 Name: "myScript",
33 Contents: "<base64 encoded contents>",
34 },
35 Args: []string{"hello", "world"},
36 },
37 ReqAttr: map[string]string{
38 eaconst.VersionKey: string(eaconst.MessageVersion2_0),
39 eaconst.RequestTypeKey: string(eaconst.Executable),
40 },
41 }
42 )
43
44 func TestNewV2_0Request(t *testing.T) {
45 t.Parallel()
46
47 tests := map[string]struct {
48 payload string
49 requestType eaconst.RequestType
50 expected Request
51 errAssert assert.ErrorAssertionFunc
52 }{
53 "Command": {
54 payload: "echo hello world",
55 expected: v2_0CommandRequest{
56 ReqData: v2Command{
57 Command: "echo",
58 Args: []string{"hello", "world"},
59 },
60 ReqAttr: map[string]string{
61 eaconst.VersionKey: string(eaconst.MessageVersion2_0),
62 eaconst.RequestTypeKey: string(eaconst.Command),
63 },
64 },
65 errAssert: assert.NoError,
66 },
67 "Executable": {
68 payload: "./myScript hello world",
69 expected: &v2_0ExecutableRequest{
70 ReqData: v2Executable{
71 Executable: executable{
72 Name: "myScript",
73 },
74 Args: []string{"hello", "world"},
75 },
76 ReqAttr: map[string]string{
77 eaconst.VersionKey: string(eaconst.MessageVersion2_0),
78 eaconst.RequestTypeKey: string(eaconst.Executable),
79 },
80 },
81 errAssert: assert.NoError,
82 },
83 "Can't Parse Payload": {
84 errAssert: APIError(apierror.ErrInvalidCommand, "payload cannot be empty"),
85 },
86 "Invalid Command": {
87 payload: "\"\" echo hello world",
88 errAssert: APIError(apierror.ErrInvalidCommand, "command is empty", "No command identified"),
89 },
90 "Invalid Executable Name": {
91 payload: "./\"\" hello world",
92 errAssert: APIError(apierror.ErrInvalidCommand, "executable name is empty", "No executable name identified"),
93 },
94 }
95
96 for name, tc := range tests {
97 tc := tc
98 t.Run(name, func(t *testing.T) {
99 t.Parallel()
100
101 actual, err := NewV2_0Request(tc.payload)
102 tc.errAssert(t, err)
103 assert.Equal(t, tc.expected, actual)
104 })
105 }
106 }
107
108 func TestV2_0RequestData(t *testing.T) {
109 t.Parallel()
110
111 tests := map[string]struct {
112 req Request
113 expected string
114 }{
115 "Command": {
116 req: defaultV2_0CommandRequest,
117 expected: `{
118 "command": "echo",
119 "args": ["hello", "world"]
120 }`,
121 },
122 "Executable": {
123 req: &defaultV2_0ExecutableRequest,
124 expected: `{
125 "executable": {
126 "name": "myScript",
127 "contents": "<base64 encoded contents>"
128 },
129 "args": ["hello", "world"]
130 }`,
131 },
132 }
133
134 for name, tc := range tests {
135 tc := tc
136 t.Run(name, func(t *testing.T) {
137 t.Parallel()
138
139 data, err := tc.req.Data()
140 assert.NoError(t, err)
141 assert.JSONEq(t, tc.expected, string(data))
142 })
143 }
144 }
145
146 func TestV2_0CommandRequestAttributes(t *testing.T) {
147 t.Parallel()
148
149 req := defaultV2_0CommandRequest
150 assert.Equal(t, req.ReqAttr, req.Attributes())
151 }
152
153 func TestV2_0ExecutableRequestAttributes(t *testing.T) {
154 t.Parallel()
155
156 req := defaultV2_0ExecutableRequest
157 assert.Equal(t, req.ReqAttr, req.Attributes())
158 }
159
160 func TestV2_0CommandRequestAddAttribute(t *testing.T) {
161 t.Parallel()
162
163 req := v2_0CommandRequest{
164 ReqAttr: deepCopyMap(defaultV2_0CommandRequest.ReqAttr),
165 ReqData: defaultV2_0CommandRequest.ReqData,
166 }
167 attr := req.Attributes()
168
169 req.AddAttribute(defaultKey, defaultVal)
170 assert.NotEqual(t, attr, req.Attributes())
171 attr[defaultKey] = defaultVal
172 assert.Equal(t, attr, req.Attributes())
173
174
175 req.AddAttribute(defaultKey, updatedVal)
176 assert.Equal(t, attr, req.Attributes())
177 attr[defaultKey] = updatedVal
178 assert.NotEqual(t, attr, req.Attributes())
179 }
180
181 func TestV2_0ExectuableRequestAddAttribute(t *testing.T) {
182 t.Parallel()
183
184 req := v2_0ExecutableRequest{
185 ReqAttr: deepCopyMap(defaultV2_0ExecutableRequest.ReqAttr),
186 ReqData: defaultV2_0ExecutableRequest.ReqData,
187 }
188 attr := req.Attributes()
189
190 req.AddAttribute(defaultKey, defaultVal)
191 assert.NotEqual(t, attr, req.Attributes())
192 attr[defaultKey] = defaultVal
193 assert.Equal(t, attr, req.Attributes())
194
195
196 req.AddAttribute(defaultKey, updatedVal)
197 assert.Equal(t, attr, req.Attributes())
198 attr[defaultKey] = updatedVal
199 assert.NotEqual(t, attr, req.Attributes())
200 }
201
202 func TestV2_0CommandRequestCommandToBeAuthorized(t *testing.T) {
203 t.Parallel()
204
205 req := defaultV2_0CommandRequest
206 assert.Equal(t, req.ReqData.Command, req.CommandToBeAuthorized())
207 }
208
209 func TestV2_0ExecutableRequestCommandToBeAuthorized(t *testing.T) {
210 t.Parallel()
211
212 req := defaultV2_0ExecutableRequest
213 assert.Equal(t, req.ReqData.Executable.Name, req.CommandToBeAuthorized())
214 }
215
216 func TestV2_0CommandRequestType(t *testing.T) {
217 t.Parallel()
218
219 req := defaultV2_0CommandRequest
220 assert.Equal(t, eaconst.Command, req.RequestType())
221 }
222
223 func TestV2_0ExecutableRequestType(t *testing.T) {
224 t.Parallel()
225
226 req := defaultV2_0ExecutableRequest
227 assert.Equal(t, eaconst.Executable, req.RequestType())
228 }
229
230 func TestV2_0CommandRequestValidate(t *testing.T) {
231 t.Parallel()
232
233 tests := map[string]struct {
234 req v2_0CommandRequest
235 errAssert assert.ErrorAssertionFunc
236 }{
237 "Valid": {
238 req: v2_0CommandRequest{
239 ReqData: v2Command{
240 Command: "echo hello world",
241 },
242 ReqAttr: map[string]string{
243 eaconst.VersionKey: string(eaconst.MessageVersion2_0),
244 eaconst.RequestTypeKey: string(eaconst.Command),
245 },
246 },
247 errAssert: assert.NoError,
248 },
249 "All Invalid": {
250 errAssert: APIError(
251 apierror.ErrInvalidCommand,
252 "command is empty\nversion attribute \"\" should be \"2.0\"\ntype attribute \"\" should be \"command\"",
253 "No command identified",
254 ),
255 },
256 }
257
258 for name, tc := range tests {
259 tc := tc
260 t.Run(name, func(t *testing.T) {
261 t.Parallel()
262
263 tc.errAssert(t, tc.req.validate())
264 })
265 }
266 }
267
268 func TestV2_0ExecutableRequestValidate(t *testing.T) {
269 t.Parallel()
270
271 tests := map[string]struct {
272 req v2_0ExecutableRequest
273 errAssert assert.ErrorAssertionFunc
274 }{
275 "Valid": {
276 req: v2_0ExecutableRequest{
277 ReqData: v2Executable{
278 Executable: executable{
279 Name: "myScript",
280 },
281 },
282 ReqAttr: map[string]string{
283 eaconst.VersionKey: string(eaconst.MessageVersion2_0),
284 eaconst.RequestTypeKey: string(eaconst.Executable),
285 },
286 },
287 errAssert: assert.NoError,
288 },
289 "Valid With Contents": {
290 req: v2_0ExecutableRequest{
291 ReqData: v2Executable{
292 Executable: executable{
293 Name: "myScript",
294 Contents: base64.StdEncoding.EncodeToString([]byte("some contents")),
295 },
296 },
297 ReqAttr: map[string]string{
298 eaconst.VersionKey: string(eaconst.MessageVersion2_0),
299 eaconst.RequestTypeKey: string(eaconst.Executable),
300 },
301 },
302 errAssert: assert.NoError,
303 },
304 "All Invalid": {
305 errAssert: APIError(
306 apierror.ErrInvalidCommand,
307 "executable name is empty\nversion attribute \"\" should be \"2.0\"\ntype attribute \"\" should be \"executable\"",
308 "No executable name identified",
309 ),
310 },
311 }
312
313 for name, tc := range tests {
314 tc := tc
315 t.Run(name, func(t *testing.T) {
316 t.Parallel()
317
318 tc.errAssert(t, tc.req.validate())
319 })
320 }
321 }
322
323 func TestV2_0ArtifactorWriteContents(t *testing.T) {
324 t.Parallel()
325
326 contents := "<example contents>"
327
328
329
330 v2Request := defaultV2_0ExecutableRequest
331
332 tests := map[string]struct {
333 req Request
334 expOk bool
335 expData string
336 }{
337 "Command": {
338 req: defaultV2_0CommandRequest,
339 expOk: false,
340 },
341 "Executable": {
342 req: &v2Request,
343 expOk: true,
344 expData: fmt.Sprintf(`{
345 "executable": {
346 "name": "myScript",
347 "contents": "%s"
348 },
349 "args": ["hello", "world"]
350 }`, contents),
351 },
352 }
353
354 for name, tc := range tests {
355 tc := tc
356 t.Run(name, func(t *testing.T) {
357 t.Parallel()
358
359 artifactor, ok := tc.req.(Artifactor)
360 assert.Equal(t, tc.expOk, ok)
361
362 if ok {
363 artifactor.WriteContents(contents)
364 data, err := tc.req.Data()
365 assert.NoError(t, err)
366 assert.JSONEq(t, tc.expData, string(data))
367 }
368 })
369 }
370 }
371
View as plain text