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