1 package msgdata
2
3 import (
4 "testing"
5
6 "github.com/stretchr/testify/assert"
7
8 "edge-infra.dev/pkg/sds/emergencyaccess/apierror"
9 "edge-infra.dev/pkg/sds/emergencyaccess/eaconst"
10 )
11
12 type helper interface {
13 Helper()
14 }
15
16 func EqualError(message string) assert.ErrorAssertionFunc {
17 return func(t assert.TestingT, err error, i ...interface{}) bool {
18 if help, ok := t.(helper); ok {
19 help.Helper()
20 }
21 return assert.EqualError(t, err, message, i...)
22 }
23 }
24
25
26
27 func APIError(code apierror.ErrorCode, message string, userError ...string) assert.ErrorAssertionFunc {
28 return func(tt assert.TestingT, err error, i ...interface{}) bool {
29 if help, ok := tt.(helper); ok {
30 help.Helper()
31 }
32
33 if !assert.ErrorContains(tt, err, message, i...) {
34 return false
35 }
36
37 if !assert.Implements(tt, (*apierror.APIError)(nil), err, i...) {
38 return false
39 }
40
41 e := err.(apierror.APIError)
42 assert.Equal(tt, userError, e.UserError())
43 return assert.Equal(tt, code, e.Code(), i...)
44 }
45 }
46
47 func TestNewRequest(t *testing.T) {
48 t.Parallel()
49
50 tests := map[string]struct {
51 data []byte
52 attributes map[string]string
53 expected Request
54 errAssert assert.ErrorAssertionFunc
55 }{
56 "1.0: Success": {
57 data: []byte(`{
58 "command": "echo hello world"
59 }`),
60 attributes: map[string]string{
61 eaconst.VersionKey: string(eaconst.MessageVersion1_0),
62 eaconst.RequestTypeKey: string(eaconst.Command),
63 },
64 expected: v1_0Request{
65 ReqData: v1Data{
66 Command: "echo hello world",
67 },
68 ReqAttr: map[string]string{
69 eaconst.VersionKey: string(eaconst.MessageVersion1_0),
70 eaconst.RequestTypeKey: string(eaconst.Command),
71 },
72 command: "echo",
73 },
74 errAssert: assert.NoError,
75 },
76 "1.0: Bad Data": {
77 data: []byte(`{
78 "command": "echo hel`),
79 attributes: map[string]string{
80 eaconst.VersionKey: string(eaconst.MessageVersion1_0),
81 eaconst.RequestTypeKey: string(eaconst.Command),
82 },
83 errAssert: EqualError(`failed to unmarshal: unexpected end of JSON input`),
84 },
85 "1.0: No Command": {
86 data: []byte(`{
87 "something": "something"
88 }`),
89 attributes: map[string]string{
90 eaconst.VersionKey: string(eaconst.MessageVersion1_0),
91 eaconst.RequestTypeKey: string(eaconst.Command),
92 },
93 errAssert: APIError(apierror.ErrInvalidCommand, `payload cannot be empty`),
94 },
95 "1.0: No Command Identified": {
96 data: []byte(`{
97 "command": "\"\" echo hello there"
98 }`),
99 attributes: map[string]string{
100 eaconst.VersionKey: string(eaconst.MessageVersion1_0),
101 eaconst.RequestTypeKey: string(eaconst.Command),
102 },
103 errAssert: APIError(apierror.ErrInvalidCommand, "command is empty", `No command identified`),
104 },
105 "2.0 Command: Success": {
106 data: []byte(`{
107 "command": "echo",
108 "args": ["hello", "world"]
109 }`),
110 attributes: map[string]string{
111 eaconst.VersionKey: string(eaconst.MessageVersion2_0),
112 eaconst.RequestTypeKey: string(eaconst.Command),
113 },
114 expected: v2_0CommandRequest{
115 ReqData: v2Command{
116 Command: "echo",
117 Args: []string{"hello", "world"},
118 },
119 ReqAttr: map[string]string{
120 eaconst.VersionKey: string(eaconst.MessageVersion2_0),
121 eaconst.RequestTypeKey: string(eaconst.Command),
122 },
123 },
124 errAssert: assert.NoError,
125 },
126 "2.0 Command: Bad Data": {
127 data: []byte(`{
128 "command": "ech`),
129 attributes: map[string]string{
130 eaconst.VersionKey: string(eaconst.MessageVersion2_0),
131 eaconst.RequestTypeKey: string(eaconst.Command),
132 },
133 errAssert: EqualError(`failed to unmarshal data: unexpected end of JSON input`),
134 },
135 "2.0 Command: Invalid": {
136 data: []byte(`{
137 "args": ["hello", "world"]
138 }`),
139 attributes: map[string]string{
140 eaconst.VersionKey: string(eaconst.MessageVersion2_0),
141 eaconst.RequestTypeKey: string(eaconst.Command),
142 },
143 errAssert: APIError(apierror.ErrInvalidCommand, "command is empty", `No command identified`),
144 },
145 "2.0 Executable: Success": {
146 data: []byte(`{
147 "executable": {
148 "name": "myScript"
149 },
150 "args": ["hello", "world"]
151 }`),
152 attributes: map[string]string{
153 eaconst.VersionKey: string(eaconst.MessageVersion2_0),
154 eaconst.RequestTypeKey: string(eaconst.Executable),
155 },
156 expected: &v2_0ExecutableRequest{
157 ReqData: v2Executable{
158 Executable: executable{
159 Name: "myScript",
160 },
161 Args: []string{"hello", "world"},
162 },
163 ReqAttr: map[string]string{
164 eaconst.VersionKey: string(eaconst.MessageVersion2_0),
165 eaconst.RequestTypeKey: string(eaconst.Executable),
166 },
167 },
168 errAssert: assert.NoError,
169 },
170 "2.0 Executable: Bad Data": {
171 data: []byte(`{
172 "executable": {`),
173 attributes: map[string]string{
174 eaconst.VersionKey: string(eaconst.MessageVersion2_0),
175 eaconst.RequestTypeKey: string(eaconst.Executable),
176 },
177 errAssert: EqualError(`failed to unmarshal data: unexpected end of JSON input`),
178 },
179 "2.0 Executable: Invalid": {
180 data: []byte(`{
181 "args": ["hello", "world"]
182 }`),
183 attributes: map[string]string{
184 eaconst.VersionKey: string(eaconst.MessageVersion2_0),
185 eaconst.RequestTypeKey: string(eaconst.Executable),
186 },
187 errAssert: APIError(apierror.ErrInvalidCommand, "executable name is empty", `No executable name identified`),
188 },
189 "2.0: Unsupported Type": {
190 attributes: map[string]string{
191 eaconst.VersionKey: string(eaconst.MessageVersion2_0),
192 eaconst.RequestTypeKey: string(notAType),
193 },
194 errAssert: EqualError(`received version 2.0 message with unsupported request type "not-a-type"`),
195 },
196 "No Message Version": {
197 attributes: map[string]string{
198 eaconst.RequestTypeKey: string(eaconst.Command),
199 },
200 errAssert: EqualError(`failed to find version attribute`),
201 },
202 "No Request Type": {
203 attributes: map[string]string{
204 eaconst.VersionKey: string(eaconst.MessageVersion1_0),
205 },
206 errAssert: EqualError(`failed to find requestType attribute`),
207 },
208 "Unsupported Message Version": {
209 attributes: map[string]string{
210 eaconst.VersionKey: "0.1",
211 eaconst.RequestTypeKey: string(eaconst.Command),
212 },
213 errAssert: EqualError(`received unsupported request message version "0.1"`),
214 },
215 }
216
217 for name, tc := range tests {
218 tc := tc
219 t.Run(name, func(t *testing.T) {
220 t.Parallel()
221
222 req, err := NewRequest(tc.data, tc.attributes)
223 tc.errAssert(t, err)
224 assert.Equal(t, tc.expected, req)
225 })
226 }
227 }
228
229 func TestParsePayload(t *testing.T) {
230 t.Parallel()
231
232 tests := map[string]struct {
233 input string
234 expName string
235 expArgs []string
236 errAssert assert.ErrorAssertionFunc
237 }{
238 "Only command": {
239 input: "echo",
240 expName: "echo",
241 expArgs: []string{},
242 errAssert: assert.NoError,
243 },
244 "Multiple words": {
245 input: "echo hello there",
246 expName: "echo",
247 expArgs: []string{"hello", "there"},
248 errAssert: assert.NoError,
249 },
250 "Command with space": {
251 input: "\"echo hello\"",
252 expName: "echo hello",
253 expArgs: []string{},
254 errAssert: assert.NoError,
255 },
256 "Command with space 2": {
257 input: "echo\\ hello",
258 expName: "echo hello",
259 expArgs: []string{},
260 errAssert: assert.NoError,
261 },
262 "Full Path": {
263 input: "/bin/mybinary hello",
264 expName: "/bin/mybinary",
265 expArgs: []string{"hello"},
266 errAssert: assert.NoError,
267 },
268 "Relative path": {
269 input: "./mybinary hello",
270 expName: "./mybinary",
271 expArgs: []string{"hello"},
272 errAssert: assert.NoError,
273 },
274 "Env Var": {
275 input: "a=b c",
276 expName: "a=b",
277 expArgs: []string{"c"},
278 errAssert: assert.NoError,
279 },
280 "Env Var with underscore": {
281 input: "_a=b c",
282 expName: "_a=b",
283 expArgs: []string{"c"},
284 errAssert: assert.NoError,
285 },
286 "Env Var with number": {
287 input: "a3=b c",
288 expName: "a3=b",
289 expArgs: []string{"c"},
290 errAssert: assert.NoError,
291 },
292 "Not a var number": {
293 input: "1a=b c",
294 expName: "1a=b",
295 expArgs: []string{"c"},
296 errAssert: assert.NoError,
297 },
298 "Not a var special character": {
299 input: "a=f|3 echo 8",
300 expName: "a=f|3",
301 expArgs: []string{"echo", "8"},
302 errAssert: assert.NoError,
303 },
304 "2 Env Var": {
305 input: "_a=b KA=3 c",
306 expName: "_a=b",
307 expArgs: []string{"KA=3", "c"},
308 errAssert: assert.NoError,
309 },
310 "Leading space": {
311 input: " _a=b KA=3 c",
312 expName: "_a=b",
313 expArgs: []string{"KA=3", "c"},
314 errAssert: assert.NoError,
315 },
316 "Trailing space": {
317 input: " _a=b KA=3 c ",
318 expName: "_a=b",
319 expArgs: []string{"KA=3", "c"},
320 errAssert: assert.NoError,
321 },
322 "No command": {
323 input: " KA=3 ",
324 expName: "KA=3",
325 expArgs: []string{},
326 errAssert: assert.NoError,
327 },
328 "Unicode number": {
329 input: "aⅧ=3 echo 8",
330 expName: "aⅧ=3",
331 expArgs: []string{"echo", "8"},
332 errAssert: assert.NoError,
333 },
334 "Unicode digit": {
335 input: "a৩=3 echo 8",
336 expName: "a৩=3",
337 expArgs: []string{"echo", "8"},
338 errAssert: assert.NoError,
339 },
340 "Unicode letter": {
341 input: "aḸ=3 echo 8",
342 expName: "aḸ=3",
343 expArgs: []string{"echo", "8"},
344 errAssert: assert.NoError,
345 },
346 "Empty Payload": {
347 errAssert: EqualError("payload cannot be empty"),
348 },
349 }
350
351 for name, tc := range tests {
352 t.Run(name, func(t *testing.T) {
353 t.Parallel()
354
355 name, args, err := parsePayload(tc.input)
356 tc.errAssert(t, err)
357 assert.Equal(t, tc.expName, name)
358 assert.Equal(t, tc.expArgs, args)
359 })
360 }
361 }
362
View as plain text