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