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