]> git.r.bdr.sh - rbdr/mobius/blame_incremental - hotline/transaction_handlers_test.go
Use fixed size array types in Transaction fields
[rbdr/mobius] / hotline / transaction_handlers_test.go
... / ...
CommitLineData
1package hotline
2
3import (
4 "errors"
5 "fmt"
6 "github.com/stretchr/testify/assert"
7 "github.com/stretchr/testify/mock"
8 "io"
9 "io/fs"
10 "os"
11 "path/filepath"
12 "strings"
13 "testing"
14 "time"
15)
16
17func TestHandleSetChatSubject(t *testing.T) {
18 type args struct {
19 cc *ClientConn
20 t *Transaction
21 }
22 tests := []struct {
23 name string
24 args args
25 want []Transaction
26 wantErr bool
27 }{
28 {
29 name: "sends chat subject to private chat members",
30 args: args{
31 cc: &ClientConn{
32 UserName: []byte{0x00, 0x01},
33 Server: &Server{
34 PrivateChats: map[uint32]*PrivateChat{
35 uint32(1): {
36 Subject: "unset",
37 ClientConn: map[uint16]*ClientConn{
38 uint16(1): {
39 Account: &Account{
40 Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
41 },
42 ID: &[]byte{0, 1},
43 },
44 uint16(2): {
45 Account: &Account{
46 Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
47 },
48 ID: &[]byte{0, 2},
49 },
50 },
51 },
52 },
53 Clients: map[uint16]*ClientConn{
54 uint16(1): {
55 Account: &Account{
56 Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
57 },
58 ID: &[]byte{0, 1},
59 },
60 uint16(2): {
61 Account: &Account{
62 Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
63 },
64 ID: &[]byte{0, 2},
65 },
66 },
67 },
68 },
69 t: &Transaction{
70 Type: [2]byte{0, 0x6a},
71 ID: [4]byte{0, 0, 0, 1},
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 Type: [2]byte{0, 0x77},
82 Fields: []Field{
83 NewField(FieldChatID, []byte{0, 0, 0, 1}),
84 NewField(FieldChatSubject, []byte("Test Subject")),
85 },
86 },
87 {
88 clientID: &[]byte{0, 2},
89 Type: [2]byte{0, 0x77},
90 Fields: []Field{
91 NewField(FieldChatID, []byte{0, 0, 0, 1}),
92 NewField(FieldChatSubject, []byte("Test Subject")),
93 },
94 },
95 },
96 wantErr: false,
97 },
98 }
99 for _, tt := range tests {
100 t.Run(tt.name, func(t *testing.T) {
101 got, err := HandleSetChatSubject(tt.args.cc, tt.args.t)
102 if (err != nil) != tt.wantErr {
103 t.Errorf("HandleSetChatSubject() error = %v, wantErr %v", err, tt.wantErr)
104 return
105 }
106 if !tranAssertEqual(t, tt.want, got) {
107 t.Errorf("HandleSetChatSubject() got = %v, want %v", got, tt.want)
108 }
109 })
110 }
111}
112
113func TestHandleLeaveChat(t *testing.T) {
114 type args struct {
115 cc *ClientConn
116 t *Transaction
117 }
118 tests := []struct {
119 name string
120 args args
121 want []Transaction
122 wantErr bool
123 }{
124 {
125 name: "returns expected transactions",
126 args: args{
127 cc: &ClientConn{
128 ID: &[]byte{0, 2},
129 Server: &Server{
130 PrivateChats: map[uint32]*PrivateChat{
131 uint32(1): {
132 ClientConn: map[uint16]*ClientConn{
133 uint16(1): {
134 Account: &Account{
135 Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
136 },
137 ID: &[]byte{0, 1},
138 },
139 uint16(2): {
140 Account: &Account{
141 Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
142 },
143 ID: &[]byte{0, 2},
144 },
145 },
146 },
147 },
148 Clients: map[uint16]*ClientConn{
149 uint16(1): {
150 Account: &Account{
151 Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
152 },
153 ID: &[]byte{0, 1},
154 },
155 uint16(2): {
156 Account: &Account{
157 Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
158 },
159 ID: &[]byte{0, 2},
160 },
161 },
162 },
163 },
164 t: NewTransaction(TranDeleteUser, nil, NewField(FieldChatID, []byte{0, 0, 0, 1})),
165 },
166 want: []Transaction{
167 {
168 clientID: &[]byte{0, 1},
169 Flags: 0x00,
170 IsReply: 0x00,
171 Type: [2]byte{0, 0x76},
172 Fields: []Field{
173 NewField(FieldChatID, []byte{0, 0, 0, 1}),
174 NewField(FieldUserID, []byte{0, 2}),
175 },
176 },
177 },
178 wantErr: false,
179 },
180 }
181 for _, tt := range tests {
182 t.Run(tt.name, func(t *testing.T) {
183 got, err := HandleLeaveChat(tt.args.cc, tt.args.t)
184 if (err != nil) != tt.wantErr {
185 t.Errorf("HandleLeaveChat() error = %v, wantErr %v", err, tt.wantErr)
186 return
187 }
188 if !tranAssertEqual(t, tt.want, got) {
189 t.Errorf("HandleLeaveChat() got = %v, want %v", got, tt.want)
190 }
191 })
192 }
193}
194
195func TestHandleGetUserNameList(t *testing.T) {
196 type args struct {
197 cc *ClientConn
198 t *Transaction
199 }
200 tests := []struct {
201 name string
202 args args
203 want []Transaction
204 wantErr bool
205 }{
206 {
207 name: "replies with userlist transaction",
208 args: args{
209 cc: &ClientConn{
210
211 ID: &[]byte{1, 1},
212 Server: &Server{
213 Clients: map[uint16]*ClientConn{
214 uint16(1): {
215 ID: &[]byte{0, 1},
216 Icon: []byte{0, 2},
217 Flags: []byte{0, 3},
218 UserName: []byte{0, 4},
219 },
220 uint16(2): {
221 ID: &[]byte{0, 2},
222 Icon: []byte{0, 2},
223 Flags: []byte{0, 3},
224 UserName: []byte{0, 4},
225 },
226 },
227 },
228 },
229 t: &Transaction{},
230 },
231 want: []Transaction{
232 {
233 clientID: &[]byte{1, 1},
234 IsReply: 0x01,
235 Fields: []Field{
236 NewField(
237 FieldUsernameWithInfo,
238 []byte{00, 01, 00, 02, 00, 03, 00, 02, 00, 04},
239 ),
240 NewField(
241 FieldUsernameWithInfo,
242 []byte{00, 02, 00, 02, 00, 03, 00, 02, 00, 04},
243 ),
244 },
245 },
246 },
247 wantErr: false,
248 },
249 }
250 for _, tt := range tests {
251 t.Run(tt.name, func(t *testing.T) {
252 got, err := HandleGetUserNameList(tt.args.cc, tt.args.t)
253 if (err != nil) != tt.wantErr {
254 t.Errorf("HandleGetUserNameList() error = %v, wantErr %v", err, tt.wantErr)
255 return
256 }
257 assert.Equal(t, tt.want, got)
258 })
259 }
260}
261
262func TestHandleChatSend(t *testing.T) {
263 type args struct {
264 cc *ClientConn
265 t *Transaction
266 }
267 tests := []struct {
268 name string
269 args args
270 want []Transaction
271 wantErr bool
272 }{
273 {
274 name: "sends chat msg transaction to all clients",
275 args: args{
276 cc: &ClientConn{
277 Account: &Account{
278 Access: func() accessBitmap {
279 var bits accessBitmap
280 bits.Set(accessSendChat)
281 return bits
282 }(),
283 },
284 UserName: []byte{0x00, 0x01},
285 Server: &Server{
286 Clients: map[uint16]*ClientConn{
287 uint16(1): {
288 Account: &Account{
289 Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
290 },
291 ID: &[]byte{0, 1},
292 },
293 uint16(2): {
294 Account: &Account{
295 Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
296 },
297 ID: &[]byte{0, 2},
298 },
299 },
300 },
301 },
302 t: &Transaction{
303 Fields: []Field{
304 NewField(FieldData, []byte("hai")),
305 },
306 },
307 },
308 want: []Transaction{
309 {
310 clientID: &[]byte{0, 1},
311 Flags: 0x00,
312 IsReply: 0x00,
313 Type: [2]byte{0, 0x6a},
314 Fields: []Field{
315 NewField(FieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
316 },
317 },
318 {
319 clientID: &[]byte{0, 2},
320 Flags: 0x00,
321 IsReply: 0x00,
322 Type: [2]byte{0, 0x6a},
323 Fields: []Field{
324 NewField(FieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
325 },
326 },
327 },
328 wantErr: false,
329 },
330 {
331 name: "treats Chat ID 00 00 00 00 as a public chat message",
332 args: args{
333 cc: &ClientConn{
334 Account: &Account{
335 Access: func() accessBitmap {
336 var bits accessBitmap
337 bits.Set(accessSendChat)
338 return bits
339 }(),
340 },
341 UserName: []byte{0x00, 0x01},
342 Server: &Server{
343 Clients: map[uint16]*ClientConn{
344 uint16(1): {
345 Account: &Account{
346 Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
347 },
348 ID: &[]byte{0, 1},
349 },
350 uint16(2): {
351 Account: &Account{
352 Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
353 },
354 ID: &[]byte{0, 2},
355 },
356 },
357 },
358 },
359 t: &Transaction{
360 Fields: []Field{
361 NewField(FieldData, []byte("hai")),
362 NewField(FieldChatID, []byte{0, 0, 0, 0}),
363 },
364 },
365 },
366 want: []Transaction{
367 {
368 clientID: &[]byte{0, 1},
369 Type: [2]byte{0, 0x6a},
370 Fields: []Field{
371 NewField(FieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
372 },
373 },
374 {
375 clientID: &[]byte{0, 2},
376 Type: [2]byte{0, 0x6a},
377 Fields: []Field{
378 NewField(FieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
379 },
380 },
381 },
382 wantErr: false,
383 },
384 {
385 name: "when user does not have required permission",
386 args: args{
387 cc: &ClientConn{
388 Account: &Account{
389 Access: func() accessBitmap {
390 var bits accessBitmap
391 return bits
392 }(),
393 },
394 Server: &Server{
395 Accounts: map[string]*Account{},
396 },
397 },
398 t: NewTransaction(
399 TranChatSend, &[]byte{0, 1},
400 NewField(FieldData, []byte("hai")),
401 ),
402 },
403 want: []Transaction{
404 {
405 IsReply: 0x01,
406 ErrorCode: [4]byte{0, 0, 0, 1},
407 Fields: []Field{
408 NewField(FieldError, []byte("You are not allowed to participate in chat.")),
409 },
410 },
411 },
412 wantErr: false,
413 },
414 {
415 name: "sends chat msg as emote if FieldChatOptions is set to 1",
416 args: args{
417 cc: &ClientConn{
418 Account: &Account{
419 Access: func() accessBitmap {
420 var bits accessBitmap
421 bits.Set(accessSendChat)
422 return bits
423 }(),
424 },
425 UserName: []byte("Testy McTest"),
426 Server: &Server{
427 Clients: map[uint16]*ClientConn{
428 uint16(1): {
429 Account: &Account{
430 Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
431 },
432 ID: &[]byte{0, 1},
433 },
434 uint16(2): {
435 Account: &Account{
436 Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
437 },
438 ID: &[]byte{0, 2},
439 },
440 },
441 },
442 },
443 t: &Transaction{
444 Fields: []Field{
445 NewField(FieldData, []byte("performed action")),
446 NewField(FieldChatOptions, []byte{0x00, 0x01}),
447 },
448 },
449 },
450 want: []Transaction{
451 {
452 clientID: &[]byte{0, 1},
453 Flags: 0x00,
454 IsReply: 0x00,
455 Type: [2]byte{0, 0x6a},
456 Fields: []Field{
457 NewField(FieldData, []byte("\r*** Testy McTest performed action")),
458 },
459 },
460 {
461 clientID: &[]byte{0, 2},
462 Flags: 0x00,
463 IsReply: 0x00,
464 Type: [2]byte{0, 0x6a},
465 Fields: []Field{
466 NewField(FieldData, []byte("\r*** Testy McTest performed action")),
467 },
468 },
469 },
470 wantErr: false,
471 },
472 {
473 name: "does not send chat msg as emote if FieldChatOptions is set to 0",
474 args: args{
475 cc: &ClientConn{
476 Account: &Account{
477 Access: func() accessBitmap {
478 var bits accessBitmap
479 bits.Set(accessSendChat)
480 return bits
481 }(),
482 },
483 UserName: []byte("Testy McTest"),
484 Server: &Server{
485 Clients: map[uint16]*ClientConn{
486 uint16(1): {
487 Account: &Account{
488 Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
489 },
490 ID: &[]byte{0, 1},
491 },
492 uint16(2): {
493 Account: &Account{
494 Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
495 },
496 ID: &[]byte{0, 2},
497 },
498 },
499 },
500 },
501 t: &Transaction{
502 Fields: []Field{
503 NewField(FieldData, []byte("hello")),
504 NewField(FieldChatOptions, []byte{0x00, 0x00}),
505 },
506 },
507 },
508 want: []Transaction{
509 {
510 clientID: &[]byte{0, 1},
511 Type: [2]byte{0, 0x6a},
512 Fields: []Field{
513 NewField(FieldData, []byte("\r Testy McTest: hello")),
514 },
515 },
516 {
517 clientID: &[]byte{0, 2},
518 Type: [2]byte{0, 0x6a},
519 Fields: []Field{
520 NewField(FieldData, []byte("\r Testy McTest: hello")),
521 },
522 },
523 },
524 wantErr: false,
525 },
526 {
527 name: "only sends chat msg to clients with accessReadChat permission",
528 args: args{
529 cc: &ClientConn{
530 Account: &Account{
531 Access: func() accessBitmap {
532 var bits accessBitmap
533 bits.Set(accessSendChat)
534 return bits
535 }(),
536 },
537 UserName: []byte{0x00, 0x01},
538 Server: &Server{
539 Clients: map[uint16]*ClientConn{
540 uint16(1): {
541 Account: &Account{
542 Access: func() accessBitmap {
543 var bits accessBitmap
544 bits.Set(accessReadChat)
545 return bits
546 }()},
547 ID: &[]byte{0, 1},
548 },
549 uint16(2): {
550 Account: &Account{
551 Access: accessBitmap{0, 0, 0, 0, 0, 0, 0, 0},
552 },
553 ID: &[]byte{0, 2},
554 },
555 },
556 },
557 },
558 t: &Transaction{
559 Fields: []Field{
560 NewField(FieldData, []byte("hai")),
561 },
562 },
563 },
564 want: []Transaction{
565 {
566 clientID: &[]byte{0, 1},
567 Type: [2]byte{0, 0x6a},
568 Fields: []Field{
569 NewField(FieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
570 },
571 },
572 },
573 wantErr: false,
574 },
575 {
576 name: "only sends private chat msg to members of private chat",
577 args: args{
578 cc: &ClientConn{
579 Account: &Account{
580 Access: func() accessBitmap {
581 var bits accessBitmap
582 bits.Set(accessSendChat)
583 return bits
584 }(),
585 },
586 UserName: []byte{0x00, 0x01},
587 Server: &Server{
588 PrivateChats: map[uint32]*PrivateChat{
589 uint32(1): {
590 ClientConn: map[uint16]*ClientConn{
591 uint16(1): {
592 ID: &[]byte{0, 1},
593 },
594 uint16(2): {
595 ID: &[]byte{0, 2},
596 },
597 },
598 },
599 },
600 Clients: map[uint16]*ClientConn{
601 uint16(1): {
602 Account: &Account{
603 Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
604 },
605 ID: &[]byte{0, 1},
606 },
607 uint16(2): {
608 Account: &Account{
609 Access: accessBitmap{0, 0, 0, 0, 0, 0, 0, 0},
610 },
611 ID: &[]byte{0, 2},
612 },
613 uint16(3): {
614 Account: &Account{
615 Access: accessBitmap{0, 0, 0, 0, 0, 0, 0, 0},
616 },
617 ID: &[]byte{0, 3},
618 },
619 },
620 },
621 },
622 t: &Transaction{
623 Fields: []Field{
624 NewField(FieldData, []byte("hai")),
625 NewField(FieldChatID, []byte{0, 0, 0, 1}),
626 },
627 },
628 },
629 want: []Transaction{
630 {
631 clientID: &[]byte{0, 1},
632 Type: [2]byte{0, 0x6a},
633 Fields: []Field{
634 NewField(FieldChatID, []byte{0, 0, 0, 1}),
635 NewField(FieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
636 },
637 },
638 {
639 clientID: &[]byte{0, 2},
640 Type: [2]byte{0, 0x6a},
641 Fields: []Field{
642 NewField(FieldChatID, []byte{0, 0, 0, 1}),
643 NewField(FieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
644 },
645 },
646 },
647 wantErr: false,
648 },
649 }
650 for _, tt := range tests {
651 t.Run(tt.name, func(t *testing.T) {
652 got, err := HandleChatSend(tt.args.cc, tt.args.t)
653
654 if (err != nil) != tt.wantErr {
655 t.Errorf("HandleChatSend() error = %v, wantErr %v", err, tt.wantErr)
656 return
657 }
658 tranAssertEqual(t, tt.want, got)
659 })
660 }
661}
662
663func TestHandleGetFileInfo(t *testing.T) {
664 type args struct {
665 cc *ClientConn
666 t *Transaction
667 }
668 tests := []struct {
669 name string
670 args args
671 wantRes []Transaction
672 wantErr bool
673 }{
674 {
675 name: "returns expected fields when a valid file is requested",
676 args: args{
677 cc: &ClientConn{
678 ID: &[]byte{0x00, 0x01},
679 Server: &Server{
680 FS: &OSFileStore{},
681 Config: &Config{
682 FileRoot: func() string {
683 path, _ := os.Getwd()
684 return filepath.Join(path, "/test/config/Files")
685 }(),
686 },
687 },
688 },
689 t: NewTransaction(
690 TranGetFileInfo, nil,
691 NewField(FieldFileName, []byte("testfile.txt")),
692 NewField(FieldFilePath, []byte{0x00, 0x00}),
693 ),
694 },
695 wantRes: []Transaction{
696 {
697 clientID: &[]byte{0, 1},
698 IsReply: 0x01,
699 Type: [2]byte{0, 0},
700 Fields: []Field{
701 NewField(FieldFileName, []byte("testfile.txt")),
702 NewField(FieldFileTypeString, []byte("Text File")),
703 NewField(FieldFileCreatorString, []byte("ttxt")),
704 NewField(FieldFileType, []byte("TEXT")),
705 NewField(FieldFileCreateDate, make([]byte, 8)),
706 NewField(FieldFileModifyDate, make([]byte, 8)),
707 NewField(FieldFileSize, []byte{0x0, 0x0, 0x0, 0x17}),
708 },
709 },
710 },
711 wantErr: false,
712 },
713 }
714 for _, tt := range tests {
715 t.Run(tt.name, func(t *testing.T) {
716 gotRes, err := HandleGetFileInfo(tt.args.cc, tt.args.t)
717 if (err != nil) != tt.wantErr {
718 t.Errorf("HandleGetFileInfo() error = %v, wantErr %v", err, tt.wantErr)
719 return
720 }
721
722 // Clear the fileWrapper timestamp fields to work around problems running the tests in multiple timezones
723 // TODO: revisit how to test this by mocking the stat calls
724 gotRes[0].Fields[4].Data = make([]byte, 8)
725 gotRes[0].Fields[5].Data = make([]byte, 8)
726
727 if !tranAssertEqual(t, tt.wantRes, gotRes) {
728 t.Errorf("HandleGetFileInfo() gotRes = %v, want %v", gotRes, tt.wantRes)
729 }
730 })
731 }
732}
733
734func TestHandleNewFolder(t *testing.T) {
735 type args struct {
736 cc *ClientConn
737 t *Transaction
738 }
739 tests := []struct {
740 name string
741 args args
742 wantRes []Transaction
743 wantErr bool
744 }{
745 {
746 name: "without required permission",
747 args: args{
748 cc: &ClientConn{
749 Account: &Account{
750 Access: func() accessBitmap {
751 var bits accessBitmap
752 return bits
753 }(),
754 },
755 },
756 t: NewTransaction(
757 accessCreateFolder,
758 &[]byte{0, 0},
759 ),
760 },
761 wantRes: []Transaction{
762 {
763 IsReply: 0x01,
764 ErrorCode: [4]byte{0, 0, 0, 1},
765 Fields: []Field{
766 NewField(FieldError, []byte("You are not allowed to create folders.")),
767 },
768 },
769 },
770 wantErr: false,
771 },
772 {
773 name: "when path is nested",
774 args: args{
775 cc: &ClientConn{
776 Account: &Account{
777 Access: func() accessBitmap {
778 var bits accessBitmap
779 bits.Set(accessCreateFolder)
780 return bits
781 }(),
782 },
783 ID: &[]byte{0, 1},
784 Server: &Server{
785 Config: &Config{
786 FileRoot: "/Files/",
787 },
788 FS: func() *MockFileStore {
789 mfs := &MockFileStore{}
790 mfs.On("Mkdir", "/Files/aaa/testFolder", fs.FileMode(0777)).Return(nil)
791 mfs.On("Stat", "/Files/aaa/testFolder").Return(nil, os.ErrNotExist)
792 return mfs
793 }(),
794 },
795 },
796 t: NewTransaction(
797 TranNewFolder, &[]byte{0, 1},
798 NewField(FieldFileName, []byte("testFolder")),
799 NewField(FieldFilePath, []byte{
800 0x00, 0x01,
801 0x00, 0x00,
802 0x03,
803 0x61, 0x61, 0x61,
804 }),
805 ),
806 },
807 wantRes: []Transaction{
808 {
809 clientID: &[]byte{0, 1},
810 IsReply: 0x01,
811 },
812 },
813 wantErr: false,
814 },
815 {
816 name: "when path is not nested",
817 args: args{
818 cc: &ClientConn{
819 Account: &Account{
820 Access: func() accessBitmap {
821 var bits accessBitmap
822 bits.Set(accessCreateFolder)
823 return bits
824 }(),
825 },
826 ID: &[]byte{0, 1},
827 Server: &Server{
828 Config: &Config{
829 FileRoot: "/Files",
830 },
831 FS: func() *MockFileStore {
832 mfs := &MockFileStore{}
833 mfs.On("Mkdir", "/Files/testFolder", fs.FileMode(0777)).Return(nil)
834 mfs.On("Stat", "/Files/testFolder").Return(nil, os.ErrNotExist)
835 return mfs
836 }(),
837 },
838 },
839 t: NewTransaction(
840 TranNewFolder, &[]byte{0, 1},
841 NewField(FieldFileName, []byte("testFolder")),
842 ),
843 },
844 wantRes: []Transaction{
845 {
846 clientID: &[]byte{0, 1},
847 IsReply: 0x01,
848 },
849 },
850 wantErr: false,
851 },
852 {
853 name: "when Write returns an err",
854 args: args{
855 cc: &ClientConn{
856 Account: &Account{
857 Access: func() accessBitmap {
858 var bits accessBitmap
859 bits.Set(accessCreateFolder)
860 return bits
861 }(),
862 },
863 ID: &[]byte{0, 1},
864 Server: &Server{
865 Config: &Config{
866 FileRoot: "/Files/",
867 },
868 FS: func() *MockFileStore {
869 mfs := &MockFileStore{}
870 mfs.On("Mkdir", "/Files/aaa/testFolder", fs.FileMode(0777)).Return(nil)
871 mfs.On("Stat", "/Files/aaa/testFolder").Return(nil, os.ErrNotExist)
872 return mfs
873 }(),
874 },
875 },
876 t: NewTransaction(
877 TranNewFolder, &[]byte{0, 1},
878 NewField(FieldFileName, []byte("testFolder")),
879 NewField(FieldFilePath, []byte{
880 0x00,
881 }),
882 ),
883 },
884 wantRes: []Transaction{},
885 wantErr: true,
886 },
887 {
888 name: "FieldFileName does not allow directory traversal",
889 args: args{
890 cc: &ClientConn{
891 Account: &Account{
892 Access: func() accessBitmap {
893 var bits accessBitmap
894 bits.Set(accessCreateFolder)
895 return bits
896 }(),
897 },
898 ID: &[]byte{0, 1},
899 Server: &Server{
900 Config: &Config{
901 FileRoot: "/Files/",
902 },
903 FS: func() *MockFileStore {
904 mfs := &MockFileStore{}
905 mfs.On("Mkdir", "/Files/testFolder", fs.FileMode(0777)).Return(nil)
906 mfs.On("Stat", "/Files/testFolder").Return(nil, os.ErrNotExist)
907 return mfs
908 }(),
909 },
910 },
911 t: NewTransaction(
912 TranNewFolder, &[]byte{0, 1},
913 NewField(FieldFileName, []byte("../../testFolder")),
914 ),
915 },
916 wantRes: []Transaction{
917 {
918 clientID: &[]byte{0, 1},
919 IsReply: 0x01,
920 },
921 }, wantErr: false,
922 },
923 {
924 name: "FieldFilePath does not allow directory traversal",
925 args: args{
926 cc: &ClientConn{
927 Account: &Account{
928 Access: func() accessBitmap {
929 var bits accessBitmap
930 bits.Set(accessCreateFolder)
931 return bits
932 }(),
933 },
934 ID: &[]byte{0, 1},
935 Server: &Server{
936 Config: &Config{
937 FileRoot: "/Files/",
938 },
939 FS: func() *MockFileStore {
940 mfs := &MockFileStore{}
941 mfs.On("Mkdir", "/Files/foo/testFolder", fs.FileMode(0777)).Return(nil)
942 mfs.On("Stat", "/Files/foo/testFolder").Return(nil, os.ErrNotExist)
943 return mfs
944 }(),
945 },
946 },
947 t: NewTransaction(
948 TranNewFolder, &[]byte{0, 1},
949 NewField(FieldFileName, []byte("testFolder")),
950 NewField(FieldFilePath, []byte{
951 0x00, 0x02,
952 0x00, 0x00,
953 0x03,
954 0x2e, 0x2e, 0x2f,
955 0x00, 0x00,
956 0x03,
957 0x66, 0x6f, 0x6f,
958 }),
959 ),
960 },
961 wantRes: []Transaction{
962 {
963 clientID: &[]byte{0, 1},
964 IsReply: 0x01,
965 },
966 }, wantErr: false,
967 },
968 }
969 for _, tt := range tests {
970 t.Run(tt.name, func(t *testing.T) {
971 gotRes, err := HandleNewFolder(tt.args.cc, tt.args.t)
972 if (err != nil) != tt.wantErr {
973 t.Errorf("HandleNewFolder() error = %v, wantErr %v", err, tt.wantErr)
974 return
975 }
976
977 if !tranAssertEqual(t, tt.wantRes, gotRes) {
978 t.Errorf("HandleNewFolder() gotRes = %v, want %v", gotRes, tt.wantRes)
979 }
980 })
981 }
982}
983
984func TestHandleUploadFile(t *testing.T) {
985 type args struct {
986 cc *ClientConn
987 t *Transaction
988 }
989 tests := []struct {
990 name string
991 args args
992 wantRes []Transaction
993 wantErr bool
994 }{
995 {
996 name: "when request is valid and user has Upload Anywhere permission",
997 args: args{
998 cc: &ClientConn{
999 Server: &Server{
1000 FS: &OSFileStore{},
1001 fileTransfers: map[[4]byte]*FileTransfer{},
1002 Config: &Config{
1003 FileRoot: func() string { path, _ := os.Getwd(); return path + "/test/config/Files" }(),
1004 }},
1005 transfers: map[int]map[[4]byte]*FileTransfer{
1006 FileUpload: {},
1007 },
1008 Account: &Account{
1009 Access: func() accessBitmap {
1010 var bits accessBitmap
1011 bits.Set(accessUploadFile)
1012 bits.Set(accessUploadAnywhere)
1013 return bits
1014 }(),
1015 },
1016 },
1017 t: NewTransaction(
1018 TranUploadFile, &[]byte{0, 1},
1019 NewField(FieldFileName, []byte("testFile")),
1020 NewField(FieldFilePath, []byte{
1021 0x00, 0x01,
1022 0x00, 0x00,
1023 0x03,
1024 0x2e, 0x2e, 0x2f,
1025 }),
1026 ),
1027 },
1028 wantRes: []Transaction{
1029 {
1030 IsReply: 0x01,
1031 Fields: []Field{
1032 NewField(FieldRefNum, []byte{0x52, 0xfd, 0xfc, 0x07}), // rand.Seed(1)
1033 },
1034 },
1035 },
1036 wantErr: false,
1037 },
1038 {
1039 name: "when user does not have required access",
1040 args: args{
1041 cc: &ClientConn{
1042 Account: &Account{
1043 Access: func() accessBitmap {
1044 var bits accessBitmap
1045 return bits
1046 }(),
1047 },
1048 },
1049 t: NewTransaction(
1050 TranUploadFile, &[]byte{0, 1},
1051 NewField(FieldFileName, []byte("testFile")),
1052 NewField(FieldFilePath, []byte{
1053 0x00, 0x01,
1054 0x00, 0x00,
1055 0x03,
1056 0x2e, 0x2e, 0x2f,
1057 }),
1058 ),
1059 },
1060 wantRes: []Transaction{
1061 {
1062 IsReply: 0x01,
1063 ErrorCode: [4]byte{0, 0, 0, 1},
1064 Fields: []Field{
1065 NewField(FieldError, []byte("You are not allowed to upload files.")), // rand.Seed(1)
1066 },
1067 },
1068 },
1069 wantErr: false,
1070 },
1071 }
1072 for _, tt := range tests {
1073 t.Run(tt.name, func(t *testing.T) {
1074 gotRes, err := HandleUploadFile(tt.args.cc, tt.args.t)
1075 if (err != nil) != tt.wantErr {
1076 t.Errorf("HandleUploadFile() error = %v, wantErr %v", err, tt.wantErr)
1077 return
1078 }
1079
1080 tranAssertEqual(t, tt.wantRes, gotRes)
1081 })
1082 }
1083}
1084
1085func TestHandleMakeAlias(t *testing.T) {
1086 type args struct {
1087 cc *ClientConn
1088 t *Transaction
1089 }
1090 tests := []struct {
1091 name string
1092 args args
1093 wantRes []Transaction
1094 wantErr bool
1095 }{
1096 {
1097 name: "with valid input and required permissions",
1098 args: args{
1099 cc: &ClientConn{
1100 logger: NewTestLogger(),
1101 Account: &Account{
1102 Access: func() accessBitmap {
1103 var bits accessBitmap
1104 bits.Set(accessMakeAlias)
1105 return bits
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 FS: func() *MockFileStore {
1117 mfs := &MockFileStore{}
1118 path, _ := os.Getwd()
1119 mfs.On(
1120 "Symlink",
1121 path+"/test/config/Files/foo/testFile",
1122 path+"/test/config/Files/bar/testFile",
1123 ).Return(nil)
1124 return mfs
1125 }(),
1126 },
1127 },
1128 t: NewTransaction(
1129 TranMakeFileAlias, &[]byte{0, 1},
1130 NewField(FieldFileName, []byte("testFile")),
1131 NewField(FieldFilePath, EncodeFilePath(strings.Join([]string{"foo"}, "/"))),
1132 NewField(FieldFileNewPath, EncodeFilePath(strings.Join([]string{"bar"}, "/"))),
1133 ),
1134 },
1135 wantRes: []Transaction{
1136 {
1137 IsReply: 0x01,
1138 Fields: []Field(nil),
1139 },
1140 },
1141 wantErr: false,
1142 },
1143 {
1144 name: "when symlink returns an error",
1145 args: args{
1146 cc: &ClientConn{
1147 logger: NewTestLogger(),
1148 Account: &Account{
1149 Access: func() accessBitmap {
1150 var bits accessBitmap
1151 bits.Set(accessMakeAlias)
1152 return bits
1153 }(),
1154 },
1155 Server: &Server{
1156 Config: &Config{
1157 FileRoot: func() string {
1158 path, _ := os.Getwd()
1159 return path + "/test/config/Files"
1160 }(),
1161 },
1162 Logger: NewTestLogger(),
1163 FS: func() *MockFileStore {
1164 mfs := &MockFileStore{}
1165 path, _ := os.Getwd()
1166 mfs.On(
1167 "Symlink",
1168 path+"/test/config/Files/foo/testFile",
1169 path+"/test/config/Files/bar/testFile",
1170 ).Return(errors.New("ohno"))
1171 return mfs
1172 }(),
1173 },
1174 },
1175 t: NewTransaction(
1176 TranMakeFileAlias, &[]byte{0, 1},
1177 NewField(FieldFileName, []byte("testFile")),
1178 NewField(FieldFilePath, EncodeFilePath(strings.Join([]string{"foo"}, "/"))),
1179 NewField(FieldFileNewPath, EncodeFilePath(strings.Join([]string{"bar"}, "/"))),
1180 ),
1181 },
1182 wantRes: []Transaction{
1183 {
1184 IsReply: 0x01,
1185 ErrorCode: [4]byte{0, 0, 0, 1},
1186 Fields: []Field{
1187 NewField(FieldError, []byte("Error creating alias")),
1188 },
1189 },
1190 },
1191 wantErr: false,
1192 },
1193 {
1194 name: "when user does not have required permission",
1195 args: args{
1196 cc: &ClientConn{
1197 logger: NewTestLogger(),
1198 Account: &Account{
1199 Access: func() accessBitmap {
1200 var bits accessBitmap
1201 return bits
1202 }(),
1203 },
1204 Server: &Server{
1205 Config: &Config{
1206 FileRoot: func() string {
1207 path, _ := os.Getwd()
1208 return path + "/test/config/Files"
1209 }(),
1210 },
1211 },
1212 },
1213 t: NewTransaction(
1214 TranMakeFileAlias, &[]byte{0, 1},
1215 NewField(FieldFileName, []byte("testFile")),
1216 NewField(FieldFilePath, []byte{
1217 0x00, 0x01,
1218 0x00, 0x00,
1219 0x03,
1220 0x2e, 0x2e, 0x2e,
1221 }),
1222 NewField(FieldFileNewPath, []byte{
1223 0x00, 0x01,
1224 0x00, 0x00,
1225 0x03,
1226 0x2e, 0x2e, 0x2e,
1227 }),
1228 ),
1229 },
1230 wantRes: []Transaction{
1231 {
1232 IsReply: 0x01,
1233 ErrorCode: [4]byte{0, 0, 0, 1},
1234 Fields: []Field{
1235 NewField(FieldError, []byte("You are not allowed to make aliases.")),
1236 },
1237 },
1238 },
1239 wantErr: false,
1240 },
1241 }
1242 for _, tt := range tests {
1243 t.Run(tt.name, func(t *testing.T) {
1244 gotRes, err := HandleMakeAlias(tt.args.cc, tt.args.t)
1245 if (err != nil) != tt.wantErr {
1246 t.Errorf("HandleMakeAlias(%v, %v)", tt.args.cc, tt.args.t)
1247 return
1248 }
1249
1250 tranAssertEqual(t, tt.wantRes, gotRes)
1251 })
1252 }
1253}
1254
1255func TestHandleGetUser(t *testing.T) {
1256 type args struct {
1257 cc *ClientConn
1258 t *Transaction
1259 }
1260 tests := []struct {
1261 name string
1262 args args
1263 wantRes []Transaction
1264 wantErr assert.ErrorAssertionFunc
1265 }{
1266 {
1267 name: "when account is valid",
1268 args: args{
1269 cc: &ClientConn{
1270 Account: &Account{
1271 Access: func() accessBitmap {
1272 var bits accessBitmap
1273 bits.Set(accessOpenUser)
1274 return bits
1275 }(),
1276 },
1277 Server: &Server{
1278 Accounts: map[string]*Account{
1279 "guest": {
1280 Login: "guest",
1281 Name: "Guest",
1282 Password: "password",
1283 Access: accessBitmap{},
1284 },
1285 },
1286 },
1287 },
1288 t: NewTransaction(
1289 TranGetUser, &[]byte{0, 1},
1290 NewField(FieldUserLogin, []byte("guest")),
1291 ),
1292 },
1293 wantRes: []Transaction{
1294 {
1295 IsReply: 0x01,
1296 Fields: []Field{
1297 NewField(FieldUserName, []byte("Guest")),
1298 NewField(FieldUserLogin, encodeString([]byte("guest"))),
1299 NewField(FieldUserPassword, []byte("password")),
1300 NewField(FieldUserAccess, []byte{0, 0, 0, 0, 0, 0, 0, 0}),
1301 },
1302 },
1303 },
1304 wantErr: assert.NoError,
1305 },
1306 {
1307 name: "when user does not have required permission",
1308 args: args{
1309 cc: &ClientConn{
1310 Account: &Account{
1311 Access: func() accessBitmap {
1312 var bits accessBitmap
1313 return bits
1314 }(),
1315 },
1316 Server: &Server{
1317 Accounts: map[string]*Account{},
1318 },
1319 },
1320 t: NewTransaction(
1321 TranGetUser, &[]byte{0, 1},
1322 NewField(FieldUserLogin, []byte("nonExistentUser")),
1323 ),
1324 },
1325 wantRes: []Transaction{
1326 {
1327 IsReply: 0x01,
1328 ErrorCode: [4]byte{0, 0, 0, 1},
1329 Fields: []Field{
1330 NewField(FieldError, []byte("You are not allowed to view accounts.")),
1331 },
1332 },
1333 },
1334 wantErr: assert.NoError,
1335 },
1336 {
1337 name: "when account does not exist",
1338 args: args{
1339 cc: &ClientConn{
1340 Account: &Account{
1341 Access: func() accessBitmap {
1342 var bits accessBitmap
1343 bits.Set(accessOpenUser)
1344 return bits
1345 }(),
1346 },
1347 Server: &Server{
1348 Accounts: map[string]*Account{},
1349 },
1350 },
1351 t: NewTransaction(
1352 TranGetUser, &[]byte{0, 1},
1353 NewField(FieldUserLogin, []byte("nonExistentUser")),
1354 ),
1355 },
1356 wantRes: []Transaction{
1357 {
1358 Flags: 0x00,
1359 IsReply: 0x01,
1360 Type: [2]byte{0, 0},
1361 ErrorCode: [4]byte{0, 0, 0, 1},
1362 Fields: []Field{
1363 NewField(FieldError, []byte("Account does not exist.")),
1364 },
1365 },
1366 },
1367 wantErr: assert.NoError,
1368 },
1369 }
1370 for _, tt := range tests {
1371 t.Run(tt.name, func(t *testing.T) {
1372 gotRes, err := HandleGetUser(tt.args.cc, tt.args.t)
1373 if !tt.wantErr(t, err, fmt.Sprintf("HandleGetUser(%v, %v)", tt.args.cc, tt.args.t)) {
1374 return
1375 }
1376
1377 tranAssertEqual(t, tt.wantRes, gotRes)
1378 })
1379 }
1380}
1381
1382func TestHandleDeleteUser(t *testing.T) {
1383 type args struct {
1384 cc *ClientConn
1385 t *Transaction
1386 }
1387 tests := []struct {
1388 name string
1389 args args
1390 wantRes []Transaction
1391 wantErr assert.ErrorAssertionFunc
1392 }{
1393 {
1394 name: "when user dataFile",
1395 args: args{
1396 cc: &ClientConn{
1397 Account: &Account{
1398 Access: func() accessBitmap {
1399 var bits accessBitmap
1400 bits.Set(accessDeleteUser)
1401 return bits
1402 }(),
1403 },
1404 Server: &Server{
1405 Accounts: map[string]*Account{
1406 "testuser": {
1407 Login: "testuser",
1408 Name: "Testy McTest",
1409 Password: "password",
1410 Access: accessBitmap{},
1411 },
1412 },
1413 FS: func() *MockFileStore {
1414 mfs := &MockFileStore{}
1415 mfs.On("Remove", "Users/testuser.yaml").Return(nil)
1416 return mfs
1417 }(),
1418 },
1419 },
1420 t: NewTransaction(
1421 TranDeleteUser, &[]byte{0, 1},
1422 NewField(FieldUserLogin, encodeString([]byte("testuser"))),
1423 ),
1424 },
1425 wantRes: []Transaction{
1426 {
1427 Flags: 0x00,
1428 IsReply: 0x01,
1429 Type: [2]byte{0, 0},
1430 Fields: []Field(nil),
1431 },
1432 },
1433 wantErr: assert.NoError,
1434 },
1435 {
1436 name: "when user does not have required permission",
1437 args: args{
1438 cc: &ClientConn{
1439 Account: &Account{
1440 Access: func() accessBitmap {
1441 var bits accessBitmap
1442 return bits
1443 }(),
1444 },
1445 Server: &Server{
1446 Accounts: map[string]*Account{},
1447 },
1448 },
1449 t: NewTransaction(
1450 TranDeleteUser, &[]byte{0, 1},
1451 NewField(FieldUserLogin, encodeString([]byte("testuser"))),
1452 ),
1453 },
1454 wantRes: []Transaction{
1455 {
1456 IsReply: 0x01,
1457 ErrorCode: [4]byte{0, 0, 0, 1},
1458 Fields: []Field{
1459 NewField(FieldError, []byte("You are not allowed to delete accounts.")),
1460 },
1461 },
1462 },
1463 wantErr: assert.NoError,
1464 },
1465 }
1466 for _, tt := range tests {
1467 t.Run(tt.name, func(t *testing.T) {
1468 gotRes, err := HandleDeleteUser(tt.args.cc, tt.args.t)
1469 if !tt.wantErr(t, err, fmt.Sprintf("HandleDeleteUser(%v, %v)", tt.args.cc, tt.args.t)) {
1470 return
1471 }
1472
1473 tranAssertEqual(t, tt.wantRes, gotRes)
1474 })
1475 }
1476}
1477
1478func TestHandleGetMsgs(t *testing.T) {
1479 type args struct {
1480 cc *ClientConn
1481 t *Transaction
1482 }
1483 tests := []struct {
1484 name string
1485 args args
1486 wantRes []Transaction
1487 wantErr assert.ErrorAssertionFunc
1488 }{
1489 {
1490 name: "returns news data",
1491 args: args{
1492 cc: &ClientConn{
1493 Account: &Account{
1494 Access: func() accessBitmap {
1495 var bits accessBitmap
1496 bits.Set(accessNewsReadArt)
1497 return bits
1498 }(),
1499 },
1500 Server: &Server{
1501 FlatNews: []byte("TEST"),
1502 },
1503 },
1504 t: NewTransaction(
1505 TranGetMsgs, &[]byte{0, 1},
1506 ),
1507 },
1508 wantRes: []Transaction{
1509 {
1510 IsReply: 0x01,
1511 Fields: []Field{
1512 NewField(FieldData, []byte("TEST")),
1513 },
1514 },
1515 },
1516 wantErr: assert.NoError,
1517 },
1518 {
1519 name: "when user does not have required permission",
1520 args: args{
1521 cc: &ClientConn{
1522 Account: &Account{
1523 Access: func() accessBitmap {
1524 var bits accessBitmap
1525 return bits
1526 }(),
1527 },
1528 Server: &Server{
1529 Accounts: map[string]*Account{},
1530 },
1531 },
1532 t: NewTransaction(
1533 TranGetMsgs, &[]byte{0, 1},
1534 ),
1535 },
1536 wantRes: []Transaction{
1537 {
1538 IsReply: 0x01,
1539 ErrorCode: [4]byte{0, 0, 0, 1},
1540 Fields: []Field{
1541 NewField(FieldError, []byte("You are not allowed to read news.")),
1542 },
1543 },
1544 },
1545 wantErr: assert.NoError,
1546 },
1547 }
1548 for _, tt := range tests {
1549 t.Run(tt.name, func(t *testing.T) {
1550 gotRes, err := HandleGetMsgs(tt.args.cc, tt.args.t)
1551 if !tt.wantErr(t, err, fmt.Sprintf("HandleGetMsgs(%v, %v)", tt.args.cc, tt.args.t)) {
1552 return
1553 }
1554
1555 tranAssertEqual(t, tt.wantRes, gotRes)
1556 })
1557 }
1558}
1559
1560func TestHandleNewUser(t *testing.T) {
1561 type args struct {
1562 cc *ClientConn
1563 t *Transaction
1564 }
1565 tests := []struct {
1566 name string
1567 args args
1568 wantRes []Transaction
1569 wantErr assert.ErrorAssertionFunc
1570 }{
1571 {
1572 name: "when user does not have required permission",
1573 args: args{
1574 cc: &ClientConn{
1575 Account: &Account{
1576 Access: func() accessBitmap {
1577 var bits accessBitmap
1578 return bits
1579 }(),
1580 },
1581 Server: &Server{
1582 Accounts: map[string]*Account{},
1583 },
1584 },
1585 t: NewTransaction(
1586 TranNewUser, &[]byte{0, 1},
1587 ),
1588 },
1589 wantRes: []Transaction{
1590 {
1591 IsReply: 0x01,
1592 ErrorCode: [4]byte{0, 0, 0, 1},
1593 Fields: []Field{
1594 NewField(FieldError, []byte("You are not allowed to create new accounts.")),
1595 },
1596 },
1597 },
1598 wantErr: assert.NoError,
1599 },
1600 {
1601 name: "when user attempts to create account with greater access",
1602 args: args{
1603 cc: &ClientConn{
1604 Account: &Account{
1605 Access: func() accessBitmap {
1606 var bits accessBitmap
1607 bits.Set(accessCreateUser)
1608 return bits
1609 }(),
1610 },
1611 Server: &Server{
1612 Accounts: map[string]*Account{},
1613 },
1614 },
1615 t: NewTransaction(
1616 TranNewUser, &[]byte{0, 1},
1617 NewField(FieldUserLogin, []byte("userB")),
1618 NewField(
1619 FieldUserAccess,
1620 func() []byte {
1621 var bits accessBitmap
1622 bits.Set(accessDisconUser)
1623 return bits[:]
1624 }(),
1625 ),
1626 ),
1627 },
1628 wantRes: []Transaction{
1629 {
1630 IsReply: 0x01,
1631 ErrorCode: [4]byte{0, 0, 0, 1},
1632 Fields: []Field{
1633 NewField(FieldError, []byte("Cannot create account with more access than yourself.")),
1634 },
1635 },
1636 },
1637 wantErr: assert.NoError,
1638 },
1639 }
1640 for _, tt := range tests {
1641 t.Run(tt.name, func(t *testing.T) {
1642 gotRes, err := HandleNewUser(tt.args.cc, tt.args.t)
1643 if !tt.wantErr(t, err, fmt.Sprintf("HandleNewUser(%v, %v)", tt.args.cc, tt.args.t)) {
1644 return
1645 }
1646
1647 tranAssertEqual(t, tt.wantRes, gotRes)
1648 })
1649 }
1650}
1651
1652func TestHandleListUsers(t *testing.T) {
1653 type args struct {
1654 cc *ClientConn
1655 t *Transaction
1656 }
1657 tests := []struct {
1658 name string
1659 args args
1660 wantRes []Transaction
1661 wantErr assert.ErrorAssertionFunc
1662 }{
1663 {
1664 name: "when user does not have required permission",
1665 args: args{
1666 cc: &ClientConn{
1667 Account: &Account{
1668 Access: func() accessBitmap {
1669 var bits accessBitmap
1670 return bits
1671 }(),
1672 },
1673 Server: &Server{
1674 Accounts: map[string]*Account{},
1675 },
1676 },
1677 t: NewTransaction(
1678 TranNewUser, &[]byte{0, 1},
1679 ),
1680 },
1681 wantRes: []Transaction{
1682 {
1683 IsReply: 0x01,
1684 ErrorCode: [4]byte{0, 0, 0, 1},
1685 Fields: []Field{
1686 NewField(FieldError, []byte("You are not allowed to view accounts.")),
1687 },
1688 },
1689 },
1690 wantErr: assert.NoError,
1691 },
1692 {
1693 name: "when user has required permission",
1694 args: args{
1695 cc: &ClientConn{
1696 Account: &Account{
1697 Access: func() accessBitmap {
1698 var bits accessBitmap
1699 bits.Set(accessOpenUser)
1700 return bits
1701 }(),
1702 },
1703 Server: &Server{
1704 Accounts: map[string]*Account{
1705 "guest": {
1706 Name: "guest",
1707 Login: "guest",
1708 Password: "zz",
1709 Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
1710 },
1711 },
1712 },
1713 },
1714 t: NewTransaction(
1715 TranGetClientInfoText, &[]byte{0, 1},
1716 NewField(FieldUserID, []byte{0, 1}),
1717 ),
1718 },
1719 wantRes: []Transaction{
1720 {
1721 IsReply: 0x01,
1722 Fields: []Field{
1723 NewField(FieldData, []byte{
1724 0x00, 0x04, 0x00, 0x66, 0x00, 0x05, 0x67, 0x75, 0x65, 0x73, 0x74, 0x00, 0x69, 0x00, 0x05, 0x98,
1725 0x8a, 0x9a, 0x8c, 0x8b, 0x00, 0x6e, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1726 0x00, 0x6a, 0x00, 0x01, 0x78,
1727 }),
1728 },
1729 },
1730 },
1731 wantErr: assert.NoError,
1732 },
1733 }
1734 for _, tt := range tests {
1735 t.Run(tt.name, func(t *testing.T) {
1736 gotRes, err := HandleListUsers(tt.args.cc, tt.args.t)
1737 if !tt.wantErr(t, err, fmt.Sprintf("HandleListUsers(%v, %v)", tt.args.cc, tt.args.t)) {
1738 return
1739 }
1740
1741 tranAssertEqual(t, tt.wantRes, gotRes)
1742 })
1743 }
1744}
1745
1746func TestHandleDownloadFile(t *testing.T) {
1747 type args struct {
1748 cc *ClientConn
1749 t *Transaction
1750 }
1751 tests := []struct {
1752 name string
1753 args args
1754 wantRes []Transaction
1755 wantErr assert.ErrorAssertionFunc
1756 }{
1757 {
1758 name: "when user does not have required permission",
1759 args: args{
1760 cc: &ClientConn{
1761 Account: &Account{
1762 Access: func() accessBitmap {
1763 var bits accessBitmap
1764 return bits
1765 }(),
1766 },
1767 Server: &Server{},
1768 },
1769 t: NewTransaction(TranDownloadFile, &[]byte{0, 1}),
1770 },
1771 wantRes: []Transaction{
1772 {
1773 IsReply: 0x01,
1774 ErrorCode: [4]byte{0, 0, 0, 1},
1775 Fields: []Field{
1776 NewField(FieldError, []byte("You are not allowed to download files.")),
1777 },
1778 },
1779 },
1780 wantErr: assert.NoError,
1781 },
1782 {
1783 name: "with a valid file",
1784 args: args{
1785 cc: &ClientConn{
1786 transfers: map[int]map[[4]byte]*FileTransfer{
1787 FileDownload: {},
1788 },
1789 Account: &Account{
1790 Access: func() accessBitmap {
1791 var bits accessBitmap
1792 bits.Set(accessDownloadFile)
1793 return bits
1794 }(),
1795 },
1796 Server: &Server{
1797 FS: &OSFileStore{},
1798 fileTransfers: map[[4]byte]*FileTransfer{},
1799 Config: &Config{
1800 FileRoot: func() string { path, _ := os.Getwd(); return path + "/test/config/Files" }(),
1801 },
1802 Accounts: map[string]*Account{},
1803 },
1804 },
1805 t: NewTransaction(
1806 accessDownloadFile,
1807 &[]byte{0, 1},
1808 NewField(FieldFileName, []byte("testfile.txt")),
1809 NewField(FieldFilePath, []byte{0x0, 0x00}),
1810 ),
1811 },
1812 wantRes: []Transaction{
1813 {
1814 IsReply: 0x01,
1815 Fields: []Field{
1816 NewField(FieldRefNum, []byte{0x52, 0xfd, 0xfc, 0x07}),
1817 NewField(FieldWaitingCount, []byte{0x00, 0x00}),
1818 NewField(FieldTransferSize, []byte{0x00, 0x00, 0x00, 0xa5}),
1819 NewField(FieldFileSize, []byte{0x00, 0x00, 0x00, 0x17}),
1820 },
1821 },
1822 },
1823 wantErr: assert.NoError,
1824 },
1825 {
1826 name: "when client requests to resume 1k test file at offset 256",
1827 args: args{
1828 cc: &ClientConn{
1829 transfers: map[int]map[[4]byte]*FileTransfer{
1830 FileDownload: {},
1831 }, Account: &Account{
1832 Access: func() accessBitmap {
1833 var bits accessBitmap
1834 bits.Set(accessDownloadFile)
1835 return bits
1836 }(),
1837 },
1838 Server: &Server{
1839 FS: &OSFileStore{},
1840
1841 // FS: func() *MockFileStore {
1842 // path, _ := os.Getwd()
1843 // testFile, err := os.Open(path + "/test/config/Files/testfile-1k")
1844 // if err != nil {
1845 // panic(err)
1846 // }
1847 //
1848 // mfi := &MockFileInfo{}
1849 // mfi.On("Mode").Return(fs.FileMode(0))
1850 // mfs := &MockFileStore{}
1851 // mfs.On("Stat", "/fakeRoot/Files/testfile.txt").Return(mfi, nil)
1852 // mfs.On("Open", "/fakeRoot/Files/testfile.txt").Return(testFile, nil)
1853 // mfs.On("Stat", "/fakeRoot/Files/.info_testfile.txt").Return(nil, errors.New("no"))
1854 // mfs.On("Stat", "/fakeRoot/Files/.rsrc_testfile.txt").Return(nil, errors.New("no"))
1855 //
1856 // return mfs
1857 // }(),
1858 fileTransfers: map[[4]byte]*FileTransfer{},
1859 Config: &Config{
1860 FileRoot: func() string { path, _ := os.Getwd(); return path + "/test/config/Files" }(),
1861 },
1862 Accounts: map[string]*Account{},
1863 },
1864 },
1865 t: NewTransaction(
1866 accessDownloadFile,
1867 &[]byte{0, 1},
1868 NewField(FieldFileName, []byte("testfile-1k")),
1869 NewField(FieldFilePath, []byte{0x00, 0x00}),
1870 NewField(
1871 FieldFileResumeData,
1872 func() []byte {
1873 frd := FileResumeData{
1874 Format: [4]byte{},
1875 Version: [2]byte{},
1876 RSVD: [34]byte{},
1877 ForkCount: [2]byte{0, 2},
1878 ForkInfoList: []ForkInfoList{
1879 {
1880 Fork: [4]byte{0x44, 0x41, 0x54, 0x41}, // "DATA"
1881 DataSize: [4]byte{0, 0, 0x01, 0x00}, // request offset 256
1882 RSVDA: [4]byte{},
1883 RSVDB: [4]byte{},
1884 },
1885 {
1886 Fork: [4]byte{0x4d, 0x41, 0x43, 0x52}, // "MACR"
1887 DataSize: [4]byte{0, 0, 0, 0},
1888 RSVDA: [4]byte{},
1889 RSVDB: [4]byte{},
1890 },
1891 },
1892 }
1893 b, _ := frd.BinaryMarshal()
1894 return b
1895 }(),
1896 ),
1897 ),
1898 },
1899 wantRes: []Transaction{
1900 {
1901 IsReply: 0x01,
1902 Fields: []Field{
1903 NewField(FieldRefNum, []byte{0x52, 0xfd, 0xfc, 0x07}),
1904 NewField(FieldWaitingCount, []byte{0x00, 0x00}),
1905 NewField(FieldTransferSize, []byte{0x00, 0x00, 0x03, 0x8d}),
1906 NewField(FieldFileSize, []byte{0x00, 0x00, 0x03, 0x00}),
1907 },
1908 },
1909 },
1910 wantErr: assert.NoError,
1911 },
1912 }
1913 for _, tt := range tests {
1914 t.Run(tt.name, func(t *testing.T) {
1915 gotRes, err := HandleDownloadFile(tt.args.cc, tt.args.t)
1916 if !tt.wantErr(t, err, fmt.Sprintf("HandleDownloadFile(%v, %v)", tt.args.cc, tt.args.t)) {
1917 return
1918 }
1919
1920 tranAssertEqual(t, tt.wantRes, gotRes)
1921 })
1922 }
1923}
1924
1925func TestHandleUpdateUser(t *testing.T) {
1926 type args struct {
1927 cc *ClientConn
1928 t *Transaction
1929 }
1930 tests := []struct {
1931 name string
1932 args args
1933 wantRes []Transaction
1934 wantErr assert.ErrorAssertionFunc
1935 }{
1936 {
1937 name: "when action is create user without required permission",
1938 args: args{
1939 cc: &ClientConn{
1940 logger: NewTestLogger(),
1941 Server: &Server{
1942 Logger: NewTestLogger(),
1943 },
1944 Account: &Account{
1945 Access: func() accessBitmap {
1946 var bits accessBitmap
1947 return bits
1948 }(),
1949 },
1950 },
1951 t: NewTransaction(
1952 TranUpdateUser,
1953 &[]byte{0, 0},
1954 NewField(FieldData, []byte{
1955 0x00, 0x04, // field count
1956
1957 0x00, 0x69, // FieldUserLogin = 105
1958 0x00, 0x03,
1959 0x9d, 0x9d, 0x9d,
1960
1961 0x00, 0x6a, // FieldUserPassword = 106
1962 0x00, 0x03,
1963 0x9c, 0x9c, 0x9c,
1964
1965 0x00, 0x66, // FieldUserName = 102
1966 0x00, 0x03,
1967 0x61, 0x61, 0x61,
1968
1969 0x00, 0x6e, // FieldUserAccess = 110
1970 0x00, 0x08,
1971 0x60, 0x70, 0x0c, 0x20, 0x03, 0x80, 0x00, 0x00,
1972 }),
1973 ),
1974 },
1975 wantRes: []Transaction{
1976 {
1977 IsReply: 0x01,
1978 ErrorCode: [4]byte{0, 0, 0, 1},
1979 Fields: []Field{
1980 NewField(FieldError, []byte("You are not allowed to create new accounts.")),
1981 },
1982 },
1983 },
1984 wantErr: assert.NoError,
1985 },
1986 {
1987 name: "when action is modify user without required permission",
1988 args: args{
1989 cc: &ClientConn{
1990 logger: NewTestLogger(),
1991 Server: &Server{
1992 Logger: NewTestLogger(),
1993 Accounts: map[string]*Account{
1994 "bbb": {},
1995 },
1996 },
1997 Account: &Account{
1998 Access: func() accessBitmap {
1999 var bits accessBitmap
2000 return bits
2001 }(),
2002 },
2003 },
2004 t: NewTransaction(
2005 TranUpdateUser,
2006 &[]byte{0, 0},
2007 NewField(FieldData, []byte{
2008 0x00, 0x04, // field count
2009
2010 0x00, 0x69, // FieldUserLogin = 105
2011 0x00, 0x03,
2012 0x9d, 0x9d, 0x9d,
2013
2014 0x00, 0x6a, // FieldUserPassword = 106
2015 0x00, 0x03,
2016 0x9c, 0x9c, 0x9c,
2017
2018 0x00, 0x66, // FieldUserName = 102
2019 0x00, 0x03,
2020 0x61, 0x61, 0x61,
2021
2022 0x00, 0x6e, // FieldUserAccess = 110
2023 0x00, 0x08,
2024 0x60, 0x70, 0x0c, 0x20, 0x03, 0x80, 0x00, 0x00,
2025 }),
2026 ),
2027 },
2028 wantRes: []Transaction{
2029 {
2030 IsReply: 0x01,
2031 ErrorCode: [4]byte{0, 0, 0, 1},
2032 Fields: []Field{
2033 NewField(FieldError, []byte("You are not allowed to modify accounts.")),
2034 },
2035 },
2036 },
2037 wantErr: assert.NoError,
2038 },
2039 {
2040 name: "when action is delete user without required permission",
2041 args: args{
2042 cc: &ClientConn{
2043 logger: NewTestLogger(),
2044 Server: &Server{
2045 Accounts: map[string]*Account{
2046 "bbb": {},
2047 },
2048 },
2049 Account: &Account{
2050 Access: func() accessBitmap {
2051 var bits accessBitmap
2052 return bits
2053 }(),
2054 },
2055 },
2056 t: NewTransaction(
2057 TranUpdateUser,
2058 &[]byte{0, 0},
2059 NewField(FieldData, []byte{
2060 0x00, 0x01,
2061 0x00, 0x65,
2062 0x00, 0x03,
2063 0x88, 0x9e, 0x8b,
2064 }),
2065 ),
2066 },
2067 wantRes: []Transaction{
2068 {
2069 IsReply: 0x01,
2070 ErrorCode: [4]byte{0, 0, 0, 1},
2071 Fields: []Field{
2072 NewField(FieldError, []byte("You are not allowed to delete accounts.")),
2073 },
2074 },
2075 },
2076 wantErr: assert.NoError,
2077 },
2078 }
2079 for _, tt := range tests {
2080 t.Run(tt.name, func(t *testing.T) {
2081 gotRes, err := HandleUpdateUser(tt.args.cc, tt.args.t)
2082 if !tt.wantErr(t, err, fmt.Sprintf("HandleUpdateUser(%v, %v)", tt.args.cc, tt.args.t)) {
2083 return
2084 }
2085
2086 tranAssertEqual(t, tt.wantRes, gotRes)
2087 })
2088 }
2089}
2090
2091func TestHandleDelNewsArt(t *testing.T) {
2092 type args struct {
2093 cc *ClientConn
2094 t *Transaction
2095 }
2096 tests := []struct {
2097 name string
2098 args args
2099 wantRes []Transaction
2100 wantErr assert.ErrorAssertionFunc
2101 }{
2102 {
2103 name: "without required permission",
2104 args: args{
2105 cc: &ClientConn{
2106 Account: &Account{
2107 Access: func() accessBitmap {
2108 var bits accessBitmap
2109 return bits
2110 }(),
2111 },
2112 },
2113 t: NewTransaction(
2114 TranDelNewsArt,
2115 &[]byte{0, 0},
2116 ),
2117 },
2118 wantRes: []Transaction{
2119 {
2120 IsReply: 0x01,
2121 ErrorCode: [4]byte{0, 0, 0, 1},
2122 Fields: []Field{
2123 NewField(FieldError, []byte("You are not allowed to delete news articles.")),
2124 },
2125 },
2126 },
2127 wantErr: assert.NoError,
2128 },
2129 }
2130 for _, tt := range tests {
2131 t.Run(tt.name, func(t *testing.T) {
2132 gotRes, err := HandleDelNewsArt(tt.args.cc, tt.args.t)
2133 if !tt.wantErr(t, err, fmt.Sprintf("HandleDelNewsArt(%v, %v)", tt.args.cc, tt.args.t)) {
2134 return
2135 }
2136 tranAssertEqual(t, tt.wantRes, gotRes)
2137 })
2138 }
2139}
2140
2141func TestHandleDisconnectUser(t *testing.T) {
2142 type args struct {
2143 cc *ClientConn
2144 t *Transaction
2145 }
2146 tests := []struct {
2147 name string
2148 args args
2149 wantRes []Transaction
2150 wantErr assert.ErrorAssertionFunc
2151 }{
2152 {
2153 name: "without required permission",
2154 args: args{
2155 cc: &ClientConn{
2156 Account: &Account{
2157 Access: func() accessBitmap {
2158 var bits accessBitmap
2159 return bits
2160 }(),
2161 },
2162 },
2163 t: NewTransaction(
2164 TranDelNewsArt,
2165 &[]byte{0, 0},
2166 ),
2167 },
2168 wantRes: []Transaction{
2169 {
2170 IsReply: 0x01,
2171 ErrorCode: [4]byte{0, 0, 0, 1},
2172 Fields: []Field{
2173 NewField(FieldError, []byte("You are not allowed to disconnect users.")),
2174 },
2175 },
2176 },
2177 wantErr: assert.NoError,
2178 },
2179 {
2180 name: "when target user has 'cannot be disconnected' priv",
2181 args: args{
2182 cc: &ClientConn{
2183 Server: &Server{
2184 Clients: map[uint16]*ClientConn{
2185 uint16(1): {
2186 Account: &Account{
2187 Login: "unnamed",
2188 Access: func() accessBitmap {
2189 var bits accessBitmap
2190 bits.Set(accessCannotBeDiscon)
2191 return bits
2192 }(),
2193 },
2194 },
2195 },
2196 },
2197 Account: &Account{
2198 Access: func() accessBitmap {
2199 var bits accessBitmap
2200 bits.Set(accessDisconUser)
2201 return bits
2202 }(),
2203 },
2204 },
2205 t: NewTransaction(
2206 TranDelNewsArt,
2207 &[]byte{0, 0},
2208 NewField(FieldUserID, []byte{0, 1}),
2209 ),
2210 },
2211 wantRes: []Transaction{
2212 {
2213 IsReply: 0x01,
2214 ErrorCode: [4]byte{0, 0, 0, 1},
2215 Fields: []Field{
2216 NewField(FieldError, []byte("unnamed is not allowed to be disconnected.")),
2217 },
2218 },
2219 },
2220 wantErr: assert.NoError,
2221 },
2222 }
2223 for _, tt := range tests {
2224 t.Run(tt.name, func(t *testing.T) {
2225 gotRes, err := HandleDisconnectUser(tt.args.cc, tt.args.t)
2226 if !tt.wantErr(t, err, fmt.Sprintf("HandleDisconnectUser(%v, %v)", tt.args.cc, tt.args.t)) {
2227 return
2228 }
2229 tranAssertEqual(t, tt.wantRes, gotRes)
2230 })
2231 }
2232}
2233
2234func TestHandleSendInstantMsg(t *testing.T) {
2235 type args struct {
2236 cc *ClientConn
2237 t *Transaction
2238 }
2239 tests := []struct {
2240 name string
2241 args args
2242 wantRes []Transaction
2243 wantErr assert.ErrorAssertionFunc
2244 }{
2245 {
2246 name: "without required permission",
2247 args: args{
2248 cc: &ClientConn{
2249 Account: &Account{
2250 Access: func() accessBitmap {
2251 var bits accessBitmap
2252 return bits
2253 }(),
2254 },
2255 },
2256 t: NewTransaction(
2257 TranDelNewsArt,
2258 &[]byte{0, 0},
2259 ),
2260 },
2261 wantRes: []Transaction{
2262 {
2263 IsReply: 0x01,
2264 ErrorCode: [4]byte{0, 0, 0, 1},
2265 Fields: []Field{
2266 NewField(FieldError, []byte("You are not allowed to send private messages.")),
2267 },
2268 },
2269 },
2270 wantErr: assert.Error,
2271 },
2272 {
2273 name: "when client 1 sends a message to client 2",
2274 args: args{
2275 cc: &ClientConn{
2276 Account: &Account{
2277 Access: func() accessBitmap {
2278 var bits accessBitmap
2279 bits.Set(accessSendPrivMsg)
2280 return bits
2281 }(),
2282 },
2283 ID: &[]byte{0, 1},
2284 UserName: []byte("User1"),
2285 Server: &Server{
2286 Clients: map[uint16]*ClientConn{
2287 uint16(2): {
2288 AutoReply: []byte(nil),
2289 Flags: []byte{0, 0},
2290 },
2291 },
2292 },
2293 },
2294 t: NewTransaction(
2295 TranSendInstantMsg,
2296 &[]byte{0, 1},
2297 NewField(FieldData, []byte("hai")),
2298 NewField(FieldUserID, []byte{0, 2}),
2299 ),
2300 },
2301 wantRes: []Transaction{
2302 *NewTransaction(
2303 TranServerMsg,
2304 &[]byte{0, 2},
2305 NewField(FieldData, []byte("hai")),
2306 NewField(FieldUserName, []byte("User1")),
2307 NewField(FieldUserID, []byte{0, 1}),
2308 NewField(FieldOptions, []byte{0, 1}),
2309 ),
2310 {
2311 clientID: &[]byte{0, 1},
2312 IsReply: 0x01,
2313 Fields: []Field(nil),
2314 },
2315 },
2316 wantErr: assert.NoError,
2317 },
2318 {
2319 name: "when client 2 has autoreply enabled",
2320 args: args{
2321 cc: &ClientConn{
2322 Account: &Account{
2323 Access: func() accessBitmap {
2324 var bits accessBitmap
2325 bits.Set(accessSendPrivMsg)
2326 return bits
2327 }(),
2328 },
2329 ID: &[]byte{0, 1},
2330 UserName: []byte("User1"),
2331 Server: &Server{
2332 Clients: map[uint16]*ClientConn{
2333 uint16(2): {
2334 Flags: []byte{0, 0},
2335 ID: &[]byte{0, 2},
2336 UserName: []byte("User2"),
2337 AutoReply: []byte("autohai"),
2338 },
2339 },
2340 },
2341 },
2342 t: NewTransaction(
2343 TranSendInstantMsg,
2344 &[]byte{0, 1},
2345 NewField(FieldData, []byte("hai")),
2346 NewField(FieldUserID, []byte{0, 2}),
2347 ),
2348 },
2349 wantRes: []Transaction{
2350 *NewTransaction(
2351 TranServerMsg,
2352 &[]byte{0, 2},
2353 NewField(FieldData, []byte("hai")),
2354 NewField(FieldUserName, []byte("User1")),
2355 NewField(FieldUserID, []byte{0, 1}),
2356 NewField(FieldOptions, []byte{0, 1}),
2357 ),
2358 *NewTransaction(
2359 TranServerMsg,
2360 &[]byte{0, 1},
2361 NewField(FieldData, []byte("autohai")),
2362 NewField(FieldUserName, []byte("User2")),
2363 NewField(FieldUserID, []byte{0, 2}),
2364 NewField(FieldOptions, []byte{0, 1}),
2365 ),
2366 {
2367 clientID: &[]byte{0, 1},
2368 IsReply: 0x01,
2369 Fields: []Field(nil),
2370 },
2371 },
2372 wantErr: assert.NoError,
2373 },
2374 {
2375 name: "when client 2 has refuse private messages enabled",
2376 args: args{
2377 cc: &ClientConn{
2378 Account: &Account{
2379 Access: func() accessBitmap {
2380 var bits accessBitmap
2381 bits.Set(accessSendPrivMsg)
2382 return bits
2383 }(),
2384 },
2385 ID: &[]byte{0, 1},
2386 UserName: []byte("User1"),
2387 Server: &Server{
2388 Clients: map[uint16]*ClientConn{
2389 uint16(2): {
2390 Flags: []byte{255, 255},
2391 ID: &[]byte{0, 2},
2392 UserName: []byte("User2"),
2393 },
2394 },
2395 },
2396 },
2397 t: NewTransaction(
2398 TranSendInstantMsg,
2399 &[]byte{0, 1},
2400 NewField(FieldData, []byte("hai")),
2401 NewField(FieldUserID, []byte{0, 2}),
2402 ),
2403 },
2404 wantRes: []Transaction{
2405 *NewTransaction(
2406 TranServerMsg,
2407 &[]byte{0, 1},
2408 NewField(FieldData, []byte("User2 does not accept private messages.")),
2409 NewField(FieldUserName, []byte("User2")),
2410 NewField(FieldUserID, []byte{0, 2}),
2411 NewField(FieldOptions, []byte{0, 2}),
2412 ),
2413 {
2414 clientID: &[]byte{0, 1},
2415 IsReply: 0x01,
2416 Fields: []Field(nil),
2417 },
2418 },
2419 wantErr: assert.NoError,
2420 },
2421 }
2422 for _, tt := range tests {
2423 t.Run(tt.name, func(t *testing.T) {
2424 gotRes, err := HandleSendInstantMsg(tt.args.cc, tt.args.t)
2425 if !tt.wantErr(t, err, fmt.Sprintf("HandleSendInstantMsg(%v, %v)", tt.args.cc, tt.args.t)) {
2426 return
2427 }
2428
2429 tranAssertEqual(t, tt.wantRes, gotRes)
2430 })
2431 }
2432}
2433
2434func TestHandleDeleteFile(t *testing.T) {
2435 type args struct {
2436 cc *ClientConn
2437 t *Transaction
2438 }
2439 tests := []struct {
2440 name string
2441 args args
2442 wantRes []Transaction
2443 wantErr assert.ErrorAssertionFunc
2444 }{
2445 {
2446 name: "when user does not have required permission to delete a folder",
2447 args: args{
2448 cc: &ClientConn{
2449 Account: &Account{
2450 Access: func() accessBitmap {
2451 var bits accessBitmap
2452 return bits
2453 }(),
2454 },
2455 Server: &Server{
2456 Config: &Config{
2457 FileRoot: func() string {
2458 return "/fakeRoot/Files"
2459 }(),
2460 },
2461 FS: func() *MockFileStore {
2462 mfi := &MockFileInfo{}
2463 mfi.On("Mode").Return(fs.FileMode(0))
2464 mfi.On("Size").Return(int64(100))
2465 mfi.On("ModTime").Return(time.Parse(time.Layout, time.Layout))
2466 mfi.On("IsDir").Return(false)
2467 mfi.On("Name").Return("testfile")
2468
2469 mfs := &MockFileStore{}
2470 mfs.On("Stat", "/fakeRoot/Files/aaa/testfile").Return(mfi, nil)
2471 mfs.On("Stat", "/fakeRoot/Files/aaa/.info_testfile").Return(nil, errors.New("err"))
2472 mfs.On("Stat", "/fakeRoot/Files/aaa/.rsrc_testfile").Return(nil, errors.New("err"))
2473
2474 return mfs
2475 }(),
2476 Accounts: map[string]*Account{},
2477 },
2478 },
2479 t: NewTransaction(
2480 TranDeleteFile, &[]byte{0, 1},
2481 NewField(FieldFileName, []byte("testfile")),
2482 NewField(FieldFilePath, []byte{
2483 0x00, 0x01,
2484 0x00, 0x00,
2485 0x03,
2486 0x61, 0x61, 0x61,
2487 }),
2488 ),
2489 },
2490 wantRes: []Transaction{
2491 {
2492 IsReply: 0x01,
2493 ErrorCode: [4]byte{0, 0, 0, 1},
2494 Fields: []Field{
2495 NewField(FieldError, []byte("You are not allowed to delete files.")),
2496 },
2497 },
2498 },
2499 wantErr: assert.NoError,
2500 },
2501 {
2502 name: "deletes all associated metadata files",
2503 args: args{
2504 cc: &ClientConn{
2505 Account: &Account{
2506 Access: func() accessBitmap {
2507 var bits accessBitmap
2508 bits.Set(accessDeleteFile)
2509 return bits
2510 }(),
2511 },
2512 Server: &Server{
2513 Config: &Config{
2514 FileRoot: func() string {
2515 return "/fakeRoot/Files"
2516 }(),
2517 },
2518 FS: func() *MockFileStore {
2519 mfi := &MockFileInfo{}
2520 mfi.On("Mode").Return(fs.FileMode(0))
2521 mfi.On("Size").Return(int64(100))
2522 mfi.On("ModTime").Return(time.Parse(time.Layout, time.Layout))
2523 mfi.On("IsDir").Return(false)
2524 mfi.On("Name").Return("testfile")
2525
2526 mfs := &MockFileStore{}
2527 mfs.On("Stat", "/fakeRoot/Files/aaa/testfile").Return(mfi, nil)
2528 mfs.On("Stat", "/fakeRoot/Files/aaa/.info_testfile").Return(nil, errors.New("err"))
2529 mfs.On("Stat", "/fakeRoot/Files/aaa/.rsrc_testfile").Return(nil, errors.New("err"))
2530
2531 mfs.On("RemoveAll", "/fakeRoot/Files/aaa/testfile").Return(nil)
2532 mfs.On("Remove", "/fakeRoot/Files/aaa/testfile.incomplete").Return(nil)
2533 mfs.On("Remove", "/fakeRoot/Files/aaa/.rsrc_testfile").Return(nil)
2534 mfs.On("Remove", "/fakeRoot/Files/aaa/.info_testfile").Return(nil)
2535
2536 return mfs
2537 }(),
2538 Accounts: map[string]*Account{},
2539 },
2540 },
2541 t: NewTransaction(
2542 TranDeleteFile, &[]byte{0, 1},
2543 NewField(FieldFileName, []byte("testfile")),
2544 NewField(FieldFilePath, []byte{
2545 0x00, 0x01,
2546 0x00, 0x00,
2547 0x03,
2548 0x61, 0x61, 0x61,
2549 }),
2550 ),
2551 },
2552 wantRes: []Transaction{
2553 {
2554 IsReply: 0x01,
2555 Fields: []Field(nil),
2556 },
2557 },
2558 wantErr: assert.NoError,
2559 },
2560 }
2561 for _, tt := range tests {
2562 t.Run(tt.name, func(t *testing.T) {
2563 gotRes, err := HandleDeleteFile(tt.args.cc, tt.args.t)
2564 if !tt.wantErr(t, err, fmt.Sprintf("HandleDeleteFile(%v, %v)", tt.args.cc, tt.args.t)) {
2565 return
2566 }
2567
2568 tranAssertEqual(t, tt.wantRes, gotRes)
2569
2570 tt.args.cc.Server.FS.(*MockFileStore).AssertExpectations(t)
2571 })
2572 }
2573}
2574
2575func TestHandleGetFileNameList(t *testing.T) {
2576 type args struct {
2577 cc *ClientConn
2578 t *Transaction
2579 }
2580 tests := []struct {
2581 name string
2582 args args
2583 wantRes []Transaction
2584 wantErr assert.ErrorAssertionFunc
2585 }{
2586 {
2587 name: "when FieldFilePath is a drop box, but user does not have accessViewDropBoxes ",
2588 args: args{
2589 cc: &ClientConn{
2590 Account: &Account{
2591 Access: func() accessBitmap {
2592 var bits accessBitmap
2593 return bits
2594 }(),
2595 },
2596 Server: &Server{
2597
2598 Config: &Config{
2599 FileRoot: func() string {
2600 path, _ := os.Getwd()
2601 return filepath.Join(path, "/test/config/Files/getFileNameListTestDir")
2602 }(),
2603 },
2604 },
2605 },
2606 t: NewTransaction(
2607 TranGetFileNameList, &[]byte{0, 1},
2608 NewField(FieldFilePath, []byte{
2609 0x00, 0x01,
2610 0x00, 0x00,
2611 0x08,
2612 0x64, 0x72, 0x6f, 0x70, 0x20, 0x62, 0x6f, 0x78, // "drop box"
2613 }),
2614 ),
2615 },
2616 wantRes: []Transaction{
2617 {
2618 IsReply: 0x01,
2619 ErrorCode: [4]byte{0, 0, 0, 1},
2620 Fields: []Field{
2621 NewField(FieldError, []byte("You are not allowed to view drop boxes.")),
2622 },
2623 },
2624 },
2625 wantErr: assert.NoError,
2626 },
2627 {
2628 name: "with file root",
2629 args: args{
2630 cc: &ClientConn{
2631 Server: &Server{
2632 Config: &Config{
2633 FileRoot: func() string {
2634 path, _ := os.Getwd()
2635 return filepath.Join(path, "/test/config/Files/getFileNameListTestDir")
2636 }(),
2637 },
2638 },
2639 },
2640 t: NewTransaction(
2641 TranGetFileNameList, &[]byte{0, 1},
2642 NewField(FieldFilePath, []byte{
2643 0x00, 0x00,
2644 0x00, 0x00,
2645 }),
2646 ),
2647 },
2648 wantRes: []Transaction{
2649 {
2650 IsReply: 0x01,
2651 Fields: []Field{
2652 NewField(
2653 FieldFileNameWithInfo,
2654 func() []byte {
2655 fnwi := FileNameWithInfo{
2656 fileNameWithInfoHeader: fileNameWithInfoHeader{
2657 Type: [4]byte{0x54, 0x45, 0x58, 0x54},
2658 Creator: [4]byte{0x54, 0x54, 0x58, 0x54},
2659 FileSize: [4]byte{0, 0, 0x04, 0},
2660 RSVD: [4]byte{},
2661 NameScript: [2]byte{},
2662 NameSize: [2]byte{0, 0x0b},
2663 },
2664 Name: []byte("testfile-1k"),
2665 }
2666 b, _ := io.ReadAll(&fnwi)
2667 return b
2668 }(),
2669 ),
2670 },
2671 },
2672 },
2673 wantErr: assert.NoError,
2674 },
2675 }
2676 for _, tt := range tests {
2677 t.Run(tt.name, func(t *testing.T) {
2678 gotRes, err := HandleGetFileNameList(tt.args.cc, tt.args.t)
2679 if !tt.wantErr(t, err, fmt.Sprintf("HandleGetFileNameList(%v, %v)", tt.args.cc, tt.args.t)) {
2680 return
2681 }
2682
2683 tranAssertEqual(t, tt.wantRes, gotRes)
2684 })
2685 }
2686}
2687
2688func TestHandleGetClientInfoText(t *testing.T) {
2689 type args struct {
2690 cc *ClientConn
2691 t *Transaction
2692 }
2693 tests := []struct {
2694 name string
2695 args args
2696 wantRes []Transaction
2697 wantErr assert.ErrorAssertionFunc
2698 }{
2699 {
2700 name: "when user does not have required permission",
2701 args: args{
2702 cc: &ClientConn{
2703 Account: &Account{
2704 Access: func() accessBitmap {
2705 var bits accessBitmap
2706 return bits
2707 }(),
2708 },
2709 Server: &Server{
2710 Accounts: map[string]*Account{},
2711 },
2712 },
2713 t: NewTransaction(
2714 TranGetClientInfoText, &[]byte{0, 1},
2715 NewField(FieldUserID, []byte{0, 1}),
2716 ),
2717 },
2718 wantRes: []Transaction{
2719 {
2720 IsReply: 0x01,
2721 ErrorCode: [4]byte{0, 0, 0, 1},
2722 Fields: []Field{
2723 NewField(FieldError, []byte("You are not allowed to get client info.")),
2724 },
2725 },
2726 },
2727 wantErr: assert.NoError,
2728 },
2729 {
2730 name: "with a valid user",
2731 args: args{
2732 cc: &ClientConn{
2733 UserName: []byte("Testy McTest"),
2734 RemoteAddr: "1.2.3.4:12345",
2735 Account: &Account{
2736 Access: func() accessBitmap {
2737 var bits accessBitmap
2738 bits.Set(accessGetClientInfo)
2739 return bits
2740 }(),
2741 Name: "test",
2742 Login: "test",
2743 },
2744 Server: &Server{
2745 Accounts: map[string]*Account{},
2746 Clients: map[uint16]*ClientConn{
2747 uint16(1): {
2748 UserName: []byte("Testy McTest"),
2749 RemoteAddr: "1.2.3.4:12345",
2750 Account: &Account{
2751 Access: func() accessBitmap {
2752 var bits accessBitmap
2753 bits.Set(accessGetClientInfo)
2754 return bits
2755 }(),
2756 Name: "test",
2757 Login: "test",
2758 },
2759 },
2760 },
2761 },
2762 transfers: map[int]map[[4]byte]*FileTransfer{
2763 FileDownload: {},
2764 FileUpload: {},
2765 FolderDownload: {},
2766 FolderUpload: {},
2767 },
2768 },
2769 t: NewTransaction(
2770 TranGetClientInfoText, &[]byte{0, 1},
2771 NewField(FieldUserID, []byte{0, 1}),
2772 ),
2773 },
2774 wantRes: []Transaction{
2775 {
2776 IsReply: 0x01,
2777 Fields: []Field{
2778 NewField(FieldData, []byte(
2779 strings.ReplaceAll(`Nickname: Testy McTest
2780Name: test
2781Account: test
2782Address: 1.2.3.4:12345
2783
2784-------- File Downloads ---------
2785
2786None.
2787
2788------- Folder Downloads --------
2789
2790None.
2791
2792--------- File Uploads ----------
2793
2794None.
2795
2796-------- Folder Uploads ---------
2797
2798None.
2799
2800------- Waiting Downloads -------
2801
2802None.
2803
2804`, "\n", "\r")),
2805 ),
2806 NewField(FieldUserName, []byte("Testy McTest")),
2807 },
2808 },
2809 },
2810 wantErr: assert.NoError,
2811 },
2812 }
2813 for _, tt := range tests {
2814 t.Run(tt.name, func(t *testing.T) {
2815 gotRes, err := HandleGetClientInfoText(tt.args.cc, tt.args.t)
2816 if !tt.wantErr(t, err, fmt.Sprintf("HandleGetClientInfoText(%v, %v)", tt.args.cc, tt.args.t)) {
2817 return
2818 }
2819 tranAssertEqual(t, tt.wantRes, gotRes)
2820 })
2821 }
2822}
2823
2824func TestHandleTranAgreed(t *testing.T) {
2825 type args struct {
2826 cc *ClientConn
2827 t *Transaction
2828 }
2829 tests := []struct {
2830 name string
2831 args args
2832 wantRes []Transaction
2833 wantErr assert.ErrorAssertionFunc
2834 }{
2835 {
2836 name: "normal request flow",
2837 args: args{
2838 cc: &ClientConn{
2839 Account: &Account{
2840 Access: func() accessBitmap {
2841 var bits accessBitmap
2842 bits.Set(accessDisconUser)
2843 bits.Set(accessAnyName)
2844 return bits
2845 }()},
2846 Icon: []byte{0, 1},
2847 Flags: []byte{0, 1},
2848 Version: []byte{0, 1},
2849 ID: &[]byte{0, 1},
2850 logger: NewTestLogger(),
2851 Server: &Server{
2852 Config: &Config{
2853 BannerFile: "banner.jpg",
2854 },
2855 },
2856 },
2857 t: NewTransaction(
2858 TranAgreed, nil,
2859 NewField(FieldUserName, []byte("username")),
2860 NewField(FieldUserIconID, []byte{0, 1}),
2861 NewField(FieldOptions, []byte{0, 0}),
2862 ),
2863 },
2864 wantRes: []Transaction{
2865 {
2866 clientID: &[]byte{0, 1},
2867 Type: [2]byte{0, 0x7a},
2868 Fields: []Field{
2869 NewField(FieldBannerType, []byte("JPEG")),
2870 },
2871 },
2872 {
2873 clientID: &[]byte{0, 1},
2874 IsReply: 0x01,
2875 Fields: []Field{},
2876 },
2877 },
2878 wantErr: assert.NoError,
2879 },
2880 }
2881 for _, tt := range tests {
2882 t.Run(tt.name, func(t *testing.T) {
2883 gotRes, err := HandleTranAgreed(tt.args.cc, tt.args.t)
2884 if !tt.wantErr(t, err, fmt.Sprintf("HandleTranAgreed(%v, %v)", tt.args.cc, tt.args.t)) {
2885 return
2886 }
2887 tranAssertEqual(t, tt.wantRes, gotRes)
2888 })
2889 }
2890}
2891
2892func TestHandleSetClientUserInfo(t *testing.T) {
2893 type args struct {
2894 cc *ClientConn
2895 t *Transaction
2896 }
2897 tests := []struct {
2898 name string
2899 args args
2900 wantRes []Transaction
2901 wantErr assert.ErrorAssertionFunc
2902 }{
2903 {
2904 name: "when client does not have accessAnyName",
2905 args: args{
2906 cc: &ClientConn{
2907 Account: &Account{
2908 Access: func() accessBitmap {
2909 var bits accessBitmap
2910 return bits
2911 }(),
2912 },
2913 ID: &[]byte{0, 1},
2914 UserName: []byte("Guest"),
2915 Flags: []byte{0, 1},
2916 Server: &Server{
2917 Clients: map[uint16]*ClientConn{
2918 uint16(1): {
2919 ID: &[]byte{0, 1},
2920 },
2921 },
2922 },
2923 },
2924 t: NewTransaction(
2925 TranSetClientUserInfo, nil,
2926 NewField(FieldUserIconID, []byte{0, 1}),
2927 NewField(FieldUserName, []byte("NOPE")),
2928 ),
2929 },
2930 wantRes: []Transaction{
2931 {
2932 clientID: &[]byte{0, 1},
2933 Type: [2]byte{0x01, 0x2d},
2934 Fields: []Field{
2935 NewField(FieldUserID, []byte{0, 1}),
2936 NewField(FieldUserIconID, []byte{0, 1}),
2937 NewField(FieldUserFlags, []byte{0, 1}),
2938 NewField(FieldUserName, []byte("Guest"))},
2939 },
2940 },
2941 wantErr: assert.NoError,
2942 },
2943 }
2944 for _, tt := range tests {
2945 t.Run(tt.name, func(t *testing.T) {
2946 gotRes, err := HandleSetClientUserInfo(tt.args.cc, tt.args.t)
2947 if !tt.wantErr(t, err, fmt.Sprintf("HandleSetClientUserInfo(%v, %v)", tt.args.cc, tt.args.t)) {
2948 return
2949 }
2950
2951 tranAssertEqual(t, tt.wantRes, gotRes)
2952 })
2953 }
2954}
2955
2956func TestHandleDelNewsItem(t *testing.T) {
2957 type args struct {
2958 cc *ClientConn
2959 t *Transaction
2960 }
2961 tests := []struct {
2962 name string
2963 args args
2964 wantRes []Transaction
2965 wantErr assert.ErrorAssertionFunc
2966 }{
2967 {
2968 name: "when user does not have permission to delete a news category",
2969 args: args{
2970 cc: &ClientConn{
2971 Account: &Account{
2972 Access: accessBitmap{},
2973 },
2974 ID: &[]byte{0, 1},
2975 Server: &Server{
2976 ThreadedNews: &ThreadedNews{Categories: map[string]NewsCategoryListData15{
2977 "test": {
2978 Type: [2]byte{0, 3},
2979 Name: "zz",
2980 },
2981 }},
2982 },
2983 },
2984 t: NewTransaction(
2985 TranDelNewsItem, nil,
2986 NewField(FieldNewsPath,
2987 []byte{
2988 0, 1,
2989 0, 0,
2990 4,
2991 0x74, 0x65, 0x73, 0x74,
2992 },
2993 ),
2994 ),
2995 },
2996 wantRes: []Transaction{
2997 {
2998 clientID: &[]byte{0, 1},
2999 IsReply: 0x01,
3000 ErrorCode: [4]byte{0, 0, 0, 1},
3001 Fields: []Field{
3002 NewField(FieldError, []byte("You are not allowed to delete news categories.")),
3003 },
3004 },
3005 },
3006 wantErr: assert.NoError,
3007 },
3008 {
3009 name: "when user does not have permission to delete a news folder",
3010 args: args{
3011 cc: &ClientConn{
3012 Account: &Account{
3013 Access: accessBitmap{},
3014 },
3015 ID: &[]byte{0, 1},
3016 Server: &Server{
3017 ThreadedNews: &ThreadedNews{Categories: map[string]NewsCategoryListData15{
3018 "testcat": {
3019 Type: [2]byte{0, 2},
3020 Name: "test",
3021 },
3022 }},
3023 },
3024 },
3025 t: NewTransaction(
3026 TranDelNewsItem, nil,
3027 NewField(FieldNewsPath,
3028 []byte{
3029 0, 1,
3030 0, 0,
3031 4,
3032 0x74, 0x65, 0x73, 0x74,
3033 },
3034 ),
3035 ),
3036 },
3037 wantRes: []Transaction{
3038 {
3039 clientID: &[]byte{0, 1},
3040 IsReply: 0x01,
3041 ErrorCode: [4]byte{0, 0, 0, 1},
3042 Fields: []Field{
3043 NewField(FieldError, []byte("You are not allowed to delete news folders.")),
3044 },
3045 },
3046 },
3047 wantErr: assert.NoError,
3048 },
3049 {
3050 name: "when user deletes a news folder",
3051 args: args{
3052 cc: &ClientConn{
3053 Account: &Account{
3054 Access: func() accessBitmap {
3055 var bits accessBitmap
3056 bits.Set(accessNewsDeleteFldr)
3057 return bits
3058 }(),
3059 },
3060 ID: &[]byte{0, 1},
3061 Server: &Server{
3062 ConfigDir: "/fakeConfigRoot",
3063 FS: func() *MockFileStore {
3064 mfs := &MockFileStore{}
3065 mfs.On("WriteFile", "/fakeConfigRoot/ThreadedNews.yaml", mock.Anything, mock.Anything).Return(nil, os.ErrNotExist)
3066 return mfs
3067 }(),
3068 ThreadedNews: &ThreadedNews{Categories: map[string]NewsCategoryListData15{
3069 "testcat": {
3070 Type: [2]byte{0, 2},
3071 Name: "test",
3072 },
3073 }},
3074 },
3075 },
3076 t: NewTransaction(
3077 TranDelNewsItem, nil,
3078 NewField(FieldNewsPath,
3079 []byte{
3080 0, 1,
3081 0, 0,
3082 4,
3083 0x74, 0x65, 0x73, 0x74,
3084 },
3085 ),
3086 ),
3087 },
3088 wantRes: []Transaction{
3089 {
3090 clientID: &[]byte{0, 1},
3091 IsReply: 0x01,
3092 Fields: []Field{},
3093 },
3094 },
3095 wantErr: assert.NoError,
3096 },
3097 }
3098 for _, tt := range tests {
3099 t.Run(tt.name, func(t *testing.T) {
3100 gotRes, err := HandleDelNewsItem(tt.args.cc, tt.args.t)
3101 if !tt.wantErr(t, err, fmt.Sprintf("HandleDelNewsItem(%v, %v)", tt.args.cc, tt.args.t)) {
3102 return
3103 }
3104 tranAssertEqual(t, tt.wantRes, gotRes)
3105 })
3106 }
3107}
3108
3109func TestHandleTranOldPostNews(t *testing.T) {
3110 type args struct {
3111 cc *ClientConn
3112 t *Transaction
3113 }
3114 tests := []struct {
3115 name string
3116 args args
3117 wantRes []Transaction
3118 wantErr assert.ErrorAssertionFunc
3119 }{
3120 {
3121 name: "when user does not have required permission",
3122 args: args{
3123 cc: &ClientConn{
3124 Account: &Account{
3125 Access: func() accessBitmap {
3126 var bits accessBitmap
3127 return bits
3128 }(),
3129 },
3130 },
3131 t: NewTransaction(
3132 TranOldPostNews, &[]byte{0, 1},
3133 NewField(FieldData, []byte("hai")),
3134 ),
3135 },
3136 wantRes: []Transaction{
3137 {
3138 IsReply: 0x01,
3139 ErrorCode: [4]byte{0, 0, 0, 1},
3140 Fields: []Field{
3141 NewField(FieldError, []byte("You are not allowed to post news.")),
3142 },
3143 },
3144 },
3145 wantErr: assert.NoError,
3146 },
3147 {
3148 name: "when user posts news update",
3149 args: args{
3150 cc: &ClientConn{
3151 Account: &Account{
3152 Access: func() accessBitmap {
3153 var bits accessBitmap
3154 bits.Set(accessNewsPostArt)
3155 return bits
3156 }(),
3157 },
3158 Server: &Server{
3159 FS: func() *MockFileStore {
3160 mfs := &MockFileStore{}
3161 mfs.On("WriteFile", "/fakeConfigRoot/MessageBoard.txt", mock.Anything, mock.Anything).Return(nil, os.ErrNotExist)
3162 return mfs
3163 }(),
3164 ConfigDir: "/fakeConfigRoot",
3165 Config: &Config{},
3166 },
3167 },
3168 t: NewTransaction(
3169 TranOldPostNews, &[]byte{0, 1},
3170 NewField(FieldData, []byte("hai")),
3171 ),
3172 },
3173 wantRes: []Transaction{
3174 {
3175 IsReply: 0x01,
3176 },
3177 },
3178 wantErr: assert.NoError,
3179 },
3180 }
3181 for _, tt := range tests {
3182 t.Run(tt.name, func(t *testing.T) {
3183 gotRes, err := HandleTranOldPostNews(tt.args.cc, tt.args.t)
3184 if !tt.wantErr(t, err, fmt.Sprintf("HandleTranOldPostNews(%v, %v)", tt.args.cc, tt.args.t)) {
3185 return
3186 }
3187
3188 tranAssertEqual(t, tt.wantRes, gotRes)
3189 })
3190 }
3191}
3192
3193func TestHandleInviteNewChat(t *testing.T) {
3194 type args struct {
3195 cc *ClientConn
3196 t *Transaction
3197 }
3198 tests := []struct {
3199 name string
3200 args args
3201 wantRes []Transaction
3202 wantErr assert.ErrorAssertionFunc
3203 }{
3204 {
3205 name: "when user does not have required permission",
3206 args: args{
3207 cc: &ClientConn{
3208 Account: &Account{
3209 Access: func() accessBitmap {
3210 var bits accessBitmap
3211 return bits
3212 }(),
3213 },
3214 },
3215 t: NewTransaction(TranInviteNewChat, &[]byte{0, 1}),
3216 },
3217 wantRes: []Transaction{
3218 {
3219 IsReply: 0x01,
3220 ErrorCode: [4]byte{0, 0, 0, 1},
3221 Fields: []Field{
3222 NewField(FieldError, []byte("You are not allowed to request private chat.")),
3223 },
3224 },
3225 },
3226 wantErr: assert.NoError,
3227 },
3228 {
3229 name: "when userA invites userB to new private chat",
3230 args: args{
3231 cc: &ClientConn{
3232 ID: &[]byte{0, 1},
3233 Account: &Account{
3234 Access: func() accessBitmap {
3235 var bits accessBitmap
3236 bits.Set(accessOpenChat)
3237 return bits
3238 }(),
3239 },
3240 UserName: []byte("UserA"),
3241 Icon: []byte{0, 1},
3242 Flags: []byte{0, 0},
3243 Server: &Server{
3244 Clients: map[uint16]*ClientConn{
3245 uint16(2): {
3246 ID: &[]byte{0, 2},
3247 UserName: []byte("UserB"),
3248 Flags: []byte{0, 0},
3249 },
3250 },
3251 PrivateChats: make(map[uint32]*PrivateChat),
3252 },
3253 },
3254 t: NewTransaction(
3255 TranInviteNewChat, &[]byte{0, 1},
3256 NewField(FieldUserID, []byte{0, 2}),
3257 ),
3258 },
3259 wantRes: []Transaction{
3260 {
3261 clientID: &[]byte{0, 2},
3262 Type: [2]byte{0, 0x71},
3263 Fields: []Field{
3264 NewField(FieldChatID, []byte{0x52, 0xfd, 0xfc, 0x07}),
3265 NewField(FieldUserName, []byte("UserA")),
3266 NewField(FieldUserID, []byte{0, 1}),
3267 },
3268 },
3269
3270 {
3271 clientID: &[]byte{0, 1},
3272 IsReply: 0x01,
3273 Fields: []Field{
3274 NewField(FieldChatID, []byte{0x52, 0xfd, 0xfc, 0x07}),
3275 NewField(FieldUserName, []byte("UserA")),
3276 NewField(FieldUserID, []byte{0, 1}),
3277 NewField(FieldUserIconID, []byte{0, 1}),
3278 NewField(FieldUserFlags, []byte{0, 0}),
3279 },
3280 },
3281 },
3282 wantErr: assert.NoError,
3283 },
3284 {
3285 name: "when userA invites userB to new private chat, but UserB has refuse private chat enabled",
3286 args: args{
3287 cc: &ClientConn{
3288 ID: &[]byte{0, 1},
3289 Account: &Account{
3290 Access: func() accessBitmap {
3291 var bits accessBitmap
3292 bits.Set(accessOpenChat)
3293 return bits
3294 }(),
3295 },
3296 UserName: []byte("UserA"),
3297 Icon: []byte{0, 1},
3298 Flags: []byte{0, 0},
3299 Server: &Server{
3300 Clients: map[uint16]*ClientConn{
3301 uint16(2): {
3302 ID: &[]byte{0, 2},
3303 UserName: []byte("UserB"),
3304 Flags: []byte{255, 255},
3305 },
3306 },
3307 PrivateChats: make(map[uint32]*PrivateChat),
3308 },
3309 },
3310 t: NewTransaction(
3311 TranInviteNewChat, &[]byte{0, 1},
3312 NewField(FieldUserID, []byte{0, 2}),
3313 ),
3314 },
3315 wantRes: []Transaction{
3316 {
3317 clientID: &[]byte{0, 1},
3318 Type: [2]byte{0, 0x68},
3319 Fields: []Field{
3320 NewField(FieldData, []byte("UserB does not accept private chats.")),
3321 NewField(FieldUserName, []byte("UserB")),
3322 NewField(FieldUserID, []byte{0, 2}),
3323 NewField(FieldOptions, []byte{0, 2}),
3324 },
3325 },
3326 {
3327 clientID: &[]byte{0, 1},
3328 IsReply: 0x01,
3329 Fields: []Field{
3330 NewField(FieldChatID, []byte{0x52, 0xfd, 0xfc, 0x07}),
3331 NewField(FieldUserName, []byte("UserA")),
3332 NewField(FieldUserID, []byte{0, 1}),
3333 NewField(FieldUserIconID, []byte{0, 1}),
3334 NewField(FieldUserFlags, []byte{0, 0}),
3335 },
3336 },
3337 },
3338 wantErr: assert.NoError,
3339 },
3340 }
3341 for _, tt := range tests {
3342 t.Run(tt.name, func(t *testing.T) {
3343 gotRes, err := HandleInviteNewChat(tt.args.cc, tt.args.t)
3344 if !tt.wantErr(t, err, fmt.Sprintf("HandleInviteNewChat(%v, %v)", tt.args.cc, tt.args.t)) {
3345 return
3346 }
3347 tranAssertEqual(t, tt.wantRes, gotRes)
3348 })
3349 }
3350}
3351
3352func TestHandleGetNewsArtData(t *testing.T) {
3353 type args struct {
3354 cc *ClientConn
3355 t *Transaction
3356 }
3357 tests := []struct {
3358 name string
3359 args args
3360 wantRes []Transaction
3361 wantErr assert.ErrorAssertionFunc
3362 }{
3363 {
3364 name: "when user does not have required permission",
3365 args: args{
3366 cc: &ClientConn{
3367 Account: &Account{
3368 Access: func() accessBitmap {
3369 var bits accessBitmap
3370 return bits
3371 }(),
3372 },
3373 Server: &Server{
3374 Accounts: map[string]*Account{},
3375 },
3376 },
3377 t: NewTransaction(
3378 TranGetNewsArtData, &[]byte{0, 1},
3379 ),
3380 },
3381 wantRes: []Transaction{
3382 {
3383 IsReply: 0x01,
3384 ErrorCode: [4]byte{0, 0, 0, 1},
3385 Fields: []Field{
3386 NewField(FieldError, []byte("You are not allowed to read news.")),
3387 },
3388 },
3389 },
3390 wantErr: assert.NoError,
3391 },
3392 }
3393 for _, tt := range tests {
3394 t.Run(tt.name, func(t *testing.T) {
3395 gotRes, err := HandleGetNewsArtData(tt.args.cc, tt.args.t)
3396 if !tt.wantErr(t, err, fmt.Sprintf("HandleGetNewsArtData(%v, %v)", tt.args.cc, tt.args.t)) {
3397 return
3398 }
3399 tranAssertEqual(t, tt.wantRes, gotRes)
3400 })
3401 }
3402}
3403
3404func TestHandleGetNewsArtNameList(t *testing.T) {
3405 type args struct {
3406 cc *ClientConn
3407 t *Transaction
3408 }
3409 tests := []struct {
3410 name string
3411 args args
3412 wantRes []Transaction
3413 wantErr assert.ErrorAssertionFunc
3414 }{
3415 {
3416 name: "when user does not have required permission",
3417 args: args{
3418 cc: &ClientConn{
3419 Account: &Account{
3420 Access: func() accessBitmap {
3421 var bits accessBitmap
3422 return bits
3423 }(),
3424 },
3425 Server: &Server{
3426 Accounts: map[string]*Account{},
3427 },
3428 },
3429 t: NewTransaction(
3430 TranGetNewsArtNameList, &[]byte{0, 1},
3431 ),
3432 },
3433 wantRes: []Transaction{
3434 {
3435 Flags: 0x00,
3436 IsReply: 0x01,
3437 Type: [2]byte{0, 0},
3438 ErrorCode: [4]byte{0, 0, 0, 1},
3439 Fields: []Field{
3440 NewField(FieldError, []byte("You are not allowed to read news.")),
3441 },
3442 },
3443 },
3444 wantErr: assert.NoError,
3445 },
3446 {
3447 name: "when user has required access",
3448 args: args{
3449 cc: &ClientConn{
3450 Account: &Account{
3451 Access: func() accessBitmap {
3452 var bits accessBitmap
3453 bits.Set(accessNewsReadArt)
3454 return bits
3455 }(),
3456 },
3457 Server: &Server{
3458 ThreadedNews: &ThreadedNews{
3459 Categories: map[string]NewsCategoryListData15{
3460 "Example Category": {
3461 Type: [2]byte{0, 2},
3462 Name: "",
3463 Articles: map[uint32]*NewsArtData{
3464 uint32(1): {
3465 Title: "testTitle",
3466 Poster: "testPoster",
3467 Data: "testBody",
3468 },
3469 },
3470 SubCats: nil,
3471 GUID: [16]byte{},
3472 AddSN: [4]byte{},
3473 DeleteSN: [4]byte{},
3474 },
3475 },
3476 },
3477
3478 //Accounts: map[string]*Account{
3479 // "guest": {
3480 // Name: "guest",
3481 // Login: "guest",
3482 // Password: "zz",
3483 // Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
3484 // },
3485 //},
3486 },
3487 },
3488 t: NewTransaction(
3489 TranGetNewsArtNameList,
3490 &[]byte{0, 1},
3491 // 00000000 00 01 00 00 10 45 78 61 6d 70 6c 65 20 43 61 74 |.....Example Cat|
3492 // 00000010 65 67 6f 72 79 |egory|
3493 NewField(FieldNewsPath, []byte{
3494 0x00, 0x01, 0x00, 0x00, 0x10, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79,
3495 }),
3496 ),
3497 },
3498 wantRes: []Transaction{
3499 {
3500 IsReply: 0x01,
3501 Fields: []Field{
3502 NewField(FieldNewsArtListData, []byte{
3503 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
3504 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
3505 0x09, 0x74, 0x65, 0x73, 0x74, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x0a, 0x74, 0x65, 0x73, 0x74, 0x50,
3506 0x6f, 0x73, 0x74, 0x65, 0x72, 0x0a, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, 0x61, 0x69, 0x6e,
3507 0x00, 0x08,
3508 },
3509 ),
3510 },
3511 },
3512 },
3513 wantErr: assert.NoError,
3514 },
3515 }
3516 for _, tt := range tests {
3517 t.Run(tt.name, func(t *testing.T) {
3518 gotRes, err := HandleGetNewsArtNameList(tt.args.cc, tt.args.t)
3519 if !tt.wantErr(t, err, fmt.Sprintf("HandleGetNewsArtNameList(%v, %v)", tt.args.cc, tt.args.t)) {
3520 return
3521 }
3522 tranAssertEqual(t, tt.wantRes, gotRes)
3523 })
3524 }
3525}
3526
3527func TestHandleNewNewsFldr(t *testing.T) {
3528 type args struct {
3529 cc *ClientConn
3530 t *Transaction
3531 }
3532 tests := []struct {
3533 name string
3534 args args
3535 wantRes []Transaction
3536 wantErr assert.ErrorAssertionFunc
3537 }{
3538 {
3539 name: "when user does not have required permission",
3540 args: args{
3541 cc: &ClientConn{
3542 Account: &Account{
3543 Access: func() accessBitmap {
3544 var bits accessBitmap
3545 return bits
3546 }(),
3547 },
3548 Server: &Server{
3549 Accounts: map[string]*Account{},
3550 },
3551 },
3552 t: NewTransaction(
3553 TranGetNewsArtNameList, &[]byte{0, 1},
3554 ),
3555 },
3556 wantRes: []Transaction{
3557 {
3558 Flags: 0x00,
3559 IsReply: 0x01,
3560 Type: [2]byte{0, 0},
3561 ErrorCode: [4]byte{0, 0, 0, 1},
3562 Fields: []Field{
3563 NewField(FieldError, []byte("You are not allowed to create news folders.")),
3564 },
3565 },
3566 },
3567 wantErr: assert.NoError,
3568 },
3569 {
3570 name: "with a valid request",
3571 args: args{
3572 cc: &ClientConn{
3573 Account: &Account{
3574 Access: func() accessBitmap {
3575 var bits accessBitmap
3576 bits.Set(accessNewsCreateFldr)
3577 return bits
3578 }(),
3579 },
3580 logger: NewTestLogger(),
3581 ID: &[]byte{0, 1},
3582 Server: &Server{
3583 ConfigDir: "/fakeConfigRoot",
3584 FS: func() *MockFileStore {
3585 mfs := &MockFileStore{}
3586 mfs.On("WriteFile", "/fakeConfigRoot/ThreadedNews.yaml", mock.Anything, mock.Anything).Return(nil)
3587 return mfs
3588 }(),
3589 ThreadedNews: &ThreadedNews{Categories: map[string]NewsCategoryListData15{
3590 "test": {
3591 Type: [2]byte{0, 2},
3592 Name: "test",
3593 SubCats: make(map[string]NewsCategoryListData15),
3594 },
3595 }},
3596 },
3597 },
3598 t: NewTransaction(
3599 TranGetNewsArtNameList, &[]byte{0, 1},
3600 NewField(FieldFileName, []byte("testFolder")),
3601 NewField(FieldNewsPath,
3602 []byte{
3603 0, 1,
3604 0, 0,
3605 4,
3606 0x74, 0x65, 0x73, 0x74,
3607 },
3608 ),
3609 ),
3610 },
3611 wantRes: []Transaction{
3612 {
3613 clientID: &[]byte{0, 1},
3614 IsReply: 0x01,
3615 Fields: []Field{},
3616 },
3617 },
3618 wantErr: assert.NoError,
3619 },
3620 //{
3621 // Name: "when there is an error writing the threaded news file",
3622 // args: args{
3623 // cc: &ClientConn{
3624 // Account: &Account{
3625 // Access: func() accessBitmap {
3626 // var bits accessBitmap
3627 // bits.Set(accessNewsCreateFldr)
3628 // return bits
3629 // }(),
3630 // },
3631 // logger: NewTestLogger(),
3632 // ID: &[]byte{0, 1},
3633 // Server: &Server{
3634 // ConfigDir: "/fakeConfigRoot",
3635 // FS: func() *MockFileStore {
3636 // mfs := &MockFileStore{}
3637 // mfs.On("WriteFile", "/fakeConfigRoot/ThreadedNews.yaml", mock.Anything, mock.Anything).Return(os.ErrNotExist)
3638 // return mfs
3639 // }(),
3640 // ThreadedNews: &ThreadedNews{Categories: map[string]NewsCategoryListData15{
3641 // "test": {
3642 // Type: []byte{0, 2},
3643 // Count: nil,
3644 // NameSize: 0,
3645 // Name: "test",
3646 // SubCats: make(map[string]NewsCategoryListData15),
3647 // },
3648 // }},
3649 // },
3650 // },
3651 // t: NewTransaction(
3652 // TranGetNewsArtNameList, &[]byte{0, 1},
3653 // NewField(FieldFileName, []byte("testFolder")),
3654 // NewField(FieldNewsPath,
3655 // []byte{
3656 // 0, 1,
3657 // 0, 0,
3658 // 4,
3659 // 0x74, 0x65, 0x73, 0x74,
3660 // },
3661 // ),
3662 // ),
3663 // },
3664 // wantRes: []Transaction{
3665 // {
3666 // clientID: &[]byte{0, 1},
3667 // Flags: 0x00,
3668 // IsReply: 0x01,
3669 // Type: [2]byte{0, 0},
3670 // ErrorCode: [4]byte{0, 0, 0, 1},
3671 // Fields: []Field{
3672 // NewField(FieldError, []byte("Error creating news folder.")),
3673 // },
3674 // },
3675 // },
3676 // wantErr: assert.Error,
3677 // },
3678 }
3679 for _, tt := range tests {
3680 t.Run(tt.name, func(t *testing.T) {
3681 gotRes, err := HandleNewNewsFldr(tt.args.cc, tt.args.t)
3682 if !tt.wantErr(t, err, fmt.Sprintf("HandleNewNewsFldr(%v, %v)", tt.args.cc, tt.args.t)) {
3683 return
3684 }
3685 tranAssertEqual(t, tt.wantRes, gotRes)
3686 })
3687 }
3688}
3689
3690func TestHandleDownloadBanner(t *testing.T) {
3691 type args struct {
3692 cc *ClientConn
3693 t *Transaction
3694 }
3695 tests := []struct {
3696 name string
3697 args args
3698 wantRes []Transaction
3699 wantErr assert.ErrorAssertionFunc
3700 }{
3701 // TODO: Add test cases.
3702 }
3703 for _, tt := range tests {
3704 t.Run(tt.name, func(t *testing.T) {
3705 gotRes, err := HandleDownloadBanner(tt.args.cc, tt.args.t)
3706 if !tt.wantErr(t, err, fmt.Sprintf("HandleDownloadBanner(%v, %v)", tt.args.cc, tt.args.t)) {
3707 return
3708 }
3709 assert.Equalf(t, tt.wantRes, gotRes, "HandleDownloadBanner(%v, %v)", tt.args.cc, tt.args.t)
3710 })
3711 }
3712}