5 "github.com/stretchr/testify/assert"
13 func TestHandleSetChatSubject(t *testing.T) {
25 name: "sends chat subject to private chat members",
28 UserName: []byte{0x00, 0x01},
30 PrivateChats: map[uint32]*PrivateChat{
33 ClientConn: map[uint16]*ClientConn{
36 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
42 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
49 Clients: map[uint16]*ClientConn{
52 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
58 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
68 Type: []byte{0, 0x6a},
69 ID: []byte{0, 0, 0, 1},
70 ErrorCode: []byte{0, 0, 0, 0},
72 NewField(fieldChatID, []byte{0, 0, 0, 1}),
73 NewField(fieldChatSubject, []byte("Test Subject")),
79 clientID: &[]byte{0, 1},
82 Type: []byte{0, 0x77},
83 ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
84 ErrorCode: []byte{0, 0, 0, 0},
86 NewField(fieldChatID, []byte{0, 0, 0, 1}),
87 NewField(fieldChatSubject, []byte("Test Subject")),
91 clientID: &[]byte{0, 2},
94 Type: []byte{0, 0x77},
95 ID: []byte{0xf0, 0xc5, 0x34, 0x1e}, // Random ID from rand.Seed(1)
96 ErrorCode: []byte{0, 0, 0, 0},
98 NewField(fieldChatID, []byte{0, 0, 0, 1}),
99 NewField(fieldChatSubject, []byte("Test Subject")),
106 for _, tt := range tests {
107 rand.Seed(1) // reset seed between tests to make transaction IDs predictable
109 t.Run(tt.name, func(t *testing.T) {
110 got, err := HandleSetChatSubject(tt.args.cc, tt.args.t)
111 if (err != nil) != tt.wantErr {
112 t.Errorf("HandleSetChatSubject() error = %v, wantErr %v", err, tt.wantErr)
115 if !assert.Equal(t, tt.want, got) {
116 t.Errorf("HandleSetChatSubject() got = %v, want %v", got, tt.want)
122 func TestHandleLeaveChat(t *testing.T) {
134 name: "returns expected transactions",
139 PrivateChats: map[uint32]*PrivateChat{
141 ClientConn: map[uint16]*ClientConn{
144 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
150 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
157 Clients: map[uint16]*ClientConn{
160 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
166 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
173 t: NewTransaction(tranDeleteUser, nil, NewField(fieldChatID, []byte{0, 0, 0, 1})),
177 clientID: &[]byte{0, 1},
180 Type: []byte{0, 0x76},
181 ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
182 ErrorCode: []byte{0, 0, 0, 0},
184 NewField(fieldChatID, []byte{0, 0, 0, 1}),
185 NewField(fieldUserID, []byte{0, 2}),
192 for _, tt := range tests {
194 t.Run(tt.name, func(t *testing.T) {
195 got, err := HandleLeaveChat(tt.args.cc, tt.args.t)
196 if (err != nil) != tt.wantErr {
197 t.Errorf("HandleLeaveChat() error = %v, wantErr %v", err, tt.wantErr)
200 if !assert.Equal(t, tt.want, got) {
201 t.Errorf("HandleLeaveChat() got = %v, want %v", got, tt.want)
207 func TestHandleGetUserNameList(t *testing.T) {
219 name: "replies with userlist transaction",
225 Clients: map[uint16]*ClientConn{
229 Flags: &[]byte{0, 3},
230 UserName: []byte{0, 4},
236 Flags: &[]byte{0, 3},
237 UserName: []byte{0, 4},
243 Flags: &[]byte{0, 3},
244 UserName: []byte{0, 4},
251 ID: []byte{0, 0, 0, 1},
257 clientID: &[]byte{1, 1},
261 ID: []byte{0, 0, 0, 1},
262 ErrorCode: []byte{0, 0, 0, 0},
265 fieldUsernameWithInfo,
266 []byte{00, 01, 00, 02, 00, 03, 00, 02, 00, 04},
269 fieldUsernameWithInfo,
270 []byte{00, 02, 00, 02, 00, 03, 00, 02, 00, 04},
278 for _, tt := range tests {
279 t.Run(tt.name, func(t *testing.T) {
280 got, err := HandleGetUserNameList(tt.args.cc, tt.args.t)
281 if (err != nil) != tt.wantErr {
282 t.Errorf("HandleGetUserNameList() error = %v, wantErr %v", err, tt.wantErr)
285 assert.Equal(t, tt.want, got)
290 func TestHandleChatSend(t *testing.T) {
302 name: "sends chat msg transaction to all clients",
305 UserName: []byte{0x00, 0x01},
307 Clients: map[uint16]*ClientConn{
310 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
316 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
325 NewField(fieldData, []byte("hai")),
331 clientID: &[]byte{0, 1},
334 Type: []byte{0, 0x6a},
335 ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
336 ErrorCode: []byte{0, 0, 0, 0},
338 NewField(fieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
342 clientID: &[]byte{0, 2},
345 Type: []byte{0, 0x6a},
346 ID: []byte{0xf0, 0xc5, 0x34, 0x1e}, // Random ID from rand.Seed(1)
347 ErrorCode: []byte{0, 0, 0, 0},
349 NewField(fieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
356 name: "sends chat msg as emote if fieldChatOptions is set",
359 UserName: []byte("Testy McTest"),
361 Clients: map[uint16]*ClientConn{
364 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
370 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
379 NewField(fieldData, []byte("performed action")),
380 NewField(fieldChatOptions, []byte{0x00, 0x01}),
386 clientID: &[]byte{0, 1},
389 Type: []byte{0, 0x6a},
390 ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
391 ErrorCode: []byte{0, 0, 0, 0},
393 NewField(fieldData, []byte("\r*** Testy McTest performed action")),
397 clientID: &[]byte{0, 2},
400 Type: []byte{0, 0x6a},
401 ID: []byte{0xf0, 0xc5, 0x34, 0x1e}, // Random ID from rand.Seed(1)
402 ErrorCode: []byte{0, 0, 0, 0},
404 NewField(fieldData, []byte("\r*** Testy McTest performed action")),
411 name: "only sends chat msg to clients with accessReadChat permission",
414 UserName: []byte{0x00, 0x01},
416 Clients: map[uint16]*ClientConn{
419 Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
425 Access: &[]byte{0, 0, 0, 0, 0, 0, 0, 0},
434 NewField(fieldData, []byte("hai")),
440 clientID: &[]byte{0, 1},
443 Type: []byte{0, 0x6a},
444 ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
445 ErrorCode: []byte{0, 0, 0, 0},
447 NewField(fieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
454 for _, tt := range tests {
455 rand.Seed(1) // reset seed between tests to make transaction IDs predictable
456 t.Run(tt.name, func(t *testing.T) {
457 got, err := HandleChatSend(tt.args.cc, tt.args.t)
459 if (err != nil) != tt.wantErr {
460 t.Errorf("HandleChatSend() error = %v, wantErr %v", err, tt.wantErr)
463 if !assert.Equal(t, tt.want, got) {
464 t.Errorf("HandleChatSend() got = %v, want %v", got, tt.want)
470 func TestHandleGetFileInfo(t *testing.T) {
471 rand.Seed(1) // reset seed between tests to make transaction IDs predictable
480 wantRes []Transaction
484 name: "returns expected fields when a valid file is requested",
487 ID: &[]byte{0x00, 0x01},
490 FileRoot: func() string {
491 path, _ := os.Getwd()
492 return path + "/test/config/Files"
498 tranGetFileInfo, nil,
499 NewField(fieldFileName, []byte("testfile.txt")),
500 NewField(fieldFilePath, []byte{0x00, 0x00}),
503 wantRes: []Transaction{
505 clientID: &[]byte{0, 1},
508 Type: []byte{0, 0xce},
509 ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
510 ErrorCode: []byte{0, 0, 0, 0},
512 NewField(fieldFileName, []byte("testfile.txt")),
513 NewField(fieldFileTypeString, []byte("TEXT")),
514 NewField(fieldFileCreatorString, []byte("ttxt")),
515 NewField(fieldFileComment, []byte{}),
516 NewField(fieldFileType, []byte("TEXT")),
517 NewField(fieldFileCreateDate, make([]byte, 8)),
518 NewField(fieldFileModifyDate, make([]byte, 8)),
519 NewField(fieldFileSize, []byte{0x0, 0x0, 0x0, 0x17}),
526 for _, tt := range tests {
527 t.Run(tt.name, func(t *testing.T) {
528 rand.Seed(1) // reset seed between tests to make transaction IDs predictable
530 gotRes, err := HandleGetFileInfo(tt.args.cc, tt.args.t)
531 if (err != nil) != tt.wantErr {
532 t.Errorf("HandleGetFileInfo() error = %v, wantErr %v", err, tt.wantErr)
536 // Clear the file timestamp fields to work around problems running the tests in multiple timezones
537 // TODO: revisit how to test this by mocking the stat calls
538 gotRes[0].Fields[5].Data = make([]byte, 8)
539 gotRes[0].Fields[6].Data = make([]byte, 8)
540 if !assert.Equal(t, tt.wantRes, gotRes) {
541 t.Errorf("HandleGetFileInfo() gotRes = %v, want %v", gotRes, tt.wantRes)
547 func TestHandleNewFolder(t *testing.T) {
556 wantRes []Transaction
560 name: "when path is nested",
571 tranNewFolder, &[]byte{0, 1},
572 NewField(fieldFileName, []byte("testFolder")),
573 NewField(fieldFilePath, []byte{
582 mfs := MockFileStore{}
583 mfs.On("Mkdir", "/Files/aaa/testFolder", fs.FileMode(0777)).Return(nil)
584 mfs.On("Stat", "/Files/aaa/testFolder").Return(nil, os.ErrNotExist)
587 wantRes: []Transaction{
589 clientID: &[]byte{0, 1},
592 Type: []byte{0, 0xcd},
593 ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
594 ErrorCode: []byte{0, 0, 0, 0},
600 name: "when path is not nested",
611 tranNewFolder, &[]byte{0, 1},
612 NewField(fieldFileName, []byte("testFolder")),
616 mfs := MockFileStore{}
617 mfs.On("Mkdir", "/Files/testFolder", fs.FileMode(0777)).Return(nil)
618 mfs.On("Stat", "/Files/testFolder").Return(nil, os.ErrNotExist)
621 wantRes: []Transaction{
623 clientID: &[]byte{0, 1},
626 Type: []byte{0, 0xcd},
627 ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
628 ErrorCode: []byte{0, 0, 0, 0},
634 name: "when UnmarshalBinary returns an err",
645 tranNewFolder, &[]byte{0, 1},
646 NewField(fieldFileName, []byte("testFolder")),
647 NewField(fieldFilePath, []byte{
653 mfs := MockFileStore{}
654 mfs.On("Mkdir", "/Files/aaa/testFolder", fs.FileMode(0777)).Return(nil)
655 mfs.On("Stat", "/Files/aaa/testFolder").Return(nil, os.ErrNotExist)
658 wantRes: []Transaction{},
662 name: "fieldFileName does not allow directory traversal",
673 tranNewFolder, &[]byte{0, 1},
674 NewField(fieldFileName, []byte("../../testFolder")),
678 mfs := MockFileStore{}
679 mfs.On("Mkdir", "/Files/testFolder", fs.FileMode(0777)).Return(nil)
680 mfs.On("Stat", "/Files/testFolder").Return(nil, os.ErrNotExist)
683 wantRes: []Transaction{
685 clientID: &[]byte{0, 1},
688 Type: []byte{0, 0xcd},
689 ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
690 ErrorCode: []byte{0, 0, 0, 0},
695 name: "fieldFilePath does not allow directory traversal",
706 tranNewFolder, &[]byte{0, 1},
707 NewField(fieldFileName, []byte("testFolder")),
708 NewField(fieldFilePath, []byte{
720 mfs := MockFileStore{}
721 mfs.On("Mkdir", "/Files/foo/testFolder", fs.FileMode(0777)).Return(nil)
722 mfs.On("Stat", "/Files/foo/testFolder").Return(nil, os.ErrNotExist)
725 wantRes: []Transaction{
727 clientID: &[]byte{0, 1},
730 Type: []byte{0, 0xcd},
731 ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
732 ErrorCode: []byte{0, 0, 0, 0},
737 for _, tt := range tests {
738 t.Run(tt.name, func(t *testing.T) {
741 gotRes, err := HandleNewFolder(tt.args.cc, tt.args.t)
742 if (err != nil) != tt.wantErr {
743 t.Errorf("HandleNewFolder() error = %v, wantErr %v", err, tt.wantErr)
746 if !tranAssertEqual(t, tt.wantRes, gotRes) {
747 t.Errorf("HandleNewFolder() gotRes = %v, want %v", gotRes, tt.wantRes)
753 func TestHandleUploadFile(t *testing.T) {
761 wantRes []Transaction
765 name: "when request is valid",
769 FileTransfers: map[uint32]*FileTransfer{},
772 Access: func() *[]byte {
773 var bits accessBitmap
774 bits.Set(accessUploadFile)
781 tranUploadFile, &[]byte{0, 1},
782 NewField(fieldFileName, []byte("testFile")),
783 NewField(fieldFilePath, []byte{
791 wantRes: []Transaction{
795 Type: []byte{0, 0xcb},
796 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
797 ErrorCode: []byte{0, 0, 0, 0},
799 NewField(fieldRefNum, []byte{0x52, 0xfd, 0xfc, 0x07}), // rand.Seed(1)
806 name: "when user does not have required access",
810 Access: func() *[]byte {
811 var bits accessBitmap
817 FileTransfers: map[uint32]*FileTransfer{},
821 tranUploadFile, &[]byte{0, 1},
822 NewField(fieldFileName, []byte("testFile")),
823 NewField(fieldFilePath, []byte{
831 wantRes: []Transaction{
835 Type: []byte{0, 0x00},
836 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
837 ErrorCode: []byte{0, 0, 0, 1},
839 NewField(fieldError, []byte("You are not allowed to upload files.")), // rand.Seed(1)
846 for _, tt := range tests {
847 t.Run(tt.name, func(t *testing.T) {
849 gotRes, err := HandleUploadFile(tt.args.cc, tt.args.t)
850 if (err != nil) != tt.wantErr {
851 t.Errorf("HandleUploadFile() error = %v, wantErr %v", err, tt.wantErr)
854 if !tranAssertEqual(t, tt.wantRes, gotRes) {
855 t.Errorf("HandleUploadFile() gotRes = %v, want %v", gotRes, tt.wantRes)
861 func TestHandleMakeAlias(t *testing.T) {
870 wantRes []Transaction
874 name: "with valid input and required permissions",
876 mfs := MockFileStore{}
877 path, _ := os.Getwd()
880 path+"/test/config/Files/foo/testFile",
881 path+"/test/config/Files/bar/testFile",
888 Access: func() *[]byte {
889 var bits accessBitmap
890 bits.Set(accessMakeAlias)
897 FileRoot: func() string {
898 path, _ := os.Getwd()
899 return path + "/test/config/Files"
902 Logger: NewTestLogger(),
906 tranMakeFileAlias, &[]byte{0, 1},
907 NewField(fieldFileName, []byte("testFile")),
908 NewField(fieldFilePath, EncodeFilePath(strings.Join([]string{"foo"}, "/"))),
909 NewField(fieldFileNewPath, EncodeFilePath(strings.Join([]string{"bar"}, "/"))),
912 wantRes: []Transaction{
916 Type: []byte{0, 0xd1},
917 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
918 ErrorCode: []byte{0, 0, 0, 0},
919 Fields: []Field(nil),
925 name: "when symlink returns an error",
927 mfs := MockFileStore{}
928 path, _ := os.Getwd()
931 path+"/test/config/Files/foo/testFile",
932 path+"/test/config/Files/bar/testFile",
933 ).Return(errors.New("ohno"))
939 Access: func() *[]byte {
940 var bits accessBitmap
941 bits.Set(accessMakeAlias)
948 FileRoot: func() string {
949 path, _ := os.Getwd()
950 return path + "/test/config/Files"
953 Logger: NewTestLogger(),
957 tranMakeFileAlias, &[]byte{0, 1},
958 NewField(fieldFileName, []byte("testFile")),
959 NewField(fieldFilePath, EncodeFilePath(strings.Join([]string{"foo"}, "/"))),
960 NewField(fieldFileNewPath, EncodeFilePath(strings.Join([]string{"bar"}, "/"))),
963 wantRes: []Transaction{
967 Type: []byte{0, 0x00},
968 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
969 ErrorCode: []byte{0, 0, 0, 1},
971 NewField(fieldError, []byte("Error creating alias")),
978 name: "when user does not have required permission",
983 Access: func() *[]byte {
984 var bits accessBitmap
991 FileRoot: func() string {
992 path, _ := os.Getwd()
993 return path + "/test/config/Files"
999 tranMakeFileAlias, &[]byte{0, 1},
1000 NewField(fieldFileName, []byte("testFile")),
1001 NewField(fieldFilePath, []byte{
1007 NewField(fieldFileNewPath, []byte{
1015 wantRes: []Transaction{
1019 Type: []byte{0, 0x00},
1020 ID: []byte{0x9a, 0xcb, 0x04, 0x42},
1021 ErrorCode: []byte{0, 0, 0, 1},
1023 NewField(fieldError, []byte("You are not allowed to make aliases.")),
1030 for _, tt := range tests {
1031 t.Run(tt.name, func(t *testing.T) {
1034 gotRes, err := HandleMakeAlias(tt.args.cc, tt.args.t)
1035 if (err != nil) != tt.wantErr {
1036 t.Errorf("HandleMakeAlias(%v, %v)", tt.args.cc, tt.args.t)
1040 tranAssertEqual(t, tt.wantRes, gotRes)