]> git.r.bdr.sh - rbdr/mobius/blob - hotline/transaction_handlers_test.go
e141c78dd29d0f52b19c0c4587a66a58cdec07bb
[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 },
2306 },
2307 },
2308 },
2309 t: NewTransaction(
2310 tranSendInstantMsg,
2311 &[]byte{0, 1},
2312 NewField(fieldData, []byte("hai")),
2313 NewField(fieldUserID, []byte{0, 2}),
2314 ),
2315 },
2316 wantRes: []Transaction{
2317 *NewTransaction(
2318 tranServerMsg,
2319 &[]byte{0, 2},
2320 NewField(fieldData, []byte("hai")),
2321 NewField(fieldUserName, []byte("User1")),
2322 NewField(fieldUserID, []byte{0, 1}),
2323 NewField(fieldOptions, []byte{0, 1}),
2324 ),
2325 {
2326 clientID: &[]byte{0, 1},
2327 Flags: 0x00,
2328 IsReply: 0x01,
2329 Type: []byte{0x0, 0x6c},
2330 ID: []byte{0, 0, 0, 0},
2331 ErrorCode: []byte{0, 0, 0, 0},
2332 Fields: []Field(nil),
2333 },
2334 },
2335 wantErr: assert.NoError,
2336 },
2337 {
2338 name: "when client 2 has autoreply enabled",
2339 args: args{
2340 cc: &ClientConn{
2341 Account: &Account{
2342 Access: func() accessBitmap {
2343 var bits accessBitmap
2344 bits.Set(accessSendPrivMsg)
2345 return bits
2346 }(),
2347 },
2348 ID: &[]byte{0, 1},
2349 UserName: []byte("User1"),
2350 Server: &Server{
2351 Clients: map[uint16]*ClientConn{
2352 uint16(2): {
2353 ID: &[]byte{0, 2},
2354 UserName: []byte("User2"),
2355 AutoReply: []byte("autohai"),
2356 },
2357 },
2358 },
2359 },
2360 t: NewTransaction(
2361 tranSendInstantMsg,
2362 &[]byte{0, 1},
2363 NewField(fieldData, []byte("hai")),
2364 NewField(fieldUserID, []byte{0, 2}),
2365 ),
2366 },
2367 wantRes: []Transaction{
2368 *NewTransaction(
2369 tranServerMsg,
2370 &[]byte{0, 2},
2371 NewField(fieldData, []byte("hai")),
2372 NewField(fieldUserName, []byte("User1")),
2373 NewField(fieldUserID, []byte{0, 1}),
2374 NewField(fieldOptions, []byte{0, 1}),
2375 ),
2376 *NewTransaction(
2377 tranServerMsg,
2378 &[]byte{0, 1},
2379 NewField(fieldData, []byte("autohai")),
2380 NewField(fieldUserName, []byte("User2")),
2381 NewField(fieldUserID, []byte{0, 2}),
2382 NewField(fieldOptions, []byte{0, 1}),
2383 ),
2384 {
2385 clientID: &[]byte{0, 1},
2386 Flags: 0x00,
2387 IsReply: 0x01,
2388 Type: []byte{0x0, 0x6c},
2389 ID: []byte{0, 0, 0, 0},
2390 ErrorCode: []byte{0, 0, 0, 0},
2391 Fields: []Field(nil),
2392 },
2393 },
2394 wantErr: assert.NoError,
2395 },
2396 }
2397 for _, tt := range tests {
2398 t.Run(tt.name, func(t *testing.T) {
2399 gotRes, err := HandleSendInstantMsg(tt.args.cc, tt.args.t)
2400 if !tt.wantErr(t, err, fmt.Sprintf("HandleSendInstantMsg(%v, %v)", tt.args.cc, tt.args.t)) {
2401 return
2402 }
2403
2404 tranAssertEqual(t, tt.wantRes, gotRes)
2405 })
2406 }
2407 }
2408
2409 func TestHandleDeleteFile(t *testing.T) {
2410 type args struct {
2411 cc *ClientConn
2412 t *Transaction
2413 }
2414 tests := []struct {
2415 name string
2416 args args
2417 wantRes []Transaction
2418 wantErr assert.ErrorAssertionFunc
2419 }{
2420 {
2421 name: "when user does not have required permission to delete a folder",
2422 args: args{
2423 cc: &ClientConn{
2424 Account: &Account{
2425 Access: func() accessBitmap {
2426 var bits accessBitmap
2427 return bits
2428 }(),
2429 },
2430 Server: &Server{
2431 Config: &Config{
2432 FileRoot: func() string {
2433 return "/fakeRoot/Files"
2434 }(),
2435 },
2436 FS: func() *MockFileStore {
2437 mfi := &MockFileInfo{}
2438 mfi.On("Mode").Return(fs.FileMode(0))
2439 mfi.On("Size").Return(int64(100))
2440 mfi.On("ModTime").Return(time.Parse(time.Layout, time.Layout))
2441 mfi.On("IsDir").Return(false)
2442 mfi.On("Name").Return("testfile")
2443
2444 mfs := &MockFileStore{}
2445 mfs.On("Stat", "/fakeRoot/Files/aaa/testfile").Return(mfi, nil)
2446 mfs.On("Stat", "/fakeRoot/Files/aaa/.info_testfile").Return(nil, errors.New("err"))
2447 mfs.On("Stat", "/fakeRoot/Files/aaa/.rsrc_testfile").Return(nil, errors.New("err"))
2448
2449 return mfs
2450 }(),
2451 Accounts: map[string]*Account{},
2452 },
2453 },
2454 t: NewTransaction(
2455 tranDeleteFile, &[]byte{0, 1},
2456 NewField(fieldFileName, []byte("testfile")),
2457 NewField(fieldFilePath, []byte{
2458 0x00, 0x01,
2459 0x00, 0x00,
2460 0x03,
2461 0x61, 0x61, 0x61,
2462 }),
2463 ),
2464 },
2465 wantRes: []Transaction{
2466 {
2467 Flags: 0x00,
2468 IsReply: 0x01,
2469 Type: []byte{0, 0x00},
2470 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
2471 ErrorCode: []byte{0, 0, 0, 1},
2472 Fields: []Field{
2473 NewField(fieldError, []byte("You are not allowed to delete files.")),
2474 },
2475 },
2476 },
2477 wantErr: assert.NoError,
2478 },
2479 {
2480 name: "deletes all associated metadata files",
2481 args: args{
2482 cc: &ClientConn{
2483 Account: &Account{
2484 Access: func() accessBitmap {
2485 var bits accessBitmap
2486 bits.Set(accessDeleteFile)
2487 return bits
2488 }(),
2489 },
2490 Server: &Server{
2491 Config: &Config{
2492 FileRoot: func() string {
2493 return "/fakeRoot/Files"
2494 }(),
2495 },
2496 FS: func() *MockFileStore {
2497 mfi := &MockFileInfo{}
2498 mfi.On("Mode").Return(fs.FileMode(0))
2499 mfi.On("Size").Return(int64(100))
2500 mfi.On("ModTime").Return(time.Parse(time.Layout, time.Layout))
2501 mfi.On("IsDir").Return(false)
2502 mfi.On("Name").Return("testfile")
2503
2504 mfs := &MockFileStore{}
2505 mfs.On("Stat", "/fakeRoot/Files/aaa/testfile").Return(mfi, nil)
2506 mfs.On("Stat", "/fakeRoot/Files/aaa/.info_testfile").Return(nil, errors.New("err"))
2507 mfs.On("Stat", "/fakeRoot/Files/aaa/.rsrc_testfile").Return(nil, errors.New("err"))
2508
2509 mfs.On("RemoveAll", "/fakeRoot/Files/aaa/testfile").Return(nil)
2510 mfs.On("Remove", "/fakeRoot/Files/aaa/testfile.incomplete").Return(nil)
2511 mfs.On("Remove", "/fakeRoot/Files/aaa/.rsrc_testfile").Return(nil)
2512 mfs.On("Remove", "/fakeRoot/Files/aaa/.info_testfile").Return(nil)
2513
2514 return mfs
2515 }(),
2516 Accounts: map[string]*Account{},
2517 },
2518 },
2519 t: NewTransaction(
2520 tranDeleteFile, &[]byte{0, 1},
2521 NewField(fieldFileName, []byte("testfile")),
2522 NewField(fieldFilePath, []byte{
2523 0x00, 0x01,
2524 0x00, 0x00,
2525 0x03,
2526 0x61, 0x61, 0x61,
2527 }),
2528 ),
2529 },
2530 wantRes: []Transaction{
2531 {
2532 Flags: 0x00,
2533 IsReply: 0x01,
2534 Type: []byte{0x0, 0xcc},
2535 ID: []byte{0x0, 0x0, 0x0, 0x0},
2536 ErrorCode: []byte{0, 0, 0, 0},
2537 Fields: []Field(nil),
2538 },
2539 },
2540 wantErr: assert.NoError,
2541 },
2542 }
2543 for _, tt := range tests {
2544 t.Run(tt.name, func(t *testing.T) {
2545 gotRes, err := HandleDeleteFile(tt.args.cc, tt.args.t)
2546 if !tt.wantErr(t, err, fmt.Sprintf("HandleDeleteFile(%v, %v)", tt.args.cc, tt.args.t)) {
2547 return
2548 }
2549
2550 tranAssertEqual(t, tt.wantRes, gotRes)
2551
2552 tt.args.cc.Server.FS.(*MockFileStore).AssertExpectations(t)
2553 })
2554 }
2555 }
2556
2557 func TestHandleGetFileNameList(t *testing.T) {
2558 type args struct {
2559 cc *ClientConn
2560 t *Transaction
2561 }
2562 tests := []struct {
2563 name string
2564 args args
2565 wantRes []Transaction
2566 wantErr assert.ErrorAssertionFunc
2567 }{
2568 {
2569 name: "when fieldFilePath is a drop box, but user does not have accessViewDropBoxes ",
2570 args: args{
2571 cc: &ClientConn{
2572 Account: &Account{
2573 Access: func() accessBitmap {
2574 var bits accessBitmap
2575 return bits
2576 }(),
2577 },
2578 Server: &Server{
2579
2580 Config: &Config{
2581 FileRoot: func() string {
2582 path, _ := os.Getwd()
2583 return filepath.Join(path, "/test/config/Files/getFileNameListTestDir")
2584 }(),
2585 },
2586 },
2587 },
2588 t: NewTransaction(
2589 tranGetFileNameList, &[]byte{0, 1},
2590 NewField(fieldFilePath, []byte{
2591 0x00, 0x01,
2592 0x00, 0x00,
2593 0x08,
2594 0x64, 0x72, 0x6f, 0x70, 0x20, 0x62, 0x6f, 0x78, // "drop box"
2595 }),
2596 ),
2597 },
2598 wantRes: []Transaction{
2599 {
2600 Flags: 0x00,
2601 IsReply: 0x01,
2602 Type: []byte{0, 0x00},
2603 ID: []byte{0, 0, 0, 0},
2604 ErrorCode: []byte{0, 0, 0, 1},
2605 Fields: []Field{
2606 NewField(fieldError, []byte("You are not allowed to view drop boxes.")),
2607 },
2608 },
2609 },
2610 wantErr: assert.NoError,
2611 },
2612 {
2613 name: "with file root",
2614 args: args{
2615 cc: &ClientConn{
2616 Server: &Server{
2617 Config: &Config{
2618 FileRoot: func() string {
2619 path, _ := os.Getwd()
2620 return filepath.Join(path, "/test/config/Files/getFileNameListTestDir")
2621 }(),
2622 },
2623 },
2624 },
2625 t: NewTransaction(
2626 tranGetFileNameList, &[]byte{0, 1},
2627 NewField(fieldFilePath, []byte{
2628 0x00, 0x00,
2629 0x00, 0x00,
2630 }),
2631 ),
2632 },
2633 wantRes: []Transaction{
2634 {
2635 Flags: 0x00,
2636 IsReply: 0x01,
2637 Type: []byte{0, 0xc8},
2638 ID: []byte{0, 0, 0, 0},
2639 ErrorCode: []byte{0, 0, 0, 0},
2640 Fields: []Field{
2641 NewField(
2642 fieldFileNameWithInfo,
2643 func() []byte {
2644 fnwi := FileNameWithInfo{
2645 fileNameWithInfoHeader: fileNameWithInfoHeader{
2646 Type: [4]byte{0x54, 0x45, 0x58, 0x54},
2647 Creator: [4]byte{0x54, 0x54, 0x58, 0x54},
2648 FileSize: [4]byte{0, 0, 0x04, 0},
2649 RSVD: [4]byte{},
2650 NameScript: [2]byte{},
2651 NameSize: [2]byte{0, 0x0b},
2652 },
2653 name: []byte("testfile-1k"),
2654 }
2655 b, _ := fnwi.MarshalBinary()
2656 return b
2657 }(),
2658 ),
2659 },
2660 },
2661 },
2662 wantErr: assert.NoError,
2663 },
2664 }
2665 for _, tt := range tests {
2666 t.Run(tt.name, func(t *testing.T) {
2667 gotRes, err := HandleGetFileNameList(tt.args.cc, tt.args.t)
2668 if !tt.wantErr(t, err, fmt.Sprintf("HandleGetFileNameList(%v, %v)", tt.args.cc, tt.args.t)) {
2669 return
2670 }
2671
2672 tranAssertEqual(t, tt.wantRes, gotRes)
2673 })
2674 }
2675 }
2676
2677 func TestHandleGetClientInfoText(t *testing.T) {
2678 type args struct {
2679 cc *ClientConn
2680 t *Transaction
2681 }
2682 tests := []struct {
2683 name string
2684 args args
2685 wantRes []Transaction
2686 wantErr assert.ErrorAssertionFunc
2687 }{
2688 {
2689 name: "when user does not have required permission",
2690 args: args{
2691 cc: &ClientConn{
2692 Account: &Account{
2693 Access: func() accessBitmap {
2694 var bits accessBitmap
2695 return bits
2696 }(),
2697 },
2698 Server: &Server{
2699 Accounts: map[string]*Account{},
2700 },
2701 },
2702 t: NewTransaction(
2703 tranGetClientInfoText, &[]byte{0, 1},
2704 NewField(fieldUserID, []byte{0, 1}),
2705 ),
2706 },
2707 wantRes: []Transaction{
2708 {
2709 Flags: 0x00,
2710 IsReply: 0x01,
2711 Type: []byte{0, 0x00},
2712 ID: []byte{0, 0, 0, 0},
2713 ErrorCode: []byte{0, 0, 0, 1},
2714 Fields: []Field{
2715 NewField(fieldError, []byte("You are not allowed to get client info.")),
2716 },
2717 },
2718 },
2719 wantErr: assert.NoError,
2720 },
2721 {
2722 name: "with a valid user",
2723 args: args{
2724 cc: &ClientConn{
2725 UserName: []byte("Testy McTest"),
2726 RemoteAddr: "1.2.3.4:12345",
2727 Account: &Account{
2728 Access: func() accessBitmap {
2729 var bits accessBitmap
2730 bits.Set(accessGetClientInfo)
2731 return bits
2732 }(),
2733 Name: "test",
2734 Login: "test",
2735 },
2736 Server: &Server{
2737 Accounts: map[string]*Account{},
2738 Clients: map[uint16]*ClientConn{
2739 uint16(1): {
2740 UserName: []byte("Testy McTest"),
2741 RemoteAddr: "1.2.3.4:12345",
2742 Account: &Account{
2743 Access: func() accessBitmap {
2744 var bits accessBitmap
2745 bits.Set(accessGetClientInfo)
2746 return bits
2747 }(),
2748 Name: "test",
2749 Login: "test",
2750 },
2751 },
2752 },
2753 },
2754 transfers: map[int]map[[4]byte]*FileTransfer{
2755 FileDownload: {},
2756 FileUpload: {},
2757 FolderDownload: {},
2758 FolderUpload: {},
2759 },
2760 },
2761 t: NewTransaction(
2762 tranGetClientInfoText, &[]byte{0, 1},
2763 NewField(fieldUserID, []byte{0, 1}),
2764 ),
2765 },
2766 wantRes: []Transaction{
2767 {
2768 Flags: 0x00,
2769 IsReply: 0x01,
2770 Type: []byte{0x1, 0x2f},
2771 ID: []byte{0, 0, 0, 0},
2772 ErrorCode: []byte{0, 0, 0, 0},
2773 Fields: []Field{
2774 NewField(fieldData, []byte(
2775 strings.Replace(`Nickname: Testy McTest
2776 Name: test
2777 Account: test
2778 Address: 1.2.3.4:12345
2779
2780 -------- File Downloads ---------
2781
2782 None.
2783
2784 ------- Folder Downloads --------
2785
2786 None.
2787
2788 --------- File Uploads ----------
2789
2790 None.
2791
2792 -------- Folder Uploads ---------
2793
2794 None.
2795
2796 ------- Waiting Downloads -------
2797
2798 None.
2799
2800 `, "\n", "\r", -1)),
2801 ),
2802 NewField(fieldUserName, []byte("Testy McTest")),
2803 },
2804 },
2805 },
2806 wantErr: assert.NoError,
2807 },
2808 }
2809 for _, tt := range tests {
2810 t.Run(tt.name, func(t *testing.T) {
2811 gotRes, err := HandleGetClientInfoText(tt.args.cc, tt.args.t)
2812 if !tt.wantErr(t, err, fmt.Sprintf("HandleGetClientInfoText(%v, %v)", tt.args.cc, tt.args.t)) {
2813 return
2814 }
2815 tranAssertEqual(t, tt.wantRes, gotRes)
2816 })
2817 }
2818 }
2819
2820 func TestHandleTranAgreed(t *testing.T) {
2821 type args struct {
2822 cc *ClientConn
2823 t *Transaction
2824 }
2825 tests := []struct {
2826 name string
2827 args args
2828 wantRes []Transaction
2829 wantErr assert.ErrorAssertionFunc
2830 }{
2831 {
2832 name: "normal request flow",
2833 args: args{
2834 cc: &ClientConn{
2835 Account: &Account{
2836 Access: func() accessBitmap {
2837 var bits accessBitmap
2838 bits.Set(accessDisconUser)
2839 bits.Set(accessAnyName)
2840 return bits
2841 }()},
2842 Icon: []byte{0, 1},
2843 Flags: []byte{0, 1},
2844 Version: []byte{0, 1},
2845 ID: &[]byte{0, 1},
2846 logger: NewTestLogger(),
2847 Server: &Server{
2848 Config: &Config{
2849 BannerFile: "banner.jpg",
2850 },
2851 },
2852 },
2853 t: NewTransaction(
2854 tranAgreed, nil,
2855 NewField(fieldUserName, []byte("username")),
2856 NewField(fieldUserIconID, []byte{0, 1}),
2857 NewField(fieldOptions, []byte{0, 0}),
2858 ),
2859 },
2860 wantRes: []Transaction{
2861 {
2862 clientID: &[]byte{0, 1},
2863 Flags: 0x00,
2864 IsReply: 0x00,
2865 Type: []byte{0, 0x7a},
2866 ID: []byte{0, 0, 0, 0},
2867 ErrorCode: []byte{0, 0, 0, 0},
2868 Fields: []Field{
2869 NewField(fieldBannerType, []byte("JPEG")),
2870 },
2871 },
2872 {
2873 clientID: &[]byte{0, 1},
2874 Flags: 0x00,
2875 IsReply: 0x01,
2876 Type: []byte{0, 0x79},
2877 ID: []byte{0, 0, 0, 0},
2878 ErrorCode: []byte{0, 0, 0, 0},
2879 Fields: []Field{},
2880 },
2881 },
2882 wantErr: assert.NoError,
2883 },
2884 }
2885 for _, tt := range tests {
2886 t.Run(tt.name, func(t *testing.T) {
2887 gotRes, err := HandleTranAgreed(tt.args.cc, tt.args.t)
2888 if !tt.wantErr(t, err, fmt.Sprintf("HandleTranAgreed(%v, %v)", tt.args.cc, tt.args.t)) {
2889 return
2890 }
2891 tranAssertEqual(t, tt.wantRes, gotRes)
2892 })
2893 }
2894 }
2895
2896 func TestHandleSetClientUserInfo(t *testing.T) {
2897 type args struct {
2898 cc *ClientConn
2899 t *Transaction
2900 }
2901 tests := []struct {
2902 name string
2903 args args
2904 wantRes []Transaction
2905 wantErr assert.ErrorAssertionFunc
2906 }{
2907 {
2908 name: "when client does not have accessAnyName",
2909 args: args{
2910 cc: &ClientConn{
2911 Account: &Account{
2912 Access: func() accessBitmap {
2913 var bits accessBitmap
2914 return bits
2915 }(),
2916 },
2917 ID: &[]byte{0, 1},
2918 UserName: []byte("Guest"),
2919 Flags: []byte{0, 1},
2920 Server: &Server{
2921 Clients: map[uint16]*ClientConn{
2922 uint16(1): {
2923 ID: &[]byte{0, 1},
2924 },
2925 },
2926 },
2927 },
2928 t: NewTransaction(
2929 tranSetClientUserInfo, nil,
2930 NewField(fieldUserIconID, []byte{0, 1}),
2931 NewField(fieldUserName, []byte("NOPE")),
2932 ),
2933 },
2934 wantRes: []Transaction{
2935 {
2936 clientID: &[]byte{0, 1},
2937 Flags: 0x00,
2938 IsReply: 0x00,
2939 Type: []byte{0x01, 0x2d},
2940 ID: []byte{0, 0, 0, 0},
2941 ErrorCode: []byte{0, 0, 0, 0},
2942 Fields: []Field{
2943 NewField(fieldUserID, []byte{0, 1}),
2944 NewField(fieldUserIconID, []byte{0, 1}),
2945 NewField(fieldUserFlags, []byte{0, 1}),
2946 NewField(fieldUserName, []byte("Guest"))},
2947 },
2948 },
2949 wantErr: assert.NoError,
2950 },
2951 }
2952 for _, tt := range tests {
2953 t.Run(tt.name, func(t *testing.T) {
2954 gotRes, err := HandleSetClientUserInfo(tt.args.cc, tt.args.t)
2955 if !tt.wantErr(t, err, fmt.Sprintf("HandleSetClientUserInfo(%v, %v)", tt.args.cc, tt.args.t)) {
2956 return
2957 }
2958
2959 tranAssertEqual(t, tt.wantRes, gotRes)
2960 })
2961 }
2962 }
2963
2964 func TestHandleDelNewsItem(t *testing.T) {
2965 type args struct {
2966 cc *ClientConn
2967 t *Transaction
2968 }
2969 tests := []struct {
2970 name string
2971 args args
2972 wantRes []Transaction
2973 wantErr assert.ErrorAssertionFunc
2974 }{
2975 {
2976 name: "when user does not have permission to delete a news category",
2977 args: args{
2978 cc: &ClientConn{
2979 Account: &Account{
2980 Access: accessBitmap{},
2981 },
2982 ID: &[]byte{0, 1},
2983 Server: &Server{
2984 ThreadedNews: &ThreadedNews{Categories: map[string]NewsCategoryListData15{
2985 "test": {
2986 Type: []byte{0, 3},
2987 Count: nil,
2988 NameSize: 0,
2989 Name: "zz",
2990 },
2991 }},
2992 },
2993 },
2994 t: NewTransaction(
2995 tranDelNewsItem, nil,
2996 NewField(fieldNewsPath,
2997 []byte{
2998 0, 1,
2999 0, 0,
3000 4,
3001 0x74, 0x65, 0x73, 0x74,
3002 },
3003 ),
3004 ),
3005 },
3006 wantRes: []Transaction{
3007 {
3008 clientID: &[]byte{0, 1},
3009 Flags: 0x00,
3010 IsReply: 0x01,
3011 Type: []byte{0, 0x00},
3012 ID: []byte{0, 0, 0, 0},
3013 ErrorCode: []byte{0, 0, 0, 1},
3014 Fields: []Field{
3015 NewField(fieldError, []byte("You are not allowed to delete news categories.")),
3016 },
3017 },
3018 },
3019 wantErr: assert.NoError,
3020 },
3021 {
3022 name: "when user does not have permission to delete a news folder",
3023 args: args{
3024 cc: &ClientConn{
3025 Account: &Account{
3026 Access: accessBitmap{},
3027 },
3028 ID: &[]byte{0, 1},
3029 Server: &Server{
3030 ThreadedNews: &ThreadedNews{Categories: map[string]NewsCategoryListData15{
3031 "testcat": {
3032 Type: []byte{0, 2},
3033 Count: nil,
3034 NameSize: 0,
3035 Name: "test",
3036 },
3037 }},
3038 },
3039 },
3040 t: NewTransaction(
3041 tranDelNewsItem, nil,
3042 NewField(fieldNewsPath,
3043 []byte{
3044 0, 1,
3045 0, 0,
3046 4,
3047 0x74, 0x65, 0x73, 0x74,
3048 },
3049 ),
3050 ),
3051 },
3052 wantRes: []Transaction{
3053 {
3054 clientID: &[]byte{0, 1},
3055 Flags: 0x00,
3056 IsReply: 0x01,
3057 Type: []byte{0, 0x00},
3058 ID: []byte{0, 0, 0, 0},
3059 ErrorCode: []byte{0, 0, 0, 1},
3060 Fields: []Field{
3061 NewField(fieldError, []byte("You are not allowed to delete news folders.")),
3062 },
3063 },
3064 },
3065 wantErr: assert.NoError,
3066 },
3067 {
3068 name: "when user deletes a news folder",
3069 args: args{
3070 cc: &ClientConn{
3071 Account: &Account{
3072 Access: func() accessBitmap {
3073 var bits accessBitmap
3074 bits.Set(accessNewsDeleteFldr)
3075 return bits
3076 }(),
3077 },
3078 ID: &[]byte{0, 1},
3079 Server: &Server{
3080 ConfigDir: "/fakeConfigRoot",
3081 FS: func() *MockFileStore {
3082 mfs := &MockFileStore{}
3083 mfs.On("WriteFile", "/fakeConfigRoot/ThreadedNews.yaml", mock.Anything, mock.Anything).Return(nil, os.ErrNotExist)
3084 return mfs
3085 }(),
3086 ThreadedNews: &ThreadedNews{Categories: map[string]NewsCategoryListData15{
3087 "testcat": {
3088 Type: []byte{0, 2},
3089 Count: nil,
3090 NameSize: 0,
3091 Name: "test",
3092 },
3093 }},
3094 },
3095 },
3096 t: NewTransaction(
3097 tranDelNewsItem, nil,
3098 NewField(fieldNewsPath,
3099 []byte{
3100 0, 1,
3101 0, 0,
3102 4,
3103 0x74, 0x65, 0x73, 0x74,
3104 },
3105 ),
3106 ),
3107 },
3108 wantRes: []Transaction{
3109 {
3110 clientID: &[]byte{0, 1},
3111 Flags: 0x00,
3112 IsReply: 0x01,
3113 Type: []byte{0x01, 0x7c},
3114 ID: []byte{0, 0, 0, 0},
3115 ErrorCode: []byte{0, 0, 0, 0},
3116 Fields: []Field{},
3117 },
3118 },
3119 wantErr: assert.NoError,
3120 },
3121 }
3122 for _, tt := range tests {
3123 t.Run(tt.name, func(t *testing.T) {
3124 gotRes, err := HandleDelNewsItem(tt.args.cc, tt.args.t)
3125 if !tt.wantErr(t, err, fmt.Sprintf("HandleDelNewsItem(%v, %v)", tt.args.cc, tt.args.t)) {
3126 return
3127 }
3128 tranAssertEqual(t, tt.wantRes, gotRes)
3129 })
3130 }
3131 }
3132
3133 func TestHandleDownloadBanner(t *testing.T) {
3134 type args struct {
3135 cc *ClientConn
3136 t *Transaction
3137 }
3138 tests := []struct {
3139 name string
3140 args args
3141 wantRes []Transaction
3142 wantErr assert.ErrorAssertionFunc
3143 }{
3144 {
3145 name: "returns expected response",
3146 args: args{
3147 cc: &ClientConn{
3148 ID: &[]byte{0, 1},
3149 transfers: map[int]map[[4]byte]*FileTransfer{
3150 bannerDownload: {},
3151 },
3152 Server: &Server{
3153 ConfigDir: "/config",
3154 Config: &Config{
3155 BannerFile: "banner.jpg",
3156 },
3157 fileTransfers: map[[4]byte]*FileTransfer{},
3158 FS: func() *MockFileStore {
3159 mfi := &MockFileInfo{}
3160 mfi.On("Size").Return(int64(100))
3161
3162 mfs := &MockFileStore{}
3163 mfs.On("Stat", "/config/banner.jpg").Return(mfi, nil)
3164 return mfs
3165 }(),
3166 },
3167 },
3168 t: NewTransaction(tranDownloadBanner, nil),
3169 },
3170 wantRes: []Transaction{
3171 {
3172 clientID: &[]byte{0, 1},
3173 Flags: 0x00,
3174 IsReply: 0x01,
3175 Type: []byte{0x00, 0xd4},
3176 ID: []byte{0, 0, 0, 0},
3177 ErrorCode: []byte{0, 0, 0, 0},
3178 Fields: []Field{
3179 NewField(fieldRefNum, []byte{1, 2, 3, 4}),
3180 NewField(fieldTransferSize, []byte{0, 0, 0, 0x64}),
3181 },
3182 },
3183 },
3184 wantErr: assert.NoError,
3185 },
3186 }
3187 for _, tt := range tests {
3188 t.Run(tt.name, func(t *testing.T) {
3189 gotRes, err := HandleDownloadBanner(tt.args.cc, tt.args.t)
3190 if !tt.wantErr(t, err, fmt.Sprintf("HandleDownloadBanner(%v, %v)", tt.args.cc, tt.args.t)) {
3191 return
3192 }
3193
3194 tranAssertEqual(t, tt.wantRes, gotRes)
3195 })
3196 }
3197 }
3198
3199 func TestHandleTranOldPostNews(t *testing.T) {
3200 type args struct {
3201 cc *ClientConn
3202 t *Transaction
3203 }
3204 tests := []struct {
3205 name string
3206 args args
3207 wantRes []Transaction
3208 wantErr assert.ErrorAssertionFunc
3209 }{
3210 {
3211 name: "when user does not have required permission",
3212 args: args{
3213 cc: &ClientConn{
3214 Account: &Account{
3215 Access: func() accessBitmap {
3216 var bits accessBitmap
3217 return bits
3218 }(),
3219 },
3220 },
3221 t: NewTransaction(
3222 tranOldPostNews, &[]byte{0, 1},
3223 NewField(fieldData, []byte("hai")),
3224 ),
3225 },
3226 wantRes: []Transaction{
3227 {
3228 Flags: 0x00,
3229 IsReply: 0x01,
3230 Type: []byte{0, 0x00},
3231 ID: []byte{0, 0, 0, 0},
3232 ErrorCode: []byte{0, 0, 0, 1},
3233 Fields: []Field{
3234 NewField(fieldError, []byte("You are not allowed to post news.")),
3235 },
3236 },
3237 },
3238 wantErr: assert.NoError,
3239 },
3240 {
3241 name: "when user posts news update",
3242 args: args{
3243 cc: &ClientConn{
3244 Account: &Account{
3245 Access: func() accessBitmap {
3246 var bits accessBitmap
3247 bits.Set(accessNewsPostArt)
3248 return bits
3249 }(),
3250 },
3251 Server: &Server{
3252 FS: func() *MockFileStore {
3253 mfs := &MockFileStore{}
3254 mfs.On("WriteFile", "/fakeConfigRoot/MessageBoard.txt", mock.Anything, mock.Anything).Return(nil, os.ErrNotExist)
3255 return mfs
3256 }(),
3257 ConfigDir: "/fakeConfigRoot",
3258 Config: &Config{},
3259 },
3260 },
3261 t: NewTransaction(
3262 tranOldPostNews, &[]byte{0, 1},
3263 NewField(fieldData, []byte("hai")),
3264 ),
3265 },
3266 wantRes: []Transaction{
3267 {
3268 Flags: 0x00,
3269 IsReply: 0x01,
3270 Type: []byte{0, 0x67},
3271 ID: []byte{0, 0, 0, 0},
3272 ErrorCode: []byte{0, 0, 0, 0},
3273 },
3274 },
3275 wantErr: assert.NoError,
3276 },
3277 }
3278 for _, tt := range tests {
3279 t.Run(tt.name, func(t *testing.T) {
3280 gotRes, err := HandleTranOldPostNews(tt.args.cc, tt.args.t)
3281 if !tt.wantErr(t, err, fmt.Sprintf("HandleTranOldPostNews(%v, %v)", tt.args.cc, tt.args.t)) {
3282 return
3283 }
3284
3285 tranAssertEqual(t, tt.wantRes, gotRes)
3286 })
3287 }
3288 }
3289
3290 func TestHandleInviteNewChat(t *testing.T) {
3291 type args struct {
3292 cc *ClientConn
3293 t *Transaction
3294 }
3295 tests := []struct {
3296 name string
3297 args args
3298 wantRes []Transaction
3299 wantErr assert.ErrorAssertionFunc
3300 }{
3301 {
3302 name: "when user does not have required permission",
3303 args: args{
3304 cc: &ClientConn{
3305 Account: &Account{
3306 Access: func() accessBitmap {
3307 var bits accessBitmap
3308 return bits
3309 }(),
3310 },
3311 },
3312 t: NewTransaction(tranInviteNewChat, &[]byte{0, 1}),
3313 },
3314 wantRes: []Transaction{
3315 {
3316 Flags: 0x00,
3317 IsReply: 0x01,
3318 Type: []byte{0, 0x00},
3319 ID: []byte{0, 0, 0, 0},
3320 ErrorCode: []byte{0, 0, 0, 1},
3321 Fields: []Field{
3322 NewField(fieldError, []byte("You are not allowed to request private chat.")),
3323 },
3324 },
3325 },
3326 wantErr: assert.NoError,
3327 },
3328 {
3329 name: "when userA invites userB to new private chat",
3330 args: args{
3331 cc: &ClientConn{
3332 ID: &[]byte{0, 1},
3333 Account: &Account{
3334 Access: func() accessBitmap {
3335 var bits accessBitmap
3336 bits.Set(accessOpenChat)
3337 return bits
3338 }(),
3339 },
3340 UserName: []byte("UserA"),
3341 Icon: []byte{0, 1},
3342 Flags: []byte{0, 0},
3343 Server: &Server{
3344 Clients: map[uint16]*ClientConn{
3345 uint16(2): {
3346 ID: &[]byte{0, 2},
3347 UserName: []byte("UserB"),
3348 Flags: []byte{0, 0},
3349 },
3350 },
3351 PrivateChats: make(map[uint32]*PrivateChat),
3352 },
3353 },
3354 t: NewTransaction(
3355 tranInviteNewChat, &[]byte{0, 1},
3356 NewField(fieldUserID, []byte{0, 2}),
3357 ),
3358 },
3359 wantRes: []Transaction{
3360 {
3361 clientID: &[]byte{0, 2},
3362 Flags: 0x00,
3363 IsReply: 0x00,
3364 Type: []byte{0, 0x71},
3365 ID: []byte{0, 0, 0, 0},
3366 ErrorCode: []byte{0, 0, 0, 0},
3367 Fields: []Field{
3368 NewField(fieldChatID, []byte{0x52, 0xfd, 0xfc, 0x07}),
3369 NewField(fieldUserName, []byte("UserA")),
3370 NewField(fieldUserID, []byte{0, 1}),
3371 },
3372 },
3373
3374 {
3375 clientID: &[]byte{0, 1},
3376 Flags: 0x00,
3377 IsReply: 0x01,
3378 Type: []byte{0, 0x70},
3379 ID: []byte{0, 0, 0, 0},
3380 ErrorCode: []byte{0, 0, 0, 0},
3381 Fields: []Field{
3382 NewField(fieldChatID, []byte{0x52, 0xfd, 0xfc, 0x07}),
3383 NewField(fieldUserName, []byte("UserA")),
3384 NewField(fieldUserID, []byte{0, 1}),
3385 NewField(fieldUserIconID, []byte{0, 1}),
3386 NewField(fieldUserFlags, []byte{0, 0}),
3387 },
3388 },
3389 },
3390 wantErr: assert.NoError,
3391 },
3392 {
3393 name: "when userA invites userB to new private chat, but UserB has refuse private chat enabled",
3394 args: args{
3395 cc: &ClientConn{
3396 ID: &[]byte{0, 1},
3397 Account: &Account{
3398 Access: func() accessBitmap {
3399 var bits accessBitmap
3400 bits.Set(accessOpenChat)
3401 return bits
3402 }(),
3403 },
3404 UserName: []byte("UserA"),
3405 Icon: []byte{0, 1},
3406 Flags: []byte{0, 0},
3407 Server: &Server{
3408 Clients: map[uint16]*ClientConn{
3409 uint16(2): {
3410 ID: &[]byte{0, 2},
3411 UserName: []byte("UserB"),
3412 Flags: []byte{255, 255},
3413 },
3414 },
3415 PrivateChats: make(map[uint32]*PrivateChat),
3416 },
3417 },
3418 t: NewTransaction(
3419 tranInviteNewChat, &[]byte{0, 1},
3420 NewField(fieldUserID, []byte{0, 2}),
3421 ),
3422 },
3423 wantRes: []Transaction{
3424 {
3425 clientID: &[]byte{0, 1},
3426 Flags: 0x00,
3427 IsReply: 0x00,
3428 Type: []byte{0, 0x68},
3429 ID: []byte{0, 0, 0, 0},
3430 ErrorCode: []byte{0, 0, 0, 0},
3431 Fields: []Field{
3432 NewField(fieldData, []byte("UserB does not accept private chats.")),
3433 NewField(fieldUserName, []byte("UserB")),
3434 NewField(fieldUserID, []byte{0, 2}),
3435 NewField(fieldOptions, []byte{0, 2}),
3436 },
3437 },
3438 {
3439 clientID: &[]byte{0, 1},
3440 Flags: 0x00,
3441 IsReply: 0x01,
3442 Type: []byte{0, 0x70},
3443 ID: []byte{0, 0, 0, 0},
3444 ErrorCode: []byte{0, 0, 0, 0},
3445 Fields: []Field{
3446 NewField(fieldChatID, []byte{0x52, 0xfd, 0xfc, 0x07}),
3447 NewField(fieldUserName, []byte("UserA")),
3448 NewField(fieldUserID, []byte{0, 1}),
3449 NewField(fieldUserIconID, []byte{0, 1}),
3450 NewField(fieldUserFlags, []byte{0, 0}),
3451 },
3452 },
3453 },
3454 wantErr: assert.NoError,
3455 },
3456 }
3457 for _, tt := range tests {
3458 t.Run(tt.name, func(t *testing.T) {
3459 rand.Seed(1)
3460 gotRes, err := HandleInviteNewChat(tt.args.cc, tt.args.t)
3461 if !tt.wantErr(t, err, fmt.Sprintf("HandleInviteNewChat(%v, %v)", tt.args.cc, tt.args.t)) {
3462 return
3463 }
3464 tranAssertEqual(t, tt.wantRes, gotRes)
3465 })
3466 }
3467 }