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