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