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