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