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