8 "github.com/jhalter/mobius/hotline"
9 "github.com/stretchr/testify/assert"
10 "github.com/stretchr/testify/mock"
22 type mockReadWriteSeeker struct {
26 func (m *mockReadWriteSeeker) Read(p []byte) (int, error) {
29 return args.Int(0), args.Error(1)
32 func (m *mockReadWriteSeeker) Write(p []byte) (int, error) {
35 return args.Int(0), args.Error(1)
38 func (m *mockReadWriteSeeker) Seek(offset int64, whence int) (int64, error) {
39 args := m.Called(offset, whence)
41 return args.Get(0).(int64), args.Error(1)
44 func NewTestLogger() *slog.Logger {
45 return slog.New(slog.NewTextHandler(os.Stdout, nil))
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 == "" {
58 clean := slices.Concat(
63 return assert.Equal(t, wantHexDump, hex.Dump(clean))
66 var tranSortFunc = func(a, b hotline.Transaction) int {
68 binary.BigEndian.Uint16(a.ClientID[:]),
69 binary.BigEndian.Uint16(b.ClientID[:]),
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
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
85 if field.Type == hotline.FieldChatID { // FieldChatID
89 fs = append(fs, field)
92 newT1 = append(newT1, trans)
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
102 if field.Type == hotline.FieldChatID { // FieldChatID
106 fs = append(fs, field)
109 newT2 = append(newT2, trans)
112 slices.SortFunc(newT1, tranSortFunc)
113 slices.SortFunc(newT2, tranSortFunc)
115 return assert.Equal(t, newT1, newT2)
118 func TestHandleSetChatSubject(t *testing.T) {
120 cc *hotline.ClientConn
121 t hotline.Transaction
126 want []hotline.Transaction
129 name: "sends chat subject to private chat members",
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{
138 Account: &hotline.Account{
139 Access: hotline.AccessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
144 Account: &hotline.Account{
145 Access: hotline.AccessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
150 m.On("SetSubject", hotline.ChatID{0x0, 0x0, 0x0, 0x1}, "Test Subject")
153 //PrivateChats: map[[4]byte]*PrivateChat{
154 // [4]byte{0, 0, 0, 1}: {
156 // ClientConn: map[[2]byte]*ClientConn{
158 // Account: &hotline.Account{
159 // Access: AccessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
161 // ID: [2]byte{0, 1},
164 // Account: &hotline.Account{
165 // Access: AccessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
167 // ID: [2]byte{0, 2},
172 ClientMgr: func() *hotline.MockClientMgr {
173 m := hotline.MockClientMgr{}
174 m.On("List").Return([]*hotline.ClientConn{
176 Account: &hotline.Account{
177 Access: hotline.AccessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
182 Account: &hotline.Account{
183 Access: hotline.AccessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
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")),
202 want: []hotline.Transaction{
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")),
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")),
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)
232 func TestHandleLeaveChat(t *testing.T) {
234 cc *hotline.ClientConn
235 t hotline.Transaction
240 want []hotline.Transaction
243 name: "when client 2 leaves chat",
245 cc: &hotline.ClientConn{
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{
252 Account: &hotline.Account{
253 Access: hotline.AccessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
258 m.On("Leave", hotline.ChatID{0x0, 0x0, 0x0, 0x1}, [2]uint8{0x0, 0x2})
259 m.On("GetSubject").Return("unset")
262 ClientMgr: func() *hotline.MockClientMgr {
263 m := hotline.MockClientMgr{}
264 m.On("Get").Return([]*hotline.ClientConn{
266 Account: &hotline.Account{
267 Access: hotline.AccessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
272 Account: &hotline.Account{
273 Access: hotline.AccessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
283 t: hotline.NewTransaction(hotline.TranDeleteUser, [2]byte{}, hotline.NewField(hotline.FieldChatID, []byte{0, 0, 0, 1})),
285 want: []hotline.Transaction{
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}),
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)
307 func TestHandleGetUserNameList(t *testing.T) {
309 cc *hotline.ClientConn
310 t hotline.Transaction
315 want []hotline.Transaction
318 name: "replies with userlist transaction",
320 cc: &hotline.ClientConn{
322 Server: &hotline.Server{
323 ClientMgr: func() *hotline.MockClientMgr {
324 m := hotline.MockClientMgr{}
325 m.On("List").Return([]*hotline.ClientConn{
329 Flags: [2]byte{0, 3},
330 UserName: []byte{0, 4},
335 Flags: [2]byte{0, 3},
336 UserName: []byte{0, 4},
344 t: hotline.Transaction{},
346 want: []hotline.Transaction{
348 ClientID: [2]byte{0, 1},
350 Fields: []hotline.Field{
352 hotline.FieldUsernameWithInfo,
353 []byte{00, 01, 00, 02, 00, 03, 00, 02, 00, 04},
356 hotline.FieldUsernameWithInfo,
357 []byte{00, 02, 00, 02, 00, 03, 00, 02, 00, 04},
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)
372 func TestHandleChatSend(t *testing.T) {
374 cc *hotline.ClientConn
375 t hotline.Transaction
380 want []hotline.Transaction
383 name: "sends chat msg transaction to all clients",
385 cc: &hotline.ClientConn{
386 Account: &hotline.Account{
387 Access: func() hotline.AccessBitmap {
388 var bits hotline.AccessBitmap
389 bits.Set(hotline.AccessSendChat)
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{
399 Account: &hotline.Account{
400 Access: hotline.AccessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
405 Account: &hotline.Account{
406 Access: hotline.AccessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
416 t: hotline.Transaction{
417 Fields: []hotline.Field{
418 hotline.NewField(hotline.FieldData, []byte("hai")),
422 want: []hotline.Transaction{
424 ClientID: [2]byte{0, 1},
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}),
433 ClientID: [2]byte{0, 2},
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}),
444 name: "treats Chat Type 00 00 00 00 as a public chat message",
446 cc: &hotline.ClientConn{
447 Account: &hotline.Account{
448 Access: func() hotline.AccessBitmap {
449 var bits hotline.AccessBitmap
450 bits.Set(hotline.AccessSendChat)
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{
460 Account: &hotline.Account{
461 Access: hotline.AccessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
466 Account: &hotline.Account{
467 Access: hotline.AccessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
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}),
484 want: []hotline.Transaction{
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}),
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}),
502 name: "when user does not have required permission",
504 cc: &hotline.ClientConn{
505 Account: &hotline.Account{
506 Access: func() hotline.AccessBitmap {
507 var bits hotline.AccessBitmap
511 Server: &hotline.Server{
512 //Accounts: map[string]*Account{},
515 t: hotline.NewTransaction(
516 hotline.TranChatSend, [2]byte{0, 1},
517 hotline.NewField(hotline.FieldData, []byte("hai")),
520 want: []hotline.Transaction{
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.")),
531 name: "sends chat msg as emote if FieldChatOptions is set to 1",
533 cc: &hotline.ClientConn{
534 Account: &hotline.Account{
535 Access: func() hotline.AccessBitmap {
536 var bits hotline.AccessBitmap
537 bits.Set(hotline.AccessSendChat)
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{
547 Account: &hotline.Account{
548 Access: hotline.AccessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
553 Account: &hotline.Account{
554 Access: hotline.AccessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
564 t: hotline.Transaction{
565 Fields: []hotline.Field{
566 hotline.NewField(hotline.FieldData, []byte("performed action")),
567 hotline.NewField(hotline.FieldChatOptions, []byte{0x00, 0x01}),
571 want: []hotline.Transaction{
573 ClientID: [2]byte{0, 1},
576 Type: [2]byte{0, 0x6a},
577 Fields: []hotline.Field{
578 hotline.NewField(hotline.FieldData, []byte("\r*** Testy McTest performed action")),
582 ClientID: [2]byte{0, 2},
585 Type: [2]byte{0, 0x6a},
586 Fields: []hotline.Field{
587 hotline.NewField(hotline.FieldData, []byte("\r*** Testy McTest performed action")),
593 name: "does not send chat msg as emote if FieldChatOptions is set to 0",
595 cc: &hotline.ClientConn{
596 Account: &hotline.Account{
597 Access: func() hotline.AccessBitmap {
598 var bits hotline.AccessBitmap
599 bits.Set(hotline.AccessSendChat)
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{
609 Account: &hotline.Account{
610 Access: hotline.AccessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
615 Account: &hotline.Account{
616 Access: hotline.AccessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
626 t: hotline.Transaction{
627 Fields: []hotline.Field{
628 hotline.NewField(hotline.FieldData, []byte("hello")),
629 hotline.NewField(hotline.FieldChatOptions, []byte{0x00, 0x00}),
633 want: []hotline.Transaction{
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")),
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")),
651 name: "only sends chat msg to clients with AccessReadChat permission",
653 cc: &hotline.ClientConn{
654 Account: &hotline.Account{
655 Access: func() hotline.AccessBitmap {
656 var bits hotline.AccessBitmap
657 bits.Set(hotline.AccessSendChat)
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{
667 Account: &hotline.Account{
668 Access: func() hotline.AccessBitmap {
669 var bits hotline.AccessBitmap
670 bits.Set(hotline.AccessReadChat)
677 Account: &hotline.Account{},
686 t: hotline.Transaction{
687 Fields: []hotline.Field{
688 hotline.NewField(hotline.FieldData, []byte("hai")),
692 want: []hotline.Transaction{
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}),
703 name: "only sends private chat msg to members of private chat",
705 cc: &hotline.ClientConn{
706 Account: &hotline.Account{
707 Access: func() hotline.AccessBitmap {
708 var bits hotline.AccessBitmap
709 bits.Set(hotline.AccessSendChat)
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{
725 m.On("GetSubject").Return("unset")
728 ClientMgr: func() *hotline.MockClientMgr {
729 m := hotline.MockClientMgr{}
730 m.On("List").Return([]*hotline.ClientConn{
732 Account: &hotline.Account{
733 Access: hotline.AccessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
738 Account: &hotline.Account{
739 Access: hotline.AccessBitmap{0, 0, 0, 0, 0, 0, 0, 0},
744 Account: &hotline.Account{
745 Access: hotline.AccessBitmap{0, 0, 0, 0, 0, 0, 0, 0},
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}),
762 want: []hotline.Transaction{
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}),
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}),
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)
790 func TestHandleGetFileInfo(t *testing.T) {
792 cc *hotline.ClientConn
793 t hotline.Transaction
798 wantRes []hotline.Transaction
801 name: "returns expected fields when a valid file is requested",
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")
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}),
821 wantRes: []hotline.Transaction{
823 ClientID: [2]byte{0, 1},
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}),
839 for _, tt := range tests {
840 t.Run(tt.name, func(t *testing.T) {
841 gotRes := HandleGetFileInfo(tt.args.cc, &tt.args.t)
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)
848 if !TranAssertEqual(t, tt.wantRes, gotRes) {
849 t.Errorf("HandleGetFileInfo() gotRes = %v, want %v", gotRes, tt.wantRes)
855 func TestHandleNewFolder(t *testing.T) {
857 cc *hotline.ClientConn
858 t hotline.Transaction
863 wantRes []hotline.Transaction
866 name: "without required permission",
868 cc: &hotline.ClientConn{
869 Account: &hotline.Account{
870 Access: func() hotline.AccessBitmap {
871 var bits hotline.AccessBitmap
876 t: hotline.NewTransaction(
877 hotline.TranNewFolder,
881 wantRes: []hotline.Transaction{
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.")),
892 name: "when path is nested",
894 cc: &hotline.ClientConn{
895 Account: &hotline.Account{
896 Access: func() hotline.AccessBitmap {
897 var bits hotline.AccessBitmap
898 bits.Set(hotline.AccessCreateFolder)
903 Server: &hotline.Server{
904 Config: hotline.Config{
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)
915 t: hotline.NewTransaction(
916 hotline.TranNewFolder, [2]byte{0, 1},
917 hotline.NewField(hotline.FieldFileName, []byte("testFolder")),
918 hotline.NewField(hotline.FieldFilePath, []byte{
926 wantRes: []hotline.Transaction{
928 ClientID: [2]byte{0, 1},
934 name: "when path is not nested",
936 cc: &hotline.ClientConn{
937 Account: &hotline.Account{
938 Access: func() hotline.AccessBitmap {
939 var bits hotline.AccessBitmap
940 bits.Set(hotline.AccessCreateFolder)
945 Server: &hotline.Server{
946 Config: hotline.Config{
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)
957 t: hotline.NewTransaction(
958 hotline.TranNewFolder, [2]byte{0, 1},
959 hotline.NewField(hotline.FieldFileName, []byte("testFolder")),
962 wantRes: []hotline.Transaction{
964 ClientID: [2]byte{0, 1},
970 name: "when Write returns an err",
972 cc: &hotline.ClientConn{
973 Account: &hotline.Account{
974 Access: func() hotline.AccessBitmap {
975 var bits hotline.AccessBitmap
976 bits.Set(hotline.AccessCreateFolder)
981 Server: &hotline.Server{
982 Config: hotline.Config{
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)
993 t: hotline.NewTransaction(
994 hotline.TranNewFolder, [2]byte{0, 1},
995 hotline.NewField(hotline.FieldFileName, []byte("testFolder")),
996 hotline.NewField(hotline.FieldFilePath, []byte{
1001 wantRes: []hotline.Transaction{},
1004 name: "FieldFileName does not allow directory traversal",
1006 cc: &hotline.ClientConn{
1007 Account: &hotline.Account{
1008 Access: func() hotline.AccessBitmap {
1009 var bits hotline.AccessBitmap
1010 bits.Set(hotline.AccessCreateFolder)
1015 Server: &hotline.Server{
1016 Config: hotline.Config{
1017 FileRoot: "/Files/",
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)
1027 t: hotline.NewTransaction(
1028 hotline.TranNewFolder, [2]byte{0, 1},
1029 hotline.NewField(hotline.FieldFileName, []byte("../../testFolder")),
1032 wantRes: []hotline.Transaction{
1034 ClientID: [2]byte{0, 1},
1040 name: "FieldFilePath does not allow directory traversal",
1042 cc: &hotline.ClientConn{
1043 Account: &hotline.Account{
1044 Access: func() hotline.AccessBitmap {
1045 var bits hotline.AccessBitmap
1046 bits.Set(hotline.AccessCreateFolder)
1051 Server: &hotline.Server{
1052 Config: hotline.Config{
1053 FileRoot: "/Files/",
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)
1063 t: hotline.NewTransaction(
1064 hotline.TranNewFolder, [2]byte{0, 1},
1065 hotline.NewField(hotline.FieldFileName, []byte("testFolder")),
1066 hotline.NewField(hotline.FieldFilePath, []byte{
1077 wantRes: []hotline.Transaction{
1079 ClientID: [2]byte{0, 1},
1085 for _, tt := range tests {
1086 t.Run(tt.name, func(t *testing.T) {
1087 gotRes := HandleNewFolder(tt.args.cc, &tt.args.t)
1089 if !TranAssertEqual(t, tt.wantRes, gotRes) {
1090 t.Errorf("HandleNewFolder() gotRes = %v, want %v", gotRes, tt.wantRes)
1096 func TestHandleUploadFile(t *testing.T) {
1098 cc *hotline.ClientConn
1099 t hotline.Transaction
1104 wantRes []hotline.Transaction
1107 name: "when request is valid and user has Upload Anywhere permission",
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" }(),
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)
1126 t: hotline.NewTransaction(
1127 hotline.TranUploadFile, [2]byte{0, 1},
1128 hotline.NewField(hotline.FieldFileName, []byte("testFile")),
1129 hotline.NewField(hotline.FieldFilePath, []byte{
1137 wantRes: []hotline.Transaction{
1140 Fields: []hotline.Field{
1141 hotline.NewField(hotline.FieldRefNum, []byte{0x52, 0xfd, 0xfc, 0x07}), // rand.Seed(1)
1147 name: "when user does not have required access",
1149 cc: &hotline.ClientConn{
1150 Account: &hotline.Account{
1151 Access: func() hotline.AccessBitmap {
1152 var bits hotline.AccessBitmap
1157 t: hotline.NewTransaction(
1158 hotline.TranUploadFile, [2]byte{0, 1},
1159 hotline.NewField(hotline.FieldFileName, []byte("testFile")),
1160 hotline.NewField(hotline.FieldFilePath, []byte{
1168 wantRes: []hotline.Transaction{
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)
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)
1187 func TestHandleMakeAlias(t *testing.T) {
1189 cc *hotline.ClientConn
1190 t hotline.Transaction
1195 wantRes []hotline.Transaction
1198 name: "with valid input and required permissions",
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)
1209 Server: &hotline.Server{
1210 Config: hotline.Config{
1211 FileRoot: func() string {
1212 path, _ := os.Getwd()
1213 return path + "/test/config/Files"
1216 Logger: NewTestLogger(),
1217 FS: func() *hotline.MockFileStore {
1218 mfs := &hotline.MockFileStore{}
1219 path, _ := os.Getwd()
1222 path+"/test/config/Files/foo/testFile",
1223 path+"/test/config/Files/bar/testFile",
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"}, "/"))),
1236 wantRes: []hotline.Transaction{
1239 Fields: []hotline.Field(nil),
1244 name: "when symlink returns an error",
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)
1255 Server: &hotline.Server{
1256 Config: hotline.Config{
1257 FileRoot: func() string {
1258 path, _ := os.Getwd()
1259 return path + "/test/config/Files"
1262 Logger: NewTestLogger(),
1263 FS: func() *hotline.MockFileStore {
1264 mfs := &hotline.MockFileStore{}
1265 path, _ := os.Getwd()
1268 path+"/test/config/Files/foo/testFile",
1269 path+"/test/config/Files/bar/testFile",
1270 ).Return(errors.New("ohno"))
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"}, "/"))),
1282 wantRes: []hotline.Transaction{
1285 ErrorCode: [4]byte{0, 0, 0, 1},
1286 Fields: []hotline.Field{
1287 hotline.NewField(hotline.FieldError, []byte("Error creating alias")),
1293 name: "when user does not have required permission",
1295 cc: &hotline.ClientConn{
1296 Logger: NewTestLogger(),
1297 Account: &hotline.Account{
1298 Access: hotline.AccessBitmap{},
1300 Server: &hotline.Server{
1301 Config: hotline.Config{
1302 FileRoot: func() string {
1303 path, _ := os.Getwd()
1304 return path + "/test/config/Files"
1309 t: hotline.NewTransaction(
1310 hotline.TranMakeFileAlias, [2]byte{0, 1},
1311 hotline.NewField(hotline.FieldFileName, []byte("testFile")),
1312 hotline.NewField(hotline.FieldFilePath, []byte{
1318 hotline.NewField(hotline.FieldFileNewPath, []byte{
1326 wantRes: []hotline.Transaction{
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.")),
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)
1345 func TestHandleGetUser(t *testing.T) {
1347 cc *hotline.ClientConn
1348 t hotline.Transaction
1353 wantRes []hotline.Transaction
1356 name: "when account is valid",
1358 cc: &hotline.ClientConn{
1359 Account: &hotline.Account{
1360 Access: func() hotline.AccessBitmap {
1361 var bits hotline.AccessBitmap
1362 bits.Set(hotline.AccessOpenUser)
1366 Server: &hotline.Server{
1367 AccountManager: func() *MockAccountManager {
1368 m := MockAccountManager{}
1369 m.On("Get", "guest").Return(&hotline.Account{
1372 Password: "password",
1373 Access: hotline.AccessBitmap{},
1379 t: hotline.NewTransaction(
1380 hotline.TranGetUser, [2]byte{0, 1},
1381 hotline.NewField(hotline.FieldUserLogin, []byte("guest")),
1384 wantRes: []hotline.Transaction{
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}),
1397 name: "when user does not have required permission",
1399 cc: &hotline.ClientConn{
1400 Account: &hotline.Account{
1401 Access: func() hotline.AccessBitmap {
1402 var bits hotline.AccessBitmap
1406 Server: &hotline.Server{
1407 //Accounts: map[string]*Account{},
1410 t: hotline.NewTransaction(
1411 hotline.TranGetUser, [2]byte{0, 1},
1412 hotline.NewField(hotline.FieldUserLogin, []byte("nonExistentUser")),
1415 wantRes: []hotline.Transaction{
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.")),
1426 name: "when account does not exist",
1428 cc: &hotline.ClientConn{
1429 Account: &hotline.Account{
1430 Access: func() hotline.AccessBitmap {
1431 var bits hotline.AccessBitmap
1432 bits.Set(hotline.AccessOpenUser)
1436 Server: &hotline.Server{
1437 AccountManager: func() *MockAccountManager {
1438 m := MockAccountManager{}
1439 m.On("Get", "nonExistentUser").Return((*hotline.Account)(nil))
1444 t: hotline.NewTransaction(
1445 hotline.TranGetUser, [2]byte{0, 1},
1446 hotline.NewField(hotline.FieldUserLogin, []byte("nonExistentUser")),
1449 wantRes: []hotline.Transaction{
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.")),
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)
1470 func TestHandleDeleteUser(t *testing.T) {
1472 cc *hotline.ClientConn
1473 t hotline.Transaction
1478 wantRes []hotline.Transaction
1481 name: "when user exists",
1483 cc: &hotline.ClientConn{
1484 Account: &hotline.Account{
1485 Access: func() hotline.AccessBitmap {
1486 var bits hotline.AccessBitmap
1487 bits.Set(hotline.AccessDeleteUser)
1491 Server: &hotline.Server{
1492 AccountManager: func() *MockAccountManager {
1493 m := MockAccountManager{}
1494 m.On("Delete", "testuser").Return(nil)
1497 ClientMgr: func() *hotline.MockClientMgr {
1498 m := hotline.MockClientMgr{}
1499 m.On("List").Return([]*hotline.ClientConn{}) // TODO
1504 t: hotline.NewTransaction(
1505 hotline.TranDeleteUser, [2]byte{0, 1},
1506 hotline.NewField(hotline.FieldUserLogin, hotline.EncodeString([]byte("testuser"))),
1509 wantRes: []hotline.Transaction{
1513 Type: [2]byte{0, 0},
1514 Fields: []hotline.Field(nil),
1519 name: "when user does not have required permission",
1521 cc: &hotline.ClientConn{
1522 Account: &hotline.Account{
1523 Access: hotline.AccessBitmap{},
1525 Server: &hotline.Server{
1526 //Accounts: map[string]*Account{},
1529 t: hotline.NewTransaction(
1530 hotline.TranDeleteUser, [2]byte{0, 1},
1531 hotline.NewField(hotline.FieldUserLogin, hotline.EncodeString([]byte("testuser"))),
1534 wantRes: []hotline.Transaction{
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.")),
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)
1553 func TestHandleGetMsgs(t *testing.T) {
1555 cc *hotline.ClientConn
1556 t hotline.Transaction
1561 wantRes []hotline.Transaction
1564 name: "returns news data",
1566 cc: &hotline.ClientConn{
1567 Account: &hotline.Account{
1568 Access: func() hotline.AccessBitmap {
1569 var bits hotline.AccessBitmap
1570 bits.Set(hotline.AccessNewsReadArt)
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)
1581 }).Return(4, io.EOF)
1586 t: hotline.NewTransaction(
1587 hotline.TranGetMsgs, [2]byte{0, 1},
1590 wantRes: []hotline.Transaction{
1593 Fields: []hotline.Field{
1594 hotline.NewField(hotline.FieldData, []byte("TEST")),
1600 name: "when user does not have required permission",
1602 cc: &hotline.ClientConn{
1603 Account: &hotline.Account{
1604 Access: hotline.AccessBitmap{},
1606 Server: &hotline.Server{
1607 //Accounts: map[string]*Account{},
1610 t: hotline.NewTransaction(
1611 hotline.TranGetMsgs, [2]byte{0, 1},
1614 wantRes: []hotline.Transaction{
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.")),
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)
1633 func TestHandleNewUser(t *testing.T) {
1635 cc *hotline.ClientConn
1636 t hotline.Transaction
1641 wantRes []hotline.Transaction
1644 name: "when user does not have required permission",
1646 cc: &hotline.ClientConn{
1647 Account: &hotline.Account{
1648 Access: func() hotline.AccessBitmap {
1649 var bits hotline.AccessBitmap
1653 Server: &hotline.Server{
1654 //Accounts: map[string]*Account{},
1657 t: hotline.NewTransaction(
1658 hotline.TranNewUser, [2]byte{0, 1},
1661 wantRes: []hotline.Transaction{
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.")),
1672 name: "when user attempts to create account with greater access",
1674 cc: &hotline.ClientConn{
1675 Account: &hotline.Account{
1676 Access: func() hotline.AccessBitmap {
1677 var bits hotline.AccessBitmap
1678 bits.Set(hotline.AccessCreateUser)
1682 Server: &hotline.Server{
1683 AccountManager: func() *MockAccountManager {
1684 m := MockAccountManager{}
1685 m.On("Get", "userB").Return((*hotline.Account)(nil))
1690 t: hotline.NewTransaction(
1691 hotline.TranNewUser, [2]byte{0, 1},
1692 hotline.NewField(hotline.FieldUserLogin, hotline.EncodeString([]byte("userB"))),
1694 hotline.FieldUserAccess,
1696 var bits hotline.AccessBitmap
1697 bits.Set(hotline.AccessDisconUser)
1703 wantRes: []hotline.Transaction{
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.")),
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)
1722 func TestHandleListUsers(t *testing.T) {
1724 cc *hotline.ClientConn
1725 t hotline.Transaction
1730 wantRes []hotline.Transaction
1733 name: "when user does not have required permission",
1735 cc: &hotline.ClientConn{
1736 Account: &hotline.Account{
1737 Access: func() hotline.AccessBitmap {
1738 var bits hotline.AccessBitmap
1742 Server: &hotline.Server{
1743 //Accounts: map[string]*Account{},
1746 t: hotline.NewTransaction(
1747 hotline.TranNewUser, [2]byte{0, 1},
1750 wantRes: []hotline.Transaction{
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.")),
1761 name: "when user has required permission",
1763 cc: &hotline.ClientConn{
1764 Account: &hotline.Account{
1765 Access: func() hotline.AccessBitmap {
1766 var bits hotline.AccessBitmap
1767 bits.Set(hotline.AccessOpenUser)
1771 Server: &hotline.Server{
1772 AccountManager: func() *MockAccountManager {
1773 m := MockAccountManager{}
1774 m.On("List").Return([]hotline.Account{
1779 Access: hotline.AccessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
1786 t: hotline.NewTransaction(
1787 hotline.TranGetClientInfoText, [2]byte{0, 1},
1788 hotline.NewField(hotline.FieldUserID, []byte{0, 1}),
1791 wantRes: []hotline.Transaction{
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,
1805 for _, tt := range tests {
1806 t.Run(tt.name, func(t *testing.T) {
1807 gotRes := HandleListUsers(tt.args.cc, &tt.args.t)
1809 TranAssertEqual(t, tt.wantRes, gotRes)
1814 func TestHandleDownloadFile(t *testing.T) {
1816 cc *hotline.ClientConn
1817 t hotline.Transaction
1822 wantRes []hotline.Transaction
1825 name: "when user does not have required permission",
1827 cc: &hotline.ClientConn{
1828 Account: &hotline.Account{
1829 Access: func() hotline.AccessBitmap {
1830 var bits hotline.AccessBitmap
1834 Server: &hotline.Server{},
1836 t: hotline.NewTransaction(hotline.TranDownloadFile, [2]byte{0, 1}),
1838 wantRes: []hotline.Transaction{
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.")),
1849 name: "with a valid file",
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)
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" }(),
1868 t: hotline.NewTransaction(
1869 hotline.TranDownloadFile,
1871 hotline.NewField(hotline.FieldFileName, []byte("testfile.txt")),
1872 hotline.NewField(hotline.FieldFilePath, []byte{0x0, 0x00}),
1875 wantRes: []hotline.Transaction{
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}),
1888 name: "when client requests to resume 1k test file at offset 256",
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)
1899 Server: &hotline.Server{
1900 FS: &hotline.OSFileStore{},
1902 // FS: func() *hotline.MockFileStore {
1903 // path, _ := os.Getwd()
1904 // testFile, err := os.Open(path + "/test/config/Files/testfile-1k")
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"))
1919 FileTransferMgr: hotline.NewMemFileTransferMgr(),
1920 Config: hotline.Config{
1921 FileRoot: func() string { path, _ := os.Getwd(); return path + "/test/config/Files" }(),
1923 //Accounts: map[string]*Account{},
1926 t: hotline.NewTransaction(
1927 hotline.TranDownloadFile,
1929 hotline.NewField(hotline.FieldFileName, []byte("testfile-1k")),
1930 hotline.NewField(hotline.FieldFilePath, []byte{0x00, 0x00}),
1932 hotline.FieldFileResumeData,
1934 frd := hotline.FileResumeData{
1935 ForkCount: [2]byte{0, 2},
1936 ForkInfoList: []hotline.ForkInfoList{
1938 Fork: [4]byte{0x44, 0x41, 0x54, 0x41}, // "DATA"
1939 DataSize: [4]byte{0, 0, 0x01, 0x00}, // request offset 256
1942 Fork: [4]byte{0x4d, 0x41, 0x43, 0x52}, // "MACR"
1943 DataSize: [4]byte{0, 0, 0, 0},
1947 b, _ := frd.BinaryMarshal()
1953 wantRes: []hotline.Transaction{
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}),
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)
1974 func TestHandleUpdateUser(t *testing.T) {
1976 cc *hotline.ClientConn
1977 t hotline.Transaction
1982 wantRes []hotline.Transaction
1985 name: "when action is create user without required permission",
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))
1995 Logger: NewTestLogger(),
1997 Account: &hotline.Account{
1998 Access: hotline.AccessBitmap{},
2001 t: hotline.NewTransaction(
2002 hotline.TranUpdateUser,
2004 hotline.NewField(hotline.FieldData, []byte{
2005 0x00, 0x04, // field count
2007 0x00, 0x69, // FieldUserLogin = 105
2011 0x00, 0x6a, // FieldUserPassword = 106
2015 0x00, 0x66, // FieldUserName = 102
2019 0x00, 0x6e, // FieldUserAccess = 110
2021 0x60, 0x70, 0x0c, 0x20, 0x03, 0x80, 0x00, 0x00,
2025 wantRes: []hotline.Transaction{
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.")),
2036 name: "when action is modify user without required permission",
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{})
2048 Account: &hotline.Account{
2049 Access: func() hotline.AccessBitmap {
2050 var bits hotline.AccessBitmap
2055 t: hotline.NewTransaction(
2056 hotline.TranUpdateUser,
2058 hotline.NewField(hotline.FieldData, []byte{
2059 0x00, 0x04, // field count
2061 0x00, 0x69, // FieldUserLogin = 105
2065 0x00, 0x6a, // FieldUserPassword = 106
2069 0x00, 0x66, // FieldUserName = 102
2073 0x00, 0x6e, // FieldUserAccess = 110
2075 0x60, 0x70, 0x0c, 0x20, 0x03, 0x80, 0x00, 0x00,
2079 wantRes: []hotline.Transaction{
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.")),
2090 name: "when action is delete user without required permission",
2092 cc: &hotline.ClientConn{
2093 Logger: NewTestLogger(),
2094 Server: &hotline.Server{},
2095 Account: &hotline.Account{
2096 Access: hotline.AccessBitmap{},
2099 t: hotline.NewTransaction(
2100 hotline.TranUpdateUser,
2102 hotline.NewField(hotline.FieldData, []byte{
2110 wantRes: []hotline.Transaction{
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.")),
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)
2129 func TestHandleDelNewsArt(t *testing.T) {
2131 cc *hotline.ClientConn
2132 t hotline.Transaction
2137 wantRes []hotline.Transaction
2140 name: "without required permission",
2142 cc: &hotline.ClientConn{
2143 Account: &hotline.Account{
2144 Access: func() hotline.AccessBitmap {
2145 var bits hotline.AccessBitmap
2150 t: hotline.NewTransaction(
2151 hotline.TranDelNewsArt,
2155 wantRes: []hotline.Transaction{
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.")),
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)
2174 func TestHandleDisconnectUser(t *testing.T) {
2176 cc *hotline.ClientConn
2177 t hotline.Transaction
2182 wantRes []hotline.Transaction
2185 name: "without required permission",
2187 cc: &hotline.ClientConn{
2188 Account: &hotline.Account{
2189 Access: func() hotline.AccessBitmap {
2190 var bits hotline.AccessBitmap
2195 t: hotline.NewTransaction(
2196 hotline.TranDelNewsArt,
2200 wantRes: []hotline.Transaction{
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.")),
2211 name: "when target user has 'cannot be disconnected' priv",
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{
2220 Access: func() hotline.AccessBitmap {
2221 var bits hotline.AccessBitmap
2222 bits.Set(hotline.AccessCannotBeDiscon)
2231 Account: &hotline.Account{
2232 Access: func() hotline.AccessBitmap {
2233 var bits hotline.AccessBitmap
2234 bits.Set(hotline.AccessDisconUser)
2239 t: hotline.NewTransaction(
2240 hotline.TranDelNewsArt,
2242 hotline.NewField(hotline.FieldUserID, []byte{0, 1}),
2245 wantRes: []hotline.Transaction{
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.")),
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)
2264 func TestHandleSendInstantMsg(t *testing.T) {
2266 cc *hotline.ClientConn
2267 t hotline.Transaction
2272 wantRes []hotline.Transaction
2275 name: "without required permission",
2277 cc: &hotline.ClientConn{
2278 Account: &hotline.Account{
2279 Access: func() hotline.AccessBitmap {
2280 var bits hotline.AccessBitmap
2285 t: hotline.NewTransaction(
2286 hotline.TranDelNewsArt,
2290 wantRes: []hotline.Transaction{
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.")),
2301 name: "when client 1 sends a message to client 2",
2303 cc: &hotline.ClientConn{
2304 Account: &hotline.Account{
2305 Access: func() hotline.AccessBitmap {
2306 var bits hotline.AccessBitmap
2307 bits.Set(hotline.AccessSendPrivMsg)
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},
2325 t: hotline.NewTransaction(
2326 hotline.TranSendInstantMsg,
2328 hotline.NewField(hotline.FieldData, []byte("hai")),
2329 hotline.NewField(hotline.FieldUserID, []byte{0, 2}),
2332 wantRes: []hotline.Transaction{
2333 hotline.NewTransaction(
2334 hotline.TranServerMsg,
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}),
2342 ClientID: [2]byte{0, 1},
2344 Fields: []hotline.Field(nil),
2349 name: "when client 2 has autoreply enabled",
2351 cc: &hotline.ClientConn{
2352 Account: &hotline.Account{
2353 Access: func() hotline.AccessBitmap {
2354 var bits hotline.AccessBitmap
2355 bits.Set(hotline.AccessSendPrivMsg)
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},
2367 UserName: []byte("User2"),
2368 AutoReply: []byte("autohai"),
2374 t: hotline.NewTransaction(
2375 hotline.TranSendInstantMsg,
2377 hotline.NewField(hotline.FieldData, []byte("hai")),
2378 hotline.NewField(hotline.FieldUserID, []byte{0, 2}),
2381 wantRes: []hotline.Transaction{
2382 hotline.NewTransaction(
2383 hotline.TranServerMsg,
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}),
2390 hotline.NewTransaction(
2391 hotline.TranServerMsg,
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}),
2399 ClientID: [2]byte{0, 1},
2401 Fields: []hotline.Field(nil),
2406 name: "when client 2 has refuse private messages enabled",
2408 cc: &hotline.ClientConn{
2409 Account: &hotline.Account{
2410 Access: func() hotline.AccessBitmap {
2411 var bits hotline.AccessBitmap
2412 bits.Set(hotline.AccessSendPrivMsg)
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},
2424 UserName: []byte("User2"),
2431 t: hotline.NewTransaction(
2432 hotline.TranSendInstantMsg,
2434 hotline.NewField(hotline.FieldData, []byte("hai")),
2435 hotline.NewField(hotline.FieldUserID, []byte{0, 2}),
2438 wantRes: []hotline.Transaction{
2439 hotline.NewTransaction(
2440 hotline.TranServerMsg,
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}),
2448 ClientID: [2]byte{0, 1},
2450 Fields: []hotline.Field(nil),
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)
2463 func TestHandleDeleteFile(t *testing.T) {
2465 cc *hotline.ClientConn
2466 t hotline.Transaction
2471 wantRes []hotline.Transaction
2474 name: "when user does not have required permission to delete a folder",
2476 cc: &hotline.ClientConn{
2477 Account: &hotline.Account{
2478 Access: func() hotline.AccessBitmap {
2479 var bits hotline.AccessBitmap
2483 Server: &hotline.Server{
2484 Config: hotline.Config{
2485 FileRoot: func() string {
2486 return "/fakeRoot/Files"
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")
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"))
2504 //Accounts: map[string]*Account{},
2507 t: hotline.NewTransaction(
2508 hotline.TranDeleteFile, [2]byte{0, 1},
2509 hotline.NewField(hotline.FieldFileName, []byte("testfile")),
2510 hotline.NewField(hotline.FieldFilePath, []byte{
2518 wantRes: []hotline.Transaction{
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.")),
2529 name: "deletes all associated metadata files",
2531 cc: &hotline.ClientConn{
2532 Account: &hotline.Account{
2533 Access: func() hotline.AccessBitmap {
2534 var bits hotline.AccessBitmap
2535 bits.Set(hotline.AccessDeleteFile)
2539 Server: &hotline.Server{
2540 Config: hotline.Config{
2541 FileRoot: func() string {
2542 return "/fakeRoot/Files"
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")
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"))
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)
2565 //Accounts: map[string]*Account{},
2568 t: hotline.NewTransaction(
2569 hotline.TranDeleteFile, [2]byte{0, 1},
2570 hotline.NewField(hotline.FieldFileName, []byte("testfile")),
2571 hotline.NewField(hotline.FieldFilePath, []byte{
2579 wantRes: []hotline.Transaction{
2582 Fields: []hotline.Field(nil),
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)
2592 tt.args.cc.Server.FS.(*hotline.MockFileStore).AssertExpectations(t)
2597 func TestHandleGetFileNameList(t *testing.T) {
2599 cc *hotline.ClientConn
2600 t hotline.Transaction
2605 wantRes []hotline.Transaction
2608 name: "when FieldFilePath is a drop box, but user does not have AccessViewDropBoxes ",
2610 cc: &hotline.ClientConn{
2611 Account: &hotline.Account{
2612 Access: func() hotline.AccessBitmap {
2613 var bits hotline.AccessBitmap
2617 Server: &hotline.Server{
2619 Config: hotline.Config{
2620 FileRoot: func() string {
2621 path, _ := os.Getwd()
2622 return filepath.Join(path, "/test/config/Files/getFileNameListTestDir")
2627 t: hotline.NewTransaction(
2628 hotline.TranGetFileNameList, [2]byte{0, 1},
2629 hotline.NewField(hotline.FieldFilePath, []byte{
2633 0x64, 0x72, 0x6f, 0x70, 0x20, 0x62, 0x6f, 0x78, // "drop box"
2637 wantRes: []hotline.Transaction{
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.")),
2648 name: "with file root",
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")
2660 t: hotline.NewTransaction(
2661 hotline.TranGetFileNameList, [2]byte{0, 1},
2662 hotline.NewField(hotline.FieldFilePath, []byte{
2668 wantRes: []hotline.Transaction{
2671 Fields: []hotline.Field{
2673 hotline.FieldFileNameWithInfo,
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},
2681 NameScript: [2]byte{},
2682 NameSize: [2]byte{0, 0x0b},
2684 Name: []byte("testfile-1k"),
2686 b, _ := io.ReadAll(&fnwi)
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)
2703 func TestHandleGetClientInfoText(t *testing.T) {
2705 cc *hotline.ClientConn
2706 t hotline.Transaction
2711 wantRes []hotline.Transaction
2714 name: "when user does not have required permission",
2716 cc: &hotline.ClientConn{
2717 Account: &hotline.Account{
2718 Access: func() hotline.AccessBitmap {
2719 var bits hotline.AccessBitmap
2723 Server: &hotline.Server{
2724 //Accounts: map[string]*Account{},
2727 t: hotline.NewTransaction(
2728 hotline.TranGetClientInfoText, [2]byte{0, 1},
2729 hotline.NewField(hotline.FieldUserID, []byte{0, 1}),
2732 wantRes: []hotline.Transaction{
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.")),
2743 name: "with a valid user",
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)
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)
2777 ClientFileTransferMgr: hotline.ClientFileTransferMgr{},
2779 t: hotline.NewTransaction(
2780 hotline.TranGetClientInfoText, [2]byte{0, 1},
2781 hotline.NewField(hotline.FieldUserID, []byte{0, 1}),
2784 wantRes: []hotline.Transaction{
2787 Fields: []hotline.Field{
2788 hotline.NewField(hotline.FieldData, []byte(
2789 strings.ReplaceAll(`Nickname: Testy McTest
2792 Address: 1.2.3.4:12345
2794 -------- File Downloads ---------
2798 ------- Folder Downloads --------
2802 --------- File Uploads ----------
2806 -------- Folder Uploads ---------
2810 ------- Waiting Downloads -------
2816 hotline.NewField(hotline.FieldUserName, []byte("Testy McTest")),
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)
2830 func TestHandleTranAgreed(t *testing.T) {
2832 cc *hotline.ClientConn
2833 t hotline.Transaction
2838 wantRes []hotline.Transaction
2841 name: "normal request flow",
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)
2852 Flags: [2]byte{0, 1},
2853 Version: []byte{0, 1},
2855 Logger: NewTestLogger(),
2856 Server: &hotline.Server{
2857 Config: hotline.Config{
2858 BannerFile: "Banner.jpg",
2860 ClientMgr: func() *hotline.MockClientMgr {
2861 m := hotline.MockClientMgr{}
2862 m.On("List").Return([]*hotline.ClientConn{
2864 // ID: [2]byte{0, 2},
2865 // UserName: []byte("UserB"),
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}),
2880 wantRes: []hotline.Transaction{
2882 ClientID: [2]byte{0, 1},
2883 Type: [2]byte{0, 0x7a},
2884 Fields: []hotline.Field{
2885 hotline.NewField(hotline.FieldBannerType, []byte("JPEG")),
2889 ClientID: [2]byte{0, 1},
2891 Fields: []hotline.Field{},
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)
2904 func TestHandleSetClientUserInfo(t *testing.T) {
2906 cc *hotline.ClientConn
2907 t hotline.Transaction
2912 wantRes []hotline.Transaction
2915 name: "when client does not have AccessAnyName",
2917 cc: &hotline.ClientConn{
2918 Account: &hotline.Account{
2919 Access: func() hotline.AccessBitmap {
2920 var bits hotline.AccessBitmap
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{
2939 t: hotline.NewTransaction(
2940 hotline.TranSetClientUserInfo, [2]byte{},
2941 hotline.NewField(hotline.FieldUserIconID, []byte{0, 1}),
2942 hotline.NewField(hotline.FieldUserName, []byte("NOPE")),
2945 wantRes: []hotline.Transaction{
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"))},
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)
2966 func TestHandleDelNewsItem(t *testing.T) {
2968 cc *hotline.ClientConn
2969 t hotline.Transaction
2974 wantRes []hotline.Transaction
2977 name: "when user does not have permission to delete a news category",
2979 cc: &hotline.ClientConn{
2980 Account: &hotline.Account{
2981 Access: hotline.AccessBitmap{},
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,
2994 t: hotline.NewTransaction(
2995 hotline.TranDelNewsItem, [2]byte{},
2996 hotline.NewField(hotline.FieldNewsPath,
3001 0x74, 0x65, 0x73, 0x74,
3006 wantRes: []hotline.Transaction{
3008 ClientID: [2]byte{0, 1},
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.")),
3018 name: "when user does not have permission to delete a news folder",
3020 cc: &hotline.ClientConn{
3021 Account: &hotline.Account{
3022 Access: hotline.AccessBitmap{},
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,
3035 t: hotline.NewTransaction(
3036 hotline.TranDelNewsItem, [2]byte{},
3037 hotline.NewField(hotline.FieldNewsPath,
3042 0x74, 0x65, 0x73, 0x74,
3047 wantRes: []hotline.Transaction{
3049 ClientID: [2]byte{0, 1},
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.")),
3059 name: "when user deletes a news folder",
3061 cc: &hotline.ClientConn{
3062 Account: &hotline.Account{
3063 Access: func() hotline.AccessBitmap {
3064 var bits hotline.AccessBitmap
3065 bits.Set(hotline.AccessNewsDeleteFldr)
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)
3079 t: hotline.NewTransaction(
3080 hotline.TranDelNewsItem, [2]byte{},
3081 hotline.NewField(hotline.FieldNewsPath,
3086 0x74, 0x65, 0x73, 0x74,
3091 wantRes: []hotline.Transaction{
3093 ClientID: [2]byte{0, 1},
3095 Fields: []hotline.Field{},
3100 for _, tt := range tests {
3101 t.Run(tt.name, func(t *testing.T) {
3102 gotRes := HandleDelNewsItem(tt.args.cc, &tt.args.t)
3104 TranAssertEqual(t, tt.wantRes, gotRes)
3109 func TestHandleTranOldPostNews(t *testing.T) {
3111 cc *hotline.ClientConn
3112 t hotline.Transaction
3117 wantRes []hotline.Transaction
3120 name: "when user does not have required permission",
3122 cc: &hotline.ClientConn{
3123 Account: &hotline.Account{
3124 Access: hotline.AccessBitmap{},
3127 t: hotline.NewTransaction(
3128 hotline.TranOldPostNews, [2]byte{0, 1},
3129 hotline.NewField(hotline.FieldData, []byte("hai")),
3132 wantRes: []hotline.Transaction{
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.")),
3143 name: "when user posts news update",
3145 cc: &hotline.ClientConn{
3146 Account: &hotline.Account{
3147 Access: func() hotline.AccessBitmap {
3148 var bits hotline.AccessBitmap
3149 bits.Set(hotline.AccessNewsPostArt)
3153 Server: &hotline.Server{
3154 Config: hotline.Config{
3157 ClientMgr: func() *hotline.MockClientMgr {
3158 m := hotline.MockClientMgr{}
3159 m.On("List").Return([]*hotline.ClientConn{})
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)
3168 }).Return(4, io.EOF)
3169 m.On("Write", mock.AnythingOfType("[]uint8")).Return(3, nil)
3174 t: hotline.NewTransaction(
3175 hotline.TranOldPostNews, [2]byte{0, 1},
3176 hotline.NewField(hotline.FieldData, []byte("hai")),
3179 wantRes: []hotline.Transaction{
3186 for _, tt := range tests {
3187 t.Run(tt.name, func(t *testing.T) {
3188 gotRes := HandleTranOldPostNews(tt.args.cc, &tt.args.t)
3190 TranAssertEqual(t, tt.wantRes, gotRes)
3195 func TestHandleInviteNewChat(t *testing.T) {
3197 cc *hotline.ClientConn
3198 t hotline.Transaction
3203 wantRes []hotline.Transaction
3206 name: "when user does not have required permission",
3208 cc: &hotline.ClientConn{
3209 Account: &hotline.Account{
3210 Access: func() hotline.AccessBitmap {
3211 var bits hotline.AccessBitmap
3216 t: hotline.NewTransaction(hotline.TranInviteNewChat, [2]byte{0, 1}),
3218 wantRes: []hotline.Transaction{
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.")),
3229 name: "when userA invites userB to new private chat",
3231 cc: &hotline.ClientConn{
3233 Account: &hotline.Account{
3234 Access: func() hotline.AccessBitmap {
3235 var bits hotline.AccessBitmap
3236 bits.Set(hotline.AccessOpenChat)
3240 UserName: []byte("UserA"),
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{
3248 UserName: []byte("UserB"),
3252 ChatMgr: func() *hotline.MockChatManager {
3253 m := hotline.MockChatManager{}
3254 m.On("New", mock.AnythingOfType("*hotline.ClientConn")).Return(hotline.ChatID{0x52, 0xfd, 0xfc, 0x07})
3259 t: hotline.NewTransaction(
3260 hotline.TranInviteNewChat, [2]byte{0, 1},
3261 hotline.NewField(hotline.FieldUserID, []byte{0, 2}),
3264 wantRes: []hotline.Transaction{
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}),
3275 ClientID: [2]byte{0, 1},
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}),
3288 name: "when userA invites userB to new private chat, but UserB has refuse private chat enabled",
3290 cc: &hotline.ClientConn{
3292 Account: &hotline.Account{
3293 Access: func() hotline.AccessBitmap {
3294 var bits hotline.AccessBitmap
3295 bits.Set(hotline.AccessOpenChat)
3299 UserName: []byte("UserA"),
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{
3308 UserName: []byte("UserB"),
3309 Flags: [2]byte{255, 255},
3313 ChatMgr: func() *hotline.MockChatManager {
3314 m := hotline.MockChatManager{}
3315 m.On("New", mock.AnythingOfType("*hotline.ClientConn")).Return(hotline.ChatID{0x52, 0xfd, 0xfc, 0x07})
3320 t: hotline.NewTransaction(
3321 hotline.TranInviteNewChat, [2]byte{0, 1},
3322 hotline.NewField(hotline.FieldUserID, []byte{0, 2}),
3325 wantRes: []hotline.Transaction{
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}),
3337 ClientID: [2]byte{0, 1},
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}),
3350 for _, tt := range tests {
3351 t.Run(tt.name, func(t *testing.T) {
3353 gotRes := HandleInviteNewChat(tt.args.cc, &tt.args.t)
3355 TranAssertEqual(t, tt.wantRes, gotRes)
3360 func TestHandleGetNewsArtData(t *testing.T) {
3362 cc *hotline.ClientConn
3363 t hotline.Transaction
3368 wantRes []hotline.Transaction
3371 name: "when user does not have required permission",
3373 cc: &hotline.ClientConn{Account: &hotline.Account{}},
3374 t: hotline.NewTransaction(
3375 hotline.TranGetNewsArtData, [2]byte{0, 1},
3378 wantRes: []hotline.Transaction{
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.")),
3389 name: "when user has required permission",
3391 cc: &hotline.ClientConn{
3392 Account: &hotline.Account{
3393 Access: func() hotline.AccessBitmap {
3394 var bits hotline.AccessBitmap
3395 bits.Set(hotline.AccessNewsReadArt)
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{
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",
3417 t: hotline.NewTransaction(
3418 hotline.TranGetNewsArtData, [2]byte{0, 1},
3419 hotline.NewField(hotline.FieldNewsPath, []byte{
3421 0x00, 0x01, 0x00, 0x00, 0x10, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79,
3423 hotline.NewField(hotline.FieldNewsArtID, []byte{0, 1}),
3426 wantRes: []hotline.Transaction{
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")),
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)
3452 func TestHandleGetNewsArtNameList(t *testing.T) {
3454 cc *hotline.ClientConn
3455 t hotline.Transaction
3460 wantRes []hotline.Transaction
3463 name: "when user does not have required permission",
3465 cc: &hotline.ClientConn{
3466 Account: &hotline.Account{
3467 Access: func() hotline.AccessBitmap {
3468 var bits hotline.AccessBitmap
3472 Server: &hotline.Server{
3473 //Accounts: map[string]*Account{},
3476 t: hotline.NewTransaction(
3477 hotline.TranGetNewsArtNameList, [2]byte{0, 1},
3480 wantRes: []hotline.Transaction{
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.")),
3493 // name: "when user has required access",
3495 // cc: &hotline.ClientConn{
3496 // Account: &hotline.Account{
3497 // Access: func() hotline.AccessBitmap {
3498 // var bits hotline.AccessBitmap
3499 // bits.Set(hotline.AccessNewsReadArt)
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{},
3514 // t: NewTransaction(
3515 // TranGetNewsArtNameList,
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,
3524 // wantRes: []hotline.Transaction{
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,
3541 for _, tt := range tests {
3542 t.Run(tt.name, func(t *testing.T) {
3543 gotRes := HandleGetNewsArtNameList(tt.args.cc, &tt.args.t)
3545 TranAssertEqual(t, tt.wantRes, gotRes)
3550 func TestHandleNewNewsFldr(t *testing.T) {
3552 cc *hotline.ClientConn
3553 t hotline.Transaction
3558 wantRes []hotline.Transaction
3561 name: "when user does not have required permission",
3563 cc: &hotline.ClientConn{
3564 Account: &hotline.Account{
3565 Access: func() hotline.AccessBitmap {
3566 var bits hotline.AccessBitmap
3570 Server: &hotline.Server{
3571 //Accounts: map[string]*Account{},
3574 t: hotline.NewTransaction(
3575 hotline.TranGetNewsArtNameList, [2]byte{0, 1},
3578 wantRes: []hotline.Transaction{
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.")),
3591 name: "with a valid request",
3593 cc: &hotline.ClientConn{
3594 Account: &hotline.Account{
3595 Access: func() hotline.AccessBitmap {
3596 var bits hotline.AccessBitmap
3597 bits.Set(hotline.AccessNewsCreateFldr)
3601 Logger: NewTestLogger(),
3603 Server: &hotline.Server{
3604 ThreadedNewsMgr: func() *hotline.MockThreadNewsMgr {
3605 m := hotline.MockThreadNewsMgr{}
3606 m.On("CreateGrouping", []string{"test"}, "testFolder", hotline.NewsBundle).Return(nil)
3611 t: hotline.NewTransaction(
3612 hotline.TranGetNewsArtNameList, [2]byte{0, 1},
3613 hotline.NewField(hotline.FieldFileName, []byte("testFolder")),
3614 hotline.NewField(hotline.FieldNewsPath,
3619 0x74, 0x65, 0x73, 0x74,
3624 wantRes: []hotline.Transaction{
3626 ClientID: [2]byte{0, 1},
3628 Fields: []hotline.Field{},
3633 // Name: "when there is an error writing the threaded news file",
3635 // cc: &hotline.ClientConn{
3636 // Account: &hotline.Account{
3637 // Access: func() hotline.AccessBitmap {
3638 // var bits hotline.AccessBitmap
3639 // bits.Set(hotline.AccessNewsCreateFldr)
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)
3652 // ThreadedNews: &ThreadedNews{Categories: map[string]NewsCategoryListData15{
3654 // Type: []byte{0, 2},
3658 // SubCats: make(map[string]NewsCategoryListData15),
3663 // t: NewTransaction(
3664 // TranGetNewsArtNameList, [2]byte{0, 1},
3665 // NewField(hotline.FieldFileName, []byte("testFolder")),
3666 // NewField(hotline.FieldNewsPath,
3671 // 0x74, 0x65, 0x73, 0x74,
3676 // wantRes: []hotline.Transaction{
3678 // ClientID: [2]byte{0, 1},
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.")),
3689 for _, tt := range tests {
3690 t.Run(tt.name, func(t *testing.T) {
3691 gotRes := HandleNewNewsFldr(tt.args.cc, &tt.args.t)
3693 TranAssertEqual(t, tt.wantRes, gotRes)
3698 func TestHandleDownloadBanner(t *testing.T) {
3700 cc *hotline.ClientConn
3701 t hotline.Transaction
3706 wantRes []hotline.Transaction
3708 // TODO: Add test cases.
3710 for _, tt := range tests {
3711 t.Run(tt.name, func(t *testing.T) {
3712 gotRes := HandleDownloadBanner(tt.args.cc, &tt.args.t)
3714 assert.Equalf(t, tt.wantRes, gotRes, "HandleDownloadBanner(%v, %v)", tt.args.cc, &tt.args.t)
3719 func TestHandlePostNewsArt(t *testing.T) {
3721 cc *hotline.ClientConn
3722 t hotline.Transaction
3727 wantRes []hotline.Transaction
3730 name: "without required permission",
3732 cc: &hotline.ClientConn{
3733 Account: &hotline.Account{
3734 Access: func() hotline.AccessBitmap {
3735 var bits hotline.AccessBitmap
3740 t: hotline.NewTransaction(
3741 hotline.TranPostNewsArt,
3745 wantRes: []hotline.Transaction{
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.")),
3756 name: "with required permission",
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)
3766 Account: &hotline.Account{
3767 Access: func() hotline.AccessBitmap {
3768 var bits hotline.AccessBitmap
3769 bits.Set(hotline.AccessNewsPostArt)
3774 t: hotline.NewTransaction(
3775 hotline.TranPostNewsArt,
3777 hotline.NewField(hotline.FieldNewsPath, []byte{0x00, 0x01, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77}),
3778 hotline.NewField(hotline.FieldNewsArtID, []byte{0x00, 0x00, 0x00, 0x00}),
3781 wantRes: []hotline.Transaction{
3784 ErrorCode: [4]byte{0, 0, 0, 0},
3785 Fields: []hotline.Field{},
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))