]> git.r.bdr.sh - rbdr/mobius/blob - hotline/transaction_handlers_test.go
Implement Make Alias transaction
[rbdr/mobius] / hotline / transaction_handlers_test.go
1 package hotline
2
3 import (
4 "errors"
5 "github.com/stretchr/testify/assert"
6 "io/fs"
7 "math/rand"
8 "os"
9 "strings"
10 "testing"
11 )
12
13 func TestHandleSetChatSubject(t *testing.T) {
14 type args struct {
15 cc *ClientConn
16 t *Transaction
17 }
18 tests := []struct {
19 name string
20 args args
21 want []Transaction
22 wantErr bool
23 }{
24 {
25 name: "sends chat subject to private chat members",
26 args: args{
27 cc: &ClientConn{
28 UserName: []byte{0x00, 0x01},
29 Server: &Server{
30 PrivateChats: map[uint32]*PrivateChat{
31 uint32(1): {
32 Subject: "unset",
33 ClientConn: map[uint16]*ClientConn{
34 uint16(1): {
35 Account: &Account{
36 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
37 },
38 ID: &[]byte{0, 1},
39 },
40 uint16(2): {
41 Account: &Account{
42 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
43 },
44 ID: &[]byte{0, 2},
45 },
46 },
47 },
48 },
49 Clients: map[uint16]*ClientConn{
50 uint16(1): {
51 Account: &Account{
52 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
53 },
54 ID: &[]byte{0, 1},
55 },
56 uint16(2): {
57 Account: &Account{
58 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
59 },
60 ID: &[]byte{0, 2},
61 },
62 },
63 },
64 },
65 t: &Transaction{
66 Flags: 0x00,
67 IsReply: 0x00,
68 Type: []byte{0, 0x6a},
69 ID: []byte{0, 0, 0, 1},
70 ErrorCode: []byte{0, 0, 0, 0},
71 Fields: []Field{
72 NewField(fieldChatID, []byte{0, 0, 0, 1}),
73 NewField(fieldChatSubject, []byte("Test Subject")),
74 },
75 },
76 },
77 want: []Transaction{
78 {
79 clientID: &[]byte{0, 1},
80 Flags: 0x00,
81 IsReply: 0x00,
82 Type: []byte{0, 0x77},
83 ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
84 ErrorCode: []byte{0, 0, 0, 0},
85 Fields: []Field{
86 NewField(fieldChatID, []byte{0, 0, 0, 1}),
87 NewField(fieldChatSubject, []byte("Test Subject")),
88 },
89 },
90 {
91 clientID: &[]byte{0, 2},
92 Flags: 0x00,
93 IsReply: 0x00,
94 Type: []byte{0, 0x77},
95 ID: []byte{0xf0, 0xc5, 0x34, 0x1e}, // Random ID from rand.Seed(1)
96 ErrorCode: []byte{0, 0, 0, 0},
97 Fields: []Field{
98 NewField(fieldChatID, []byte{0, 0, 0, 1}),
99 NewField(fieldChatSubject, []byte("Test Subject")),
100 },
101 },
102 },
103 wantErr: false,
104 },
105 }
106 for _, tt := range tests {
107 rand.Seed(1) // reset seed between tests to make transaction IDs predictable
108
109 t.Run(tt.name, func(t *testing.T) {
110 got, err := HandleSetChatSubject(tt.args.cc, tt.args.t)
111 if (err != nil) != tt.wantErr {
112 t.Errorf("HandleSetChatSubject() error = %v, wantErr %v", err, tt.wantErr)
113 return
114 }
115 if !assert.Equal(t, tt.want, got) {
116 t.Errorf("HandleSetChatSubject() got = %v, want %v", got, tt.want)
117 }
118 })
119 }
120 }
121
122 func TestHandleLeaveChat(t *testing.T) {
123 type args struct {
124 cc *ClientConn
125 t *Transaction
126 }
127 tests := []struct {
128 name string
129 args args
130 want []Transaction
131 wantErr bool
132 }{
133 {
134 name: "returns expected transactions",
135 args: args{
136 cc: &ClientConn{
137 ID: &[]byte{0, 2},
138 Server: &Server{
139 PrivateChats: map[uint32]*PrivateChat{
140 uint32(1): {
141 ClientConn: map[uint16]*ClientConn{
142 uint16(1): {
143 Account: &Account{
144 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
145 },
146 ID: &[]byte{0, 1},
147 },
148 uint16(2): {
149 Account: &Account{
150 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
151 },
152 ID: &[]byte{0, 2},
153 },
154 },
155 },
156 },
157 Clients: map[uint16]*ClientConn{
158 uint16(1): {
159 Account: &Account{
160 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
161 },
162 ID: &[]byte{0, 1},
163 },
164 uint16(2): {
165 Account: &Account{
166 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
167 },
168 ID: &[]byte{0, 2},
169 },
170 },
171 },
172 },
173 t: NewTransaction(tranDeleteUser, nil, NewField(fieldChatID, []byte{0, 0, 0, 1})),
174 },
175 want: []Transaction{
176 {
177 clientID: &[]byte{0, 1},
178 Flags: 0x00,
179 IsReply: 0x00,
180 Type: []byte{0, 0x76},
181 ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
182 ErrorCode: []byte{0, 0, 0, 0},
183 Fields: []Field{
184 NewField(fieldChatID, []byte{0, 0, 0, 1}),
185 NewField(fieldUserID, []byte{0, 2}),
186 },
187 },
188 },
189 wantErr: false,
190 },
191 }
192 for _, tt := range tests {
193 rand.Seed(1)
194 t.Run(tt.name, func(t *testing.T) {
195 got, err := HandleLeaveChat(tt.args.cc, tt.args.t)
196 if (err != nil) != tt.wantErr {
197 t.Errorf("HandleLeaveChat() error = %v, wantErr %v", err, tt.wantErr)
198 return
199 }
200 if !assert.Equal(t, tt.want, got) {
201 t.Errorf("HandleLeaveChat() got = %v, want %v", got, tt.want)
202 }
203 })
204 }
205 }
206
207 func TestHandleGetUserNameList(t *testing.T) {
208 type args struct {
209 cc *ClientConn
210 t *Transaction
211 }
212 tests := []struct {
213 name string
214 args args
215 want []Transaction
216 wantErr bool
217 }{
218 {
219 name: "replies with userlist transaction",
220 args: args{
221 cc: &ClientConn{
222
223 ID: &[]byte{1, 1},
224 Server: &Server{
225 Clients: map[uint16]*ClientConn{
226 uint16(1): {
227 ID: &[]byte{0, 1},
228 Icon: &[]byte{0, 2},
229 Flags: &[]byte{0, 3},
230 UserName: []byte{0, 4},
231 Agreed: true,
232 },
233 uint16(2): {
234 ID: &[]byte{0, 2},
235 Icon: &[]byte{0, 2},
236 Flags: &[]byte{0, 3},
237 UserName: []byte{0, 4},
238 Agreed: true,
239 },
240 uint16(3): {
241 ID: &[]byte{0, 3},
242 Icon: &[]byte{0, 2},
243 Flags: &[]byte{0, 3},
244 UserName: []byte{0, 4},
245 Agreed: false,
246 },
247 },
248 },
249 },
250 t: &Transaction{
251 ID: []byte{0, 0, 0, 1},
252 Type: []byte{0, 1},
253 },
254 },
255 want: []Transaction{
256 {
257 clientID: &[]byte{1, 1},
258 Flags: 0x00,
259 IsReply: 0x01,
260 Type: []byte{0, 1},
261 ID: []byte{0, 0, 0, 1},
262 ErrorCode: []byte{0, 0, 0, 0},
263 Fields: []Field{
264 NewField(
265 fieldUsernameWithInfo,
266 []byte{00, 01, 00, 02, 00, 03, 00, 02, 00, 04},
267 ),
268 NewField(
269 fieldUsernameWithInfo,
270 []byte{00, 02, 00, 02, 00, 03, 00, 02, 00, 04},
271 ),
272 },
273 },
274 },
275 wantErr: false,
276 },
277 }
278 for _, tt := range tests {
279 t.Run(tt.name, func(t *testing.T) {
280 got, err := HandleGetUserNameList(tt.args.cc, tt.args.t)
281 if (err != nil) != tt.wantErr {
282 t.Errorf("HandleGetUserNameList() error = %v, wantErr %v", err, tt.wantErr)
283 return
284 }
285 assert.Equal(t, tt.want, got)
286 })
287 }
288 }
289
290 func TestHandleChatSend(t *testing.T) {
291 type args struct {
292 cc *ClientConn
293 t *Transaction
294 }
295 tests := []struct {
296 name string
297 args args
298 want []Transaction
299 wantErr bool
300 }{
301 {
302 name: "sends chat msg transaction to all clients",
303 args: args{
304 cc: &ClientConn{
305 UserName: []byte{0x00, 0x01},
306 Server: &Server{
307 Clients: map[uint16]*ClientConn{
308 uint16(1): {
309 Account: &Account{
310 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
311 },
312 ID: &[]byte{0, 1},
313 },
314 uint16(2): {
315 Account: &Account{
316 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
317 },
318 ID: &[]byte{0, 2},
319 },
320 },
321 },
322 },
323 t: &Transaction{
324 Fields: []Field{
325 NewField(fieldData, []byte("hai")),
326 },
327 },
328 },
329 want: []Transaction{
330 {
331 clientID: &[]byte{0, 1},
332 Flags: 0x00,
333 IsReply: 0x00,
334 Type: []byte{0, 0x6a},
335 ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
336 ErrorCode: []byte{0, 0, 0, 0},
337 Fields: []Field{
338 NewField(fieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
339 },
340 },
341 {
342 clientID: &[]byte{0, 2},
343 Flags: 0x00,
344 IsReply: 0x00,
345 Type: []byte{0, 0x6a},
346 ID: []byte{0xf0, 0xc5, 0x34, 0x1e}, // Random ID from rand.Seed(1)
347 ErrorCode: []byte{0, 0, 0, 0},
348 Fields: []Field{
349 NewField(fieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
350 },
351 },
352 },
353 wantErr: false,
354 },
355 {
356 name: "sends chat msg as emote if fieldChatOptions is set",
357 args: args{
358 cc: &ClientConn{
359 UserName: []byte("Testy McTest"),
360 Server: &Server{
361 Clients: map[uint16]*ClientConn{
362 uint16(1): {
363 Account: &Account{
364 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
365 },
366 ID: &[]byte{0, 1},
367 },
368 uint16(2): {
369 Account: &Account{
370 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
371 },
372 ID: &[]byte{0, 2},
373 },
374 },
375 },
376 },
377 t: &Transaction{
378 Fields: []Field{
379 NewField(fieldData, []byte("performed action")),
380 NewField(fieldChatOptions, []byte{0x00, 0x01}),
381 },
382 },
383 },
384 want: []Transaction{
385 {
386 clientID: &[]byte{0, 1},
387 Flags: 0x00,
388 IsReply: 0x00,
389 Type: []byte{0, 0x6a},
390 ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
391 ErrorCode: []byte{0, 0, 0, 0},
392 Fields: []Field{
393 NewField(fieldData, []byte("\r*** Testy McTest performed action")),
394 },
395 },
396 {
397 clientID: &[]byte{0, 2},
398 Flags: 0x00,
399 IsReply: 0x00,
400 Type: []byte{0, 0x6a},
401 ID: []byte{0xf0, 0xc5, 0x34, 0x1e}, // Random ID from rand.Seed(1)
402 ErrorCode: []byte{0, 0, 0, 0},
403 Fields: []Field{
404 NewField(fieldData, []byte("\r*** Testy McTest performed action")),
405 },
406 },
407 },
408 wantErr: false,
409 },
410 {
411 name: "only sends chat msg to clients with accessReadChat permission",
412 args: args{
413 cc: &ClientConn{
414 UserName: []byte{0x00, 0x01},
415 Server: &Server{
416 Clients: map[uint16]*ClientConn{
417 uint16(1): {
418 Account: &Account{
419 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
420 },
421 ID: &[]byte{0, 1},
422 },
423 uint16(2): {
424 Account: &Account{
425 Access: &[]byte{0, 0, 0, 0, 0, 0, 0, 0},
426 },
427 ID: &[]byte{0, 2},
428 },
429 },
430 },
431 },
432 t: &Transaction{
433 Fields: []Field{
434 NewField(fieldData, []byte("hai")),
435 },
436 },
437 },
438 want: []Transaction{
439 {
440 clientID: &[]byte{0, 1},
441 Flags: 0x00,
442 IsReply: 0x00,
443 Type: []byte{0, 0x6a},
444 ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
445 ErrorCode: []byte{0, 0, 0, 0},
446 Fields: []Field{
447 NewField(fieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
448 },
449 },
450 },
451 wantErr: false,
452 },
453 }
454 for _, tt := range tests {
455 rand.Seed(1) // reset seed between tests to make transaction IDs predictable
456 t.Run(tt.name, func(t *testing.T) {
457 got, err := HandleChatSend(tt.args.cc, tt.args.t)
458
459 if (err != nil) != tt.wantErr {
460 t.Errorf("HandleChatSend() error = %v, wantErr %v", err, tt.wantErr)
461 return
462 }
463 if !assert.Equal(t, tt.want, got) {
464 t.Errorf("HandleChatSend() got = %v, want %v", got, tt.want)
465 }
466 })
467 }
468 }
469
470 func TestHandleGetFileInfo(t *testing.T) {
471 rand.Seed(1) // reset seed between tests to make transaction IDs predictable
472
473 type args struct {
474 cc *ClientConn
475 t *Transaction
476 }
477 tests := []struct {
478 name string
479 args args
480 wantRes []Transaction
481 wantErr bool
482 }{
483 {
484 name: "returns expected fields when a valid file is requested",
485 args: args{
486 cc: &ClientConn{
487 ID: &[]byte{0x00, 0x01},
488 Server: &Server{
489 Config: &Config{
490 FileRoot: func() string {
491 path, _ := os.Getwd()
492 return path + "/test/config/Files"
493 }(),
494 },
495 },
496 },
497 t: NewTransaction(
498 tranGetFileInfo, nil,
499 NewField(fieldFileName, []byte("testfile.txt")),
500 NewField(fieldFilePath, []byte{0x00, 0x00}),
501 ),
502 },
503 wantRes: []Transaction{
504 {
505 clientID: &[]byte{0, 1},
506 Flags: 0x00,
507 IsReply: 0x01,
508 Type: []byte{0, 0xce},
509 ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
510 ErrorCode: []byte{0, 0, 0, 0},
511 Fields: []Field{
512 NewField(fieldFileName, []byte("testfile.txt")),
513 NewField(fieldFileTypeString, []byte("TEXT")),
514 NewField(fieldFileCreatorString, []byte("ttxt")),
515 NewField(fieldFileComment, []byte{}),
516 NewField(fieldFileType, []byte("TEXT")),
517 NewField(fieldFileCreateDate, make([]byte, 8)),
518 NewField(fieldFileModifyDate, make([]byte, 8)),
519 NewField(fieldFileSize, []byte{0x0, 0x0, 0x0, 0x17}),
520 },
521 },
522 },
523 wantErr: false,
524 },
525 }
526 for _, tt := range tests {
527 t.Run(tt.name, func(t *testing.T) {
528 rand.Seed(1) // reset seed between tests to make transaction IDs predictable
529
530 gotRes, err := HandleGetFileInfo(tt.args.cc, tt.args.t)
531 if (err != nil) != tt.wantErr {
532 t.Errorf("HandleGetFileInfo() error = %v, wantErr %v", err, tt.wantErr)
533 return
534 }
535
536 // Clear the file timestamp fields to work around problems running the tests in multiple timezones
537 // TODO: revisit how to test this by mocking the stat calls
538 gotRes[0].Fields[5].Data = make([]byte, 8)
539 gotRes[0].Fields[6].Data = make([]byte, 8)
540 if !assert.Equal(t, tt.wantRes, gotRes) {
541 t.Errorf("HandleGetFileInfo() gotRes = %v, want %v", gotRes, tt.wantRes)
542 }
543 })
544 }
545 }
546
547 func TestHandleNewFolder(t *testing.T) {
548 type args struct {
549 cc *ClientConn
550 t *Transaction
551 }
552 tests := []struct {
553 setup func()
554 name string
555 args args
556 wantRes []Transaction
557 wantErr bool
558 }{
559 {
560 name: "when path is nested",
561 args: args{
562 cc: &ClientConn{
563 ID: &[]byte{0, 1},
564 Server: &Server{
565 Config: &Config{
566 FileRoot: "/Files/",
567 },
568 },
569 },
570 t: NewTransaction(
571 tranNewFolder, &[]byte{0, 1},
572 NewField(fieldFileName, []byte("testFolder")),
573 NewField(fieldFilePath, []byte{
574 0x00, 0x01,
575 0x00, 0x00,
576 0x03,
577 0x61, 0x61, 0x61,
578 }),
579 ),
580 },
581 setup: func() {
582 mfs := MockFileStore{}
583 mfs.On("Mkdir", "/Files/aaa/testFolder", fs.FileMode(0777)).Return(nil)
584 mfs.On("Stat", "/Files/aaa/testFolder").Return(nil, os.ErrNotExist)
585 FS = mfs
586 },
587 wantRes: []Transaction{
588 {
589 clientID: &[]byte{0, 1},
590 Flags: 0x00,
591 IsReply: 0x01,
592 Type: []byte{0, 0xcd},
593 ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
594 ErrorCode: []byte{0, 0, 0, 0},
595 },
596 },
597 wantErr: false,
598 },
599 {
600 name: "when path is not nested",
601 args: args{
602 cc: &ClientConn{
603 ID: &[]byte{0, 1},
604 Server: &Server{
605 Config: &Config{
606 FileRoot: "/Files",
607 },
608 },
609 },
610 t: NewTransaction(
611 tranNewFolder, &[]byte{0, 1},
612 NewField(fieldFileName, []byte("testFolder")),
613 ),
614 },
615 setup: func() {
616 mfs := MockFileStore{}
617 mfs.On("Mkdir", "/Files/testFolder", fs.FileMode(0777)).Return(nil)
618 mfs.On("Stat", "/Files/testFolder").Return(nil, os.ErrNotExist)
619 FS = mfs
620 },
621 wantRes: []Transaction{
622 {
623 clientID: &[]byte{0, 1},
624 Flags: 0x00,
625 IsReply: 0x01,
626 Type: []byte{0, 0xcd},
627 ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
628 ErrorCode: []byte{0, 0, 0, 0},
629 },
630 },
631 wantErr: false,
632 },
633 {
634 name: "when UnmarshalBinary returns an err",
635 args: args{
636 cc: &ClientConn{
637 ID: &[]byte{0, 1},
638 Server: &Server{
639 Config: &Config{
640 FileRoot: "/Files/",
641 },
642 },
643 },
644 t: NewTransaction(
645 tranNewFolder, &[]byte{0, 1},
646 NewField(fieldFileName, []byte("testFolder")),
647 NewField(fieldFilePath, []byte{
648 0x00,
649 }),
650 ),
651 },
652 setup: func() {
653 mfs := MockFileStore{}
654 mfs.On("Mkdir", "/Files/aaa/testFolder", fs.FileMode(0777)).Return(nil)
655 mfs.On("Stat", "/Files/aaa/testFolder").Return(nil, os.ErrNotExist)
656 FS = mfs
657 },
658 wantRes: []Transaction{},
659 wantErr: true,
660 },
661 {
662 name: "fieldFileName does not allow directory traversal",
663 args: args{
664 cc: &ClientConn{
665 ID: &[]byte{0, 1},
666 Server: &Server{
667 Config: &Config{
668 FileRoot: "/Files/",
669 },
670 },
671 },
672 t: NewTransaction(
673 tranNewFolder, &[]byte{0, 1},
674 NewField(fieldFileName, []byte("../../testFolder")),
675 ),
676 },
677 setup: func() {
678 mfs := MockFileStore{}
679 mfs.On("Mkdir", "/Files/testFolder", fs.FileMode(0777)).Return(nil)
680 mfs.On("Stat", "/Files/testFolder").Return(nil, os.ErrNotExist)
681 FS = mfs
682 },
683 wantRes: []Transaction{
684 {
685 clientID: &[]byte{0, 1},
686 Flags: 0x00,
687 IsReply: 0x01,
688 Type: []byte{0, 0xcd},
689 ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
690 ErrorCode: []byte{0, 0, 0, 0},
691 },
692 }, wantErr: false,
693 },
694 {
695 name: "fieldFilePath does not allow directory traversal",
696 args: args{
697 cc: &ClientConn{
698 ID: &[]byte{0, 1},
699 Server: &Server{
700 Config: &Config{
701 FileRoot: "/Files/",
702 },
703 },
704 },
705 t: NewTransaction(
706 tranNewFolder, &[]byte{0, 1},
707 NewField(fieldFileName, []byte("testFolder")),
708 NewField(fieldFilePath, []byte{
709 0x00, 0x02,
710 0x00, 0x00,
711 0x03,
712 0x2e, 0x2e, 0x2f,
713 0x00, 0x00,
714 0x03,
715 0x66, 0x6f, 0x6f,
716 }),
717 ),
718 },
719 setup: func() {
720 mfs := MockFileStore{}
721 mfs.On("Mkdir", "/Files/foo/testFolder", fs.FileMode(0777)).Return(nil)
722 mfs.On("Stat", "/Files/foo/testFolder").Return(nil, os.ErrNotExist)
723 FS = mfs
724 },
725 wantRes: []Transaction{
726 {
727 clientID: &[]byte{0, 1},
728 Flags: 0x00,
729 IsReply: 0x01,
730 Type: []byte{0, 0xcd},
731 ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
732 ErrorCode: []byte{0, 0, 0, 0},
733 },
734 }, wantErr: false,
735 },
736 }
737 for _, tt := range tests {
738 t.Run(tt.name, func(t *testing.T) {
739 tt.setup()
740
741 gotRes, err := HandleNewFolder(tt.args.cc, tt.args.t)
742 if (err != nil) != tt.wantErr {
743 t.Errorf("HandleNewFolder() error = %v, wantErr %v", err, tt.wantErr)
744 return
745 }
746 if !tranAssertEqual(t, tt.wantRes, gotRes) {
747 t.Errorf("HandleNewFolder() gotRes = %v, want %v", gotRes, tt.wantRes)
748 }
749 })
750 }
751 }
752
753 func TestHandleUploadFile(t *testing.T) {
754 type args struct {
755 cc *ClientConn
756 t *Transaction
757 }
758 tests := []struct {
759 name string
760 args args
761 wantRes []Transaction
762 wantErr bool
763 }{
764 {
765 name: "when request is valid",
766 args: args{
767 cc: &ClientConn{
768 Server: &Server{
769 FileTransfers: map[uint32]*FileTransfer{},
770 },
771 Account: &Account{
772 Access: func() *[]byte {
773 var bits accessBitmap
774 bits.Set(accessUploadFile)
775 access := bits[:]
776 return &access
777 }(),
778 },
779 },
780 t: NewTransaction(
781 tranUploadFile, &[]byte{0, 1},
782 NewField(fieldFileName, []byte("testFile")),
783 NewField(fieldFilePath, []byte{
784 0x00, 0x01,
785 0x00, 0x00,
786 0x03,
787 0x2e, 0x2e, 0x2f,
788 }),
789 ),
790 },
791 wantRes: []Transaction{
792 {
793 Flags: 0x00,
794 IsReply: 0x01,
795 Type: []byte{0, 0xcb},
796 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
797 ErrorCode: []byte{0, 0, 0, 0},
798 Fields: []Field{
799 NewField(fieldRefNum, []byte{0x52, 0xfd, 0xfc, 0x07}), // rand.Seed(1)
800 },
801 },
802 },
803 wantErr: false,
804 },
805 {
806 name: "when user does not have required access",
807 args: args{
808 cc: &ClientConn{
809 Account: &Account{
810 Access: func() *[]byte {
811 var bits accessBitmap
812 access := bits[:]
813 return &access
814 }(),
815 },
816 Server: &Server{
817 FileTransfers: map[uint32]*FileTransfer{},
818 },
819 },
820 t: NewTransaction(
821 tranUploadFile, &[]byte{0, 1},
822 NewField(fieldFileName, []byte("testFile")),
823 NewField(fieldFilePath, []byte{
824 0x00, 0x01,
825 0x00, 0x00,
826 0x03,
827 0x2e, 0x2e, 0x2f,
828 }),
829 ),
830 },
831 wantRes: []Transaction{
832 {
833 Flags: 0x00,
834 IsReply: 0x01,
835 Type: []byte{0, 0x00},
836 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
837 ErrorCode: []byte{0, 0, 0, 1},
838 Fields: []Field{
839 NewField(fieldError, []byte("You are not allowed to upload files.")), // rand.Seed(1)
840 },
841 },
842 },
843 wantErr: false,
844 },
845 }
846 for _, tt := range tests {
847 t.Run(tt.name, func(t *testing.T) {
848 rand.Seed(1)
849 gotRes, err := HandleUploadFile(tt.args.cc, tt.args.t)
850 if (err != nil) != tt.wantErr {
851 t.Errorf("HandleUploadFile() error = %v, wantErr %v", err, tt.wantErr)
852 return
853 }
854 if !tranAssertEqual(t, tt.wantRes, gotRes) {
855 t.Errorf("HandleUploadFile() gotRes = %v, want %v", gotRes, tt.wantRes)
856 }
857 })
858 }
859 }
860
861 func TestHandleMakeAlias(t *testing.T) {
862 type args struct {
863 cc *ClientConn
864 t *Transaction
865 }
866 tests := []struct {
867 name string
868 setup func()
869 args args
870 wantRes []Transaction
871 wantErr bool
872 }{
873 {
874 name: "with valid input and required permissions",
875 setup: func() {
876 mfs := MockFileStore{}
877 path, _ := os.Getwd()
878 mfs.On(
879 "Symlink",
880 path+"/test/config/Files/foo/testFile",
881 path+"/test/config/Files/bar/testFile",
882 ).Return(nil)
883 FS = mfs
884 },
885 args: args{
886 cc: &ClientConn{
887 Account: &Account{
888 Access: func() *[]byte {
889 var bits accessBitmap
890 bits.Set(accessMakeAlias)
891 access := bits[:]
892 return &access
893 }(),
894 },
895 Server: &Server{
896 Config: &Config{
897 FileRoot: func() string {
898 path, _ := os.Getwd()
899 return path + "/test/config/Files"
900 }(),
901 },
902 Logger: NewTestLogger(),
903 },
904 },
905 t: NewTransaction(
906 tranMakeFileAlias, &[]byte{0, 1},
907 NewField(fieldFileName, []byte("testFile")),
908 NewField(fieldFilePath, EncodeFilePath(strings.Join([]string{"foo"}, "/"))),
909 NewField(fieldFileNewPath, EncodeFilePath(strings.Join([]string{"bar"}, "/"))),
910 ),
911 },
912 wantRes: []Transaction{
913 {
914 Flags: 0x00,
915 IsReply: 0x01,
916 Type: []byte{0, 0xd1},
917 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
918 ErrorCode: []byte{0, 0, 0, 0},
919 Fields: []Field(nil),
920 },
921 },
922 wantErr: false,
923 },
924 {
925 name: "when symlink returns an error",
926 setup: func() {
927 mfs := MockFileStore{}
928 path, _ := os.Getwd()
929 mfs.On(
930 "Symlink",
931 path+"/test/config/Files/foo/testFile",
932 path+"/test/config/Files/bar/testFile",
933 ).Return(errors.New("ohno"))
934 FS = mfs
935 },
936 args: args{
937 cc: &ClientConn{
938 Account: &Account{
939 Access: func() *[]byte {
940 var bits accessBitmap
941 bits.Set(accessMakeAlias)
942 access := bits[:]
943 return &access
944 }(),
945 },
946 Server: &Server{
947 Config: &Config{
948 FileRoot: func() string {
949 path, _ := os.Getwd()
950 return path + "/test/config/Files"
951 }(),
952 },
953 Logger: NewTestLogger(),
954 },
955 },
956 t: NewTransaction(
957 tranMakeFileAlias, &[]byte{0, 1},
958 NewField(fieldFileName, []byte("testFile")),
959 NewField(fieldFilePath, EncodeFilePath(strings.Join([]string{"foo"}, "/"))),
960 NewField(fieldFileNewPath, EncodeFilePath(strings.Join([]string{"bar"}, "/"))),
961 ),
962 },
963 wantRes: []Transaction{
964 {
965 Flags: 0x00,
966 IsReply: 0x01,
967 Type: []byte{0, 0x00},
968 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
969 ErrorCode: []byte{0, 0, 0, 1},
970 Fields: []Field{
971 NewField(fieldError, []byte("Error creating alias")),
972 },
973 },
974 },
975 wantErr: false,
976 },
977 {
978 name: "when user does not have required permission",
979 setup: func() {},
980 args: args{
981 cc: &ClientConn{
982 Account: &Account{
983 Access: func() *[]byte {
984 var bits accessBitmap
985 access := bits[:]
986 return &access
987 }(),
988 },
989 Server: &Server{
990 Config: &Config{
991 FileRoot: func() string {
992 path, _ := os.Getwd()
993 return path + "/test/config/Files"
994 }(),
995 },
996 },
997 },
998 t: NewTransaction(
999 tranMakeFileAlias, &[]byte{0, 1},
1000 NewField(fieldFileName, []byte("testFile")),
1001 NewField(fieldFilePath, []byte{
1002 0x00, 0x01,
1003 0x00, 0x00,
1004 0x03,
1005 0x2e, 0x2e, 0x2e,
1006 }),
1007 NewField(fieldFileNewPath, []byte{
1008 0x00, 0x01,
1009 0x00, 0x00,
1010 0x03,
1011 0x2e, 0x2e, 0x2e,
1012 }),
1013 ),
1014 },
1015 wantRes: []Transaction{
1016 {
1017 Flags: 0x00,
1018 IsReply: 0x01,
1019 Type: []byte{0, 0x00},
1020 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
1021 ErrorCode: []byte{0, 0, 0, 1},
1022 Fields: []Field{
1023 NewField(fieldError, []byte("You are not allowed to make aliases.")),
1024 },
1025 },
1026 },
1027 wantErr: false,
1028 },
1029 }
1030 for _, tt := range tests {
1031 t.Run(tt.name, func(t *testing.T) {
1032 tt.setup()
1033
1034 gotRes, err := HandleMakeAlias(tt.args.cc, tt.args.t)
1035 if (err != nil) != tt.wantErr {
1036 t.Errorf("HandleMakeAlias(%v, %v)", tt.args.cc, tt.args.t)
1037 return
1038 }
1039
1040 tranAssertEqual(t, tt.wantRes, gotRes)
1041 })
1042 }
1043 }