]> git.r.bdr.sh - rbdr/mobius/blob - hotline/transaction_handlers_test.go
Exclude unsupported >4GiB files from file listings
[rbdr/mobius] / hotline / transaction_handlers_test.go
1 package hotline
2
3 import (
4 "errors"
5 "fmt"
6 "github.com/stretchr/testify/assert"
7 "io/fs"
8 "math/rand"
9 "os"
10 "strings"
11 "testing"
12 )
13
14 func TestHandleSetChatSubject(t *testing.T) {
15 type args struct {
16 cc *ClientConn
17 t *Transaction
18 }
19 tests := []struct {
20 name string
21 args args
22 want []Transaction
23 wantErr bool
24 }{
25 {
26 name: "sends chat subject to private chat members",
27 args: args{
28 cc: &ClientConn{
29 UserName: []byte{0x00, 0x01},
30 Server: &Server{
31 PrivateChats: map[uint32]*PrivateChat{
32 uint32(1): {
33 Subject: "unset",
34 ClientConn: map[uint16]*ClientConn{
35 uint16(1): {
36 Account: &Account{
37 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
38 },
39 ID: &[]byte{0, 1},
40 },
41 uint16(2): {
42 Account: &Account{
43 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
44 },
45 ID: &[]byte{0, 2},
46 },
47 },
48 },
49 },
50 Clients: map[uint16]*ClientConn{
51 uint16(1): {
52 Account: &Account{
53 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
54 },
55 ID: &[]byte{0, 1},
56 },
57 uint16(2): {
58 Account: &Account{
59 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
60 },
61 ID: &[]byte{0, 2},
62 },
63 },
64 },
65 },
66 t: &Transaction{
67 Flags: 0x00,
68 IsReply: 0x00,
69 Type: []byte{0, 0x6a},
70 ID: []byte{0, 0, 0, 1},
71 ErrorCode: []byte{0, 0, 0, 0},
72 Fields: []Field{
73 NewField(fieldChatID, []byte{0, 0, 0, 1}),
74 NewField(fieldChatSubject, []byte("Test Subject")),
75 },
76 },
77 },
78 want: []Transaction{
79 {
80 clientID: &[]byte{0, 1},
81 Flags: 0x00,
82 IsReply: 0x00,
83 Type: []byte{0, 0x77},
84 ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
85 ErrorCode: []byte{0, 0, 0, 0},
86 Fields: []Field{
87 NewField(fieldChatID, []byte{0, 0, 0, 1}),
88 NewField(fieldChatSubject, []byte("Test Subject")),
89 },
90 },
91 {
92 clientID: &[]byte{0, 2},
93 Flags: 0x00,
94 IsReply: 0x00,
95 Type: []byte{0, 0x77},
96 ID: []byte{0xf0, 0xc5, 0x34, 0x1e}, // Random ID from rand.Seed(1)
97 ErrorCode: []byte{0, 0, 0, 0},
98 Fields: []Field{
99 NewField(fieldChatID, []byte{0, 0, 0, 1}),
100 NewField(fieldChatSubject, []byte("Test Subject")),
101 },
102 },
103 },
104 wantErr: false,
105 },
106 }
107 for _, tt := range tests {
108 rand.Seed(1) // reset seed between tests to make transaction IDs predictable
109
110 t.Run(tt.name, func(t *testing.T) {
111 got, err := HandleSetChatSubject(tt.args.cc, tt.args.t)
112 if (err != nil) != tt.wantErr {
113 t.Errorf("HandleSetChatSubject() error = %v, wantErr %v", err, tt.wantErr)
114 return
115 }
116 if !assert.Equal(t, tt.want, got) {
117 t.Errorf("HandleSetChatSubject() got = %v, want %v", got, tt.want)
118 }
119 })
120 }
121 }
122
123 func TestHandleLeaveChat(t *testing.T) {
124 type args struct {
125 cc *ClientConn
126 t *Transaction
127 }
128 tests := []struct {
129 name string
130 args args
131 want []Transaction
132 wantErr bool
133 }{
134 {
135 name: "returns expected transactions",
136 args: args{
137 cc: &ClientConn{
138 ID: &[]byte{0, 2},
139 Server: &Server{
140 PrivateChats: map[uint32]*PrivateChat{
141 uint32(1): {
142 ClientConn: map[uint16]*ClientConn{
143 uint16(1): {
144 Account: &Account{
145 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
146 },
147 ID: &[]byte{0, 1},
148 },
149 uint16(2): {
150 Account: &Account{
151 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
152 },
153 ID: &[]byte{0, 2},
154 },
155 },
156 },
157 },
158 Clients: map[uint16]*ClientConn{
159 uint16(1): {
160 Account: &Account{
161 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
162 },
163 ID: &[]byte{0, 1},
164 },
165 uint16(2): {
166 Account: &Account{
167 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
168 },
169 ID: &[]byte{0, 2},
170 },
171 },
172 },
173 },
174 t: NewTransaction(tranDeleteUser, nil, NewField(fieldChatID, []byte{0, 0, 0, 1})),
175 },
176 want: []Transaction{
177 {
178 clientID: &[]byte{0, 1},
179 Flags: 0x00,
180 IsReply: 0x00,
181 Type: []byte{0, 0x76},
182 ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
183 ErrorCode: []byte{0, 0, 0, 0},
184 Fields: []Field{
185 NewField(fieldChatID, []byte{0, 0, 0, 1}),
186 NewField(fieldUserID, []byte{0, 2}),
187 },
188 },
189 },
190 wantErr: false,
191 },
192 }
193 for _, tt := range tests {
194 rand.Seed(1)
195 t.Run(tt.name, func(t *testing.T) {
196 got, err := HandleLeaveChat(tt.args.cc, tt.args.t)
197 if (err != nil) != tt.wantErr {
198 t.Errorf("HandleLeaveChat() error = %v, wantErr %v", err, tt.wantErr)
199 return
200 }
201 if !assert.Equal(t, tt.want, got) {
202 t.Errorf("HandleLeaveChat() got = %v, want %v", got, tt.want)
203 }
204 })
205 }
206 }
207
208 func TestHandleGetUserNameList(t *testing.T) {
209 type args struct {
210 cc *ClientConn
211 t *Transaction
212 }
213 tests := []struct {
214 name string
215 args args
216 want []Transaction
217 wantErr bool
218 }{
219 {
220 name: "replies with userlist transaction",
221 args: args{
222 cc: &ClientConn{
223
224 ID: &[]byte{1, 1},
225 Server: &Server{
226 Clients: map[uint16]*ClientConn{
227 uint16(1): {
228 ID: &[]byte{0, 1},
229 Icon: &[]byte{0, 2},
230 Flags: &[]byte{0, 3},
231 UserName: []byte{0, 4},
232 Agreed: true,
233 },
234 uint16(2): {
235 ID: &[]byte{0, 2},
236 Icon: &[]byte{0, 2},
237 Flags: &[]byte{0, 3},
238 UserName: []byte{0, 4},
239 Agreed: true,
240 },
241 uint16(3): {
242 ID: &[]byte{0, 3},
243 Icon: &[]byte{0, 2},
244 Flags: &[]byte{0, 3},
245 UserName: []byte{0, 4},
246 Agreed: false,
247 },
248 },
249 },
250 },
251 t: &Transaction{
252 ID: []byte{0, 0, 0, 1},
253 Type: []byte{0, 1},
254 },
255 },
256 want: []Transaction{
257 {
258 clientID: &[]byte{1, 1},
259 Flags: 0x00,
260 IsReply: 0x01,
261 Type: []byte{0, 1},
262 ID: []byte{0, 0, 0, 1},
263 ErrorCode: []byte{0, 0, 0, 0},
264 Fields: []Field{
265 NewField(
266 fieldUsernameWithInfo,
267 []byte{00, 01, 00, 02, 00, 03, 00, 02, 00, 04},
268 ),
269 NewField(
270 fieldUsernameWithInfo,
271 []byte{00, 02, 00, 02, 00, 03, 00, 02, 00, 04},
272 ),
273 },
274 },
275 },
276 wantErr: false,
277 },
278 }
279 for _, tt := range tests {
280 t.Run(tt.name, func(t *testing.T) {
281 got, err := HandleGetUserNameList(tt.args.cc, tt.args.t)
282 if (err != nil) != tt.wantErr {
283 t.Errorf("HandleGetUserNameList() error = %v, wantErr %v", err, tt.wantErr)
284 return
285 }
286 assert.Equal(t, tt.want, got)
287 })
288 }
289 }
290
291 func TestHandleChatSend(t *testing.T) {
292 type args struct {
293 cc *ClientConn
294 t *Transaction
295 }
296 tests := []struct {
297 name string
298 args args
299 want []Transaction
300 wantErr bool
301 }{
302 {
303 name: "sends chat msg transaction to all clients",
304 args: args{
305 cc: &ClientConn{
306 Account: &Account{
307 Access: func() *[]byte {
308 var bits accessBitmap
309 bits.Set(accessSendChat)
310 access := bits[:]
311 return &access
312 }(),
313 },
314 UserName: []byte{0x00, 0x01},
315 Server: &Server{
316 Clients: map[uint16]*ClientConn{
317 uint16(1): {
318 Account: &Account{
319 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
320 },
321 ID: &[]byte{0, 1},
322 },
323 uint16(2): {
324 Account: &Account{
325 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
326 },
327 ID: &[]byte{0, 2},
328 },
329 },
330 },
331 },
332 t: &Transaction{
333 Fields: []Field{
334 NewField(fieldData, []byte("hai")),
335 },
336 },
337 },
338 want: []Transaction{
339 {
340 clientID: &[]byte{0, 1},
341 Flags: 0x00,
342 IsReply: 0x00,
343 Type: []byte{0, 0x6a},
344 ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
345 ErrorCode: []byte{0, 0, 0, 0},
346 Fields: []Field{
347 NewField(fieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
348 },
349 },
350 {
351 clientID: &[]byte{0, 2},
352 Flags: 0x00,
353 IsReply: 0x00,
354 Type: []byte{0, 0x6a},
355 ID: []byte{0xf0, 0xc5, 0x34, 0x1e}, // Random ID from rand.Seed(1)
356 ErrorCode: []byte{0, 0, 0, 0},
357 Fields: []Field{
358 NewField(fieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
359 },
360 },
361 },
362 wantErr: false,
363 },
364 {
365 name: "when user does not have required permission",
366 args: args{
367 cc: &ClientConn{
368 Account: &Account{
369 Access: func() *[]byte {
370 var bits accessBitmap
371 access := bits[:]
372 return &access
373 }(),
374 },
375 Server: &Server{
376 Accounts: map[string]*Account{},
377 },
378 },
379 t: NewTransaction(
380 tranChatSend, &[]byte{0, 1},
381 NewField(fieldData, []byte("hai")),
382 ),
383 },
384 want: []Transaction{
385 {
386 Flags: 0x00,
387 IsReply: 0x01,
388 Type: []byte{0, 0x00},
389 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
390 ErrorCode: []byte{0, 0, 0, 1},
391 Fields: []Field{
392 NewField(fieldError, []byte("You are not allowed to participate in chat.")),
393 },
394 },
395 },
396 wantErr: false,
397 },
398 {
399 name: "sends chat msg as emote if fieldChatOptions is set",
400 args: args{
401 cc: &ClientConn{
402 Account: &Account{
403 Access: func() *[]byte {
404 var bits accessBitmap
405 bits.Set(accessSendChat)
406 access := bits[:]
407 return &access
408 }(),
409 },
410 UserName: []byte("Testy McTest"),
411 Server: &Server{
412 Clients: map[uint16]*ClientConn{
413 uint16(1): {
414 Account: &Account{
415 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
416 },
417 ID: &[]byte{0, 1},
418 },
419 uint16(2): {
420 Account: &Account{
421 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
422 },
423 ID: &[]byte{0, 2},
424 },
425 },
426 },
427 },
428 t: &Transaction{
429 Fields: []Field{
430 NewField(fieldData, []byte("performed action")),
431 NewField(fieldChatOptions, []byte{0x00, 0x01}),
432 },
433 },
434 },
435 want: []Transaction{
436 {
437 clientID: &[]byte{0, 1},
438 Flags: 0x00,
439 IsReply: 0x00,
440 Type: []byte{0, 0x6a},
441 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
442 ErrorCode: []byte{0, 0, 0, 0},
443 Fields: []Field{
444 NewField(fieldData, []byte("\r*** Testy McTest performed action")),
445 },
446 },
447 {
448 clientID: &[]byte{0, 2},
449 Flags: 0x00,
450 IsReply: 0x00,
451 Type: []byte{0, 0x6a},
452 ID: []byte{0xf0, 0xc5, 0x34, 0x1e},
453 ErrorCode: []byte{0, 0, 0, 0},
454 Fields: []Field{
455 NewField(fieldData, []byte("\r*** Testy McTest performed action")),
456 },
457 },
458 },
459 wantErr: false,
460 },
461 {
462 name: "only sends chat msg to clients with accessReadChat permission",
463 args: args{
464 cc: &ClientConn{
465 Account: &Account{
466 Access: func() *[]byte {
467 var bits accessBitmap
468 bits.Set(accessSendChat)
469 access := bits[:]
470 return &access
471 }(),
472 },
473 UserName: []byte{0x00, 0x01},
474 Server: &Server{
475 Clients: map[uint16]*ClientConn{
476 uint16(1): {
477 Account: &Account{
478 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
479 },
480 ID: &[]byte{0, 1},
481 },
482 uint16(2): {
483 Account: &Account{
484 Access: &[]byte{0, 0, 0, 0, 0, 0, 0, 0},
485 },
486 ID: &[]byte{0, 2},
487 },
488 },
489 },
490 },
491 t: &Transaction{
492 Fields: []Field{
493 NewField(fieldData, []byte("hai")),
494 },
495 },
496 },
497 want: []Transaction{
498 {
499 clientID: &[]byte{0, 1},
500 Flags: 0x00,
501 IsReply: 0x00,
502 Type: []byte{0, 0x6a},
503 ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
504 ErrorCode: []byte{0, 0, 0, 0},
505 Fields: []Field{
506 NewField(fieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
507 },
508 },
509 },
510 wantErr: false,
511 },
512 {
513 name: "only sends private chat msg to members of private chat",
514 args: args{
515 cc: &ClientConn{
516 Account: &Account{
517 Access: func() *[]byte {
518 var bits accessBitmap
519 bits.Set(accessSendChat)
520 access := bits[:]
521 return &access
522 }(),
523 },
524 UserName: []byte{0x00, 0x01},
525 Server: &Server{
526 PrivateChats: map[uint32]*PrivateChat{
527 uint32(1): {
528 ClientConn: map[uint16]*ClientConn{
529 uint16(1): {
530 ID: &[]byte{0, 1},
531 },
532 uint16(2): {
533 ID: &[]byte{0, 2},
534 },
535 },
536 },
537 },
538 Clients: map[uint16]*ClientConn{
539 uint16(1): {
540 Account: &Account{
541 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
542 },
543 ID: &[]byte{0, 1},
544 },
545 uint16(2): {
546 Account: &Account{
547 Access: &[]byte{0, 0, 0, 0, 0, 0, 0, 0},
548 },
549 ID: &[]byte{0, 2},
550 },
551 uint16(3): {
552 Account: &Account{
553 Access: &[]byte{0, 0, 0, 0, 0, 0, 0, 0},
554 },
555 ID: &[]byte{0, 3},
556 },
557 },
558 },
559 },
560 t: &Transaction{
561 Fields: []Field{
562 NewField(fieldData, []byte("hai")),
563 NewField(fieldChatID, []byte{0, 0, 0, 1}),
564 },
565 },
566 },
567 want: []Transaction{
568 {
569 clientID: &[]byte{0, 1},
570 Flags: 0x00,
571 IsReply: 0x00,
572 Type: []byte{0, 0x6a},
573 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
574 ErrorCode: []byte{0, 0, 0, 0},
575 Fields: []Field{
576 NewField(fieldChatID, []byte{0, 0, 0, 1}),
577 NewField(fieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
578 },
579 },
580 {
581 clientID: &[]byte{0, 2},
582 Flags: 0x00,
583 IsReply: 0x00,
584 Type: []byte{0, 0x6a},
585 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
586 ErrorCode: []byte{0, 0, 0, 0},
587 Fields: []Field{
588 NewField(fieldChatID, []byte{0, 0, 0, 1}),
589 NewField(fieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
590 },
591 },
592 },
593 wantErr: false,
594 },
595 }
596 for _, tt := range tests {
597 t.Run(tt.name, func(t *testing.T) {
598 got, err := HandleChatSend(tt.args.cc, tt.args.t)
599
600 if (err != nil) != tt.wantErr {
601 t.Errorf("HandleChatSend() error = %v, wantErr %v", err, tt.wantErr)
602 return
603 }
604 tranAssertEqual(t, tt.want, got)
605 })
606 }
607 }
608
609 func TestHandleGetFileInfo(t *testing.T) {
610 rand.Seed(1) // reset seed between tests to make transaction IDs predictable
611
612 type args struct {
613 cc *ClientConn
614 t *Transaction
615 }
616 tests := []struct {
617 name string
618 args args
619 wantRes []Transaction
620 wantErr bool
621 }{
622 {
623 name: "returns expected fields when a valid file is requested",
624 args: args{
625 cc: &ClientConn{
626 ID: &[]byte{0x00, 0x01},
627 Server: &Server{
628 Config: &Config{
629 FileRoot: func() string {
630 path, _ := os.Getwd()
631 return path + "/test/config/Files"
632 }(),
633 },
634 },
635 },
636 t: NewTransaction(
637 tranGetFileInfo, nil,
638 NewField(fieldFileName, []byte("testfile.txt")),
639 NewField(fieldFilePath, []byte{0x00, 0x00}),
640 ),
641 },
642 wantRes: []Transaction{
643 {
644 clientID: &[]byte{0, 1},
645 Flags: 0x00,
646 IsReply: 0x01,
647 Type: []byte{0, 0xce},
648 ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
649 ErrorCode: []byte{0, 0, 0, 0},
650 Fields: []Field{
651 NewField(fieldFileName, []byte("testfile.txt")),
652 NewField(fieldFileTypeString, []byte("Text File")),
653 NewField(fieldFileCreatorString, []byte("ttxt")),
654 NewField(fieldFileComment, []byte{}),
655 NewField(fieldFileType, []byte("TEXT")),
656 NewField(fieldFileCreateDate, make([]byte, 8)),
657 NewField(fieldFileModifyDate, make([]byte, 8)),
658 NewField(fieldFileSize, []byte{0x0, 0x0, 0x0, 0x17}),
659 },
660 },
661 },
662 wantErr: false,
663 },
664 }
665 for _, tt := range tests {
666 t.Run(tt.name, func(t *testing.T) {
667 rand.Seed(1) // reset seed between tests to make transaction IDs predictable
668
669 gotRes, err := HandleGetFileInfo(tt.args.cc, tt.args.t)
670 if (err != nil) != tt.wantErr {
671 t.Errorf("HandleGetFileInfo() error = %v, wantErr %v", err, tt.wantErr)
672 return
673 }
674
675 // Clear the file timestamp fields to work around problems running the tests in multiple timezones
676 // TODO: revisit how to test this by mocking the stat calls
677 gotRes[0].Fields[5].Data = make([]byte, 8)
678 gotRes[0].Fields[6].Data = make([]byte, 8)
679 if !assert.Equal(t, tt.wantRes, gotRes) {
680 t.Errorf("HandleGetFileInfo() gotRes = %v, want %v", gotRes, tt.wantRes)
681 }
682 })
683 }
684 }
685
686 func TestHandleNewFolder(t *testing.T) {
687 type args struct {
688 cc *ClientConn
689 t *Transaction
690 }
691 tests := []struct {
692 setup func()
693 name string
694 args args
695 wantRes []Transaction
696 wantErr bool
697 }{
698 {
699 name: "without required permission",
700 setup: func() {},
701 args: args{
702 cc: &ClientConn{
703 Account: &Account{
704 Access: func() *[]byte {
705 var bits accessBitmap
706 access := bits[:]
707 return &access
708 }(),
709 },
710 },
711 t: NewTransaction(
712 accessCreateFolder,
713 &[]byte{0, 0},
714 ),
715 },
716 wantRes: []Transaction{
717 {
718 Flags: 0x00,
719 IsReply: 0x01,
720 Type: []byte{0, 0x00},
721 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
722 ErrorCode: []byte{0, 0, 0, 1},
723 Fields: []Field{
724 NewField(fieldError, []byte("You are not allowed to create folders.")),
725 },
726 },
727 },
728 wantErr: false,
729 },
730 {
731 name: "when path is nested",
732 args: args{
733 cc: &ClientConn{
734 Account: &Account{
735 Access: func() *[]byte {
736 var bits accessBitmap
737 bits.Set(accessCreateFolder)
738 access := bits[:]
739 return &access
740 }(),
741 },
742 ID: &[]byte{0, 1},
743 Server: &Server{
744 Config: &Config{
745 FileRoot: "/Files/",
746 },
747 },
748 },
749 t: NewTransaction(
750 tranNewFolder, &[]byte{0, 1},
751 NewField(fieldFileName, []byte("testFolder")),
752 NewField(fieldFilePath, []byte{
753 0x00, 0x01,
754 0x00, 0x00,
755 0x03,
756 0x61, 0x61, 0x61,
757 }),
758 ),
759 },
760 setup: func() {
761 mfs := &MockFileStore{}
762 mfs.On("Mkdir", "/Files/aaa/testFolder", fs.FileMode(0777)).Return(nil)
763 mfs.On("Stat", "/Files/aaa/testFolder").Return(nil, os.ErrNotExist)
764 FS = mfs
765 },
766 wantRes: []Transaction{
767 {
768 clientID: &[]byte{0, 1},
769 Flags: 0x00,
770 IsReply: 0x01,
771 Type: []byte{0, 0xcd},
772 ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
773 ErrorCode: []byte{0, 0, 0, 0},
774 },
775 },
776 wantErr: false,
777 },
778 {
779 name: "when path is not nested",
780 args: args{
781 cc: &ClientConn{
782 Account: &Account{
783 Access: func() *[]byte {
784 var bits accessBitmap
785 bits.Set(accessCreateFolder)
786 access := bits[:]
787 return &access
788 }(),
789 },
790 ID: &[]byte{0, 1},
791 Server: &Server{
792 Config: &Config{
793 FileRoot: "/Files",
794 },
795 },
796 },
797 t: NewTransaction(
798 tranNewFolder, &[]byte{0, 1},
799 NewField(fieldFileName, []byte("testFolder")),
800 ),
801 },
802 setup: func() {
803 mfs := &MockFileStore{}
804 mfs.On("Mkdir", "/Files/testFolder", fs.FileMode(0777)).Return(nil)
805 mfs.On("Stat", "/Files/testFolder").Return(nil, os.ErrNotExist)
806 FS = mfs
807 },
808 wantRes: []Transaction{
809 {
810 clientID: &[]byte{0, 1},
811 Flags: 0x00,
812 IsReply: 0x01,
813 Type: []byte{0, 0xcd},
814 ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
815 ErrorCode: []byte{0, 0, 0, 0},
816 },
817 },
818 wantErr: false,
819 },
820 {
821 name: "when UnmarshalBinary returns an err",
822 args: args{
823 cc: &ClientConn{
824 Account: &Account{
825 Access: func() *[]byte {
826 var bits accessBitmap
827 bits.Set(accessCreateFolder)
828 access := bits[:]
829 return &access
830 }(),
831 },
832 ID: &[]byte{0, 1},
833 Server: &Server{
834 Config: &Config{
835 FileRoot: "/Files/",
836 },
837 },
838 },
839 t: NewTransaction(
840 tranNewFolder, &[]byte{0, 1},
841 NewField(fieldFileName, []byte("testFolder")),
842 NewField(fieldFilePath, []byte{
843 0x00,
844 }),
845 ),
846 },
847 setup: func() {
848 mfs := &MockFileStore{}
849 mfs.On("Mkdir", "/Files/aaa/testFolder", fs.FileMode(0777)).Return(nil)
850 mfs.On("Stat", "/Files/aaa/testFolder").Return(nil, os.ErrNotExist)
851 FS = mfs
852 },
853 wantRes: []Transaction{},
854 wantErr: true,
855 },
856 {
857 name: "fieldFileName does not allow directory traversal",
858 args: args{
859 cc: &ClientConn{
860 Account: &Account{
861 Access: func() *[]byte {
862 var bits accessBitmap
863 bits.Set(accessCreateFolder)
864 access := bits[:]
865 return &access
866 }(),
867 },
868 ID: &[]byte{0, 1},
869 Server: &Server{
870 Config: &Config{
871 FileRoot: "/Files/",
872 },
873 },
874 },
875 t: NewTransaction(
876 tranNewFolder, &[]byte{0, 1},
877 NewField(fieldFileName, []byte("../../testFolder")),
878 ),
879 },
880 setup: func() {
881 mfs := &MockFileStore{}
882 mfs.On("Mkdir", "/Files/testFolder", fs.FileMode(0777)).Return(nil)
883 mfs.On("Stat", "/Files/testFolder").Return(nil, os.ErrNotExist)
884 FS = mfs
885 },
886 wantRes: []Transaction{
887 {
888 clientID: &[]byte{0, 1},
889 Flags: 0x00,
890 IsReply: 0x01,
891 Type: []byte{0, 0xcd},
892 ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
893 ErrorCode: []byte{0, 0, 0, 0},
894 },
895 }, wantErr: false,
896 },
897 {
898 name: "fieldFilePath does not allow directory traversal",
899 args: args{
900 cc: &ClientConn{
901 Account: &Account{
902 Access: func() *[]byte {
903 var bits accessBitmap
904 bits.Set(accessCreateFolder)
905 access := bits[:]
906 return &access
907 }(),
908 },
909 ID: &[]byte{0, 1},
910 Server: &Server{
911 Config: &Config{
912 FileRoot: "/Files/",
913 },
914 },
915 },
916 t: NewTransaction(
917 tranNewFolder, &[]byte{0, 1},
918 NewField(fieldFileName, []byte("testFolder")),
919 NewField(fieldFilePath, []byte{
920 0x00, 0x02,
921 0x00, 0x00,
922 0x03,
923 0x2e, 0x2e, 0x2f,
924 0x00, 0x00,
925 0x03,
926 0x66, 0x6f, 0x6f,
927 }),
928 ),
929 },
930 setup: func() {
931 mfs := &MockFileStore{}
932 mfs.On("Mkdir", "/Files/foo/testFolder", fs.FileMode(0777)).Return(nil)
933 mfs.On("Stat", "/Files/foo/testFolder").Return(nil, os.ErrNotExist)
934 FS = mfs
935 },
936 wantRes: []Transaction{
937 {
938 clientID: &[]byte{0, 1},
939 Flags: 0x00,
940 IsReply: 0x01,
941 Type: []byte{0, 0xcd},
942 ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
943 ErrorCode: []byte{0, 0, 0, 0},
944 },
945 }, wantErr: false,
946 },
947 }
948 for _, tt := range tests {
949 t.Run(tt.name, func(t *testing.T) {
950 tt.setup()
951
952 gotRes, err := HandleNewFolder(tt.args.cc, tt.args.t)
953 if (err != nil) != tt.wantErr {
954 t.Errorf("HandleNewFolder() error = %v, wantErr %v", err, tt.wantErr)
955 return
956 }
957
958 if !tranAssertEqual(t, tt.wantRes, gotRes) {
959 t.Errorf("HandleNewFolder() gotRes = %v, want %v", gotRes, tt.wantRes)
960 }
961 })
962 }
963 }
964
965 func TestHandleUploadFile(t *testing.T) {
966 type args struct {
967 cc *ClientConn
968 t *Transaction
969 }
970 tests := []struct {
971 name string
972 args args
973 wantRes []Transaction
974 wantErr bool
975 }{
976 {
977 name: "when request is valid and user has Upload Anywhere permission",
978 args: args{
979 cc: &ClientConn{
980 Server: &Server{
981 FileTransfers: map[uint32]*FileTransfer{},
982 },
983 Account: &Account{
984 Access: func() *[]byte {
985 var bits accessBitmap
986 bits.Set(accessUploadFile)
987 bits.Set(accessUploadAnywhere)
988 access := bits[:]
989 return &access
990 }(),
991 },
992 },
993 t: NewTransaction(
994 tranUploadFile, &[]byte{0, 1},
995 NewField(fieldFileName, []byte("testFile")),
996 NewField(fieldFilePath, []byte{
997 0x00, 0x01,
998 0x00, 0x00,
999 0x03,
1000 0x2e, 0x2e, 0x2f,
1001 }),
1002 ),
1003 },
1004 wantRes: []Transaction{
1005 {
1006 Flags: 0x00,
1007 IsReply: 0x01,
1008 Type: []byte{0, 0xcb},
1009 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
1010 ErrorCode: []byte{0, 0, 0, 0},
1011 Fields: []Field{
1012 NewField(fieldRefNum, []byte{0x52, 0xfd, 0xfc, 0x07}), // rand.Seed(1)
1013 },
1014 },
1015 },
1016 wantErr: false,
1017 },
1018 {
1019 name: "when user does not have required access",
1020 args: args{
1021 cc: &ClientConn{
1022 Account: &Account{
1023 Access: func() *[]byte {
1024 var bits accessBitmap
1025 access := bits[:]
1026 return &access
1027 }(),
1028 },
1029 Server: &Server{
1030 FileTransfers: map[uint32]*FileTransfer{},
1031 },
1032 },
1033 t: NewTransaction(
1034 tranUploadFile, &[]byte{0, 1},
1035 NewField(fieldFileName, []byte("testFile")),
1036 NewField(fieldFilePath, []byte{
1037 0x00, 0x01,
1038 0x00, 0x00,
1039 0x03,
1040 0x2e, 0x2e, 0x2f,
1041 }),
1042 ),
1043 },
1044 wantRes: []Transaction{
1045 {
1046 Flags: 0x00,
1047 IsReply: 0x01,
1048 Type: []byte{0, 0x00},
1049 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
1050 ErrorCode: []byte{0, 0, 0, 1},
1051 Fields: []Field{
1052 NewField(fieldError, []byte("You are not allowed to upload files.")), // rand.Seed(1)
1053 },
1054 },
1055 },
1056 wantErr: false,
1057 },
1058 }
1059 for _, tt := range tests {
1060 t.Run(tt.name, func(t *testing.T) {
1061 rand.Seed(1)
1062 gotRes, err := HandleUploadFile(tt.args.cc, tt.args.t)
1063 if (err != nil) != tt.wantErr {
1064 t.Errorf("HandleUploadFile() error = %v, wantErr %v", err, tt.wantErr)
1065 return
1066 }
1067
1068 tranAssertEqual(t, tt.wantRes, gotRes)
1069
1070 })
1071 }
1072 }
1073
1074 func TestHandleMakeAlias(t *testing.T) {
1075 type args struct {
1076 cc *ClientConn
1077 t *Transaction
1078 }
1079 tests := []struct {
1080 name string
1081 setup func()
1082 args args
1083 wantRes []Transaction
1084 wantErr bool
1085 }{
1086 {
1087 name: "with valid input and required permissions",
1088 setup: func() {
1089 mfs := &MockFileStore{}
1090 path, _ := os.Getwd()
1091 mfs.On(
1092 "Symlink",
1093 path+"/test/config/Files/foo/testFile",
1094 path+"/test/config/Files/bar/testFile",
1095 ).Return(nil)
1096 FS = mfs
1097 },
1098 args: args{
1099 cc: &ClientConn{
1100 Account: &Account{
1101 Access: func() *[]byte {
1102 var bits accessBitmap
1103 bits.Set(accessMakeAlias)
1104 access := bits[:]
1105 return &access
1106 }(),
1107 },
1108 Server: &Server{
1109 Config: &Config{
1110 FileRoot: func() string {
1111 path, _ := os.Getwd()
1112 return path + "/test/config/Files"
1113 }(),
1114 },
1115 Logger: NewTestLogger(),
1116 },
1117 },
1118 t: NewTransaction(
1119 tranMakeFileAlias, &[]byte{0, 1},
1120 NewField(fieldFileName, []byte("testFile")),
1121 NewField(fieldFilePath, EncodeFilePath(strings.Join([]string{"foo"}, "/"))),
1122 NewField(fieldFileNewPath, EncodeFilePath(strings.Join([]string{"bar"}, "/"))),
1123 ),
1124 },
1125 wantRes: []Transaction{
1126 {
1127 Flags: 0x00,
1128 IsReply: 0x01,
1129 Type: []byte{0, 0xd1},
1130 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
1131 ErrorCode: []byte{0, 0, 0, 0},
1132 Fields: []Field(nil),
1133 },
1134 },
1135 wantErr: false,
1136 },
1137 {
1138 name: "when symlink returns an error",
1139 setup: func() {
1140 mfs := &MockFileStore{}
1141 path, _ := os.Getwd()
1142 mfs.On(
1143 "Symlink",
1144 path+"/test/config/Files/foo/testFile",
1145 path+"/test/config/Files/bar/testFile",
1146 ).Return(errors.New("ohno"))
1147 FS = mfs
1148 },
1149 args: args{
1150 cc: &ClientConn{
1151 Account: &Account{
1152 Access: func() *[]byte {
1153 var bits accessBitmap
1154 bits.Set(accessMakeAlias)
1155 access := bits[:]
1156 return &access
1157 }(),
1158 },
1159 Server: &Server{
1160 Config: &Config{
1161 FileRoot: func() string {
1162 path, _ := os.Getwd()
1163 return path + "/test/config/Files"
1164 }(),
1165 },
1166 Logger: NewTestLogger(),
1167 },
1168 },
1169 t: NewTransaction(
1170 tranMakeFileAlias, &[]byte{0, 1},
1171 NewField(fieldFileName, []byte("testFile")),
1172 NewField(fieldFilePath, EncodeFilePath(strings.Join([]string{"foo"}, "/"))),
1173 NewField(fieldFileNewPath, EncodeFilePath(strings.Join([]string{"bar"}, "/"))),
1174 ),
1175 },
1176 wantRes: []Transaction{
1177 {
1178 Flags: 0x00,
1179 IsReply: 0x01,
1180 Type: []byte{0, 0x00},
1181 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
1182 ErrorCode: []byte{0, 0, 0, 1},
1183 Fields: []Field{
1184 NewField(fieldError, []byte("Error creating alias")),
1185 },
1186 },
1187 },
1188 wantErr: false,
1189 },
1190 {
1191 name: "when user does not have required permission",
1192 setup: func() {},
1193 args: args{
1194 cc: &ClientConn{
1195 Account: &Account{
1196 Access: func() *[]byte {
1197 var bits accessBitmap
1198 access := bits[:]
1199 return &access
1200 }(),
1201 },
1202 Server: &Server{
1203 Config: &Config{
1204 FileRoot: func() string {
1205 path, _ := os.Getwd()
1206 return path + "/test/config/Files"
1207 }(),
1208 },
1209 },
1210 },
1211 t: NewTransaction(
1212 tranMakeFileAlias, &[]byte{0, 1},
1213 NewField(fieldFileName, []byte("testFile")),
1214 NewField(fieldFilePath, []byte{
1215 0x00, 0x01,
1216 0x00, 0x00,
1217 0x03,
1218 0x2e, 0x2e, 0x2e,
1219 }),
1220 NewField(fieldFileNewPath, []byte{
1221 0x00, 0x01,
1222 0x00, 0x00,
1223 0x03,
1224 0x2e, 0x2e, 0x2e,
1225 }),
1226 ),
1227 },
1228 wantRes: []Transaction{
1229 {
1230 Flags: 0x00,
1231 IsReply: 0x01,
1232 Type: []byte{0, 0x00},
1233 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
1234 ErrorCode: []byte{0, 0, 0, 1},
1235 Fields: []Field{
1236 NewField(fieldError, []byte("You are not allowed to make aliases.")),
1237 },
1238 },
1239 },
1240 wantErr: false,
1241 },
1242 }
1243 for _, tt := range tests {
1244 t.Run(tt.name, func(t *testing.T) {
1245 tt.setup()
1246
1247 gotRes, err := HandleMakeAlias(tt.args.cc, tt.args.t)
1248 if (err != nil) != tt.wantErr {
1249 t.Errorf("HandleMakeAlias(%v, %v)", tt.args.cc, tt.args.t)
1250 return
1251 }
1252
1253 tranAssertEqual(t, tt.wantRes, gotRes)
1254 })
1255 }
1256 }
1257
1258 func TestHandleGetUser(t *testing.T) {
1259 type args struct {
1260 cc *ClientConn
1261 t *Transaction
1262 }
1263 tests := []struct {
1264 name string
1265 args args
1266 wantRes []Transaction
1267 wantErr assert.ErrorAssertionFunc
1268 }{
1269 {
1270 name: "when account is valid",
1271 args: args{
1272 cc: &ClientConn{
1273 Account: &Account{
1274 Access: func() *[]byte {
1275 var bits accessBitmap
1276 bits.Set(accessOpenUser)
1277 access := bits[:]
1278 return &access
1279 }(),
1280 },
1281 Server: &Server{
1282 Accounts: map[string]*Account{
1283 "guest": {
1284 Login: "guest",
1285 Name: "Guest",
1286 Password: "password",
1287 Access: &[]byte{1},
1288 },
1289 },
1290 },
1291 },
1292 t: NewTransaction(
1293 tranGetUser, &[]byte{0, 1},
1294 NewField(fieldUserLogin, []byte("guest")),
1295 ),
1296 },
1297 wantRes: []Transaction{
1298 {
1299 Flags: 0x00,
1300 IsReply: 0x01,
1301 Type: []byte{0x01, 0x60},
1302 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
1303 ErrorCode: []byte{0, 0, 0, 0},
1304 Fields: []Field{
1305 NewField(fieldUserName, []byte("Guest")),
1306 NewField(fieldUserLogin, negateString([]byte("guest"))),
1307 NewField(fieldUserPassword, []byte("password")),
1308 NewField(fieldUserAccess, []byte{1}),
1309 },
1310 },
1311 },
1312 wantErr: assert.NoError,
1313 },
1314 {
1315 name: "when user does not have required permission",
1316 args: args{
1317 cc: &ClientConn{
1318 Account: &Account{
1319 Access: func() *[]byte {
1320 var bits accessBitmap
1321 access := bits[:]
1322 return &access
1323 }(),
1324 },
1325 Server: &Server{
1326 Accounts: map[string]*Account{},
1327 },
1328 },
1329 t: NewTransaction(
1330 tranGetUser, &[]byte{0, 1},
1331 NewField(fieldUserLogin, []byte("nonExistentUser")),
1332 ),
1333 },
1334 wantRes: []Transaction{
1335 {
1336 Flags: 0x00,
1337 IsReply: 0x01,
1338 Type: []byte{0, 0x00},
1339 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
1340 ErrorCode: []byte{0, 0, 0, 1},
1341 Fields: []Field{
1342 NewField(fieldError, []byte("You are not allowed to view accounts.")),
1343 },
1344 },
1345 },
1346 wantErr: assert.NoError,
1347 },
1348 {
1349 name: "when account does not exist",
1350 args: args{
1351 cc: &ClientConn{
1352 Account: &Account{
1353 Access: func() *[]byte {
1354 var bits accessBitmap
1355 bits.Set(accessOpenUser)
1356 access := bits[:]
1357 return &access
1358 }(),
1359 },
1360 Server: &Server{
1361 Accounts: map[string]*Account{},
1362 },
1363 },
1364 t: NewTransaction(
1365 tranGetUser, &[]byte{0, 1},
1366 NewField(fieldUserLogin, []byte("nonExistentUser")),
1367 ),
1368 },
1369 wantRes: []Transaction{
1370 {
1371 Flags: 0x00,
1372 IsReply: 0x01,
1373 Type: []byte{0, 0x00},
1374 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
1375 ErrorCode: []byte{0, 0, 0, 1},
1376 Fields: []Field{
1377 NewField(fieldError, []byte("Account does not exist.")),
1378 },
1379 },
1380 },
1381 wantErr: assert.NoError,
1382 },
1383 }
1384 for _, tt := range tests {
1385 t.Run(tt.name, func(t *testing.T) {
1386 gotRes, err := HandleGetUser(tt.args.cc, tt.args.t)
1387 if !tt.wantErr(t, err, fmt.Sprintf("HandleGetUser(%v, %v)", tt.args.cc, tt.args.t)) {
1388 return
1389 }
1390
1391 tranAssertEqual(t, tt.wantRes, gotRes)
1392 })
1393 }
1394 }
1395
1396 func TestHandleDeleteUser(t *testing.T) {
1397 type args struct {
1398 cc *ClientConn
1399 t *Transaction
1400 }
1401 tests := []struct {
1402 name string
1403 setup func()
1404 args args
1405 wantRes []Transaction
1406 wantErr assert.ErrorAssertionFunc
1407 }{
1408 {
1409 name: "when user exists",
1410 setup: func() {
1411 mfs := &MockFileStore{}
1412 mfs.On("Remove", "Users/testuser.yaml").Return(nil)
1413 FS = mfs
1414 },
1415 args: args{
1416 cc: &ClientConn{
1417 Account: &Account{
1418 Access: func() *[]byte {
1419 var bits accessBitmap
1420 bits.Set(accessDeleteUser)
1421 access := bits[:]
1422 return &access
1423 }(),
1424 },
1425 Server: &Server{
1426 Accounts: map[string]*Account{
1427 "testuser": {
1428 Login: "testuser",
1429 Name: "Testy McTest",
1430 Password: "password",
1431 Access: &[]byte{1},
1432 },
1433 },
1434 },
1435 },
1436 t: NewTransaction(
1437 tranDeleteUser, &[]byte{0, 1},
1438 NewField(fieldUserLogin, negateString([]byte("testuser"))),
1439 ),
1440 },
1441 wantRes: []Transaction{
1442 {
1443 Flags: 0x00,
1444 IsReply: 0x01,
1445 Type: []byte{0x1, 0x5f},
1446 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
1447 ErrorCode: []byte{0, 0, 0, 0},
1448 Fields: []Field(nil),
1449 },
1450 },
1451 wantErr: assert.NoError,
1452 },
1453 {
1454 name: "when user does not have required permission",
1455 setup: func() {},
1456 args: args{
1457 cc: &ClientConn{
1458 Account: &Account{
1459 Access: func() *[]byte {
1460 var bits accessBitmap
1461 access := bits[:]
1462 return &access
1463 }(),
1464 },
1465 Server: &Server{
1466 Accounts: map[string]*Account{},
1467 },
1468 },
1469 t: NewTransaction(
1470 tranDeleteUser, &[]byte{0, 1},
1471 NewField(fieldUserLogin, negateString([]byte("testuser"))),
1472 ),
1473 },
1474 wantRes: []Transaction{
1475 {
1476 Flags: 0x00,
1477 IsReply: 0x01,
1478 Type: []byte{0, 0x00},
1479 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
1480 ErrorCode: []byte{0, 0, 0, 1},
1481 Fields: []Field{
1482 NewField(fieldError, []byte("You are not allowed to delete accounts.")),
1483 },
1484 },
1485 },
1486 wantErr: assert.NoError,
1487 },
1488 }
1489 for _, tt := range tests {
1490 t.Run(tt.name, func(t *testing.T) {
1491 tt.setup()
1492 gotRes, err := HandleDeleteUser(tt.args.cc, tt.args.t)
1493 if !tt.wantErr(t, err, fmt.Sprintf("HandleDeleteUser(%v, %v)", tt.args.cc, tt.args.t)) {
1494 return
1495 }
1496
1497 tranAssertEqual(t, tt.wantRes, gotRes)
1498 })
1499 }
1500 }
1501
1502 func TestHandleGetMsgs(t *testing.T) {
1503 type args struct {
1504 cc *ClientConn
1505 t *Transaction
1506 }
1507 tests := []struct {
1508 name string
1509 args args
1510 wantRes []Transaction
1511 wantErr assert.ErrorAssertionFunc
1512 }{
1513 {
1514 name: "returns news data",
1515 args: args{
1516 cc: &ClientConn{
1517 Account: &Account{
1518 Access: func() *[]byte {
1519 var bits accessBitmap
1520 bits.Set(accessNewsReadArt)
1521 access := bits[:]
1522 return &access
1523 }(),
1524 },
1525 Server: &Server{
1526 FlatNews: []byte("TEST"),
1527 },
1528 },
1529 t: NewTransaction(
1530 tranGetMsgs, &[]byte{0, 1},
1531 ),
1532 },
1533 wantRes: []Transaction{
1534 {
1535 Flags: 0x00,
1536 IsReply: 0x01,
1537 Type: []byte{0, 0x65},
1538 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
1539 ErrorCode: []byte{0, 0, 0, 0},
1540 Fields: []Field{
1541 NewField(fieldData, []byte("TEST")),
1542 },
1543 },
1544 },
1545 wantErr: assert.NoError,
1546 },
1547 {
1548 name: "when user does not have required permission",
1549 args: args{
1550 cc: &ClientConn{
1551 Account: &Account{
1552 Access: func() *[]byte {
1553 var bits accessBitmap
1554 access := bits[:]
1555 return &access
1556 }(),
1557 },
1558 Server: &Server{
1559 Accounts: map[string]*Account{},
1560 },
1561 },
1562 t: NewTransaction(
1563 tranGetMsgs, &[]byte{0, 1},
1564 ),
1565 },
1566 wantRes: []Transaction{
1567 {
1568 Flags: 0x00,
1569 IsReply: 0x01,
1570 Type: []byte{0, 0x00},
1571 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
1572 ErrorCode: []byte{0, 0, 0, 1},
1573 Fields: []Field{
1574 NewField(fieldError, []byte("You are not allowed to read news.")),
1575 },
1576 },
1577 },
1578 wantErr: assert.NoError,
1579 },
1580 }
1581 for _, tt := range tests {
1582 t.Run(tt.name, func(t *testing.T) {
1583 gotRes, err := HandleGetMsgs(tt.args.cc, tt.args.t)
1584 if !tt.wantErr(t, err, fmt.Sprintf("HandleGetMsgs(%v, %v)", tt.args.cc, tt.args.t)) {
1585 return
1586 }
1587
1588 tranAssertEqual(t, tt.wantRes, gotRes)
1589 })
1590 }
1591 }
1592
1593 func TestHandleNewUser(t *testing.T) {
1594 type args struct {
1595 cc *ClientConn
1596 t *Transaction
1597 }
1598 tests := []struct {
1599 name string
1600 args args
1601 wantRes []Transaction
1602 wantErr assert.ErrorAssertionFunc
1603 }{
1604 {
1605 name: "when user does not have required permission",
1606 args: args{
1607 cc: &ClientConn{
1608 Account: &Account{
1609 Access: func() *[]byte {
1610 var bits accessBitmap
1611 access := bits[:]
1612 return &access
1613 }(),
1614 },
1615 Server: &Server{
1616 Accounts: map[string]*Account{},
1617 },
1618 },
1619 t: NewTransaction(
1620 tranNewUser, &[]byte{0, 1},
1621 ),
1622 },
1623 wantRes: []Transaction{
1624 {
1625 Flags: 0x00,
1626 IsReply: 0x01,
1627 Type: []byte{0, 0x00},
1628 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
1629 ErrorCode: []byte{0, 0, 0, 1},
1630 Fields: []Field{
1631 NewField(fieldError, []byte("You are not allowed to create new accounts.")),
1632 },
1633 },
1634 },
1635 wantErr: assert.NoError,
1636 },
1637 }
1638 for _, tt := range tests {
1639 t.Run(tt.name, func(t *testing.T) {
1640 gotRes, err := HandleNewUser(tt.args.cc, tt.args.t)
1641 if !tt.wantErr(t, err, fmt.Sprintf("HandleNewUser(%v, %v)", tt.args.cc, tt.args.t)) {
1642 return
1643 }
1644
1645 tranAssertEqual(t, tt.wantRes, gotRes)
1646 })
1647 }
1648 }
1649
1650 func TestHandleListUsers(t *testing.T) {
1651 type args struct {
1652 cc *ClientConn
1653 t *Transaction
1654 }
1655 tests := []struct {
1656 name string
1657 args args
1658 wantRes []Transaction
1659 wantErr assert.ErrorAssertionFunc
1660 }{
1661 {
1662 name: "when user does not have required permission",
1663 args: args{
1664 cc: &ClientConn{
1665 Account: &Account{
1666 Access: func() *[]byte {
1667 var bits accessBitmap
1668 access := bits[:]
1669 return &access
1670 }(),
1671 },
1672 Server: &Server{
1673 Accounts: map[string]*Account{},
1674 },
1675 },
1676 t: NewTransaction(
1677 tranNewUser, &[]byte{0, 1},
1678 ),
1679 },
1680 wantRes: []Transaction{
1681 {
1682 Flags: 0x00,
1683 IsReply: 0x01,
1684 Type: []byte{0, 0x00},
1685 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
1686 ErrorCode: []byte{0, 0, 0, 1},
1687 Fields: []Field{
1688 NewField(fieldError, []byte("You are not allowed to view accounts.")),
1689 },
1690 },
1691 },
1692 wantErr: assert.NoError,
1693 },
1694 }
1695 for _, tt := range tests {
1696 t.Run(tt.name, func(t *testing.T) {
1697 gotRes, err := HandleListUsers(tt.args.cc, tt.args.t)
1698 if !tt.wantErr(t, err, fmt.Sprintf("HandleListUsers(%v, %v)", tt.args.cc, tt.args.t)) {
1699 return
1700 }
1701
1702 tranAssertEqual(t, tt.wantRes, gotRes)
1703 })
1704 }
1705 }
1706
1707 func TestHandleDownloadFile(t *testing.T) {
1708 type args struct {
1709 cc *ClientConn
1710 t *Transaction
1711 }
1712 tests := []struct {
1713 name string
1714 args args
1715 wantRes []Transaction
1716 wantErr assert.ErrorAssertionFunc
1717 }{
1718 {
1719 name: "when user does not have required permission",
1720 args: args{
1721 cc: &ClientConn{
1722 Account: &Account{
1723 Access: func() *[]byte {
1724 var bits accessBitmap
1725 access := bits[:]
1726 return &access
1727 }(),
1728 },
1729 Server: &Server{},
1730 },
1731 t: NewTransaction(tranDownloadFile, &[]byte{0, 1}),
1732 },
1733 wantRes: []Transaction{
1734 {
1735 Flags: 0x00,
1736 IsReply: 0x01,
1737 Type: []byte{0, 0x00},
1738 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
1739 ErrorCode: []byte{0, 0, 0, 1},
1740 Fields: []Field{
1741 NewField(fieldError, []byte("You are not allowed to download files.")),
1742 },
1743 },
1744 },
1745 wantErr: assert.NoError,
1746 },
1747 {
1748 name: "with a valid file",
1749 args: args{
1750 cc: &ClientConn{
1751 Transfers: make(map[int][]*FileTransfer),
1752 Account: &Account{
1753 Access: func() *[]byte {
1754 var bits accessBitmap
1755 bits.Set(accessDownloadFile)
1756 access := bits[:]
1757 return &access
1758 }(),
1759 },
1760 Server: &Server{
1761 FileTransfers: make(map[uint32]*FileTransfer),
1762 Config: &Config{
1763 FileRoot: func() string { path, _ := os.Getwd(); return path + "/test/config/Files" }(),
1764 },
1765 Accounts: map[string]*Account{},
1766 },
1767 },
1768 t: NewTransaction(
1769 accessDownloadFile,
1770 &[]byte{0, 1},
1771 NewField(fieldFileName, []byte("testfile.txt")),
1772 NewField(fieldFilePath, []byte{0x0, 0x00}),
1773 ),
1774 },
1775 wantRes: []Transaction{
1776 {
1777 Flags: 0x00,
1778 IsReply: 0x01,
1779 Type: []byte{0, 0x2},
1780 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
1781 ErrorCode: []byte{0, 0, 0, 0},
1782 Fields: []Field{
1783 NewField(fieldRefNum, []byte{0x52, 0xfd, 0xfc, 0x07}),
1784 NewField(fieldWaitingCount, []byte{0x00, 0x00}),
1785 NewField(fieldTransferSize, []byte{0x00, 0x00, 0x00, 0xa5}),
1786 NewField(fieldFileSize, []byte{0x00, 0x00, 0x00, 0x17}),
1787 },
1788 },
1789 },
1790 wantErr: assert.NoError,
1791 },
1792 }
1793 for _, tt := range tests {
1794 t.Run(tt.name, func(t *testing.T) {
1795 // reset the rand seed so that the random fieldRefNum will be deterministic
1796 rand.Seed(1)
1797
1798 gotRes, err := HandleDownloadFile(tt.args.cc, tt.args.t)
1799 if !tt.wantErr(t, err, fmt.Sprintf("HandleDownloadFile(%v, %v)", tt.args.cc, tt.args.t)) {
1800 return
1801 }
1802
1803 tranAssertEqual(t, tt.wantRes, gotRes)
1804 })
1805 }
1806 }
1807
1808 func TestHandleUpdateUser(t *testing.T) {
1809 type args struct {
1810 cc *ClientConn
1811 t *Transaction
1812 }
1813 tests := []struct {
1814 name string
1815 args args
1816 wantRes []Transaction
1817 wantErr assert.ErrorAssertionFunc
1818 }{
1819 {
1820 name: "when action is create user without required permission",
1821 args: args{
1822 cc: &ClientConn{
1823 Server: &Server{
1824 Logger: NewTestLogger(),
1825 },
1826 Account: &Account{
1827 Access: func() *[]byte {
1828 var bits accessBitmap
1829 access := bits[:]
1830 return &access
1831 }(),
1832 },
1833 },
1834 t: NewTransaction(
1835 tranUpdateUser,
1836 &[]byte{0, 0},
1837 NewField(fieldData, []byte{
1838 0x00, 0x04, // field count
1839
1840 0x00, 0x69, // fieldUserLogin = 105
1841 0x00, 0x03,
1842 0x9d, 0x9d, 0x9d,
1843
1844 0x00, 0x6a, // fieldUserPassword = 106
1845 0x00, 0x03,
1846 0x9c, 0x9c, 0x9c,
1847
1848 0x00, 0x66, // fieldUserName = 102
1849 0x00, 0x03,
1850 0x61, 0x61, 0x61,
1851
1852 0x00, 0x6e, // fieldUserAccess = 110
1853 0x00, 0x08,
1854 0x60, 0x70, 0x0c, 0x20, 0x03, 0x80, 0x00, 0x00,
1855 }),
1856 ),
1857 },
1858 wantRes: []Transaction{
1859 {
1860 Flags: 0x00,
1861 IsReply: 0x01,
1862 Type: []byte{0, 0x00},
1863 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
1864 ErrorCode: []byte{0, 0, 0, 1},
1865 Fields: []Field{
1866 NewField(fieldError, []byte("You are not allowed to create new accounts.")),
1867 },
1868 },
1869 },
1870 wantErr: assert.NoError,
1871 },
1872 {
1873 name: "when action is modify user without required permission",
1874 args: args{
1875 cc: &ClientConn{
1876 Server: &Server{
1877 Logger: NewTestLogger(),
1878 Accounts: map[string]*Account{
1879 "bbb": {},
1880 },
1881 },
1882 Account: &Account{
1883 Access: func() *[]byte {
1884 var bits accessBitmap
1885 access := bits[:]
1886 return &access
1887 }(),
1888 },
1889 },
1890 t: NewTransaction(
1891 tranUpdateUser,
1892 &[]byte{0, 0},
1893 NewField(fieldData, []byte{
1894 0x00, 0x04, // field count
1895
1896 0x00, 0x69, // fieldUserLogin = 105
1897 0x00, 0x03,
1898 0x9d, 0x9d, 0x9d,
1899
1900 0x00, 0x6a, // fieldUserPassword = 106
1901 0x00, 0x03,
1902 0x9c, 0x9c, 0x9c,
1903
1904 0x00, 0x66, // fieldUserName = 102
1905 0x00, 0x03,
1906 0x61, 0x61, 0x61,
1907
1908 0x00, 0x6e, // fieldUserAccess = 110
1909 0x00, 0x08,
1910 0x60, 0x70, 0x0c, 0x20, 0x03, 0x80, 0x00, 0x00,
1911 }),
1912 ),
1913 },
1914 wantRes: []Transaction{
1915 {
1916 Flags: 0x00,
1917 IsReply: 0x01,
1918 Type: []byte{0, 0x00},
1919 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
1920 ErrorCode: []byte{0, 0, 0, 1},
1921 Fields: []Field{
1922 NewField(fieldError, []byte("You are not allowed to modify accounts.")),
1923 },
1924 },
1925 },
1926 wantErr: assert.NoError,
1927 },
1928 {
1929 name: "when action is delete user without required permission",
1930 args: args{
1931 cc: &ClientConn{
1932 Server: &Server{
1933 Logger: NewTestLogger(),
1934 Accounts: map[string]*Account{
1935 "bbb": {},
1936 },
1937 },
1938 Account: &Account{
1939 Access: func() *[]byte {
1940 var bits accessBitmap
1941 access := bits[:]
1942 return &access
1943 }(),
1944 },
1945 },
1946 t: NewTransaction(
1947 tranUpdateUser,
1948 &[]byte{0, 0},
1949 NewField(fieldData, []byte{
1950 0x00, 0x01,
1951 0x00, 0x65,
1952 0x00, 0x03,
1953 0x88, 0x9e, 0x8b,
1954 }),
1955 ),
1956 },
1957 wantRes: []Transaction{
1958 {
1959 Flags: 0x00,
1960 IsReply: 0x01,
1961 Type: []byte{0, 0x00},
1962 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
1963 ErrorCode: []byte{0, 0, 0, 1},
1964 Fields: []Field{
1965 NewField(fieldError, []byte("You are not allowed to delete accounts.")),
1966 },
1967 },
1968 },
1969 wantErr: assert.NoError,
1970 },
1971 }
1972 for _, tt := range tests {
1973 t.Run(tt.name, func(t *testing.T) {
1974 gotRes, err := HandleUpdateUser(tt.args.cc, tt.args.t)
1975 if !tt.wantErr(t, err, fmt.Sprintf("HandleUpdateUser(%v, %v)", tt.args.cc, tt.args.t)) {
1976 return
1977 }
1978
1979 tranAssertEqual(t, tt.wantRes, gotRes)
1980 })
1981 }
1982 }
1983
1984 func TestHandleDelNewsArt(t *testing.T) {
1985 type args struct {
1986 cc *ClientConn
1987 t *Transaction
1988 }
1989 tests := []struct {
1990 name string
1991 args args
1992 wantRes []Transaction
1993 wantErr assert.ErrorAssertionFunc
1994 }{
1995 {
1996 name: "without required permission",
1997 args: args{
1998 cc: &ClientConn{
1999 Account: &Account{
2000 Access: func() *[]byte {
2001 var bits accessBitmap
2002 access := bits[:]
2003 return &access
2004 }(),
2005 },
2006 },
2007 t: NewTransaction(
2008 tranDelNewsArt,
2009 &[]byte{0, 0},
2010 ),
2011 },
2012 wantRes: []Transaction{
2013 {
2014 Flags: 0x00,
2015 IsReply: 0x01,
2016 Type: []byte{0, 0x00},
2017 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
2018 ErrorCode: []byte{0, 0, 0, 1},
2019 Fields: []Field{
2020 NewField(fieldError, []byte("You are not allowed to delete news articles.")),
2021 },
2022 },
2023 },
2024 wantErr: assert.NoError,
2025 },
2026 }
2027 for _, tt := range tests {
2028 t.Run(tt.name, func(t *testing.T) {
2029 gotRes, err := HandleDelNewsArt(tt.args.cc, tt.args.t)
2030 if !tt.wantErr(t, err, fmt.Sprintf("HandleDelNewsArt(%v, %v)", tt.args.cc, tt.args.t)) {
2031 return
2032 }
2033 tranAssertEqual(t, tt.wantRes, gotRes)
2034 })
2035 }
2036 }
2037
2038 func TestHandleDisconnectUser(t *testing.T) {
2039 type args struct {
2040 cc *ClientConn
2041 t *Transaction
2042 }
2043 tests := []struct {
2044 name string
2045 args args
2046 wantRes []Transaction
2047 wantErr assert.ErrorAssertionFunc
2048 }{
2049 {
2050 name: "without required permission",
2051 args: args{
2052 cc: &ClientConn{
2053 Account: &Account{
2054 Access: func() *[]byte {
2055 var bits accessBitmap
2056 access := bits[:]
2057 return &access
2058 }(),
2059 },
2060 },
2061 t: NewTransaction(
2062 tranDelNewsArt,
2063 &[]byte{0, 0},
2064 ),
2065 },
2066 wantRes: []Transaction{
2067 {
2068 Flags: 0x00,
2069 IsReply: 0x01,
2070 Type: []byte{0, 0x00},
2071 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
2072 ErrorCode: []byte{0, 0, 0, 1},
2073 Fields: []Field{
2074 NewField(fieldError, []byte("You are not allowed to disconnect users.")),
2075 },
2076 },
2077 },
2078 wantErr: assert.NoError,
2079 },
2080 {
2081 name: "when target user has 'cannot be disconnected' priv",
2082 args: args{
2083 cc: &ClientConn{
2084 Server: &Server{
2085 Clients: map[uint16]*ClientConn{
2086 uint16(1): {
2087 Account: &Account{
2088 Login: "unnamed",
2089 Access: func() *[]byte {
2090 var bits accessBitmap
2091 bits.Set(accessCannotBeDiscon)
2092 access := bits[:]
2093 return &access
2094 }(),
2095 },
2096 },
2097 },
2098 },
2099 Account: &Account{
2100 Access: func() *[]byte {
2101 var bits accessBitmap
2102 bits.Set(accessDisconUser)
2103 access := bits[:]
2104 return &access
2105 }(),
2106 },
2107 },
2108 t: NewTransaction(
2109 tranDelNewsArt,
2110 &[]byte{0, 0},
2111 NewField(fieldUserID, []byte{0, 1}),
2112 ),
2113 },
2114 wantRes: []Transaction{
2115 {
2116 Flags: 0x00,
2117 IsReply: 0x01,
2118 Type: []byte{0, 0x00},
2119 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
2120 ErrorCode: []byte{0, 0, 0, 1},
2121 Fields: []Field{
2122 NewField(fieldError, []byte("unnamed is not allowed to be disconnected.")),
2123 },
2124 },
2125 },
2126 wantErr: assert.NoError,
2127 },
2128 }
2129 for _, tt := range tests {
2130 t.Run(tt.name, func(t *testing.T) {
2131 gotRes, err := HandleDisconnectUser(tt.args.cc, tt.args.t)
2132 if !tt.wantErr(t, err, fmt.Sprintf("HandleDisconnectUser(%v, %v)", tt.args.cc, tt.args.t)) {
2133 return
2134 }
2135 tranAssertEqual(t, tt.wantRes, gotRes)
2136 })
2137 }
2138 }