]> git.r.bdr.sh - rbdr/mobius/blame - hotline/transaction_handlers_test.go
Fix panic on v1.2.3 login
[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 },
60ec6281
JH
1693 {
1694 name: "when user has required permission",
1695 args: args{
1696 cc: &ClientConn{
1697 Account: &Account{
1698 Access: func() *[]byte {
1699 var bits accessBitmap
1700 bits.Set(accessOpenUser)
1701 access := bits[:]
1702 return &access
1703 }(),
1704 },
1705 Server: &Server{
1706 Accounts: map[string]*Account{
1707 "guest": {
1708 Name: "guest",
1709 Login: "guest",
1710 Password: "zz",
1711 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
1712 },
1713 },
1714 },
1715 },
1716 t: NewTransaction(
1717 tranGetClientInfoText, &[]byte{0, 1},
1718 NewField(fieldUserID, []byte{0, 1}),
1719 ),
1720 },
1721 wantRes: []Transaction{
1722 {
1723 Flags: 0x00,
1724 IsReply: 0x01,
1725 Type: []byte{0x01, 0x2f},
1726 ID: []byte{0, 0, 0, 0},
1727 ErrorCode: []byte{0, 0, 0, 0},
1728 Fields: []Field{
1729 NewField(fieldData, []byte{
1730 0x00, 0x04, 0x00, 0x66, 0x00, 0x05, 0x67, 0x75, 0x65, 0x73, 0x74, 0x00, 0x69, 0x00, 0x05, 0x98,
1731 0x8a, 0x9a, 0x8c, 0x8b, 0x00, 0x6e, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1732 0x00, 0x6a, 0x00, 0x01, 0x78,
1733 }),
1734 },
1735 },
1736 },
1737 wantErr: assert.NoError,
1738 },
481631f6
JH
1739 }
1740 for _, tt := range tests {
1741 t.Run(tt.name, func(t *testing.T) {
1742 gotRes, err := HandleListUsers(tt.args.cc, tt.args.t)
1743 if !tt.wantErr(t, err, fmt.Sprintf("HandleListUsers(%v, %v)", tt.args.cc, tt.args.t)) {
1744 return
1745 }
1746
1747 tranAssertEqual(t, tt.wantRes, gotRes)
1748 })
1749 }
1750}
1751
1752func TestHandleDownloadFile(t *testing.T) {
1753 type args struct {
1754 cc *ClientConn
1755 t *Transaction
1756 }
1757 tests := []struct {
1758 name string
1759 args args
1760 wantRes []Transaction
1761 wantErr assert.ErrorAssertionFunc
1762 }{
1763 {
1764 name: "when user does not have required permission",
1765 args: args{
1766 cc: &ClientConn{
1767 Account: &Account{
1768 Access: func() *[]byte {
1769 var bits accessBitmap
1770 access := bits[:]
1771 return &access
1772 }(),
1773 },
1774 Server: &Server{},
1775 },
1776 t: NewTransaction(tranDownloadFile, &[]byte{0, 1}),
1777 },
1778 wantRes: []Transaction{
1779 {
1780 Flags: 0x00,
1781 IsReply: 0x01,
1782 Type: []byte{0, 0x00},
1783 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
1784 ErrorCode: []byte{0, 0, 0, 1},
1785 Fields: []Field{
1786 NewField(fieldError, []byte("You are not allowed to download files.")),
1787 },
1788 },
1789 },
1790 wantErr: assert.NoError,
1791 },
1792 {
1793 name: "with a valid file",
1794 args: args{
1795 cc: &ClientConn{
df1ade54
JH
1796 transfers: map[int]map[[4]byte]*FileTransfer{
1797 FileDownload: {},
1798 },
481631f6
JH
1799 Account: &Account{
1800 Access: func() *[]byte {
1801 var bits accessBitmap
1802 bits.Set(accessDownloadFile)
1803 access := bits[:]
1804 return &access
1805 }(),
1806 },
1807 Server: &Server{
7cd900d6 1808 FS: &OSFileStore{},
df1ade54 1809 fileTransfers: map[[4]byte]*FileTransfer{},
481631f6
JH
1810 Config: &Config{
1811 FileRoot: func() string { path, _ := os.Getwd(); return path + "/test/config/Files" }(),
1812 },
1813 Accounts: map[string]*Account{},
1814 },
1815 },
1816 t: NewTransaction(
1817 accessDownloadFile,
1818 &[]byte{0, 1},
1819 NewField(fieldFileName, []byte("testfile.txt")),
1820 NewField(fieldFilePath, []byte{0x0, 0x00}),
1821 ),
1822 },
1823 wantRes: []Transaction{
1824 {
1825 Flags: 0x00,
1826 IsReply: 0x01,
1827 Type: []byte{0, 0x2},
1828 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
1829 ErrorCode: []byte{0, 0, 0, 0},
1830 Fields: []Field{
1831 NewField(fieldRefNum, []byte{0x52, 0xfd, 0xfc, 0x07}),
1832 NewField(fieldWaitingCount, []byte{0x00, 0x00}),
1833 NewField(fieldTransferSize, []byte{0x00, 0x00, 0x00, 0xa5}),
1834 NewField(fieldFileSize, []byte{0x00, 0x00, 0x00, 0x17}),
1835 },
1836 },
1837 },
1838 wantErr: assert.NoError,
1839 },
7cd900d6
JH
1840 {
1841 name: "when client requests to resume 1k test file at offset 256",
1842 args: args{
1843 cc: &ClientConn{
df1ade54
JH
1844 transfers: map[int]map[[4]byte]*FileTransfer{
1845 FileDownload: {},
1846 }, Account: &Account{
7cd900d6
JH
1847 Access: func() *[]byte {
1848 var bits accessBitmap
1849 bits.Set(accessDownloadFile)
1850 access := bits[:]
1851 return &access
1852 }(),
1853 },
1854 Server: &Server{
1855 FS: &OSFileStore{},
df1ade54 1856
7cd900d6
JH
1857 // FS: func() *MockFileStore {
1858 // path, _ := os.Getwd()
1859 // testFile, err := os.Open(path + "/test/config/Files/testfile-1k")
1860 // if err != nil {
1861 // panic(err)
1862 // }
1863 //
1864 // mfi := &MockFileInfo{}
1865 // mfi.On("Mode").Return(fs.FileMode(0))
1866 // mfs := &MockFileStore{}
1867 // mfs.On("Stat", "/fakeRoot/Files/testfile.txt").Return(mfi, nil)
1868 // mfs.On("Open", "/fakeRoot/Files/testfile.txt").Return(testFile, nil)
1869 // mfs.On("Stat", "/fakeRoot/Files/.info_testfile.txt").Return(nil, errors.New("no"))
1870 // mfs.On("Stat", "/fakeRoot/Files/.rsrc_testfile.txt").Return(nil, errors.New("no"))
1871 //
1872 // return mfs
1873 // }(),
df1ade54 1874 fileTransfers: map[[4]byte]*FileTransfer{},
7cd900d6
JH
1875 Config: &Config{
1876 FileRoot: func() string { path, _ := os.Getwd(); return path + "/test/config/Files" }(),
1877 },
1878 Accounts: map[string]*Account{},
1879 },
1880 },
1881 t: NewTransaction(
1882 accessDownloadFile,
1883 &[]byte{0, 1},
1884 NewField(fieldFileName, []byte("testfile-1k")),
1885 NewField(fieldFilePath, []byte{0x00, 0x00}),
1886 NewField(
1887 fieldFileResumeData,
1888 func() []byte {
1889 frd := FileResumeData{
1890 Format: [4]byte{},
1891 Version: [2]byte{},
1892 RSVD: [34]byte{},
1893 ForkCount: [2]byte{0, 2},
1894 ForkInfoList: []ForkInfoList{
1895 {
1896 Fork: [4]byte{0x44, 0x41, 0x54, 0x41}, // "DATA"
1897 DataSize: [4]byte{0, 0, 0x01, 0x00}, // request offset 256
1898 RSVDA: [4]byte{},
1899 RSVDB: [4]byte{},
1900 },
1901 {
1902 Fork: [4]byte{0x4d, 0x41, 0x43, 0x52}, // "MACR"
1903 DataSize: [4]byte{0, 0, 0, 0},
1904 RSVDA: [4]byte{},
1905 RSVDB: [4]byte{},
1906 },
1907 },
1908 }
1909 b, _ := frd.BinaryMarshal()
1910 return b
1911 }(),
1912 ),
1913 ),
1914 },
1915 wantRes: []Transaction{
1916 {
1917 Flags: 0x00,
1918 IsReply: 0x01,
1919 Type: []byte{0, 0x2},
1920 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
1921 ErrorCode: []byte{0, 0, 0, 0},
1922 Fields: []Field{
1923 NewField(fieldRefNum, []byte{0x52, 0xfd, 0xfc, 0x07}),
1924 NewField(fieldWaitingCount, []byte{0x00, 0x00}),
1925 NewField(fieldTransferSize, []byte{0x00, 0x00, 0x03, 0x8d}),
1926 NewField(fieldFileSize, []byte{0x00, 0x00, 0x03, 0x00}),
1927 },
1928 },
1929 },
1930 wantErr: assert.NoError,
1931 },
481631f6
JH
1932 }
1933 for _, tt := range tests {
1934 t.Run(tt.name, func(t *testing.T) {
481631f6
JH
1935 gotRes, err := HandleDownloadFile(tt.args.cc, tt.args.t)
1936 if !tt.wantErr(t, err, fmt.Sprintf("HandleDownloadFile(%v, %v)", tt.args.cc, tt.args.t)) {
1937 return
1938 }
1939
1940 tranAssertEqual(t, tt.wantRes, gotRes)
1941 })
1942 }
1943}
d2810ae9
JH
1944
1945func TestHandleUpdateUser(t *testing.T) {
1946 type args struct {
1947 cc *ClientConn
1948 t *Transaction
1949 }
1950 tests := []struct {
1951 name string
1952 args args
1953 wantRes []Transaction
1954 wantErr assert.ErrorAssertionFunc
1955 }{
1956 {
1957 name: "when action is create user without required permission",
1958 args: args{
1959 cc: &ClientConn{
02b446d8 1960 logger: NewTestLogger(),
d2810ae9
JH
1961 Server: &Server{
1962 Logger: NewTestLogger(),
1963 },
1964 Account: &Account{
1965 Access: func() *[]byte {
1966 var bits accessBitmap
1967 access := bits[:]
1968 return &access
1969 }(),
1970 },
1971 },
1972 t: NewTransaction(
1973 tranUpdateUser,
1974 &[]byte{0, 0},
1975 NewField(fieldData, []byte{
1976 0x00, 0x04, // field count
1977
1978 0x00, 0x69, // fieldUserLogin = 105
1979 0x00, 0x03,
1980 0x9d, 0x9d, 0x9d,
1981
1982 0x00, 0x6a, // fieldUserPassword = 106
1983 0x00, 0x03,
1984 0x9c, 0x9c, 0x9c,
1985
1986 0x00, 0x66, // fieldUserName = 102
1987 0x00, 0x03,
1988 0x61, 0x61, 0x61,
1989
1990 0x00, 0x6e, // fieldUserAccess = 110
1991 0x00, 0x08,
1992 0x60, 0x70, 0x0c, 0x20, 0x03, 0x80, 0x00, 0x00,
1993 }),
1994 ),
1995 },
1996 wantRes: []Transaction{
1997 {
1998 Flags: 0x00,
1999 IsReply: 0x01,
2000 Type: []byte{0, 0x00},
2001 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
2002 ErrorCode: []byte{0, 0, 0, 1},
2003 Fields: []Field{
2004 NewField(fieldError, []byte("You are not allowed to create new accounts.")),
2005 },
2006 },
2007 },
2008 wantErr: assert.NoError,
2009 },
2010 {
2011 name: "when action is modify user without required permission",
2012 args: args{
2013 cc: &ClientConn{
02b446d8 2014 logger: NewTestLogger(),
d2810ae9
JH
2015 Server: &Server{
2016 Logger: NewTestLogger(),
2017 Accounts: map[string]*Account{
2018 "bbb": {},
2019 },
2020 },
2021 Account: &Account{
2022 Access: func() *[]byte {
2023 var bits accessBitmap
2024 access := bits[:]
2025 return &access
2026 }(),
2027 },
2028 },
2029 t: NewTransaction(
2030 tranUpdateUser,
2031 &[]byte{0, 0},
2032 NewField(fieldData, []byte{
2033 0x00, 0x04, // field count
2034
2035 0x00, 0x69, // fieldUserLogin = 105
2036 0x00, 0x03,
2037 0x9d, 0x9d, 0x9d,
2038
2039 0x00, 0x6a, // fieldUserPassword = 106
2040 0x00, 0x03,
2041 0x9c, 0x9c, 0x9c,
2042
2043 0x00, 0x66, // fieldUserName = 102
2044 0x00, 0x03,
2045 0x61, 0x61, 0x61,
2046
2047 0x00, 0x6e, // fieldUserAccess = 110
2048 0x00, 0x08,
2049 0x60, 0x70, 0x0c, 0x20, 0x03, 0x80, 0x00, 0x00,
2050 }),
2051 ),
2052 },
2053 wantRes: []Transaction{
2054 {
2055 Flags: 0x00,
2056 IsReply: 0x01,
2057 Type: []byte{0, 0x00},
2058 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
2059 ErrorCode: []byte{0, 0, 0, 1},
2060 Fields: []Field{
2061 NewField(fieldError, []byte("You are not allowed to modify accounts.")),
2062 },
2063 },
2064 },
2065 wantErr: assert.NoError,
2066 },
2067 {
2068 name: "when action is delete user without required permission",
2069 args: args{
2070 cc: &ClientConn{
02b446d8 2071 logger: NewTestLogger(),
d2810ae9 2072 Server: &Server{
d2810ae9
JH
2073 Accounts: map[string]*Account{
2074 "bbb": {},
2075 },
2076 },
2077 Account: &Account{
2078 Access: func() *[]byte {
2079 var bits accessBitmap
2080 access := bits[:]
2081 return &access
2082 }(),
2083 },
2084 },
2085 t: NewTransaction(
2086 tranUpdateUser,
2087 &[]byte{0, 0},
2088 NewField(fieldData, []byte{
2089 0x00, 0x01,
2090 0x00, 0x65,
2091 0x00, 0x03,
2092 0x88, 0x9e, 0x8b,
2093 }),
2094 ),
2095 },
2096 wantRes: []Transaction{
2097 {
2098 Flags: 0x00,
2099 IsReply: 0x01,
2100 Type: []byte{0, 0x00},
2101 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
2102 ErrorCode: []byte{0, 0, 0, 1},
2103 Fields: []Field{
2104 NewField(fieldError, []byte("You are not allowed to delete accounts.")),
2105 },
2106 },
2107 },
2108 wantErr: assert.NoError,
2109 },
2110 }
2111 for _, tt := range tests {
2112 t.Run(tt.name, func(t *testing.T) {
2113 gotRes, err := HandleUpdateUser(tt.args.cc, tt.args.t)
2114 if !tt.wantErr(t, err, fmt.Sprintf("HandleUpdateUser(%v, %v)", tt.args.cc, tt.args.t)) {
2115 return
2116 }
2117
2118 tranAssertEqual(t, tt.wantRes, gotRes)
2119 })
2120 }
2121}
d4c152a4
JH
2122
2123func TestHandleDelNewsArt(t *testing.T) {
2124 type args struct {
2125 cc *ClientConn
2126 t *Transaction
2127 }
2128 tests := []struct {
2129 name string
2130 args args
2131 wantRes []Transaction
2132 wantErr assert.ErrorAssertionFunc
2133 }{
2134 {
2135 name: "without required permission",
2136 args: args{
2137 cc: &ClientConn{
2138 Account: &Account{
2139 Access: func() *[]byte {
2140 var bits accessBitmap
2141 access := bits[:]
2142 return &access
2143 }(),
2144 },
2145 },
2146 t: NewTransaction(
2147 tranDelNewsArt,
2148 &[]byte{0, 0},
2149 ),
2150 },
2151 wantRes: []Transaction{
2152 {
2153 Flags: 0x00,
2154 IsReply: 0x01,
2155 Type: []byte{0, 0x00},
2156 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
2157 ErrorCode: []byte{0, 0, 0, 1},
2158 Fields: []Field{
2159 NewField(fieldError, []byte("You are not allowed to delete news articles.")),
2160 },
2161 },
2162 },
2163 wantErr: assert.NoError,
2164 },
2165 }
2166 for _, tt := range tests {
2167 t.Run(tt.name, func(t *testing.T) {
2168 gotRes, err := HandleDelNewsArt(tt.args.cc, tt.args.t)
2169 if !tt.wantErr(t, err, fmt.Sprintf("HandleDelNewsArt(%v, %v)", tt.args.cc, tt.args.t)) {
2170 return
2171 }
2172 tranAssertEqual(t, tt.wantRes, gotRes)
2173 })
2174 }
2175}
2176
2177func TestHandleDisconnectUser(t *testing.T) {
2178 type args struct {
2179 cc *ClientConn
2180 t *Transaction
2181 }
2182 tests := []struct {
2183 name string
2184 args args
2185 wantRes []Transaction
2186 wantErr assert.ErrorAssertionFunc
2187 }{
2188 {
2189 name: "without required permission",
2190 args: args{
2191 cc: &ClientConn{
2192 Account: &Account{
2193 Access: func() *[]byte {
2194 var bits accessBitmap
2195 access := bits[:]
2196 return &access
2197 }(),
2198 },
2199 },
2200 t: NewTransaction(
2201 tranDelNewsArt,
2202 &[]byte{0, 0},
2203 ),
2204 },
2205 wantRes: []Transaction{
2206 {
2207 Flags: 0x00,
2208 IsReply: 0x01,
2209 Type: []byte{0, 0x00},
2210 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
2211 ErrorCode: []byte{0, 0, 0, 1},
2212 Fields: []Field{
2213 NewField(fieldError, []byte("You are not allowed to disconnect users.")),
2214 },
2215 },
2216 },
2217 wantErr: assert.NoError,
2218 },
2219 {
2220 name: "when target user has 'cannot be disconnected' priv",
2221 args: args{
2222 cc: &ClientConn{
2223 Server: &Server{
2224 Clients: map[uint16]*ClientConn{
2225 uint16(1): {
2226 Account: &Account{
2227 Login: "unnamed",
2228 Access: func() *[]byte {
2229 var bits accessBitmap
2230 bits.Set(accessCannotBeDiscon)
2231 access := bits[:]
2232 return &access
2233 }(),
2234 },
2235 },
2236 },
2237 },
2238 Account: &Account{
2239 Access: func() *[]byte {
2240 var bits accessBitmap
2241 bits.Set(accessDisconUser)
2242 access := bits[:]
2243 return &access
2244 }(),
2245 },
2246 },
2247 t: NewTransaction(
2248 tranDelNewsArt,
2249 &[]byte{0, 0},
2250 NewField(fieldUserID, []byte{0, 1}),
2251 ),
2252 },
2253 wantRes: []Transaction{
2254 {
2255 Flags: 0x00,
2256 IsReply: 0x01,
2257 Type: []byte{0, 0x00},
2258 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
2259 ErrorCode: []byte{0, 0, 0, 1},
2260 Fields: []Field{
2261 NewField(fieldError, []byte("unnamed is not allowed to be disconnected.")),
2262 },
2263 },
2264 },
2265 wantErr: assert.NoError,
2266 },
2267 }
2268 for _, tt := range tests {
2269 t.Run(tt.name, func(t *testing.T) {
2270 gotRes, err := HandleDisconnectUser(tt.args.cc, tt.args.t)
2271 if !tt.wantErr(t, err, fmt.Sprintf("HandleDisconnectUser(%v, %v)", tt.args.cc, tt.args.t)) {
2272 return
2273 }
2274 tranAssertEqual(t, tt.wantRes, gotRes)
2275 })
2276 }
2277}
aeec1015
JH
2278
2279func TestHandleSendInstantMsg(t *testing.T) {
2280 type args struct {
2281 cc *ClientConn
2282 t *Transaction
2283 }
2284 tests := []struct {
2285 name string
2286 args args
2287 wantRes []Transaction
2288 wantErr assert.ErrorAssertionFunc
2289 }{
2290 {
2291 name: "when client 1 sends a message to client 2",
2292 args: args{
2293 cc: &ClientConn{
2294 ID: &[]byte{0, 1},
2295 UserName: []byte("User1"),
2296 Server: &Server{
2297 Clients: map[uint16]*ClientConn{
2298 uint16(2): {
2299 AutoReply: []byte(nil),
2300 },
2301 },
2302 },
2303 },
2304 t: NewTransaction(
2305 tranSendInstantMsg,
2306 &[]byte{0, 1},
2307 NewField(fieldData, []byte("hai")),
2308 NewField(fieldUserID, []byte{0, 2}),
2309 ),
2310 },
2311 wantRes: []Transaction{
2312 *NewTransaction(
2313 tranServerMsg,
2314 &[]byte{0, 2},
2315 NewField(fieldData, []byte("hai")),
2316 NewField(fieldUserName, []byte("User1")),
2317 NewField(fieldUserID, []byte{0, 1}),
2318 NewField(fieldOptions, []byte{0, 1}),
2319 ),
2320 {
2321 clientID: &[]byte{0, 1},
2322 Flags: 0x00,
2323 IsReply: 0x01,
2324 Type: []byte{0x0, 0x6c},
2325 ID: []byte{0, 0, 0, 0},
2326 ErrorCode: []byte{0, 0, 0, 0},
2327 Fields: []Field(nil),
2328 },
2329 },
2330 wantErr: assert.NoError,
2331 },
2332 {
2333 name: "when client 2 has autoreply enabled",
2334 args: args{
2335 cc: &ClientConn{
2336 ID: &[]byte{0, 1},
2337 UserName: []byte("User1"),
2338 Server: &Server{
2339 Clients: map[uint16]*ClientConn{
2340 uint16(2): {
2341 ID: &[]byte{0, 2},
2342 UserName: []byte("User2"),
2343 AutoReply: []byte("autohai"),
2344 },
2345 },
2346 },
2347 },
2348 t: NewTransaction(
2349 tranSendInstantMsg,
2350 &[]byte{0, 1},
2351 NewField(fieldData, []byte("hai")),
2352 NewField(fieldUserID, []byte{0, 2}),
2353 ),
2354 },
2355 wantRes: []Transaction{
2356 *NewTransaction(
2357 tranServerMsg,
2358 &[]byte{0, 2},
2359 NewField(fieldData, []byte("hai")),
2360 NewField(fieldUserName, []byte("User1")),
2361 NewField(fieldUserID, []byte{0, 1}),
2362 NewField(fieldOptions, []byte{0, 1}),
2363 ),
2364 *NewTransaction(
2365 tranServerMsg,
2366 &[]byte{0, 1},
2367 NewField(fieldData, []byte("autohai")),
2368 NewField(fieldUserName, []byte("User2")),
2369 NewField(fieldUserID, []byte{0, 2}),
2370 NewField(fieldOptions, []byte{0, 1}),
2371 ),
2372 {
2373 clientID: &[]byte{0, 1},
2374 Flags: 0x00,
2375 IsReply: 0x01,
2376 Type: []byte{0x0, 0x6c},
2377 ID: []byte{0, 0, 0, 0},
2378 ErrorCode: []byte{0, 0, 0, 0},
2379 Fields: []Field(nil),
2380 },
2381 },
2382 wantErr: assert.NoError,
2383 },
2384 }
2385 for _, tt := range tests {
2386 t.Run(tt.name, func(t *testing.T) {
2387 gotRes, err := HandleSendInstantMsg(tt.args.cc, tt.args.t)
2388 if !tt.wantErr(t, err, fmt.Sprintf("HandleSendInstantMsg(%v, %v)", tt.args.cc, tt.args.t)) {
2389 return
2390 }
2391
2392 tranAssertEqual(t, tt.wantRes, gotRes)
2393 })
2394 }
2395}
7cd900d6
JH
2396
2397func TestHandleDeleteFile(t *testing.T) {
2398 type args struct {
2399 cc *ClientConn
2400 t *Transaction
2401 }
2402 tests := []struct {
2403 name string
2404 args args
2405 wantRes []Transaction
2406 wantErr assert.ErrorAssertionFunc
2407 }{
2408 {
2409 name: "when user does not have required permission to delete a folder",
2410 args: args{
2411 cc: &ClientConn{
2412 Account: &Account{
2413 Access: func() *[]byte {
2414 var bits accessBitmap
2415 access := bits[:]
2416 return &access
2417 }(),
2418 },
2419 Server: &Server{
2420 Config: &Config{
2421 FileRoot: func() string {
2422 return "/fakeRoot/Files"
2423 }(),
2424 },
2425 FS: func() *MockFileStore {
2426 mfi := &MockFileInfo{}
2427 mfi.On("Mode").Return(fs.FileMode(0))
2428 mfi.On("Size").Return(int64(100))
2429 mfi.On("ModTime").Return(time.Parse(time.Layout, time.Layout))
2430 mfi.On("IsDir").Return(false)
2431 mfi.On("Name").Return("testfile")
2432
2433 mfs := &MockFileStore{}
2434 mfs.On("Stat", "/fakeRoot/Files/aaa/testfile").Return(mfi, nil)
2435 mfs.On("Stat", "/fakeRoot/Files/aaa/.info_testfile").Return(nil, errors.New("err"))
2436 mfs.On("Stat", "/fakeRoot/Files/aaa/.rsrc_testfile").Return(nil, errors.New("err"))
2437
2438 return mfs
2439 }(),
2440 Accounts: map[string]*Account{},
2441 },
2442 },
2443 t: NewTransaction(
2444 tranDeleteFile, &[]byte{0, 1},
2445 NewField(fieldFileName, []byte("testfile")),
2446 NewField(fieldFilePath, []byte{
2447 0x00, 0x01,
2448 0x00, 0x00,
2449 0x03,
2450 0x61, 0x61, 0x61,
2451 }),
2452 ),
2453 },
2454 wantRes: []Transaction{
2455 {
2456 Flags: 0x00,
2457 IsReply: 0x01,
2458 Type: []byte{0, 0x00},
2459 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
2460 ErrorCode: []byte{0, 0, 0, 1},
2461 Fields: []Field{
2462 NewField(fieldError, []byte("You are not allowed to delete files.")),
2463 },
2464 },
2465 },
2466 wantErr: assert.NoError,
2467 },
2468 {
2469 name: "deletes all associated metadata files",
2470 args: args{
2471 cc: &ClientConn{
2472 Account: &Account{
2473 Access: func() *[]byte {
2474 var bits accessBitmap
2475 bits.Set(accessDeleteFile)
2476 access := bits[:]
2477 return &access
2478 }(),
2479 },
2480 Server: &Server{
2481 Config: &Config{
2482 FileRoot: func() string {
2483 return "/fakeRoot/Files"
2484 }(),
2485 },
2486 FS: func() *MockFileStore {
2487 mfi := &MockFileInfo{}
2488 mfi.On("Mode").Return(fs.FileMode(0))
2489 mfi.On("Size").Return(int64(100))
2490 mfi.On("ModTime").Return(time.Parse(time.Layout, time.Layout))
2491 mfi.On("IsDir").Return(false)
2492 mfi.On("Name").Return("testfile")
2493
2494 mfs := &MockFileStore{}
2495 mfs.On("Stat", "/fakeRoot/Files/aaa/testfile").Return(mfi, nil)
2496 mfs.On("Stat", "/fakeRoot/Files/aaa/.info_testfile").Return(nil, errors.New("err"))
2497 mfs.On("Stat", "/fakeRoot/Files/aaa/.rsrc_testfile").Return(nil, errors.New("err"))
2498
2499 mfs.On("RemoveAll", "/fakeRoot/Files/aaa/testfile").Return(nil)
2500 mfs.On("Remove", "/fakeRoot/Files/aaa/testfile.incomplete").Return(nil)
2501 mfs.On("Remove", "/fakeRoot/Files/aaa/.rsrc_testfile").Return(nil)
2502 mfs.On("Remove", "/fakeRoot/Files/aaa/.info_testfile").Return(nil)
2503
2504 return mfs
2505 }(),
2506 Accounts: map[string]*Account{},
2507 },
2508 },
2509 t: NewTransaction(
2510 tranDeleteFile, &[]byte{0, 1},
2511 NewField(fieldFileName, []byte("testfile")),
2512 NewField(fieldFilePath, []byte{
2513 0x00, 0x01,
2514 0x00, 0x00,
2515 0x03,
2516 0x61, 0x61, 0x61,
2517 }),
2518 ),
2519 },
2520 wantRes: []Transaction{
2521 {
2522 Flags: 0x00,
2523 IsReply: 0x01,
2524 Type: []byte{0x0, 0xcc},
2525 ID: []byte{0x0, 0x0, 0x0, 0x0},
2526 ErrorCode: []byte{0, 0, 0, 0},
2527 Fields: []Field(nil),
2528 },
2529 },
2530 wantErr: assert.NoError,
2531 },
2532 }
2533 for _, tt := range tests {
2534 t.Run(tt.name, func(t *testing.T) {
2535 gotRes, err := HandleDeleteFile(tt.args.cc, tt.args.t)
2536 if !tt.wantErr(t, err, fmt.Sprintf("HandleDeleteFile(%v, %v)", tt.args.cc, tt.args.t)) {
2537 return
2538 }
2539
2540 tranAssertEqual(t, tt.wantRes, gotRes)
2541
2542 tt.args.cc.Server.FS.(*MockFileStore).AssertExpectations(t)
2543 })
2544 }
2545}
2e08be58
JH
2546
2547func TestHandleGetFileNameList(t *testing.T) {
2548 type args struct {
2549 cc *ClientConn
2550 t *Transaction
2551 }
2552 tests := []struct {
2553 name string
2554 args args
2555 wantRes []Transaction
2556 wantErr assert.ErrorAssertionFunc
2557 }{
2558 {
2559 name: "when fieldFilePath is a drop box, but user does not have accessViewDropBoxes ",
2560 args: args{
2561 cc: &ClientConn{
2562 Account: &Account{
2563 Access: func() *[]byte {
2564 var bits accessBitmap
2565 access := bits[:]
2566 return &access
2567 }(),
2568 },
2569 Server: &Server{
2570
2571 Config: &Config{
2572 FileRoot: func() string {
2573 path, _ := os.Getwd()
2574 return filepath.Join(path, "/test/config/Files/getFileNameListTestDir")
2575 }(),
2576 },
2577 },
2578 },
2579 t: NewTransaction(
2580 tranGetFileNameList, &[]byte{0, 1},
2581 NewField(fieldFilePath, []byte{
2582 0x00, 0x01,
2583 0x00, 0x00,
2584 0x08,
2585 0x64, 0x72, 0x6f, 0x70, 0x20, 0x62, 0x6f, 0x78, // "drop box"
2586 }),
2587 ),
2588 },
2589 wantRes: []Transaction{
2590 {
2591 Flags: 0x00,
2592 IsReply: 0x01,
2593 Type: []byte{0, 0x00},
2594 ID: []byte{0, 0, 0, 0},
2595 ErrorCode: []byte{0, 0, 0, 1},
2596 Fields: []Field{
2597 NewField(fieldError, []byte("You are not allowed to view drop boxes.")),
2598 },
2599 },
2600 },
2601 wantErr: assert.NoError,
2602 },
2603 {
2604 name: "with file root",
2605 args: args{
2606 cc: &ClientConn{
2607 Server: &Server{
2608 Config: &Config{
2609 FileRoot: func() string {
2610 path, _ := os.Getwd()
2611 return filepath.Join(path, "/test/config/Files/getFileNameListTestDir")
2612 }(),
2613 },
2614 },
2615 },
2616 t: NewTransaction(
2617 tranGetFileNameList, &[]byte{0, 1},
2618 NewField(fieldFilePath, []byte{
2619 0x00, 0x00,
2620 0x00, 0x00,
2621 }),
2622 ),
2623 },
2624 wantRes: []Transaction{
2625 {
2626 Flags: 0x00,
2627 IsReply: 0x01,
2628 Type: []byte{0, 0xc8},
2629 ID: []byte{0, 0, 0, 0},
2630 ErrorCode: []byte{0, 0, 0, 0},
2631 Fields: []Field{
2632 NewField(
2633 fieldFileNameWithInfo,
2634 func() []byte {
2635 fnwi := FileNameWithInfo{
2636 fileNameWithInfoHeader: fileNameWithInfoHeader{
2637 Type: [4]byte{0x54, 0x45, 0x58, 0x54},
2638 Creator: [4]byte{0x54, 0x54, 0x58, 0x54},
2639 FileSize: [4]byte{0, 0, 0x04, 0},
2640 RSVD: [4]byte{},
2641 NameScript: [2]byte{},
2642 NameSize: [2]byte{0, 0x0b},
2643 },
2644 name: []byte("testfile-1k"),
2645 }
2646 b, _ := fnwi.MarshalBinary()
2647 return b
2648 }(),
2649 ),
2650 },
2651 },
2652 },
2653 wantErr: assert.NoError,
2654 },
2655 }
2656 for _, tt := range tests {
2657 t.Run(tt.name, func(t *testing.T) {
2658 gotRes, err := HandleGetFileNameList(tt.args.cc, tt.args.t)
2659 if !tt.wantErr(t, err, fmt.Sprintf("HandleGetFileNameList(%v, %v)", tt.args.cc, tt.args.t)) {
2660 return
2661 }
2662
2663 tranAssertEqual(t, tt.wantRes, gotRes)
2664 })
2665 }
2666}
df1ade54
JH
2667
2668func TestHandleGetClientInfoText(t *testing.T) {
2669 type args struct {
2670 cc *ClientConn
2671 t *Transaction
2672 }
2673 tests := []struct {
2674 name string
2675 args args
2676 wantRes []Transaction
2677 wantErr assert.ErrorAssertionFunc
2678 }{
2679 {
2680 name: "when user does not have required permission",
2681 args: args{
2682 cc: &ClientConn{
2683 Account: &Account{
2684 Access: func() *[]byte {
2685 var bits accessBitmap
2686 access := bits[:]
2687 return &access
2688 }(),
2689 },
2690 Server: &Server{
2691 Accounts: map[string]*Account{},
2692 },
2693 },
2694 t: NewTransaction(
2695 tranGetClientInfoText, &[]byte{0, 1},
2696 NewField(fieldUserID, []byte{0, 1}),
2697 ),
2698 },
2699 wantRes: []Transaction{
2700 {
2701 Flags: 0x00,
2702 IsReply: 0x01,
2703 Type: []byte{0, 0x00},
2704 ID: []byte{0, 0, 0, 0},
2705 ErrorCode: []byte{0, 0, 0, 1},
2706 Fields: []Field{
2707 NewField(fieldError, []byte("You are not allowed to get client info.")),
2708 },
2709 },
2710 },
2711 wantErr: assert.NoError,
2712 },
2713 {
2714 name: "with a valid user",
2715 args: args{
2716 cc: &ClientConn{
2717 UserName: []byte("Testy McTest"),
2718 RemoteAddr: "1.2.3.4:12345",
2719 Account: &Account{
2720 Access: func() *[]byte {
2721 var bits accessBitmap
2722 bits.Set(accessGetClientInfo)
2723 access := bits[:]
2724 return &access
2725 }(),
2726 Name: "test",
2727 Login: "test",
2728 },
2729 Server: &Server{
2730 Accounts: map[string]*Account{},
2731 Clients: map[uint16]*ClientConn{
2732 uint16(1): {
2733 UserName: []byte("Testy McTest"),
2734 RemoteAddr: "1.2.3.4:12345",
2735 Account: &Account{
2736 Access: func() *[]byte {
2737 var bits accessBitmap
2738 bits.Set(accessGetClientInfo)
2739 access := bits[:]
2740 return &access
2741 }(),
2742 Name: "test",
2743 Login: "test",
2744 },
2745 },
2746 },
2747 },
2748 transfers: map[int]map[[4]byte]*FileTransfer{
2749 FileDownload: {},
2750 FileUpload: {},
2751 FolderDownload: {},
2752 FolderUpload: {},
2753 },
2754 },
2755 t: NewTransaction(
2756 tranGetClientInfoText, &[]byte{0, 1},
2757 NewField(fieldUserID, []byte{0, 1}),
2758 ),
2759 },
2760 wantRes: []Transaction{
2761 {
2762 Flags: 0x00,
2763 IsReply: 0x01,
2764 Type: []byte{0x1, 0x2f},
2765 ID: []byte{0, 0, 0, 0},
2766 ErrorCode: []byte{0, 0, 0, 0},
2767 Fields: []Field{
2768 NewField(fieldData, []byte(
2769 strings.Replace(`Nickname: Testy McTest
2770Name: test
2771Account: test
2772Address: 1.2.3.4:12345
2773
2774-------- File Downloads ---------
2775
2776None.
2777
2778------- Folder Downloads --------
2779
2780None.
2781
2782--------- File Uploads ----------
2783
2784None.
2785
2786-------- Folder Uploads ---------
2787
2788None.
2789
2790------- Waiting Downloads -------
2791
2792None.
2793
2794`, "\n", "\r", -1)),
2795 ),
2796 NewField(fieldUserName, []byte("Testy McTest")),
2797 },
2798 },
2799 },
2800 wantErr: assert.NoError,
2801 },
2802 }
2803 for _, tt := range tests {
2804 t.Run(tt.name, func(t *testing.T) {
2805 gotRes, err := HandleGetClientInfoText(tt.args.cc, tt.args.t)
2806 if !tt.wantErr(t, err, fmt.Sprintf("HandleGetClientInfoText(%v, %v)", tt.args.cc, tt.args.t)) {
2807 return
2808 }
2809 tranAssertEqual(t, tt.wantRes, gotRes)
2810 })
2811 }
2812}
ea5d8c51
JH
2813
2814func TestHandleTranAgreed(t *testing.T) {
2815 type args struct {
2816 cc *ClientConn
2817 t *Transaction
2818 }
2819 tests := []struct {
2820 name string
2821 args args
2822 wantRes []Transaction
2823 wantErr assert.ErrorAssertionFunc
2824 }{
2825 {
2826 name: "normal request flow",
2827 args: args{
2828 cc: &ClientConn{
2829 Account: &Account{
2830 Access: func() *[]byte {
2831 var bits accessBitmap
2832 bits.Set(accessDisconUser)
2833 bits.Set(accessAnyName)
2834 access := bits[:]
2835 return &access
2836 }()},
2837 Icon: &[]byte{0, 1},
2838 Flags: &[]byte{0, 1},
2839 Version: &[]byte{0, 1},
2840 ID: &[]byte{0, 1},
2841 logger: NewTestLogger(),
2842 Server: &Server{
2843 Config: &Config{
2844 BannerFile: "banner.jpg",
2845 },
2846 },
2847 },
2848 t: NewTransaction(
2849 tranAgreed, nil,
2850 NewField(fieldUserName, []byte("username")),
2851 NewField(fieldUserIconID, []byte{0, 1}),
2852 NewField(fieldOptions, []byte{0, 0}),
2853 ),
2854 },
2855 wantRes: []Transaction{
2856 {
2857 clientID: &[]byte{0, 1},
2858 Flags: 0x00,
2859 IsReply: 0x00,
2860 Type: []byte{0, 0x7a},
2861 ID: []byte{0, 0, 0, 0},
2862 ErrorCode: []byte{0, 0, 0, 0},
2863 Fields: []Field{
2864 NewField(fieldBannerType, []byte("JPEG")),
2865 },
2866 },
2867 {
2868 clientID: &[]byte{0, 1},
2869 Flags: 0x00,
2870 IsReply: 0x01,
2871 Type: []byte{0, 0x79},
2872 ID: []byte{0, 0, 0, 0},
2873 ErrorCode: []byte{0, 0, 0, 0},
2874 Fields: []Field{},
2875 },
2876 },
2877 wantErr: assert.NoError,
2878 },
2879 }
2880 for _, tt := range tests {
2881 t.Run(tt.name, func(t *testing.T) {
2882 gotRes, err := HandleTranAgreed(tt.args.cc, tt.args.t)
2883 if !tt.wantErr(t, err, fmt.Sprintf("HandleTranAgreed(%v, %v)", tt.args.cc, tt.args.t)) {
2884 return
2885 }
2886 tranAssertEqual(t, tt.wantRes, gotRes)
2887 })
2888 }
2889}