diff options
| author | Jeff Halter <868228+jhalter@users.noreply.github.com> | 2024-07-17 15:41:20 -0700 |
|---|---|---|
| committer | Jeff Halter <868228+jhalter@users.noreply.github.com> | 2024-07-17 15:42:37 -0700 |
| commit | fd740bc499ebc6d3a381479316f74cdc736d02de (patch) | |
| tree | e77563ce0997e51debaec75c9f7a053759ca3a0a /hotline/transaction_handlers_test.go | |
| parent | 80aed6b19ff0b0927670e459ce5cc7a16ef047ec (diff) | |
Extensive refactor, quality of life enhancements
* Added ability to reload config, agreement, news, and user accounts without restarting the server by sending SIGHUP to the running process
* Added ability to use modern unix or windows line breaks in Agreement.txt and MessageBoard.txt instead of classic MacOS `\r` breaks.
* Extensive refactor towards swappable backends for the active server state
* Extensive refactored towards making the hotline package generic and re-usable for alternate server implemenations
* Fix bug where users whose accounts have been deleted would not be disconnected
Diffstat (limited to 'hotline/transaction_handlers_test.go')
| -rw-r--r-- | hotline/transaction_handlers_test.go | 3693 |
1 files changed, 0 insertions, 3693 deletions
diff --git a/hotline/transaction_handlers_test.go b/hotline/transaction_handlers_test.go deleted file mode 100644 index 610e113..0000000 --- a/hotline/transaction_handlers_test.go +++ /dev/null @@ -1,3693 +0,0 @@ -package hotline - -import ( - "errors" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "io" - "io/fs" - "os" - "path/filepath" - "strings" - "testing" - "time" -) - -func TestHandleSetChatSubject(t *testing.T) { - type args struct { - cc *ClientConn - t Transaction - } - tests := []struct { - name string - args args - want []Transaction - }{ - { - name: "sends chat subject to private chat members", - args: args{ - cc: &ClientConn{ - UserName: []byte{0x00, 0x01}, - Server: &Server{ - ChatMgr: func() *MockChatManager { - m := MockChatManager{} - m.On("Members", ChatID{0x0, 0x0, 0x0, 0x1}).Return([]*ClientConn{ - { - Account: &Account{ - Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255}, - }, - ID: [2]byte{0, 1}, - }, - { - Account: &Account{ - Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255}, - }, - ID: [2]byte{0, 2}, - }, - }) - m.On("SetSubject", ChatID{0x0, 0x0, 0x0, 0x1}, "Test Subject") - return &m - }(), - //PrivateChats: map[[4]byte]*PrivateChat{ - // [4]byte{0, 0, 0, 1}: { - // Subject: "unset", - // ClientConn: map[[2]byte]*ClientConn{ - // [2]byte{0, 1}: { - // Account: &Account{ - // Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255}, - // }, - // ID: [2]byte{0, 1}, - // }, - // [2]byte{0, 2}: { - // Account: &Account{ - // Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255}, - // }, - // ID: [2]byte{0, 2}, - // }, - // }, - // }, - //}, - ClientMgr: func() *MockClientMgr { - m := MockClientMgr{} - m.On("List").Return([]*ClientConn{ - { - Account: &Account{ - Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255}, - }, - ID: [2]byte{0, 1}, - }, - { - Account: &Account{ - Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255}, - }, - ID: [2]byte{0, 2}, - }, - }, - ) - return &m - }(), - }, - }, - t: Transaction{ - Type: [2]byte{0, 0x6a}, - ID: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldChatID, []byte{0, 0, 0, 1}), - NewField(FieldChatSubject, []byte("Test Subject")), - }, - }, - }, - want: []Transaction{ - { - clientID: [2]byte{0, 1}, - Type: [2]byte{0, 0x77}, - Fields: []Field{ - NewField(FieldChatID, []byte{0, 0, 0, 1}), - NewField(FieldChatSubject, []byte("Test Subject")), - }, - }, - { - clientID: [2]byte{0, 2}, - Type: [2]byte{0, 0x77}, - Fields: []Field{ - NewField(FieldChatID, []byte{0, 0, 0, 1}), - NewField(FieldChatSubject, []byte("Test Subject")), - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := HandleSetChatSubject(tt.args.cc, &tt.args.t) - if !tranAssertEqual(t, tt.want, got) { - t.Errorf("HandleSetChatSubject() got = %v, want %v", got, tt.want) - } - }) - } -} - -func TestHandleLeaveChat(t *testing.T) { - type args struct { - cc *ClientConn - t Transaction - } - tests := []struct { - name string - args args - want []Transaction - }{ - { - name: "when client 2 leaves chat", - args: args{ - cc: &ClientConn{ - ID: [2]byte{0, 2}, - Server: &Server{ - ChatMgr: func() *MockChatManager { - m := MockChatManager{} - m.On("Members", ChatID{0x0, 0x0, 0x0, 0x1}).Return([]*ClientConn{ - { - Account: &Account{ - Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255}, - }, - ID: [2]byte{0, 1}, - }, - }) - m.On("Leave", ChatID{0x0, 0x0, 0x0, 0x1}, [2]uint8{0x0, 0x2}) - m.On("GetSubject").Return("unset") - return &m - }(), - ClientMgr: func() *MockClientMgr { - m := MockClientMgr{} - m.On("Get").Return([]*ClientConn{ - { - Account: &Account{ - Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255}, - }, - ID: [2]byte{0, 1}, - }, - { - Account: &Account{ - Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255}, - }, - ID: [2]byte{0, 2}, - }, - }, - ) - return &m - }(), - }, - }, - t: NewTransaction(TranDeleteUser, [2]byte{}, NewField(FieldChatID, []byte{0, 0, 0, 1})), - }, - want: []Transaction{ - { - clientID: [2]byte{0, 1}, - Type: [2]byte{0, 0x76}, - Fields: []Field{ - NewField(FieldChatID, []byte{0, 0, 0, 1}), - NewField(FieldUserID, []byte{0, 2}), - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := HandleLeaveChat(tt.args.cc, &tt.args.t) - if !tranAssertEqual(t, tt.want, got) { - t.Errorf("HandleLeaveChat() got = %v, want %v", got, tt.want) - } - }) - } -} - -func TestHandleGetUserNameList(t *testing.T) { - type args struct { - cc *ClientConn - t Transaction - } - tests := []struct { - name string - args args - want []Transaction - }{ - { - name: "replies with userlist transaction", - args: args{ - cc: &ClientConn{ - ID: [2]byte{0, 1}, - Server: &Server{ - ClientMgr: func() *MockClientMgr { - m := MockClientMgr{} - m.On("List").Return([]*ClientConn{ - { - ID: [2]byte{0, 1}, - Icon: []byte{0, 2}, - Flags: [2]byte{0, 3}, - UserName: []byte{0, 4}, - }, - { - ID: [2]byte{0, 2}, - Icon: []byte{0, 2}, - Flags: [2]byte{0, 3}, - UserName: []byte{0, 4}, - }, - }, - ) - return &m - }(), - }, - }, - t: Transaction{}, - }, - want: []Transaction{ - { - clientID: [2]byte{0, 1}, - IsReply: 0x01, - Fields: []Field{ - NewField( - FieldUsernameWithInfo, - []byte{00, 01, 00, 02, 00, 03, 00, 02, 00, 04}, - ), - NewField( - FieldUsernameWithInfo, - []byte{00, 02, 00, 02, 00, 03, 00, 02, 00, 04}, - ), - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := HandleGetUserNameList(tt.args.cc, &tt.args.t) - assert.Equal(t, tt.want, got) - }) - } -} - -func TestHandleChatSend(t *testing.T) { - type args struct { - cc *ClientConn - t Transaction - } - tests := []struct { - name string - args args - want []Transaction - }{ - { - name: "sends chat msg transaction to all clients", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessSendChat) - return bits - }(), - }, - UserName: []byte{0x00, 0x01}, - Server: &Server{ - ClientMgr: func() *MockClientMgr { - m := MockClientMgr{} - m.On("List").Return([]*ClientConn{ - { - Account: &Account{ - Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255}, - }, - ID: [2]byte{0, 1}, - }, - { - Account: &Account{ - Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255}, - }, - ID: [2]byte{0, 2}, - }, - }, - ) - return &m - }(), - }, - }, - t: Transaction{ - Fields: []Field{ - NewField(FieldData, []byte("hai")), - }, - }, - }, - want: []Transaction{ - { - clientID: [2]byte{0, 1}, - Flags: 0x00, - IsReply: 0x00, - Type: [2]byte{0, 0x6a}, - Fields: []Field{ - NewField(FieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}), - }, - }, - { - clientID: [2]byte{0, 2}, - Flags: 0x00, - IsReply: 0x00, - Type: [2]byte{0, 0x6a}, - Fields: []Field{ - NewField(FieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}), - }, - }, - }, - }, - { - name: "treats Chat Type 00 00 00 00 as a public chat message", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessSendChat) - return bits - }(), - }, - UserName: []byte{0x00, 0x01}, - Server: &Server{ - ClientMgr: func() *MockClientMgr { - m := MockClientMgr{} - m.On("List").Return([]*ClientConn{ - { - Account: &Account{ - Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255}, - }, - ID: [2]byte{0, 1}, - }, - { - Account: &Account{ - Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255}, - }, - ID: [2]byte{0, 2}, - }, - }, - ) - return &m - }(), - }, - }, - t: Transaction{ - Fields: []Field{ - NewField(FieldData, []byte("hai")), - NewField(FieldChatID, []byte{0, 0, 0, 0}), - }, - }, - }, - want: []Transaction{ - { - clientID: [2]byte{0, 1}, - Type: [2]byte{0, 0x6a}, - Fields: []Field{ - NewField(FieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}), - }, - }, - { - clientID: [2]byte{0, 2}, - Type: [2]byte{0, 0x6a}, - Fields: []Field{ - NewField(FieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}), - }, - }, - }, - }, - { - name: "when user does not have required permission", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - return bits - }(), - }, - Server: &Server{ - //Accounts: map[string]*Account{}, - }, - }, - t: NewTransaction( - TranChatSend, [2]byte{0, 1}, - NewField(FieldData, []byte("hai")), - ), - }, - want: []Transaction{ - { - IsReply: 0x01, - ErrorCode: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldError, []byte("You are not allowed to participate in chat.")), - }, - }, - }, - }, - { - name: "sends chat msg as emote if FieldChatOptions is set to 1", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessSendChat) - return bits - }(), - }, - UserName: []byte("Testy McTest"), - Server: &Server{ - ClientMgr: func() *MockClientMgr { - m := MockClientMgr{} - m.On("List").Return([]*ClientConn{ - { - Account: &Account{ - Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255}, - }, - ID: [2]byte{0, 1}, - }, - { - Account: &Account{ - Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255}, - }, - ID: [2]byte{0, 2}, - }, - }, - ) - return &m - }(), - }, - }, - t: Transaction{ - Fields: []Field{ - NewField(FieldData, []byte("performed action")), - NewField(FieldChatOptions, []byte{0x00, 0x01}), - }, - }, - }, - want: []Transaction{ - { - clientID: [2]byte{0, 1}, - Flags: 0x00, - IsReply: 0x00, - Type: [2]byte{0, 0x6a}, - Fields: []Field{ - NewField(FieldData, []byte("\r*** Testy McTest performed action")), - }, - }, - { - clientID: [2]byte{0, 2}, - Flags: 0x00, - IsReply: 0x00, - Type: [2]byte{0, 0x6a}, - Fields: []Field{ - NewField(FieldData, []byte("\r*** Testy McTest performed action")), - }, - }, - }, - }, - { - name: "does not send chat msg as emote if FieldChatOptions is set to 0", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessSendChat) - return bits - }(), - }, - UserName: []byte("Testy McTest"), - Server: &Server{ - ClientMgr: func() *MockClientMgr { - m := MockClientMgr{} - m.On("List").Return([]*ClientConn{ - { - Account: &Account{ - Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255}, - }, - ID: [2]byte{0, 1}, - }, - { - Account: &Account{ - Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255}, - }, - ID: [2]byte{0, 2}, - }, - }, - ) - return &m - }(), - }, - }, - t: Transaction{ - Fields: []Field{ - NewField(FieldData, []byte("hello")), - NewField(FieldChatOptions, []byte{0x00, 0x00}), - }, - }, - }, - want: []Transaction{ - { - clientID: [2]byte{0, 1}, - Type: [2]byte{0, 0x6a}, - Fields: []Field{ - NewField(FieldData, []byte("\r Testy McTest: hello")), - }, - }, - { - clientID: [2]byte{0, 2}, - Type: [2]byte{0, 0x6a}, - Fields: []Field{ - NewField(FieldData, []byte("\r Testy McTest: hello")), - }, - }, - }, - }, - { - name: "only sends chat msg to clients with AccessReadChat permission", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessSendChat) - return bits - }(), - }, - UserName: []byte{0x00, 0x01}, - Server: &Server{ - ClientMgr: func() *MockClientMgr { - m := MockClientMgr{} - m.On("List").Return([]*ClientConn{ - { - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessReadChat) - return bits - }(), - }, - ID: [2]byte{0, 1}, - }, - { - Account: &Account{}, - ID: [2]byte{0, 2}, - }, - }, - ) - return &m - }(), - }, - }, - t: Transaction{ - Fields: []Field{ - NewField(FieldData, []byte("hai")), - }, - }, - }, - want: []Transaction{ - { - clientID: [2]byte{0, 1}, - Type: [2]byte{0, 0x6a}, - Fields: []Field{ - NewField(FieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}), - }, - }, - }, - }, - { - name: "only sends private chat msg to members of private chat", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessSendChat) - return bits - }(), - }, - UserName: []byte{0x00, 0x01}, - Server: &Server{ - ChatMgr: func() *MockChatManager { - m := MockChatManager{} - m.On("Members", ChatID{0x0, 0x0, 0x0, 0x1}).Return([]*ClientConn{ - { - ID: [2]byte{0, 1}, - }, - { - ID: [2]byte{0, 2}, - }, - }) - m.On("GetSubject").Return("unset") - return &m - }(), - ClientMgr: func() *MockClientMgr { - m := MockClientMgr{} - m.On("List").Return([]*ClientConn{ - { - Account: &Account{ - Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255}, - }, - ID: [2]byte{0, 1}, - }, - { - Account: &Account{ - Access: accessBitmap{0, 0, 0, 0, 0, 0, 0, 0}, - }, - ID: [2]byte{0, 2}, - }, - { - Account: &Account{ - Access: accessBitmap{0, 0, 0, 0, 0, 0, 0, 0}, - }, - ID: [2]byte{0, 3}, - }, - }, - ) - return &m - }(), - }, - }, - t: Transaction{ - Fields: []Field{ - NewField(FieldData, []byte("hai")), - NewField(FieldChatID, []byte{0, 0, 0, 1}), - }, - }, - }, - want: []Transaction{ - { - clientID: [2]byte{0, 1}, - Type: [2]byte{0, 0x6a}, - Fields: []Field{ - NewField(FieldChatID, []byte{0, 0, 0, 1}), - NewField(FieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}), - }, - }, - { - clientID: [2]byte{0, 2}, - Type: [2]byte{0, 0x6a}, - Fields: []Field{ - NewField(FieldChatID, []byte{0, 0, 0, 1}), - NewField(FieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}), - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := HandleChatSend(tt.args.cc, &tt.args.t) - tranAssertEqual(t, tt.want, got) - }) - } -} - -func TestHandleGetFileInfo(t *testing.T) { - type args struct { - cc *ClientConn - t Transaction - } - tests := []struct { - name string - args args - wantRes []Transaction - }{ - { - name: "returns expected fields when a valid file is requested", - args: args{ - cc: &ClientConn{ - ID: [2]byte{0x00, 0x01}, - Server: &Server{ - FS: &OSFileStore{}, - Config: Config{ - FileRoot: func() string { - path, _ := os.Getwd() - return filepath.Join(path, "/test/config/Files") - }(), - }, - }, - }, - t: NewTransaction( - TranGetFileInfo, [2]byte{}, - NewField(FieldFileName, []byte("testfile.txt")), - NewField(FieldFilePath, []byte{0x00, 0x00}), - ), - }, - wantRes: []Transaction{ - { - clientID: [2]byte{0, 1}, - IsReply: 0x01, - Type: [2]byte{0, 0}, - Fields: []Field{ - NewField(FieldFileName, []byte("testfile.txt")), - NewField(FieldFileTypeString, []byte("Text File")), - NewField(FieldFileCreatorString, []byte("ttxt")), - NewField(FieldFileType, []byte("TEXT")), - NewField(FieldFileCreateDate, make([]byte, 8)), - NewField(FieldFileModifyDate, make([]byte, 8)), - NewField(FieldFileSize, []byte{0x0, 0x0, 0x0, 0x17}), - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotRes := HandleGetFileInfo(tt.args.cc, &tt.args.t) - - // Clear the file timestamp fields to work around problems running the tests in multiple timezones - // TODO: revisit how to test this by mocking the stat calls - gotRes[0].Fields[4].Data = make([]byte, 8) - gotRes[0].Fields[5].Data = make([]byte, 8) - - if !tranAssertEqual(t, tt.wantRes, gotRes) { - t.Errorf("HandleGetFileInfo() gotRes = %v, want %v", gotRes, tt.wantRes) - } - }) - } -} - -func TestHandleNewFolder(t *testing.T) { - type args struct { - cc *ClientConn - t Transaction - } - tests := []struct { - name string - args args - wantRes []Transaction - }{ - { - name: "without required permission", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - return bits - }(), - }, - }, - t: NewTransaction( - TranNewFolder, - [2]byte{0, 0}, - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - ErrorCode: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldError, []byte("You are not allowed to create folders.")), - }, - }, - }, - }, - { - name: "when path is nested", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessCreateFolder) - return bits - }(), - }, - ID: [2]byte{0, 1}, - Server: &Server{ - Config: Config{ - FileRoot: "/Files/", - }, - FS: func() *MockFileStore { - mfs := &MockFileStore{} - mfs.On("Mkdir", "/Files/aaa/testFolder", fs.FileMode(0777)).Return(nil) - mfs.On("Stat", "/Files/aaa/testFolder").Return(nil, os.ErrNotExist) - return mfs - }(), - }, - }, - t: NewTransaction( - TranNewFolder, [2]byte{0, 1}, - NewField(FieldFileName, []byte("testFolder")), - NewField(FieldFilePath, []byte{ - 0x00, 0x01, - 0x00, 0x00, - 0x03, - 0x61, 0x61, 0x61, - }), - ), - }, - wantRes: []Transaction{ - { - clientID: [2]byte{0, 1}, - IsReply: 0x01, - }, - }, - }, - { - name: "when path is not nested", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessCreateFolder) - return bits - }(), - }, - ID: [2]byte{0, 1}, - Server: &Server{ - Config: Config{ - FileRoot: "/Files", - }, - FS: func() *MockFileStore { - mfs := &MockFileStore{} - mfs.On("Mkdir", "/Files/testFolder", fs.FileMode(0777)).Return(nil) - mfs.On("Stat", "/Files/testFolder").Return(nil, os.ErrNotExist) - return mfs - }(), - }, - }, - t: NewTransaction( - TranNewFolder, [2]byte{0, 1}, - NewField(FieldFileName, []byte("testFolder")), - ), - }, - wantRes: []Transaction{ - { - clientID: [2]byte{0, 1}, - IsReply: 0x01, - }, - }, - }, - { - name: "when Write returns an err", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessCreateFolder) - return bits - }(), - }, - ID: [2]byte{0, 1}, - Server: &Server{ - Config: Config{ - FileRoot: "/Files/", - }, - FS: func() *MockFileStore { - mfs := &MockFileStore{} - mfs.On("Mkdir", "/Files/aaa/testFolder", fs.FileMode(0777)).Return(nil) - mfs.On("Stat", "/Files/aaa/testFolder").Return(nil, os.ErrNotExist) - return mfs - }(), - }, - }, - t: NewTransaction( - TranNewFolder, [2]byte{0, 1}, - NewField(FieldFileName, []byte("testFolder")), - NewField(FieldFilePath, []byte{ - 0x00, - }), - ), - }, - wantRes: []Transaction{}, - }, - { - name: "FieldFileName does not allow directory traversal", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessCreateFolder) - return bits - }(), - }, - ID: [2]byte{0, 1}, - Server: &Server{ - Config: Config{ - FileRoot: "/Files/", - }, - FS: func() *MockFileStore { - mfs := &MockFileStore{} - mfs.On("Mkdir", "/Files/testFolder", fs.FileMode(0777)).Return(nil) - mfs.On("Stat", "/Files/testFolder").Return(nil, os.ErrNotExist) - return mfs - }(), - }, - }, - t: NewTransaction( - TranNewFolder, [2]byte{0, 1}, - NewField(FieldFileName, []byte("../../testFolder")), - ), - }, - wantRes: []Transaction{ - { - clientID: [2]byte{0, 1}, - IsReply: 0x01, - }, - }, - }, - { - name: "FieldFilePath does not allow directory traversal", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessCreateFolder) - return bits - }(), - }, - ID: [2]byte{0, 1}, - Server: &Server{ - Config: Config{ - FileRoot: "/Files/", - }, - FS: func() *MockFileStore { - mfs := &MockFileStore{} - mfs.On("Mkdir", "/Files/foo/testFolder", fs.FileMode(0777)).Return(nil) - mfs.On("Stat", "/Files/foo/testFolder").Return(nil, os.ErrNotExist) - return mfs - }(), - }, - }, - t: NewTransaction( - TranNewFolder, [2]byte{0, 1}, - NewField(FieldFileName, []byte("testFolder")), - NewField(FieldFilePath, []byte{ - 0x00, 0x02, - 0x00, 0x00, - 0x03, - 0x2e, 0x2e, 0x2f, - 0x00, 0x00, - 0x03, - 0x66, 0x6f, 0x6f, - }), - ), - }, - wantRes: []Transaction{ - { - clientID: [2]byte{0, 1}, - IsReply: 0x01, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotRes := HandleNewFolder(tt.args.cc, &tt.args.t) - - if !tranAssertEqual(t, tt.wantRes, gotRes) { - t.Errorf("HandleNewFolder() gotRes = %v, want %v", gotRes, tt.wantRes) - } - }) - } -} - -func TestHandleUploadFile(t *testing.T) { - type args struct { - cc *ClientConn - t Transaction - } - tests := []struct { - name string - args args - wantRes []Transaction - }{ - { - name: "when request is valid and user has Upload Anywhere permission", - args: args{ - cc: &ClientConn{ - Server: &Server{ - FS: &OSFileStore{}, - FileTransferMgr: NewMemFileTransferMgr(), - Config: Config{ - FileRoot: func() string { path, _ := os.Getwd(); return path + "/test/config/Files" }(), - }}, - ClientFileTransferMgr: NewClientFileTransferMgr(), - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessUploadFile) - bits.Set(AccessUploadAnywhere) - return bits - }(), - }, - }, - t: NewTransaction( - TranUploadFile, [2]byte{0, 1}, - NewField(FieldFileName, []byte("testFile")), - NewField(FieldFilePath, []byte{ - 0x00, 0x01, - 0x00, 0x00, - 0x03, - 0x2e, 0x2e, 0x2f, - }), - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - Fields: []Field{ - NewField(FieldRefNum, []byte{0x52, 0xfd, 0xfc, 0x07}), // rand.Seed(1) - }, - }, - }, - }, - { - name: "when user does not have required access", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - return bits - }(), - }, - }, - t: NewTransaction( - TranUploadFile, [2]byte{0, 1}, - NewField(FieldFileName, []byte("testFile")), - NewField(FieldFilePath, []byte{ - 0x00, 0x01, - 0x00, 0x00, - 0x03, - 0x2e, 0x2e, 0x2f, - }), - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - ErrorCode: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldError, []byte("You are not allowed to upload files.")), // rand.Seed(1) - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotRes := HandleUploadFile(tt.args.cc, &tt.args.t) - tranAssertEqual(t, tt.wantRes, gotRes) - }) - } -} - -func TestHandleMakeAlias(t *testing.T) { - type args struct { - cc *ClientConn - t Transaction - } - tests := []struct { - name string - args args - wantRes []Transaction - }{ - { - name: "with valid input and required permissions", - args: args{ - cc: &ClientConn{ - logger: NewTestLogger(), - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessMakeAlias) - return bits - }(), - }, - Server: &Server{ - Config: Config{ - FileRoot: func() string { - path, _ := os.Getwd() - return path + "/test/config/Files" - }(), - }, - Logger: NewTestLogger(), - FS: func() *MockFileStore { - mfs := &MockFileStore{} - path, _ := os.Getwd() - mfs.On( - "Symlink", - path+"/test/config/Files/foo/testFile", - path+"/test/config/Files/bar/testFile", - ).Return(nil) - return mfs - }(), - }, - }, - t: NewTransaction( - TranMakeFileAlias, [2]byte{0, 1}, - NewField(FieldFileName, []byte("testFile")), - NewField(FieldFilePath, EncodeFilePath(strings.Join([]string{"foo"}, "/"))), - NewField(FieldFileNewPath, EncodeFilePath(strings.Join([]string{"bar"}, "/"))), - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - Fields: []Field(nil), - }, - }, - }, - { - name: "when symlink returns an error", - args: args{ - cc: &ClientConn{ - logger: NewTestLogger(), - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessMakeAlias) - return bits - }(), - }, - Server: &Server{ - Config: Config{ - FileRoot: func() string { - path, _ := os.Getwd() - return path + "/test/config/Files" - }(), - }, - Logger: NewTestLogger(), - FS: func() *MockFileStore { - mfs := &MockFileStore{} - path, _ := os.Getwd() - mfs.On( - "Symlink", - path+"/test/config/Files/foo/testFile", - path+"/test/config/Files/bar/testFile", - ).Return(errors.New("ohno")) - return mfs - }(), - }, - }, - t: NewTransaction( - TranMakeFileAlias, [2]byte{0, 1}, - NewField(FieldFileName, []byte("testFile")), - NewField(FieldFilePath, EncodeFilePath(strings.Join([]string{"foo"}, "/"))), - NewField(FieldFileNewPath, EncodeFilePath(strings.Join([]string{"bar"}, "/"))), - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - ErrorCode: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldError, []byte("Error creating alias")), - }, - }, - }, - }, - { - name: "when user does not have required permission", - args: args{ - cc: &ClientConn{ - logger: NewTestLogger(), - Account: &Account{ - Access: accessBitmap{}, - }, - Server: &Server{ - Config: Config{ - FileRoot: func() string { - path, _ := os.Getwd() - return path + "/test/config/Files" - }(), - }, - }, - }, - t: NewTransaction( - TranMakeFileAlias, [2]byte{0, 1}, - NewField(FieldFileName, []byte("testFile")), - NewField(FieldFilePath, []byte{ - 0x00, 0x01, - 0x00, 0x00, - 0x03, - 0x2e, 0x2e, 0x2e, - }), - NewField(FieldFileNewPath, []byte{ - 0x00, 0x01, - 0x00, 0x00, - 0x03, - 0x2e, 0x2e, 0x2e, - }), - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - ErrorCode: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldError, []byte("You are not allowed to make aliases.")), - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotRes := HandleMakeAlias(tt.args.cc, &tt.args.t) - tranAssertEqual(t, tt.wantRes, gotRes) - }) - } -} - -func TestHandleGetUser(t *testing.T) { - type args struct { - cc *ClientConn - t Transaction - } - tests := []struct { - name string - args args - wantRes []Transaction - }{ - { - name: "when account is valid", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessOpenUser) - return bits - }(), - }, - Server: &Server{ - AccountManager: func() *MockAccountManager { - m := MockAccountManager{} - m.On("Get", "guest").Return(&Account{ - Login: "guest", - Name: "Guest", - Password: "password", - Access: accessBitmap{}, - }) - return &m - }(), - }, - }, - t: NewTransaction( - TranGetUser, [2]byte{0, 1}, - NewField(FieldUserLogin, []byte("guest")), - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - Fields: []Field{ - NewField(FieldUserName, []byte("Guest")), - NewField(FieldUserLogin, encodeString([]byte("guest"))), - NewField(FieldUserPassword, []byte("password")), - NewField(FieldUserAccess, []byte{0, 0, 0, 0, 0, 0, 0, 0}), - }, - }, - }, - }, - { - name: "when user does not have required permission", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - return bits - }(), - }, - Server: &Server{ - //Accounts: map[string]*Account{}, - }, - }, - t: NewTransaction( - TranGetUser, [2]byte{0, 1}, - NewField(FieldUserLogin, []byte("nonExistentUser")), - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - ErrorCode: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldError, []byte("You are not allowed to view accounts.")), - }, - }, - }, - }, - { - name: "when account does not exist", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessOpenUser) - return bits - }(), - }, - Server: &Server{ - AccountManager: func() *MockAccountManager { - m := MockAccountManager{} - m.On("Get", "nonExistentUser").Return((*Account)(nil)) - return &m - }(), - }, - }, - t: NewTransaction( - TranGetUser, [2]byte{0, 1}, - NewField(FieldUserLogin, []byte("nonExistentUser")), - ), - }, - wantRes: []Transaction{ - { - Flags: 0x00, - IsReply: 0x01, - Type: [2]byte{0, 0}, - ErrorCode: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldError, []byte("Account does not exist.")), - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotRes := HandleGetUser(tt.args.cc, &tt.args.t) - tranAssertEqual(t, tt.wantRes, gotRes) - }) - } -} - -func TestHandleDeleteUser(t *testing.T) { - type args struct { - cc *ClientConn - t Transaction - } - tests := []struct { - name string - args args - wantRes []Transaction - }{ - { - name: "when user exists", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessDeleteUser) - return bits - }(), - }, - Server: &Server{ - AccountManager: func() *MockAccountManager { - m := MockAccountManager{} - m.On("Delete", "testuser").Return(nil) - return &m - }(), - ClientMgr: func() *MockClientMgr { - m := MockClientMgr{} - m.On("List").Return([]*ClientConn{}) // TODO - return &m - }(), - }, - }, - t: NewTransaction( - TranDeleteUser, [2]byte{0, 1}, - NewField(FieldUserLogin, encodeString([]byte("testuser"))), - ), - }, - wantRes: []Transaction{ - { - Flags: 0x00, - IsReply: 0x01, - Type: [2]byte{0, 0}, - Fields: []Field(nil), - }, - }, - }, - { - name: "when user does not have required permission", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: accessBitmap{}, - }, - Server: &Server{ - //Accounts: map[string]*Account{}, - }, - }, - t: NewTransaction( - TranDeleteUser, [2]byte{0, 1}, - NewField(FieldUserLogin, encodeString([]byte("testuser"))), - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - ErrorCode: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldError, []byte("You are not allowed to delete accounts.")), - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotRes := HandleDeleteUser(tt.args.cc, &tt.args.t) - tranAssertEqual(t, tt.wantRes, gotRes) - }) - } -} - -func TestHandleGetMsgs(t *testing.T) { - type args struct { - cc *ClientConn - t Transaction - } - tests := []struct { - name string - args args - wantRes []Transaction - }{ - { - name: "returns news data", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessNewsReadArt) - return bits - }(), - }, - Server: &Server{ - MessageBoard: func() *mockReadWriteSeeker { - m := mockReadWriteSeeker{} - m.On("Seek", int64(0), 0).Return(int64(0), nil) - m.On("Read", mock.AnythingOfType("[]uint8")).Run(func(args mock.Arguments) { - arg := args.Get(0).([]uint8) - copy(arg, "TEST") - }).Return(4, io.EOF) - return &m - }(), - }, - }, - t: NewTransaction( - TranGetMsgs, [2]byte{0, 1}, - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - Fields: []Field{ - NewField(FieldData, []byte("TEST")), - }, - }, - }, - }, - { - name: "when user does not have required permission", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: accessBitmap{}, - }, - Server: &Server{ - //Accounts: map[string]*Account{}, - }, - }, - t: NewTransaction( - TranGetMsgs, [2]byte{0, 1}, - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - ErrorCode: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldError, []byte("You are not allowed to read news.")), - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotRes := HandleGetMsgs(tt.args.cc, &tt.args.t) - tranAssertEqual(t, tt.wantRes, gotRes) - }) - } -} - -func TestHandleNewUser(t *testing.T) { - type args struct { - cc *ClientConn - t Transaction - } - tests := []struct { - name string - args args - wantRes []Transaction - }{ - { - name: "when user does not have required permission", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - return bits - }(), - }, - Server: &Server{ - //Accounts: map[string]*Account{}, - }, - }, - t: NewTransaction( - TranNewUser, [2]byte{0, 1}, - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - ErrorCode: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldError, []byte("You are not allowed to create new accounts.")), - }, - }, - }, - }, - { - name: "when user attempts to create account with greater access", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessCreateUser) - return bits - }(), - }, - Server: &Server{ - AccountManager: func() *MockAccountManager { - m := MockAccountManager{} - m.On("Get", "userB").Return((*Account)(nil)) - return &m - }(), - }, - }, - t: NewTransaction( - TranNewUser, [2]byte{0, 1}, - NewField(FieldUserLogin, encodeString([]byte("userB"))), - NewField( - FieldUserAccess, - func() []byte { - var bits accessBitmap - bits.Set(AccessDisconUser) - return bits[:] - }(), - ), - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - ErrorCode: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldError, []byte("Cannot create account with more access than yourself.")), - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotRes := HandleNewUser(tt.args.cc, &tt.args.t) - tranAssertEqual(t, tt.wantRes, gotRes) - }) - } -} - -func TestHandleListUsers(t *testing.T) { - type args struct { - cc *ClientConn - t Transaction - } - tests := []struct { - name string - args args - wantRes []Transaction - }{ - { - name: "when user does not have required permission", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - return bits - }(), - }, - Server: &Server{ - //Accounts: map[string]*Account{}, - }, - }, - t: NewTransaction( - TranNewUser, [2]byte{0, 1}, - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - ErrorCode: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldError, []byte("You are not allowed to view accounts.")), - }, - }, - }, - }, - { - name: "when user has required permission", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessOpenUser) - return bits - }(), - }, - Server: &Server{ - AccountManager: func() *MockAccountManager { - m := MockAccountManager{} - m.On("List").Return([]Account{ - { - Name: "guest", - Login: "guest", - Password: "zz", - Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255}, - }, - }) - return &m - }(), - }, - }, - t: NewTransaction( - TranGetClientInfoText, [2]byte{0, 1}, - NewField(FieldUserID, []byte{0, 1}), - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - Fields: []Field{ - NewField(FieldData, []byte{ - 0x00, 0x04, 0x00, 0x66, 0x00, 0x05, 0x67, 0x75, 0x65, 0x73, 0x74, 0x00, 0x69, 0x00, 0x05, 0x98, - 0x8a, 0x9a, 0x8c, 0x8b, 0x00, 0x6e, 0x00, 0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x6a, 0x00, 0x01, 0x78, - }), - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotRes := HandleListUsers(tt.args.cc, &tt.args.t) - - tranAssertEqual(t, tt.wantRes, gotRes) - }) - } -} - -func TestHandleDownloadFile(t *testing.T) { - type args struct { - cc *ClientConn - t Transaction - } - tests := []struct { - name string - args args - wantRes []Transaction - }{ - { - name: "when user does not have required permission", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - return bits - }(), - }, - Server: &Server{}, - }, - t: NewTransaction(TranDownloadFile, [2]byte{0, 1}), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - ErrorCode: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldError, []byte("You are not allowed to download files.")), - }, - }, - }, - }, - { - name: "with a valid file", - args: args{ - cc: &ClientConn{ - ClientFileTransferMgr: NewClientFileTransferMgr(), - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessDownloadFile) - return bits - }(), - }, - Server: &Server{ - FS: &OSFileStore{}, - FileTransferMgr: NewMemFileTransferMgr(), - Config: Config{ - FileRoot: func() string { path, _ := os.Getwd(); return path + "/test/config/Files" }(), - }, - }, - }, - t: NewTransaction( - TranDownloadFile, - [2]byte{0, 1}, - NewField(FieldFileName, []byte("testfile.txt")), - NewField(FieldFilePath, []byte{0x0, 0x00}), - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - Fields: []Field{ - NewField(FieldRefNum, []byte{0x52, 0xfd, 0xfc, 0x07}), - NewField(FieldWaitingCount, []byte{0x00, 0x00}), - NewField(FieldTransferSize, []byte{0x00, 0x00, 0x00, 0xa5}), - NewField(FieldFileSize, []byte{0x00, 0x00, 0x00, 0x17}), - }, - }, - }, - }, - { - name: "when client requests to resume 1k test file at offset 256", - args: args{ - cc: &ClientConn{ - ClientFileTransferMgr: NewClientFileTransferMgr(), - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessDownloadFile) - return bits - }(), - }, - Server: &Server{ - FS: &OSFileStore{}, - - // FS: func() *MockFileStore { - // path, _ := os.Getwd() - // testFile, err := os.Open(path + "/test/config/Files/testfile-1k") - // if err != nil { - // panic(err) - // } - // - // mfi := &MockFileInfo{} - // mfi.On("Mode").Return(fs.FileMode(0)) - // mfs := &MockFileStore{} - // mfs.On("Stat", "/fakeRoot/Files/testfile.txt").Return(mfi, nil) - // mfs.On("Open", "/fakeRoot/Files/testfile.txt").Return(testFile, nil) - // mfs.On("Stat", "/fakeRoot/Files/.info_testfile.txt").Return(nil, errors.New("no")) - // mfs.On("Stat", "/fakeRoot/Files/.rsrc_testfile.txt").Return(nil, errors.New("no")) - // - // return mfs - // }(), - FileTransferMgr: NewMemFileTransferMgr(), - Config: Config{ - FileRoot: func() string { path, _ := os.Getwd(); return path + "/test/config/Files" }(), - }, - //Accounts: map[string]*Account{}, - }, - }, - t: NewTransaction( - TranDownloadFile, - [2]byte{0, 1}, - NewField(FieldFileName, []byte("testfile-1k")), - NewField(FieldFilePath, []byte{0x00, 0x00}), - NewField( - FieldFileResumeData, - func() []byte { - frd := FileResumeData{ - ForkCount: [2]byte{0, 2}, - ForkInfoList: []ForkInfoList{ - { - Fork: [4]byte{0x44, 0x41, 0x54, 0x41}, // "DATA" - DataSize: [4]byte{0, 0, 0x01, 0x00}, // request offset 256 - }, - { - Fork: [4]byte{0x4d, 0x41, 0x43, 0x52}, // "MACR" - DataSize: [4]byte{0, 0, 0, 0}, - }, - }, - } - b, _ := frd.BinaryMarshal() - return b - }(), - ), - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - Fields: []Field{ - NewField(FieldRefNum, []byte{0x52, 0xfd, 0xfc, 0x07}), - NewField(FieldWaitingCount, []byte{0x00, 0x00}), - NewField(FieldTransferSize, []byte{0x00, 0x00, 0x03, 0x8d}), - NewField(FieldFileSize, []byte{0x00, 0x00, 0x03, 0x00}), - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotRes := HandleDownloadFile(tt.args.cc, &tt.args.t) - tranAssertEqual(t, tt.wantRes, gotRes) - }) - } -} - -func TestHandleUpdateUser(t *testing.T) { - type args struct { - cc *ClientConn - t Transaction - } - tests := []struct { - name string - args args - wantRes []Transaction - }{ - { - name: "when action is create user without required permission", - args: args{ - cc: &ClientConn{ - logger: NewTestLogger(), - Server: &Server{ - AccountManager: func() *MockAccountManager { - m := MockAccountManager{} - m.On("Get", "bbb").Return((*Account)(nil)) - return &m - }(), - Logger: NewTestLogger(), - }, - Account: &Account{ - Access: accessBitmap{}, - }, - }, - t: NewTransaction( - TranUpdateUser, - [2]byte{0, 0}, - NewField(FieldData, []byte{ - 0x00, 0x04, // field count - - 0x00, 0x69, // FieldUserLogin = 105 - 0x00, 0x03, - 0x9d, 0x9d, 0x9d, - - 0x00, 0x6a, // FieldUserPassword = 106 - 0x00, 0x03, - 0x9c, 0x9c, 0x9c, - - 0x00, 0x66, // FieldUserName = 102 - 0x00, 0x03, - 0x61, 0x61, 0x61, - - 0x00, 0x6e, // FieldUserAccess = 110 - 0x00, 0x08, - 0x60, 0x70, 0x0c, 0x20, 0x03, 0x80, 0x00, 0x00, - }), - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - ErrorCode: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldError, []byte("You are not allowed to create new accounts.")), - }, - }, - }, - }, - { - name: "when action is modify user without required permission", - args: args{ - cc: &ClientConn{ - logger: NewTestLogger(), - Server: &Server{ - Logger: NewTestLogger(), - AccountManager: func() *MockAccountManager { - m := MockAccountManager{} - m.On("Get", "bbb").Return(&Account{}) - return &m - }(), - }, - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - return bits - }(), - }, - }, - t: NewTransaction( - TranUpdateUser, - [2]byte{0, 0}, - NewField(FieldData, []byte{ - 0x00, 0x04, // field count - - 0x00, 0x69, // FieldUserLogin = 105 - 0x00, 0x03, - 0x9d, 0x9d, 0x9d, - - 0x00, 0x6a, // FieldUserPassword = 106 - 0x00, 0x03, - 0x9c, 0x9c, 0x9c, - - 0x00, 0x66, // FieldUserName = 102 - 0x00, 0x03, - 0x61, 0x61, 0x61, - - 0x00, 0x6e, // FieldUserAccess = 110 - 0x00, 0x08, - 0x60, 0x70, 0x0c, 0x20, 0x03, 0x80, 0x00, 0x00, - }), - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - ErrorCode: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldError, []byte("You are not allowed to modify accounts.")), - }, - }, - }, - }, - { - name: "when action is delete user without required permission", - args: args{ - cc: &ClientConn{ - logger: NewTestLogger(), - Server: &Server{}, - Account: &Account{ - Access: accessBitmap{}, - }, - }, - t: NewTransaction( - TranUpdateUser, - [2]byte{0, 0}, - NewField(FieldData, []byte{ - 0x00, 0x01, - 0x00, 0x65, - 0x00, 0x03, - 0x88, 0x9e, 0x8b, - }), - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - ErrorCode: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldError, []byte("You are not allowed to delete accounts.")), - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotRes := HandleUpdateUser(tt.args.cc, &tt.args.t) - tranAssertEqual(t, tt.wantRes, gotRes) - }) - } -} - -func TestHandleDelNewsArt(t *testing.T) { - type args struct { - cc *ClientConn - t Transaction - } - tests := []struct { - name string - args args - wantRes []Transaction - }{ - { - name: "without required permission", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - return bits - }(), - }, - }, - t: NewTransaction( - TranDelNewsArt, - [2]byte{0, 0}, - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - ErrorCode: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldError, []byte("You are not allowed to delete news articles.")), - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotRes := HandleDelNewsArt(tt.args.cc, &tt.args.t) - tranAssertEqual(t, tt.wantRes, gotRes) - }) - } -} - -func TestHandleDisconnectUser(t *testing.T) { - type args struct { - cc *ClientConn - t Transaction - } - tests := []struct { - name string - args args - wantRes []Transaction - }{ - { - name: "without required permission", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - return bits - }(), - }, - }, - t: NewTransaction( - TranDelNewsArt, - [2]byte{0, 0}, - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - ErrorCode: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldError, []byte("You are not allowed to disconnect users.")), - }, - }, - }, - }, - { - name: "when target user has 'cannot be disconnected' priv", - args: args{ - cc: &ClientConn{ - Server: &Server{ - ClientMgr: func() *MockClientMgr { - m := MockClientMgr{} - m.On("Get", ClientID{0x0, 0x1}).Return(&ClientConn{ - Account: &Account{ - Login: "unnamed", - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessCannotBeDiscon) - return bits - }(), - }, - }, - ) - return &m - }(), - }, - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessDisconUser) - return bits - }(), - }, - }, - t: NewTransaction( - TranDelNewsArt, - [2]byte{0, 0}, - NewField(FieldUserID, []byte{0, 1}), - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - ErrorCode: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldError, []byte("unnamed is not allowed to be disconnected.")), - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotRes := HandleDisconnectUser(tt.args.cc, &tt.args.t) - tranAssertEqual(t, tt.wantRes, gotRes) - }) - } -} - -func TestHandleSendInstantMsg(t *testing.T) { - type args struct { - cc *ClientConn - t Transaction - } - tests := []struct { - name string - args args - wantRes []Transaction - }{ - { - name: "without required permission", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - return bits - }(), - }, - }, - t: NewTransaction( - TranDelNewsArt, - [2]byte{0, 0}, - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - ErrorCode: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldError, []byte("You are not allowed to send private messages.")), - }, - }, - }, - }, - { - name: "when client 1 sends a message to client 2", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessSendPrivMsg) - return bits - }(), - }, - ID: [2]byte{0, 1}, - UserName: []byte("User1"), - Server: &Server{ - ClientMgr: func() *MockClientMgr { - m := MockClientMgr{} - m.On("Get", ClientID{0x0, 0x2}).Return(&ClientConn{ - AutoReply: []byte(nil), - Flags: [2]byte{0, 0}, - }, - ) - return &m - }(), - }, - }, - t: NewTransaction( - TranSendInstantMsg, - [2]byte{0, 1}, - NewField(FieldData, []byte("hai")), - NewField(FieldUserID, []byte{0, 2}), - ), - }, - wantRes: []Transaction{ - NewTransaction( - TranServerMsg, - [2]byte{0, 2}, - NewField(FieldData, []byte("hai")), - NewField(FieldUserName, []byte("User1")), - NewField(FieldUserID, []byte{0, 1}), - NewField(FieldOptions, []byte{0, 1}), - ), - { - clientID: [2]byte{0, 1}, - IsReply: 0x01, - Fields: []Field(nil), - }, - }, - }, - { - name: "when client 2 has autoreply enabled", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessSendPrivMsg) - return bits - }(), - }, - ID: [2]byte{0, 1}, - UserName: []byte("User1"), - Server: &Server{ - ClientMgr: func() *MockClientMgr { - m := MockClientMgr{} - m.On("Get", ClientID{0x0, 0x2}).Return(&ClientConn{ - Flags: [2]byte{0, 0}, - ID: [2]byte{0, 2}, - UserName: []byte("User2"), - AutoReply: []byte("autohai"), - }) - return &m - }(), - }, - }, - t: NewTransaction( - TranSendInstantMsg, - [2]byte{0, 1}, - NewField(FieldData, []byte("hai")), - NewField(FieldUserID, []byte{0, 2}), - ), - }, - wantRes: []Transaction{ - NewTransaction( - TranServerMsg, - [2]byte{0, 2}, - NewField(FieldData, []byte("hai")), - NewField(FieldUserName, []byte("User1")), - NewField(FieldUserID, []byte{0, 1}), - NewField(FieldOptions, []byte{0, 1}), - ), - NewTransaction( - TranServerMsg, - [2]byte{0, 1}, - NewField(FieldData, []byte("autohai")), - NewField(FieldUserName, []byte("User2")), - NewField(FieldUserID, []byte{0, 2}), - NewField(FieldOptions, []byte{0, 1}), - ), - { - clientID: [2]byte{0, 1}, - IsReply: 0x01, - Fields: []Field(nil), - }, - }, - }, - { - name: "when client 2 has refuse private messages enabled", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessSendPrivMsg) - return bits - }(), - }, - ID: [2]byte{0, 1}, - UserName: []byte("User1"), - Server: &Server{ - ClientMgr: func() *MockClientMgr { - m := MockClientMgr{} - m.On("Get", ClientID{0x0, 0x2}).Return(&ClientConn{ - Flags: [2]byte{255, 255}, - ID: [2]byte{0, 2}, - UserName: []byte("User2"), - }, - ) - return &m - }(), - }, - }, - t: NewTransaction( - TranSendInstantMsg, - [2]byte{0, 1}, - NewField(FieldData, []byte("hai")), - NewField(FieldUserID, []byte{0, 2}), - ), - }, - wantRes: []Transaction{ - NewTransaction( - TranServerMsg, - [2]byte{0, 1}, - NewField(FieldData, []byte("User2 does not accept private messages.")), - NewField(FieldUserName, []byte("User2")), - NewField(FieldUserID, []byte{0, 2}), - NewField(FieldOptions, []byte{0, 2}), - ), - { - clientID: [2]byte{0, 1}, - IsReply: 0x01, - Fields: []Field(nil), - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotRes := HandleSendInstantMsg(tt.args.cc, &tt.args.t) - tranAssertEqual(t, tt.wantRes, gotRes) - }) - } -} - -func TestHandleDeleteFile(t *testing.T) { - type args struct { - cc *ClientConn - t Transaction - } - tests := []struct { - name string - args args - wantRes []Transaction - }{ - { - name: "when user does not have required permission to delete a folder", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - return bits - }(), - }, - Server: &Server{ - Config: Config{ - FileRoot: func() string { - return "/fakeRoot/Files" - }(), - }, - FS: func() *MockFileStore { - mfi := &MockFileInfo{} - mfi.On("Mode").Return(fs.FileMode(0)) - mfi.On("Size").Return(int64(100)) - mfi.On("ModTime").Return(time.Parse(time.Layout, time.Layout)) - mfi.On("IsDir").Return(false) - mfi.On("Name").Return("testfile") - - mfs := &MockFileStore{} - mfs.On("Stat", "/fakeRoot/Files/aaa/testfile").Return(mfi, nil) - mfs.On("Stat", "/fakeRoot/Files/aaa/.info_testfile").Return(nil, errors.New("err")) - mfs.On("Stat", "/fakeRoot/Files/aaa/.rsrc_testfile").Return(nil, errors.New("err")) - - return mfs - }(), - //Accounts: map[string]*Account{}, - }, - }, - t: NewTransaction( - TranDeleteFile, [2]byte{0, 1}, - NewField(FieldFileName, []byte("testfile")), - NewField(FieldFilePath, []byte{ - 0x00, 0x01, - 0x00, 0x00, - 0x03, - 0x61, 0x61, 0x61, - }), - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - ErrorCode: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldError, []byte("You are not allowed to delete files.")), - }, - }, - }, - }, - { - name: "deletes all associated metadata files", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessDeleteFile) - return bits - }(), - }, - Server: &Server{ - Config: Config{ - FileRoot: func() string { - return "/fakeRoot/Files" - }(), - }, - FS: func() *MockFileStore { - mfi := &MockFileInfo{} - mfi.On("Mode").Return(fs.FileMode(0)) - mfi.On("Size").Return(int64(100)) - mfi.On("ModTime").Return(time.Parse(time.Layout, time.Layout)) - mfi.On("IsDir").Return(false) - mfi.On("Name").Return("testfile") - - mfs := &MockFileStore{} - mfs.On("Stat", "/fakeRoot/Files/aaa/testfile").Return(mfi, nil) - mfs.On("Stat", "/fakeRoot/Files/aaa/.info_testfile").Return(nil, errors.New("err")) - mfs.On("Stat", "/fakeRoot/Files/aaa/.rsrc_testfile").Return(nil, errors.New("err")) - - mfs.On("RemoveAll", "/fakeRoot/Files/aaa/testfile").Return(nil) - mfs.On("Remove", "/fakeRoot/Files/aaa/testfile.incomplete").Return(nil) - mfs.On("Remove", "/fakeRoot/Files/aaa/.rsrc_testfile").Return(nil) - mfs.On("Remove", "/fakeRoot/Files/aaa/.info_testfile").Return(nil) - - return mfs - }(), - //Accounts: map[string]*Account{}, - }, - }, - t: NewTransaction( - TranDeleteFile, [2]byte{0, 1}, - NewField(FieldFileName, []byte("testfile")), - NewField(FieldFilePath, []byte{ - 0x00, 0x01, - 0x00, 0x00, - 0x03, - 0x61, 0x61, 0x61, - }), - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - Fields: []Field(nil), - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotRes := HandleDeleteFile(tt.args.cc, &tt.args.t) - tranAssertEqual(t, tt.wantRes, gotRes) - - tt.args.cc.Server.FS.(*MockFileStore).AssertExpectations(t) - }) - } -} - -func TestHandleGetFileNameList(t *testing.T) { - type args struct { - cc *ClientConn - t Transaction - } - tests := []struct { - name string - args args - wantRes []Transaction - }{ - { - name: "when FieldFilePath is a drop box, but user does not have AccessViewDropBoxes ", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - return bits - }(), - }, - Server: &Server{ - - Config: Config{ - FileRoot: func() string { - path, _ := os.Getwd() - return filepath.Join(path, "/test/config/Files/getFileNameListTestDir") - }(), - }, - }, - }, - t: NewTransaction( - TranGetFileNameList, [2]byte{0, 1}, - NewField(FieldFilePath, []byte{ - 0x00, 0x01, - 0x00, 0x00, - 0x08, - 0x64, 0x72, 0x6f, 0x70, 0x20, 0x62, 0x6f, 0x78, // "drop box" - }), - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - ErrorCode: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldError, []byte("You are not allowed to view drop boxes.")), - }, - }, - }, - }, - { - name: "with file root", - args: args{ - cc: &ClientConn{ - Server: &Server{ - Config: Config{ - FileRoot: func() string { - path, _ := os.Getwd() - return filepath.Join(path, "/test/config/Files/getFileNameListTestDir") - }(), - }, - }, - }, - t: NewTransaction( - TranGetFileNameList, [2]byte{0, 1}, - NewField(FieldFilePath, []byte{ - 0x00, 0x00, - 0x00, 0x00, - }), - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - Fields: []Field{ - NewField( - FieldFileNameWithInfo, - func() []byte { - fnwi := FileNameWithInfo{ - fileNameWithInfoHeader: fileNameWithInfoHeader{ - Type: [4]byte{0x54, 0x45, 0x58, 0x54}, - Creator: [4]byte{0x54, 0x54, 0x58, 0x54}, - FileSize: [4]byte{0, 0, 0x04, 0}, - RSVD: [4]byte{}, - NameScript: [2]byte{}, - NameSize: [2]byte{0, 0x0b}, - }, - Name: []byte("testfile-1k"), - } - b, _ := io.ReadAll(&fnwi) - return b - }(), - ), - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotRes := HandleGetFileNameList(tt.args.cc, &tt.args.t) - tranAssertEqual(t, tt.wantRes, gotRes) - }) - } -} - -func TestHandleGetClientInfoText(t *testing.T) { - type args struct { - cc *ClientConn - t Transaction - } - tests := []struct { - name string - args args - wantRes []Transaction - }{ - { - name: "when user does not have required permission", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - return bits - }(), - }, - Server: &Server{ - //Accounts: map[string]*Account{}, - }, - }, - t: NewTransaction( - TranGetClientInfoText, [2]byte{0, 1}, - NewField(FieldUserID, []byte{0, 1}), - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - ErrorCode: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldError, []byte("You are not allowed to get client info.")), - }, - }, - }, - }, - { - name: "with a valid user", - args: args{ - cc: &ClientConn{ - UserName: []byte("Testy McTest"), - RemoteAddr: "1.2.3.4:12345", - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessGetClientInfo) - return bits - }(), - Name: "test", - Login: "test", - }, - Server: &Server{ - ClientMgr: func() *MockClientMgr { - m := MockClientMgr{} - m.On("Get", ClientID{0x0, 0x1}).Return(&ClientConn{ - UserName: []byte("Testy McTest"), - RemoteAddr: "1.2.3.4:12345", - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessGetClientInfo) - return bits - }(), - Name: "test", - Login: "test", - }, - }, - ) - return &m - }(), - }, - ClientFileTransferMgr: ClientFileTransferMgr{}, - }, - t: NewTransaction( - TranGetClientInfoText, [2]byte{0, 1}, - NewField(FieldUserID, []byte{0, 1}), - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - Fields: []Field{ - NewField(FieldData, []byte( - strings.ReplaceAll(`Nickname: Testy McTest -Name: test -Account: test -Address: 1.2.3.4:12345 - --------- File Downloads --------- - -None. - -------- Folder Downloads -------- - -None. - ---------- File Uploads ---------- - -None. - --------- Folder Uploads --------- - -None. - -------- Waiting Downloads ------- - -None. - -`, "\n", "\r")), - ), - NewField(FieldUserName, []byte("Testy McTest")), - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotRes := HandleGetClientInfoText(tt.args.cc, &tt.args.t) - tranAssertEqual(t, tt.wantRes, gotRes) - }) - } -} - -func TestHandleTranAgreed(t *testing.T) { - type args struct { - cc *ClientConn - t Transaction - } - tests := []struct { - name string - args args - wantRes []Transaction - }{ - { - name: "normal request flow", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessDisconUser) - bits.Set(AccessAnyName) - return bits - }()}, - Icon: []byte{0, 1}, - Flags: [2]byte{0, 1}, - Version: []byte{0, 1}, - ID: [2]byte{0, 1}, - logger: NewTestLogger(), - Server: &Server{ - Config: Config{ - BannerFile: "banner.jpg", - }, - ClientMgr: func() *MockClientMgr { - m := MockClientMgr{} - m.On("List").Return([]*ClientConn{ - //{ - // ID: [2]byte{0, 2}, - // UserName: []byte("UserB"), - //}, - }, - ) - return &m - }(), - }, - }, - t: NewTransaction( - TranAgreed, [2]byte{}, - NewField(FieldUserName, []byte("username")), - NewField(FieldUserIconID, []byte{0, 1}), - NewField(FieldOptions, []byte{0, 0}), - ), - }, - wantRes: []Transaction{ - { - clientID: [2]byte{0, 1}, - Type: [2]byte{0, 0x7a}, - Fields: []Field{ - NewField(FieldBannerType, []byte("JPEG")), - }, - }, - { - clientID: [2]byte{0, 1}, - IsReply: 0x01, - Fields: []Field{}, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotRes := HandleTranAgreed(tt.args.cc, &tt.args.t) - tranAssertEqual(t, tt.wantRes, gotRes) - }) - } -} - -func TestHandleSetClientUserInfo(t *testing.T) { - type args struct { - cc *ClientConn - t Transaction - } - tests := []struct { - name string - args args - wantRes []Transaction - }{ - { - name: "when client does not have AccessAnyName", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - return bits - }(), - }, - ID: [2]byte{0, 1}, - UserName: []byte("Guest"), - Flags: [2]byte{0, 1}, - Server: &Server{ - ClientMgr: func() *MockClientMgr { - m := MockClientMgr{} - m.On("List").Return([]*ClientConn{ - { - ID: [2]byte{0, 1}, - }, - }) - return &m - }(), - }, - }, - t: NewTransaction( - TranSetClientUserInfo, [2]byte{}, - NewField(FieldUserIconID, []byte{0, 1}), - NewField(FieldUserName, []byte("NOPE")), - ), - }, - wantRes: []Transaction{ - { - clientID: [2]byte{0, 1}, - Type: [2]byte{0x01, 0x2d}, - Fields: []Field{ - NewField(FieldUserID, []byte{0, 1}), - NewField(FieldUserIconID, []byte{0, 1}), - NewField(FieldUserFlags, []byte{0, 1}), - NewField(FieldUserName, []byte("Guest"))}, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotRes := HandleSetClientUserInfo(tt.args.cc, &tt.args.t) - tranAssertEqual(t, tt.wantRes, gotRes) - }) - } -} - -func TestHandleDelNewsItem(t *testing.T) { - type args struct { - cc *ClientConn - t Transaction - } - tests := []struct { - name string - args args - wantRes []Transaction - }{ - { - name: "when user does not have permission to delete a news category", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: accessBitmap{}, - }, - ID: [2]byte{0, 1}, - Server: &Server{ - ThreadedNewsMgr: func() *mockThreadNewsMgr { - m := mockThreadNewsMgr{} - m.On("NewsItem", []string{"test"}).Return(NewsCategoryListData15{ - Type: NewsCategory, - }) - return &m - }(), - }, - }, - t: NewTransaction( - TranDelNewsItem, [2]byte{}, - NewField(FieldNewsPath, - []byte{ - 0, 1, - 0, 0, - 4, - 0x74, 0x65, 0x73, 0x74, - }, - ), - ), - }, - wantRes: []Transaction{ - { - clientID: [2]byte{0, 1}, - IsReply: 0x01, - ErrorCode: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldError, []byte("You are not allowed to delete news categories.")), - }, - }, - }, - }, - { - name: "when user does not have permission to delete a news folder", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: accessBitmap{}, - }, - ID: [2]byte{0, 1}, - Server: &Server{ - ThreadedNewsMgr: func() *mockThreadNewsMgr { - m := mockThreadNewsMgr{} - m.On("NewsItem", []string{"test"}).Return(NewsCategoryListData15{ - Type: NewsBundle, - }) - return &m - }(), - }, - }, - t: NewTransaction( - TranDelNewsItem, [2]byte{}, - NewField(FieldNewsPath, - []byte{ - 0, 1, - 0, 0, - 4, - 0x74, 0x65, 0x73, 0x74, - }, - ), - ), - }, - wantRes: []Transaction{ - { - clientID: [2]byte{0, 1}, - IsReply: 0x01, - ErrorCode: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldError, []byte("You are not allowed to delete news folders.")), - }, - }, - }, - }, - { - name: "when user deletes a news folder", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessNewsDeleteFldr) - return bits - }(), - }, - ID: [2]byte{0, 1}, - Server: &Server{ - ThreadedNewsMgr: func() *mockThreadNewsMgr { - m := mockThreadNewsMgr{} - m.On("NewsItem", []string{"test"}).Return(NewsCategoryListData15{Type: NewsBundle}) - m.On("DeleteNewsItem", []string{"test"}).Return(nil) - return &m - }(), - }, - }, - t: NewTransaction( - TranDelNewsItem, [2]byte{}, - NewField(FieldNewsPath, - []byte{ - 0, 1, - 0, 0, - 4, - 0x74, 0x65, 0x73, 0x74, - }, - ), - ), - }, - wantRes: []Transaction{ - { - clientID: [2]byte{0, 1}, - IsReply: 0x01, - Fields: []Field{}, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotRes := HandleDelNewsItem(tt.args.cc, &tt.args.t) - - tranAssertEqual(t, tt.wantRes, gotRes) - }) - } -} - -func TestHandleTranOldPostNews(t *testing.T) { - type args struct { - cc *ClientConn - t Transaction - } - tests := []struct { - name string - args args - wantRes []Transaction - }{ - { - name: "when user does not have required permission", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: accessBitmap{}, - }, - }, - t: NewTransaction( - TranOldPostNews, [2]byte{0, 1}, - NewField(FieldData, []byte("hai")), - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - ErrorCode: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldError, []byte("You are not allowed to post news.")), - }, - }, - }, - }, - { - name: "when user posts news update", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessNewsPostArt) - return bits - }(), - }, - Server: &Server{ - Config: Config{ - NewsDateFormat: "", - }, - ClientMgr: func() *MockClientMgr { - m := MockClientMgr{} - m.On("List").Return([]*ClientConn{}) - return &m - }(), - MessageBoard: func() *mockReadWriteSeeker { - m := mockReadWriteSeeker{} - m.On("Seek", int64(0), 0).Return(int64(0), nil) - m.On("Read", mock.AnythingOfType("[]uint8")).Run(func(args mock.Arguments) { - arg := args.Get(0).([]uint8) - copy(arg, "TEST") - }).Return(4, io.EOF) - m.On("Write", mock.AnythingOfType("[]uint8")).Return(3, nil) - return &m - }(), - }, - }, - t: NewTransaction( - TranOldPostNews, [2]byte{0, 1}, - NewField(FieldData, []byte("hai")), - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotRes := HandleTranOldPostNews(tt.args.cc, &tt.args.t) - - tranAssertEqual(t, tt.wantRes, gotRes) - }) - } -} - -func TestHandleInviteNewChat(t *testing.T) { - type args struct { - cc *ClientConn - t Transaction - } - tests := []struct { - name string - args args - wantRes []Transaction - }{ - { - name: "when user does not have required permission", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - return bits - }(), - }, - }, - t: NewTransaction(TranInviteNewChat, [2]byte{0, 1}), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - ErrorCode: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldError, []byte("You are not allowed to request private chat.")), - }, - }, - }, - }, - { - name: "when userA invites userB to new private chat", - args: args{ - cc: &ClientConn{ - ID: [2]byte{0, 1}, - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessOpenChat) - return bits - }(), - }, - UserName: []byte("UserA"), - Icon: []byte{0, 1}, - Flags: [2]byte{0, 0}, - Server: &Server{ - ClientMgr: func() *MockClientMgr { - m := MockClientMgr{} - m.On("Get", ClientID{0x0, 0x2}).Return(&ClientConn{ - ID: [2]byte{0, 2}, - UserName: []byte("UserB"), - }) - return &m - }(), - ChatMgr: func() *MockChatManager { - m := MockChatManager{} - m.On("New", mock.AnythingOfType("*hotline.ClientConn")).Return(ChatID{0x52, 0xfd, 0xfc, 0x07}) - return &m - }(), - }, - }, - t: NewTransaction( - TranInviteNewChat, [2]byte{0, 1}, - NewField(FieldUserID, []byte{0, 2}), - ), - }, - wantRes: []Transaction{ - { - clientID: [2]byte{0, 2}, - Type: [2]byte{0, 0x71}, - Fields: []Field{ - NewField(FieldChatID, []byte{0x52, 0xfd, 0xfc, 0x07}), - NewField(FieldUserName, []byte("UserA")), - NewField(FieldUserID, []byte{0, 1}), - }, - }, - { - clientID: [2]byte{0, 1}, - IsReply: 0x01, - Fields: []Field{ - NewField(FieldChatID, []byte{0x52, 0xfd, 0xfc, 0x07}), - NewField(FieldUserName, []byte("UserA")), - NewField(FieldUserID, []byte{0, 1}), - NewField(FieldUserIconID, []byte{0, 1}), - NewField(FieldUserFlags, []byte{0, 0}), - }, - }, - }, - }, - { - name: "when userA invites userB to new private chat, but UserB has refuse private chat enabled", - args: args{ - cc: &ClientConn{ - ID: [2]byte{0, 1}, - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessOpenChat) - return bits - }(), - }, - UserName: []byte("UserA"), - Icon: []byte{0, 1}, - Flags: [2]byte{0, 0}, - Server: &Server{ - ClientMgr: func() *MockClientMgr { - m := MockClientMgr{} - m.On("Get", ClientID{0, 2}).Return(&ClientConn{ - ID: [2]byte{0, 2}, - Icon: []byte{0, 1}, - UserName: []byte("UserB"), - Flags: [2]byte{255, 255}, - }) - return &m - }(), - ChatMgr: func() *MockChatManager { - m := MockChatManager{} - m.On("New", mock.AnythingOfType("*hotline.ClientConn")).Return(ChatID{0x52, 0xfd, 0xfc, 0x07}) - return &m - }(), - }, - }, - t: NewTransaction( - TranInviteNewChat, [2]byte{0, 1}, - NewField(FieldUserID, []byte{0, 2}), - ), - }, - wantRes: []Transaction{ - { - clientID: [2]byte{0, 1}, - Type: [2]byte{0, 0x68}, - Fields: []Field{ - NewField(FieldData, []byte("UserB does not accept private chats.")), - NewField(FieldUserName, []byte("UserB")), - NewField(FieldUserID, []byte{0, 2}), - NewField(FieldOptions, []byte{0, 2}), - }, - }, - { - clientID: [2]byte{0, 1}, - IsReply: 0x01, - Fields: []Field{ - NewField(FieldChatID, []byte{0x52, 0xfd, 0xfc, 0x07}), - NewField(FieldUserName, []byte("UserA")), - NewField(FieldUserID, []byte{0, 1}), - NewField(FieldUserIconID, []byte{0, 1}), - NewField(FieldUserFlags, []byte{0, 0}), - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - - gotRes := HandleInviteNewChat(tt.args.cc, &tt.args.t) - - tranAssertEqual(t, tt.wantRes, gotRes) - }) - } -} - -func TestHandleGetNewsArtData(t *testing.T) { - type args struct { - cc *ClientConn - t Transaction - } - tests := []struct { - name string - args args - wantRes []Transaction - }{ - { - name: "when user does not have required permission", - args: args{ - cc: &ClientConn{Account: &Account{}}, - t: NewTransaction( - TranGetNewsArtData, [2]byte{0, 1}, - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - ErrorCode: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldError, []byte("You are not allowed to read news.")), - }, - }, - }, - }, - { - name: "when user has required permission", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessNewsReadArt) - return bits - }(), - }, - Server: &Server{ - ThreadedNewsMgr: func() *mockThreadNewsMgr { - m := mockThreadNewsMgr{} - m.On("GetArticle", []string{"Example Category"}, uint32(1)).Return(&NewsArtData{ - Title: "title", - Poster: "poster", - Date: [8]byte{}, - PrevArt: [4]byte{0, 0, 0, 1}, - NextArt: [4]byte{0, 0, 0, 2}, - ParentArt: [4]byte{0, 0, 0, 3}, - FirstChildArt: [4]byte{0, 0, 0, 4}, - DataFlav: []byte("text/plain"), - Data: "article data", - }) - return &m - }(), - }, - }, - t: NewTransaction( - TranGetNewsArtData, [2]byte{0, 1}, - NewField(FieldNewsPath, []byte{ - // Example Category - 0x00, 0x01, 0x00, 0x00, 0x10, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, - }), - NewField(FieldNewsArtID, []byte{0, 1}), - ), - }, - wantRes: []Transaction{ - { - IsReply: 1, - Fields: []Field{ - NewField(FieldNewsArtTitle, []byte("title")), - NewField(FieldNewsArtPoster, []byte("poster")), - NewField(FieldNewsArtDate, []byte{0, 0, 0, 0, 0, 0, 0, 0}), - NewField(FieldNewsArtPrevArt, []byte{0, 0, 0, 1}), - NewField(FieldNewsArtNextArt, []byte{0, 0, 0, 2}), - NewField(FieldNewsArtParentArt, []byte{0, 0, 0, 3}), - NewField(FieldNewsArt1stChildArt, []byte{0, 0, 0, 4}), - NewField(FieldNewsArtDataFlav, []byte("text/plain")), - NewField(FieldNewsArtData, []byte("article data")), - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotRes := HandleGetNewsArtData(tt.args.cc, &tt.args.t) - tranAssertEqual(t, tt.wantRes, gotRes) - }) - } -} - -func TestHandleGetNewsArtNameList(t *testing.T) { - type args struct { - cc *ClientConn - t Transaction - } - tests := []struct { - name string - args args - wantRes []Transaction - }{ - { - name: "when user does not have required permission", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - return bits - }(), - }, - Server: &Server{ - //Accounts: map[string]*Account{}, - }, - }, - t: NewTransaction( - TranGetNewsArtNameList, [2]byte{0, 1}, - ), - }, - wantRes: []Transaction{ - { - Flags: 0x00, - IsReply: 0x01, - Type: [2]byte{0, 0}, - ErrorCode: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldError, []byte("You are not allowed to read news.")), - }, - }, - }, - }, - //{ - // name: "when user has required access", - // args: args{ - // cc: &ClientConn{ - // Account: &Account{ - // Access: func() accessBitmap { - // var bits accessBitmap - // bits.Set(AccessNewsReadArt) - // return bits - // }(), - // }, - // Server: &Server{ - // ThreadedNewsMgr: func() *mockThreadNewsMgr { - // m := mockThreadNewsMgr{} - // m.On("ListArticles", []string{"Example Category"}).Return(NewsArtListData{ - // Name: []byte("testTitle"), - // NewsArtList: []byte{}, - // }) - // return &m - // }(), - // }, - // }, - // t: NewTransaction( - // TranGetNewsArtNameList, - // [2]byte{0, 1}, - // // 00000000 00 01 00 00 10 45 78 61 6d 70 6c 65 20 43 61 74 |.....Example Cat| - // // 00000010 65 67 6f 72 79 |egory| - // NewField(FieldNewsPath, []byte{ - // 0x00, 0x01, 0x00, 0x00, 0x10, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, - // }), - // ), - // }, - // wantRes: []Transaction{ - // { - // IsReply: 0x01, - // Fields: []Field{ - // NewField(FieldNewsArtListData, []byte{ - // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, - // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - // 0x09, 0x74, 0x65, 0x73, 0x74, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x0a, 0x74, 0x65, 0x73, 0x74, 0x50, - // 0x6f, 0x73, 0x74, 0x65, 0x72, 0x0a, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, 0x61, 0x69, 0x6e, - // 0x00, 0x08, - // }, - // ), - // }, - // }, - // }, - //}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotRes := HandleGetNewsArtNameList(tt.args.cc, &tt.args.t) - - tranAssertEqual(t, tt.wantRes, gotRes) - }) - } -} - -func TestHandleNewNewsFldr(t *testing.T) { - type args struct { - cc *ClientConn - t Transaction - } - tests := []struct { - name string - args args - wantRes []Transaction - }{ - { - name: "when user does not have required permission", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - return bits - }(), - }, - Server: &Server{ - //Accounts: map[string]*Account{}, - }, - }, - t: NewTransaction( - TranGetNewsArtNameList, [2]byte{0, 1}, - ), - }, - wantRes: []Transaction{ - { - Flags: 0x00, - IsReply: 0x01, - Type: [2]byte{0, 0}, - ErrorCode: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldError, []byte("You are not allowed to create news folders.")), - }, - }, - }, - }, - { - name: "with a valid request", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessNewsCreateFldr) - return bits - }(), - }, - logger: NewTestLogger(), - ID: [2]byte{0, 1}, - Server: &Server{ - ThreadedNewsMgr: func() *mockThreadNewsMgr { - m := mockThreadNewsMgr{} - m.On("CreateGrouping", []string{"test"}, "testFolder", NewsBundle).Return(nil) - return &m - }(), - }, - }, - t: NewTransaction( - TranGetNewsArtNameList, [2]byte{0, 1}, - NewField(FieldFileName, []byte("testFolder")), - NewField(FieldNewsPath, - []byte{ - 0, 1, - 0, 0, - 4, - 0x74, 0x65, 0x73, 0x74, - }, - ), - ), - }, - wantRes: []Transaction{ - { - clientID: [2]byte{0, 1}, - IsReply: 0x01, - Fields: []Field{}, - }, - }, - }, - //{ - // Name: "when there is an error writing the threaded news file", - // args: args{ - // cc: &ClientConn{ - // Account: &Account{ - // Access: func() accessBitmap { - // var bits accessBitmap - // bits.Set(AccessNewsCreateFldr) - // return bits - // }(), - // }, - // logger: NewTestLogger(), - // Type: [2]byte{0, 1}, - // Server: &Server{ - // ConfigDir: "/fakeConfigRoot", - // FS: func() *MockFileStore { - // mfs := &MockFileStore{} - // mfs.On("WriteFile", "/fakeConfigRoot/ThreadedNews.yaml", mock.Anything, mock.Anything).Return(os.ErrNotExist) - // return mfs - // }(), - // ThreadedNews: &ThreadedNews{Categories: map[string]NewsCategoryListData15{ - // "test": { - // Type: []byte{0, 2}, - // Count: nil, - // NameSize: 0, - // Name: "test", - // SubCats: make(map[string]NewsCategoryListData15), - // }, - // }}, - // }, - // }, - // t: NewTransaction( - // TranGetNewsArtNameList, [2]byte{0, 1}, - // NewField(FieldFileName, []byte("testFolder")), - // NewField(FieldNewsPath, - // []byte{ - // 0, 1, - // 0, 0, - // 4, - // 0x74, 0x65, 0x73, 0x74, - // }, - // ), - // ), - // }, - // wantRes: []Transaction{ - // { - // clientID: [2]byte{0, 1}, - // Flags: 0x00, - // IsReply: 0x01, - // Type: [2]byte{0, 0}, - // ErrorCode: [4]byte{0, 0, 0, 1}, - // Fields: []Field{ - // NewField(FieldError, []byte("Error creating news folder.")), - // }, - // }, - // }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotRes := HandleNewNewsFldr(tt.args.cc, &tt.args.t) - - tranAssertEqual(t, tt.wantRes, gotRes) - }) - } -} - -func TestHandleDownloadBanner(t *testing.T) { - type args struct { - cc *ClientConn - t Transaction - } - tests := []struct { - name string - args args - wantRes []Transaction - }{ - // TODO: Add test cases. - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotRes := HandleDownloadBanner(tt.args.cc, &tt.args.t) - - assert.Equalf(t, tt.wantRes, gotRes, "HandleDownloadBanner(%v, %v)", tt.args.cc, &tt.args.t) - }) - } -} - -func TestHandlePostNewsArt(t *testing.T) { - type args struct { - cc *ClientConn - t Transaction - } - tests := []struct { - name string - args args - wantRes []Transaction - }{ - { - name: "without required permission", - args: args{ - cc: &ClientConn{ - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - return bits - }(), - }, - }, - t: NewTransaction( - TranPostNewsArt, - [2]byte{0, 0}, - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - ErrorCode: [4]byte{0, 0, 0, 1}, - Fields: []Field{ - NewField(FieldError, []byte("You are not allowed to post news articles.")), - }, - }, - }, - }, - { - name: "with required permission", - args: args{ - cc: &ClientConn{ - Server: &Server{ - ThreadedNewsMgr: func() *mockThreadNewsMgr { - m := mockThreadNewsMgr{} - m.On("PostArticle", []string{"www"}, uint32(0), mock.AnythingOfType("hotline.NewsArtData")).Return(nil) - return &m - }(), - }, - Account: &Account{ - Access: func() accessBitmap { - var bits accessBitmap - bits.Set(AccessNewsPostArt) - return bits - }(), - }, - }, - t: NewTransaction( - TranPostNewsArt, - [2]byte{0, 0}, - NewField(FieldNewsPath, []byte{0x00, 0x01, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77}), - NewField(FieldNewsArtID, []byte{0x00, 0x00, 0x00, 0x00}), - ), - }, - wantRes: []Transaction{ - { - IsReply: 0x01, - ErrorCode: [4]byte{0, 0, 0, 0}, - Fields: []Field{}, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tranAssertEqual(t, tt.wantRes, HandlePostNewsArt(tt.args.cc, &tt.args.t)) - }) - } -} |