]> git.r.bdr.sh - rbdr/mobius/blobdiff - hotline/transaction_handlers_test.go
Refactoring, cleanup, test backfilling
[rbdr/mobius] / hotline / transaction_handlers_test.go
index 56968a1e1f0b4e48797766e836964e83b5716cb5..b7cc0afbc609ac78dbaeea3506f0029fa57b8bce 100644 (file)
@@ -1,24 +1,28 @@
 package hotline
 
 import (
+       "errors"
        "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/mock"
+       "io"
        "io/fs"
-       "math/rand"
        "os"
-       "reflect"
+       "path/filepath"
+       "strings"
+       "sync"
        "testing"
+       "time"
 )
 
 func TestHandleSetChatSubject(t *testing.T) {
        type args struct {
                cc *ClientConn
-               t  *Transaction
+               t  Transaction
        }
        tests := []struct {
-               name    string
-               args    args
-               want    []Transaction
-               wantErr bool
+               name string
+               args args
+               want []Transaction
        }{
                {
                        name: "sends chat subject to private chat members",
@@ -26,92 +30,74 @@ func TestHandleSetChatSubject(t *testing.T) {
                                cc: &ClientConn{
                                        UserName: []byte{0x00, 0x01},
                                        Server: &Server{
-                                               PrivateChats: map[uint32]*PrivateChat{
-                                                       uint32(1): {
+                                               PrivateChats: map[[4]byte]*PrivateChat{
+                                                       [4]byte{0, 0, 0, 1}: {
                                                                Subject: "unset",
-                                                               ClientConn: map[uint16]*ClientConn{
-                                                                       uint16(1): {
+                                                               ClientConn: map[[2]byte]*ClientConn{
+                                                                       [2]byte{0, 1}: {
                                                                                Account: &Account{
-                                                                                       Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
+                                                                                       Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
                                                                                },
-                                                                               ID: &[]byte{0, 1},
+                                                                               ID: [2]byte{0, 1},
                                                                        },
-                                                                       uint16(2): {
+                                                                       [2]byte{0, 2}: {
                                                                                Account: &Account{
-                                                                                       Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
+                                                                                       Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
                                                                                },
-                                                                               ID: &[]byte{0, 2},
+                                                                               ID: [2]byte{0, 2},
                                                                        },
                                                                },
                                                        },
                                                },
-                                               Clients: map[uint16]*ClientConn{
-                                                       uint16(1): {
+                                               Clients: map[[2]byte]*ClientConn{
+                                                       [2]byte{0, 1}: {
                                                                Account: &Account{
-                                                                       Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
+                                                                       Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
                                                                },
-                                                               ID: &[]byte{0, 1},
+                                                               ID: [2]byte{0, 1},
                                                        },
-                                                       uint16(2): {
+                                                       [2]byte{0, 2}: {
                                                                Account: &Account{
-                                                                       Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
+                                                                       Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
                                                                },
-                                                               ID: &[]byte{0, 2},
+                                                               ID: [2]byte{0, 2},
                                                        },
                                                },
                                        },
                                },
-                               t: &Transaction{
-                                       Flags:     0x00,
-                                       IsReply:   0x00,
-                                       Type:      []byte{0, 0x6a},
-                                       ID:        []byte{0, 0, 0, 1},
-                                       ErrorCode: []byte{0, 0, 0, 0},
+                               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")),
+                                               NewField(FieldChatID, []byte{0, 0, 0, 1}),
+                                               NewField(FieldChatSubject, []byte("Test Subject")),
                                        },
                                },
                        },
                        want: []Transaction{
                                {
-                                       clientID:  &[]byte{0, 1},
-                                       Flags:     0x00,
-                                       IsReply:   0x00,
-                                       Type:      []byte{0, 0x77},
-                                       ID:        []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
-                                       ErrorCode: []byte{0, 0, 0, 0},
+                                       clientID: [2]byte{0, 1},
+                                       Type:     [2]byte{0, 0x77},
                                        Fields: []Field{
-                                               NewField(fieldChatID, []byte{0, 0, 0, 1}),
-                                               NewField(fieldChatSubject, []byte("Test Subject")),
+                                               NewField(FieldChatID, []byte{0, 0, 0, 1}),
+                                               NewField(FieldChatSubject, []byte("Test Subject")),
                                        },
                                },
                                {
-                                       clientID:  &[]byte{0, 2},
-                                       Flags:     0x00,
-                                       IsReply:   0x00,
-                                       Type:      []byte{0, 0x77},
-                                       ID:        []byte{0xf0, 0xc5, 0x34, 0x1e}, // Random ID from rand.Seed(1)
-                                       ErrorCode: []byte{0, 0, 0, 0},
+                                       clientID: [2]byte{0, 2},
+                                       Type:     [2]byte{0, 0x77},
                                        Fields: []Field{
-                                               NewField(fieldChatID, []byte{0, 0, 0, 1}),
-                                               NewField(fieldChatSubject, []byte("Test Subject")),
+                                               NewField(FieldChatID, []byte{0, 0, 0, 1}),
+                                               NewField(FieldChatSubject, []byte("Test Subject")),
                                        },
                                },
                        },
-                       wantErr: false,
                },
        }
        for _, tt := range tests {
-               rand.Seed(1) // reset seed between tests to make transaction IDs predictable
-
                t.Run(tt.name, func(t *testing.T) {
-                       got, err := HandleSetChatSubject(tt.args.cc, tt.args.t)
-                       if (err != nil) != tt.wantErr {
-                               t.Errorf("HandleSetChatSubject() error = %v, wantErr %v", err, tt.wantErr)
-                               return
-                       }
-                       if !assert.Equal(t, tt.want, got) {
+                       got := HandleSetChatSubject(tt.args.cc, &tt.args.t)
+                       if !tranAssertEqual(t, tt.want, got) {
                                t.Errorf("HandleSetChatSubject() got = %v, want %v", got, tt.want)
                        }
                })
@@ -121,82 +107,71 @@ func TestHandleSetChatSubject(t *testing.T) {
 func TestHandleLeaveChat(t *testing.T) {
        type args struct {
                cc *ClientConn
-               t  *Transaction
+               t  Transaction
        }
        tests := []struct {
-               name    string
-               args    args
-               want    []Transaction
-               wantErr bool
+               name string
+               args args
+               want []Transaction
        }{
                {
                        name: "returns expected transactions",
                        args: args{
                                cc: &ClientConn{
-                                       ID: &[]byte{0, 2},
+                                       ID: [2]byte{0, 2},
                                        Server: &Server{
-                                               PrivateChats: map[uint32]*PrivateChat{
-                                                       uint32(1): {
-                                                               ClientConn: map[uint16]*ClientConn{
-                                                                       uint16(1): {
+                                               PrivateChats: map[[4]byte]*PrivateChat{
+                                                       [4]byte{0, 0, 0, 1}: {
+                                                               ClientConn: map[[2]byte]*ClientConn{
+                                                                       [2]byte{0, 1}: {
                                                                                Account: &Account{
-                                                                                       Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
+                                                                                       Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
                                                                                },
-                                                                               ID: &[]byte{0, 1},
+                                                                               ID: [2]byte{0, 1},
                                                                        },
-                                                                       uint16(2): {
+                                                                       [2]byte{0, 2}: {
                                                                                Account: &Account{
-                                                                                       Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
+                                                                                       Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
                                                                                },
-                                                                               ID: &[]byte{0, 2},
+                                                                               ID: [2]byte{0, 2},
                                                                        },
                                                                },
                                                        },
                                                },
-                                               Clients: map[uint16]*ClientConn{
-                                                       uint16(1): {
+                                               Clients: map[[2]byte]*ClientConn{
+                                                       [2]byte{0, 1}: {
                                                                Account: &Account{
-                                                                       Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
+                                                                       Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
                                                                },
-                                                               ID: &[]byte{0, 1},
+                                                               ID: [2]byte{0, 1},
                                                        },
-                                                       uint16(2): {
+                                                       [2]byte{0, 2}: {
                                                                Account: &Account{
-                                                                       Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
+                                                                       Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
                                                                },
-                                                               ID: &[]byte{0, 2},
+                                                               ID: [2]byte{0, 2},
                                                        },
                                                },
                                        },
                                },
-                               t: NewTransaction(tranDeleteUser, nil, NewField(fieldChatID, []byte{0, 0, 0, 1})),
+                               t: NewTransaction(TranDeleteUser, [2]byte{}, NewField(FieldChatID, []byte{0, 0, 0, 1})),
                        },
                        want: []Transaction{
                                {
-                                       clientID:  &[]byte{0, 1},
-                                       Flags:     0x00,
-                                       IsReply:   0x00,
-                                       Type:      []byte{0, 0x76},
-                                       ID:        []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
-                                       ErrorCode: []byte{0, 0, 0, 0},
+                                       clientID: [2]byte{0, 1},
+                                       Type:     [2]byte{0, 0x76},
                                        Fields: []Field{
-                                               NewField(fieldChatID, []byte{0, 0, 0, 1}),
-                                               NewField(fieldUserID, []byte{0, 2}),
+                                               NewField(FieldChatID, []byte{0, 0, 0, 1}),
+                                               NewField(FieldUserID, []byte{0, 2}),
                                        },
                                },
                        },
-                       wantErr: false,
                },
        }
        for _, tt := range tests {
-               rand.Seed(1)
                t.Run(tt.name, func(t *testing.T) {
-                       got, err := HandleLeaveChat(tt.args.cc, tt.args.t)
-                       if (err != nil) != tt.wantErr {
-                               t.Errorf("HandleLeaveChat() error = %v, wantErr %v", err, tt.wantErr)
-                               return
-                       }
-                       if !assert.Equal(t, tt.want, got) {
+                       got := HandleLeaveChat(tt.args.cc, &tt.args.t)
+                       if !tranAssertEqual(t, tt.want, got) {
                                t.Errorf("HandleLeaveChat() got = %v, want %v", got, tt.want)
                        }
                })
@@ -206,75 +181,59 @@ func TestHandleLeaveChat(t *testing.T) {
 func TestHandleGetUserNameList(t *testing.T) {
        type args struct {
                cc *ClientConn
-               t  *Transaction
+               t  Transaction
        }
        tests := []struct {
-               name    string
-               args    args
-               want    []Transaction
-               wantErr bool
+               name string
+               args args
+               want []Transaction
        }{
                {
                        name: "replies with userlist transaction",
                        args: args{
                                cc: &ClientConn{
-
-                                       ID: &[]byte{1, 1},
+                                       ID: [2]byte{0, 1},
                                        Server: &Server{
-                                               Clients: map[uint16]*ClientConn{
-                                                       uint16(1): {
-                                                               ID:       &[]byte{0, 1},
-                                                               Icon:     &[]byte{0, 2},
-                                                               Flags:    &[]byte{0, 3},
+                                               Clients: map[[2]byte]*ClientConn{
+                                                       [2]byte{0, 1}: {
+                                                               ID:       [2]byte{0, 1},
+                                                               Icon:     []byte{0, 2},
+                                                               Flags:    [2]byte{0, 3},
                                                                UserName: []byte{0, 4},
                                                        },
-                                                       uint16(2): {
-                                                               ID:       &[]byte{0, 2},
-                                                               Icon:     &[]byte{0, 2},
-                                                               Flags:    &[]byte{0, 3},
+                                                       [2]byte{0, 2}: {
+                                                               ID:       [2]byte{0, 2},
+                                                               Icon:     []byte{0, 2},
+                                                               Flags:    [2]byte{0, 3},
                                                                UserName: []byte{0, 4},
                                                        },
                                                },
                                        },
                                },
-                               t: &Transaction{
-                                       ID:   []byte{0, 0, 0, 1},
-                                       Type: []byte{0, 1},
-                               },
+                               t: Transaction{},
                        },
                        want: []Transaction{
                                {
-                                       clientID:  &[]byte{1, 1},
-                                       Flags:     0x00,
-                                       IsReply:   0x01,
-                                       Type:      []byte{0, 1},
-                                       ID:        []byte{0, 0, 0, 1},
-                                       ErrorCode: []byte{0, 0, 0, 0},
+                                       clientID: [2]byte{0, 1},
+                                       IsReply:  0x01,
                                        Fields: []Field{
                                                NewField(
-                                                       fieldUsernameWithInfo,
+                                                       FieldUsernameWithInfo,
                                                        []byte{00, 01, 00, 02, 00, 03, 00, 02, 00, 04},
                                                ),
                                                NewField(
-                                                       fieldUsernameWithInfo,
+                                                       FieldUsernameWithInfo,
                                                        []byte{00, 02, 00, 02, 00, 03, 00, 02, 00, 04},
                                                ),
                                        },
                                },
                        },
-                       wantErr: false,
                },
        }
        for _, tt := range tests {
                t.Run(tt.name, func(t *testing.T) {
-                       got, err := HandleGetUserNameList(tt.args.cc, tt.args.t)
-                       if (err != nil) != tt.wantErr {
-                               t.Errorf("HandleGetUserNameList() error = %v, wantErr %v", err, tt.wantErr)
-                               return
-                       }
-                       if !reflect.DeepEqual(got, tt.want) {
-                               t.Errorf("HandleGetUserNameList() got = %v, want %v", got, tt.want)
-                       }
+                       got := HandleGetUserNameList(tt.args.cc, &tt.args.t)
+                       assert.Equal(t, tt.want, got)
                })
        }
 }
@@ -282,254 +241,450 @@ func TestHandleGetUserNameList(t *testing.T) {
 func TestHandleChatSend(t *testing.T) {
        type args struct {
                cc *ClientConn
-               t  *Transaction
+               t  Transaction
        }
        tests := []struct {
-               name    string
-               args    args
-               want    []Transaction
-               wantErr bool
+               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{
-                                               Clients: map[uint16]*ClientConn{
-                                                       uint16(1): {
+                                               Clients: map[[2]byte]*ClientConn{
+                                                       [2]byte{0, 1}: {
                                                                Account: &Account{
-                                                                       Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
+                                                                       Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
                                                                },
-                                                               ID: &[]byte{0, 1},
+                                                               ID: [2]byte{0, 1},
                                                        },
-                                                       uint16(2): {
+                                                       [2]byte{0, 2}: {
                                                                Account: &Account{
-                                                                       Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
+                                                                       Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
                                                                },
-                                                               ID: &[]byte{0, 2},
+                                                               ID: [2]byte{0, 2},
                                                        },
                                                },
                                        },
                                },
-                               t: &Transaction{
+                               t: Transaction{
                                        Fields: []Field{
-                                               NewField(fieldData, []byte("hai")),
+                                               NewField(FieldData, []byte("hai")),
                                        },
                                },
                        },
                        want: []Transaction{
                                {
-                                       clientID:  &[]byte{0, 1},
-                                       Flags:     0x00,
-                                       IsReply:   0x00,
-                                       Type:      []byte{0, 0x6a},
-                                       ID:        []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
-                                       ErrorCode: []byte{0, 0, 0, 0},
+                                       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}),
+                                               NewField(FieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
                                        },
                                },
                                {
-                                       clientID:  &[]byte{0, 2},
-                                       Flags:     0x00,
-                                       IsReply:   0x00,
-                                       Type:      []byte{0, 0x6a},
-                                       ID:        []byte{0xf0, 0xc5, 0x34, 0x1e}, // Random ID from rand.Seed(1)
-                                       ErrorCode: []byte{0, 0, 0, 0},
+                                       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 ID 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{
+                                               Clients: 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},
+                                                       },
+                                               },
+                                       },
+                               },
+                               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(fieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
+                                               NewField(FieldError, []byte("You are not allowed to participate in chat.")),
                                        },
                                },
                        },
-                       wantErr: false,
                },
                {
-                       name: "sends chat msg as emote if fieldChatOptions is set",
+                       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{
-                                               Clients: map[uint16]*ClientConn{
-                                                       uint16(1): {
+                                               Clients: map[[2]byte]*ClientConn{
+                                                       [2]byte{0, 1}: {
                                                                Account: &Account{
-                                                                       Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
+                                                                       Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
                                                                },
-                                                               ID: &[]byte{0, 1},
+                                                               ID: [2]byte{0, 1},
                                                        },
-                                                       uint16(2): {
+                                                       [2]byte{0, 2}: {
                                                                Account: &Account{
-                                                                       Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
+                                                                       Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
                                                                },
-                                                               ID: &[]byte{0, 2},
+                                                               ID: [2]byte{0, 2},
                                                        },
                                                },
                                        },
                                },
-                               t: &Transaction{
+                               t: Transaction{
                                        Fields: []Field{
-                                               NewField(fieldData, []byte("performed action")),
-                                               NewField(fieldChatOptions, []byte{0x00, 0x01}),
+                                               NewField(FieldData, []byte("performed action")),
+                                               NewField(FieldChatOptions, []byte{0x00, 0x01}),
                                        },
                                },
                        },
                        want: []Transaction{
                                {
-                                       clientID:  &[]byte{0, 1},
-                                       Flags:     0x00,
-                                       IsReply:   0x00,
-                                       Type:      []byte{0, 0x6a},
-                                       ID:        []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
-                                       ErrorCode: []byte{0, 0, 0, 0},
+                                       clientID: [2]byte{0, 1},
+                                       Flags:    0x00,
+                                       IsReply:  0x00,
+                                       Type:     [2]byte{0, 0x6a},
                                        Fields: []Field{
-                                               NewField(fieldData, []byte("\r*** Testy McTest performed action")),
+                                               NewField(FieldData, []byte("\r*** Testy McTest performed action")),
                                        },
                                },
                                {
-                                       clientID:  &[]byte{0, 2},
-                                       Flags:     0x00,
-                                       IsReply:   0x00,
-                                       Type:      []byte{0, 0x6a},
-                                       ID:        []byte{0xf0, 0xc5, 0x34, 0x1e}, // Random ID from rand.Seed(1)
-                                       ErrorCode: []byte{0, 0, 0, 0},
+                                       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{
+                                               Clients: 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},
+                                                       },
+                                               },
+                                       },
+                               },
+                               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 performed action")),
+                                               NewField(FieldData, []byte("\r Testy McTest:  hello")),
                                        },
                                },
                        },
-                       wantErr: false,
                },
                {
                        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{
+                                               Clients: map[[2]byte]*ClientConn{
+                                                       [2]byte{0, 1}: {
+                                                               Account: &Account{
+                                                                       Access: func() accessBitmap {
+                                                                               var bits accessBitmap
+                                                                               bits.Set(accessReadChat)
+                                                                               return bits
+                                                                       }()},
+                                                               ID: [2]byte{0, 1},
+                                                       },
+                                                       [2]byte{0, 2}: {
+                                                               Account: &Account{
+                                                                       Access: accessBitmap{0, 0, 0, 0, 0, 0, 0, 0},
+                                                               },
+                                                               ID: [2]byte{0, 2},
+                                                       },
+                                               },
+                                       },
+                               },
+                               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{
-                                               Clients: map[uint16]*ClientConn{
-                                                       uint16(1): {
+                                               PrivateChats: map[[4]byte]*PrivateChat{
+                                                       [4]byte{0, 0, 0, 1}: {
+                                                               ClientConn: map[[2]byte]*ClientConn{
+                                                                       [2]byte{0, 1}: {
+                                                                               ID: [2]byte{0, 1},
+                                                                       },
+                                                                       [2]byte{0, 2}: {
+                                                                               ID: [2]byte{0, 2},
+                                                                       },
+                                                               },
+                                                       },
+                                               },
+                                               Clients: 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: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
+                                                                       Access: accessBitmap{0, 0, 0, 0, 0, 0, 0, 0},
                                                                },
-                                                               ID: &[]byte{0, 1},
+                                                               ID: [2]byte{0, 2},
                                                        },
-                                                       uint16(2): {
+                                                       [2]byte{0, 3}: {
                                                                Account: &Account{
-                                                                       Access: &[]byte{0, 0, 0, 0, 0, 0, 0, 0},
+                                                                       Access: accessBitmap{0, 0, 0, 0, 0, 0, 0, 0},
                                                                },
-                                                               ID: &[]byte{0, 2},
+                                                               ID: [2]byte{0, 3},
                                                        },
                                                },
                                        },
                                },
-                               t: &Transaction{
+                               t: Transaction{
                                        Fields: []Field{
-                                               NewField(fieldData, []byte("hai")),
+                                               NewField(FieldData, []byte("hai")),
+                                               NewField(FieldChatID, []byte{0, 0, 0, 1}),
                                        },
                                },
                        },
                        want: []Transaction{
                                {
-                                       clientID:  &[]byte{0, 1},
-                                       Flags:     0x00,
-                                       IsReply:   0x00,
-                                       Type:      []byte{0, 0x6a},
-                                       ID:        []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
-                                       ErrorCode: []byte{0, 0, 0, 0},
+                                       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(fieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
+                                               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}),
                                        },
                                },
                        },
-                       wantErr: false,
                },
        }
        for _, tt := range tests {
-               rand.Seed(1) // reset seed between tests to make transaction IDs predictable
                t.Run(tt.name, func(t *testing.T) {
-                       got, err := HandleChatSend(tt.args.cc, tt.args.t)
-
-                       if (err != nil) != tt.wantErr {
-                               t.Errorf("HandleChatSend() error = %v, wantErr %v", err, tt.wantErr)
-                               return
-                       }
-                       if !assert.Equal(t, tt.want, got) {
-                               t.Errorf("HandleChatSend() got = %v, want %v", got, tt.want)
-                       }
+                       got := HandleChatSend(tt.args.cc, &tt.args.t)
+                       tranAssertEqual(t, tt.want, got)
                })
        }
 }
 
 func TestHandleGetFileInfo(t *testing.T) {
-       rand.Seed(1) // reset seed between tests to make transaction IDs predictable
-
        type args struct {
                cc *ClientConn
-               t  *Transaction
+               t  Transaction
        }
        tests := []struct {
                name    string
                args    args
                wantRes []Transaction
-               wantErr bool
        }{
                {
                        name: "returns expected fields when a valid file is requested",
                        args: args{
                                cc: &ClientConn{
-                                       ID: &[]byte{0x00, 0x01},
+                                       ID: [2]byte{0x00, 0x01},
                                        Server: &Server{
+                                               FS: &OSFileStore{},
                                                Config: &Config{
                                                        FileRoot: func() string {
                                                                path, _ := os.Getwd()
-                                                               return path + "/test/config/Files"
+                                                               return filepath.Join(path, "/test/config/Files")
                                                        }(),
                                                },
                                        },
                                },
                                t: NewTransaction(
-                                       tranGetFileInfo, nil,
-                                       NewField(fieldFileName, []byte("testfile.txt")),
-                                       NewField(fieldFilePath, []byte{0x00, 0x00}),
+                                       TranGetFileInfo, [2]byte{},
+                                       NewField(FieldFileName, []byte("testfile.txt")),
+                                       NewField(FieldFilePath, []byte{0x00, 0x00}),
                                ),
                        },
                        wantRes: []Transaction{
                                {
-                                       clientID:  &[]byte{0, 1},
-                                       Flags:     0x00,
-                                       IsReply:   0x01,
-                                       Type:      []byte{0, 0xce},
-                                       ID:        []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
-                                       ErrorCode: []byte{0, 0, 0, 0},
+                                       clientID: [2]byte{0, 1},
+                                       IsReply:  0x01,
+                                       Type:     [2]byte{0, 0},
                                        Fields: []Field{
-                                               NewField(fieldFileName, []byte("testfile.txt")),
-                                               NewField(fieldFileTypeString, []byte("TEXT")),
-                                               NewField(fieldFileCreatorString, []byte("ttxt")),
-                                               NewField(fieldFileComment, []byte{}),
-                                               NewField(fieldFileType, []byte("TEXT")),
-                                               NewField(fieldFileCreateDate, make([]byte, 8)),
-                                               NewField(fieldFileModifyDate, make([]byte, 8)),
-                                               NewField(fieldFileSize, []byte{0x0, 0x0, 0x0, 0x17}),
+                                               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}),
                                        },
                                },
                        },
-                       wantErr: false,
                },
        }
        for _, tt := range tests {
                t.Run(tt.name, func(t *testing.T) {
-                       rand.Seed(1) // reset seed between tests to make transaction IDs predictable
-
-                       gotRes, err := HandleGetFileInfo(tt.args.cc, tt.args.t)
-                       if (err != nil) != tt.wantErr {
-                               t.Errorf("HandleGetFileInfo() error = %v, wantErr %v", err, tt.wantErr)
-                               return
-                       }
+                       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)
-                       gotRes[0].Fields[6].Data = make([]byte, 8)
-                       if !assert.Equal(t, tt.wantRes, gotRes) {
+
+                       if !tranAssertEqual(t, tt.wantRes, gotRes) {
                                t.Errorf("HandleGetFileInfo() gotRes = %v, want %v", gotRes, tt.wantRes)
                        }
                })
@@ -539,30 +694,67 @@ func TestHandleGetFileInfo(t *testing.T) {
 func TestHandleNewFolder(t *testing.T) {
        type args struct {
                cc *ClientConn
-               t  *Transaction
+               t  Transaction
        }
        tests := []struct {
-               setup   func()
                name    string
                args    args
                wantRes []Transaction
-               wantErr bool
        }{
+               {
+                       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{
-                                       ID: &[]byte{0, 1},
+                                       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, &[]byte{0, 1},
-                                       NewField(fieldFileName, []byte("testFolder")),
-                                       NewField(fieldFilePath, []byte{
+                                       TranNewFolder, [2]byte{0, 1},
+                                       NewField(FieldFileName, []byte("testFolder")),
+                                       NewField(FieldFilePath, []byte{
                                                0x00, 0x01,
                                                0x00, 0x00,
                                                0x03,
@@ -570,134 +762,147 @@ func TestHandleNewFolder(t *testing.T) {
                                        }),
                                ),
                        },
-                       setup: func() {
-                               mfs := MockFileStore{}
-                               mfs.On("Mkdir", "/Files/aaa/testFolder", fs.FileMode(0777)).Return(nil)
-                               mfs.On("Stat", "/Files/aaa/testFolder").Return(nil, os.ErrNotExist)
-                               FS = mfs
-                       },
                        wantRes: []Transaction{
                                {
-                                       clientID:  &[]byte{0, 1},
-                                       Flags:     0x00,
-                                       IsReply:   0x01,
-                                       Type:      []byte{0, 0xcd},
-                                       ID:        []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
-                                       ErrorCode: []byte{0, 0, 0, 0},
+                                       clientID: [2]byte{0, 1},
+                                       IsReply:  0x01,
                                },
                        },
-                       wantErr: false,
                },
                {
                        name: "when path is not nested",
                        args: args{
                                cc: &ClientConn{
-                                       ID: &[]byte{0, 1},
+                                       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, &[]byte{0, 1},
-                                       NewField(fieldFileName, []byte("testFolder")),
+                                       TranNewFolder, [2]byte{0, 1},
+                                       NewField(FieldFileName, []byte("testFolder")),
                                ),
                        },
-                       setup: func() {
-                               mfs := MockFileStore{}
-                               mfs.On("Mkdir", "/Files/testFolder", fs.FileMode(0777)).Return(nil)
-                               mfs.On("Stat", "/Files/testFolder").Return(nil, os.ErrNotExist)
-                               FS = mfs
-                       },
                        wantRes: []Transaction{
                                {
-                                       clientID:  &[]byte{0, 1},
-                                       Flags:     0x00,
-                                       IsReply:   0x01,
-                                       Type:      []byte{0, 0xcd},
-                                       ID:        []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
-                                       ErrorCode: []byte{0, 0, 0, 0},
+                                       clientID: [2]byte{0, 1},
+                                       IsReply:  0x01,
                                },
                        },
-                       wantErr: false,
                },
                {
-                       name: "when UnmarshalBinary returns an err",
+                       name: "when Write returns an err",
                        args: args{
                                cc: &ClientConn{
-                                       ID: &[]byte{0, 1},
+                                       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, &[]byte{0, 1},
-                                       NewField(fieldFileName, []byte("testFolder")),
-                                       NewField(fieldFilePath, []byte{
+                                       TranNewFolder, [2]byte{0, 1},
+                                       NewField(FieldFileName, []byte("testFolder")),
+                                       NewField(FieldFilePath, []byte{
                                                0x00,
                                        }),
                                ),
                        },
-                       setup: func() {
-                               mfs := MockFileStore{}
-                               mfs.On("Mkdir", "/Files/aaa/testFolder", fs.FileMode(0777)).Return(nil)
-                               mfs.On("Stat", "/Files/aaa/testFolder").Return(nil, os.ErrNotExist)
-                               FS = mfs
-                       },
                        wantRes: []Transaction{},
-                       wantErr: true,
                },
                {
-                       name: "fieldFileName does not allow directory traversal",
+                       name: "FieldFileName does not allow directory traversal",
                        args: args{
                                cc: &ClientConn{
-                                       ID: &[]byte{0, 1},
+                                       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, &[]byte{0, 1},
-                                       NewField(fieldFileName, []byte("../../testFolder")),
+                                       TranNewFolder, [2]byte{0, 1},
+                                       NewField(FieldFileName, []byte("../../testFolder")),
                                ),
                        },
-                       setup: func() {
-                               mfs := MockFileStore{}
-                               mfs.On("Mkdir", "/Files/testFolder", fs.FileMode(0777)).Return(nil)
-                               mfs.On("Stat", "/Files/testFolder").Return(nil, os.ErrNotExist)
-                               FS = mfs
-                       },
                        wantRes: []Transaction{
                                {
-                                       clientID:  &[]byte{0, 1},
-                                       Flags:     0x00,
-                                       IsReply:   0x01,
-                                       Type:      []byte{0, 0xcd},
-                                       ID:        []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
-                                       ErrorCode: []byte{0, 0, 0, 0},
+                                       clientID: [2]byte{0, 1},
+                                       IsReply:  0x01,
                                },
-                       }, wantErr: false,
+                       },
                },
                {
-                       name: "fieldFilePath does not allow directory traversal",
+                       name: "FieldFilePath does not allow directory traversal",
                        args: args{
                                cc: &ClientConn{
-                                       ID: &[]byte{0, 1},
+                                       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, &[]byte{0, 1},
-                                       NewField(fieldFileName, []byte("testFolder")),
-                                       NewField(fieldFilePath, []byte{
+                                       TranNewFolder, [2]byte{0, 1},
+                                       NewField(FieldFileName, []byte("testFolder")),
+                                       NewField(FieldFilePath, []byte{
                                                0x00, 0x02,
                                                0x00, 0x00,
                                                0x03,
@@ -708,33 +913,18 @@ func TestHandleNewFolder(t *testing.T) {
                                        }),
                                ),
                        },
-                       setup: func() {
-                               mfs := MockFileStore{}
-                               mfs.On("Mkdir", "/Files/foo/testFolder", fs.FileMode(0777)).Return(nil)
-                               mfs.On("Stat", "/Files/foo/testFolder").Return(nil, os.ErrNotExist)
-                               FS = mfs
-                       },
                        wantRes: []Transaction{
                                {
-                                       clientID:  &[]byte{0, 1},
-                                       Flags:     0x00,
-                                       IsReply:   0x01,
-                                       Type:      []byte{0, 0xcd},
-                                       ID:        []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
-                                       ErrorCode: []byte{0, 0, 0, 0},
+                                       clientID: [2]byte{0, 1},
+                                       IsReply:  0x01,
                                },
-                       }, wantErr: false,
+                       },
                },
        }
        for _, tt := range tests {
                t.Run(tt.name, func(t *testing.T) {
-                       tt.setup()
+                       gotRes := HandleNewFolder(tt.args.cc, &tt.args.t)
 
-                       gotRes, err := HandleNewFolder(tt.args.cc, tt.args.t)
-                       if (err != nil) != tt.wantErr {
-                               t.Errorf("HandleNewFolder() error = %v, wantErr %v", err, tt.wantErr)
-                               return
-                       }
                        if !tranAssertEqual(t, tt.wantRes, gotRes) {
                                t.Errorf("HandleNewFolder() gotRes = %v, want %v", gotRes, tt.wantRes)
                        }
@@ -745,34 +935,39 @@ func TestHandleNewFolder(t *testing.T) {
 func TestHandleUploadFile(t *testing.T) {
        type args struct {
                cc *ClientConn
-               t  *Transaction
+               t  Transaction
        }
        tests := []struct {
                name    string
                args    args
                wantRes []Transaction
-               wantErr bool
        }{
                {
-                       name: "when request is valid",
+                       name: "when request is valid and user has Upload Anywhere permission",
                        args: args{
                                cc: &ClientConn{
                                        Server: &Server{
-                                               FileTransfers: map[uint32]*FileTransfer{},
+                                               FS:            &OSFileStore{},
+                                               fileTransfers: map[[4]byte]*FileTransfer{},
+                                               Config: &Config{
+                                                       FileRoot: func() string { path, _ := os.Getwd(); return path + "/test/config/Files" }(),
+                                               }},
+                                       transfers: map[int]map[[4]byte]*FileTransfer{
+                                               FileUpload: {},
                                        },
                                        Account: &Account{
-                                               Access: func() *[]byte {
+                                               Access: func() accessBitmap {
                                                        var bits accessBitmap
                                                        bits.Set(accessUploadFile)
-                                                       access := bits[:]
-                                                       return &access
+                                                       bits.Set(accessUploadAnywhere)
+                                                       return bits
                                                }(),
                                        },
                                },
                                t: NewTransaction(
-                                       tranUploadFile, &[]byte{0, 1},
-                                       NewField(fieldFileName, []byte("testFile")),
-                                       NewField(fieldFilePath, []byte{
+                                       TranUploadFile, [2]byte{0, 1},
+                                       NewField(FieldFileName, []byte("testFile")),
+                                       NewField(FieldFilePath, []byte{
                                                0x00, 0x01,
                                                0x00, 0x00,
                                                0x03,
@@ -782,37 +977,28 @@ func TestHandleUploadFile(t *testing.T) {
                        },
                        wantRes: []Transaction{
                                {
-                                       Flags:     0x00,
-                                       IsReply:   0x01,
-                                       Type:      []byte{0, 0xcb},
-                                       ID:        []byte{0x9a, 0xcb, 0x04, 0x42},
-                                       ErrorCode: []byte{0, 0, 0, 0},
+                                       IsReply: 0x01,
                                        Fields: []Field{
-                                               NewField(fieldRefNum, []byte{0x52, 0xfd, 0xfc, 0x07}), // rand.Seed(1)
+                                               NewField(FieldRefNum, []byte{0x52, 0xfd, 0xfc, 0x07}), // rand.Seed(1)
                                        },
                                },
                        },
-                       wantErr: false,
                },
                {
                        name: "when user does not have required access",
                        args: args{
                                cc: &ClientConn{
                                        Account: &Account{
-                                               Access: func() *[]byte {
+                                               Access: func() accessBitmap {
                                                        var bits accessBitmap
-                                                       access := bits[:]
-                                                       return &access
+                                                       return bits
                                                }(),
                                        },
-                                       Server: &Server{
-                                               FileTransfers: map[uint32]*FileTransfer{},
-                                       },
                                },
                                t: NewTransaction(
-                                       tranUploadFile, &[]byte{0, 1},
-                                       NewField(fieldFileName, []byte("testFile")),
-                                       NewField(fieldFilePath, []byte{
+                                       TranUploadFile, [2]byte{0, 1},
+                                       NewField(FieldFileName, []byte("testFile")),
+                                       NewField(FieldFilePath, []byte{
                                                0x00, 0x01,
                                                0x00, 0x00,
                                                0x03,
@@ -822,30 +1008,2585 @@ func TestHandleUploadFile(t *testing.T) {
                        },
                        wantRes: []Transaction{
                                {
-                                       Flags:     0x00,
                                        IsReply:   0x01,
-                                       Type:      []byte{0, 0x00},
-                                       ID:        []byte{0x9a, 0xcb, 0x04, 0x42},
-                                       ErrorCode: []byte{0, 0, 0, 1},
+                                       ErrorCode: [4]byte{0, 0, 0, 1},
                                        Fields: []Field{
-                                               NewField(fieldError, []byte("You are not allowed to upload files.")), // rand.Seed(1)
+                                               NewField(FieldError, []byte("You are not allowed to upload files.")), // rand.Seed(1)
                                        },
                                },
                        },
-                       wantErr: false,
                },
        }
        for _, tt := range tests {
                t.Run(tt.name, func(t *testing.T) {
-                       rand.Seed(1)
-                       gotRes, err := HandleUploadFile(tt.args.cc, tt.args.t)
-                       if (err != nil) != tt.wantErr {
-                               t.Errorf("HandleUploadFile() error = %v, wantErr %v", err, tt.wantErr)
-                               return
-                       }
-                       if !tranAssertEqual(t, tt.wantRes, gotRes) {
-                               t.Errorf("HandleUploadFile() gotRes = %v, want %v", gotRes, tt.wantRes)
-                       }
+                       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: func() accessBitmap {
+                                                       var bits accessBitmap
+                                                       return bits
+                                               }(),
+                                       },
+                                       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{
+                                               Accounts: map[string]*Account{
+                                                       "guest": {
+                                                               Login:    "guest",
+                                                               Name:     "Guest",
+                                                               Password: "password",
+                                                               Access:   accessBitmap{},
+                                                       },
+                                               },
+                                       },
+                               },
+                               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{
+                                               Accounts: map[string]*Account{},
+                                       },
+                               },
+                               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 dataFile",
+                       args: args{
+                               cc: &ClientConn{
+                                       Account: &Account{
+                                               Access: func() accessBitmap {
+                                                       var bits accessBitmap
+                                                       bits.Set(accessDeleteUser)
+                                                       return bits
+                                               }(),
+                                       },
+                                       Server: &Server{
+                                               Accounts: map[string]*Account{
+                                                       "testuser": {
+                                                               Login:    "testuser",
+                                                               Name:     "Testy McTest",
+                                                               Password: "password",
+                                                               Access:   accessBitmap{},
+                                                       },
+                                               },
+                                               FS: func() *MockFileStore {
+                                                       mfs := &MockFileStore{}
+                                                       mfs.On("Remove", "Users/testuser.yaml").Return(nil)
+                                                       return mfs
+                                               }(),
+                                       },
+                               },
+                               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: func() accessBitmap {
+                                                       var bits accessBitmap
+                                                       return bits
+                                               }(),
+                                       },
+                                       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{
+                                               FlatNews: []byte("TEST"),
+                                       },
+                               },
+                               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: func() accessBitmap {
+                                                       var bits accessBitmap
+                                                       return bits
+                                               }(),
+                                       },
+                                       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{
+                                               Accounts: map[string]*Account{},
+                                       },
+                               },
+                               t: NewTransaction(
+                                       TranNewUser, [2]byte{0, 1},
+                                       NewField(FieldUserLogin, []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{
+                                               Accounts: map[string]*Account{
+                                                       "guest": {
+                                                               Name:     "guest",
+                                                               Login:    "guest",
+                                                               Password: "zz",
+                                                               Access:   accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
+                                                       },
+                                               },
+                                       },
+                               },
+                               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{
+                                       transfers: map[int]map[[4]byte]*FileTransfer{
+                                               FileDownload: {},
+                                       },
+                                       Account: &Account{
+                                               Access: func() accessBitmap {
+                                                       var bits accessBitmap
+                                                       bits.Set(accessDownloadFile)
+                                                       return bits
+                                               }(),
+                                       },
+                                       Server: &Server{
+                                               FS:            &OSFileStore{},
+                                               fileTransfers: map[[4]byte]*FileTransfer{},
+                                               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.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{
+                                       transfers: map[int]map[[4]byte]*FileTransfer{
+                                               FileDownload: {},
+                                       }, 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
+                                               // }(),
+                                               fileTransfers: map[[4]byte]*FileTransfer{},
+                                               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{
+                                               Logger: NewTestLogger(),
+                                       },
+                                       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 create new accounts.")),
+                                       },
+                               },
+                       },
+               },
+               {
+                       name: "when action is modify user without required permission",
+                       args: args{
+                               cc: &ClientConn{
+                                       logger: NewTestLogger(),
+                                       Server: &Server{
+                                               Logger: NewTestLogger(),
+                                               Accounts: map[string]*Account{
+                                                       "bbb": {},
+                                               },
+                                       },
+                                       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{
+                                               Accounts: map[string]*Account{
+                                                       "bbb": {},
+                                               },
+                                       },
+                                       Account: &Account{
+                                               Access: func() accessBitmap {
+                                                       var bits accessBitmap
+                                                       return bits
+                                               }(),
+                                       },
+                               },
+                               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{
+                                               Clients: map[[2]byte]*ClientConn{
+                                                       [2]byte{0, 1}: {
+                                                               Account: &Account{
+                                                                       Login: "unnamed",
+                                                                       Access: func() accessBitmap {
+                                                                               var bits accessBitmap
+                                                                               bits.Set(accessCannotBeDiscon)
+                                                                               return bits
+                                                                       }(),
+                                                               },
+                                                       },
+                                               },
+                                       },
+                                       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{
+                                               Clients: map[[2]byte]*ClientConn{
+                                                       [2]byte{0, 2}: {
+                                                               AutoReply: []byte(nil),
+                                                               Flags:     [2]byte{0, 0},
+                                                       },
+                                               },
+                                       },
+                               },
+                               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{
+                                               Clients: map[[2]byte]*ClientConn{
+                                                       [2]byte{0, 2}: {
+                                                               Flags:     [2]byte{0, 0},
+                                                               ID:        [2]byte{0, 2},
+                                                               UserName:  []byte("User2"),
+                                                               AutoReply: []byte("autohai"),
+                                                       },
+                                               },
+                                       },
+                               },
+                               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{
+                                               Clients: map[[2]byte]*ClientConn{
+                                                       [2]byte{0, 2}: {
+                                                               Flags:    [2]byte{255, 255},
+                                                               ID:       [2]byte{0, 2},
+                                                               UserName: []byte("User2"),
+                                                       },
+                                               },
+                                       },
+                               },
+                               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{
+                                               Accounts: map[string]*Account{},
+                                               Clients: map[[2]byte]*ClientConn{
+                                                       [2]byte{0, 1}: {
+                                                               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",
+                                                               },
+                                                       },
+                                               },
+                                       },
+                                       transfers: map[int]map[[4]byte]*FileTransfer{
+                                               FileDownload:   {},
+                                               FileUpload:     {},
+                                               FolderDownload: {},
+                                               FolderUpload:   {},
+                                       },
+                               },
+                               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",
+                                               },
+                                       },
+                               },
+                               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{
+                                               Clients: map[[2]byte]*ClientConn{
+                                                       [2]byte{0, 1}: {
+                                                               ID: [2]byte{0, 1},
+                                                       },
+                                               },
+                                       },
+                               },
+                               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{
+                                               ThreadedNews: &ThreadedNews{Categories: map[string]NewsCategoryListData15{
+                                                       "test": {
+                                                               Type: [2]byte{0, 3},
+                                                               Name: "zz",
+                                                       },
+                                               }},
+                                       },
+                               },
+                               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{
+                                               ThreadedNews: &ThreadedNews{Categories: map[string]NewsCategoryListData15{
+                                                       "testcat": {
+                                                               Type: [2]byte{0, 2},
+                                                               Name: "test",
+                                                       },
+                                               }},
+                                       },
+                               },
+                               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{
+                                               ConfigDir: "/fakeConfigRoot",
+                                               FS: func() *MockFileStore {
+                                                       mfs := &MockFileStore{}
+                                                       mfs.On("WriteFile", "/fakeConfigRoot/ThreadedNews.yaml", mock.Anything, mock.Anything).Return(nil, os.ErrNotExist)
+                                                       return mfs
+                                               }(),
+                                               ThreadedNews: &ThreadedNews{Categories: map[string]NewsCategoryListData15{
+                                                       "testcat": {
+                                                               Type: [2]byte{0, 2},
+                                                               Name: "test",
+                                                       },
+                                               }},
+                                       },
+                               },
+                               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: func() accessBitmap {
+                                                       var bits accessBitmap
+                                                       return bits
+                                               }(),
+                                       },
+                               },
+                               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{
+                                               FS: func() *MockFileStore {
+                                                       mfs := &MockFileStore{}
+                                                       mfs.On("WriteFile", "/fakeConfigRoot/MessageBoard.txt", mock.Anything, mock.Anything).Return(nil, os.ErrNotExist)
+                                                       return mfs
+                                               }(),
+                                               ConfigDir: "/fakeConfigRoot",
+                                               Config:    &Config{},
+                                       },
+                               },
+                               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{
+                                               Clients: map[[2]byte]*ClientConn{
+                                                       [2]byte{0, 2}: {
+                                                               ID:       [2]byte{0, 2},
+                                                               UserName: []byte("UserB"),
+                                                       },
+                                               },
+                                               PrivateChats: make(map[[4]byte]*PrivateChat),
+                                       },
+                               },
+                               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{
+                                               Clients: map[[2]byte]*ClientConn{
+                                                       [2]byte{0, 2}: {
+                                                               ID:       [2]byte{0, 2},
+                                                               UserName: []byte("UserB"),
+                                                               Flags:    [2]byte{255, 255},
+                                                       },
+                                               },
+                                               PrivateChats: make(map[[4]byte]*PrivateChat),
+                                       },
+                               },
+                               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{
+                                               Access: func() accessBitmap {
+                                                       var bits accessBitmap
+                                                       return bits
+                                               }(),
+                                       },
+                                       Server: &Server{
+                                               Accounts: map[string]*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.")),
+                                       },
+                               },
+                       },
+               },
+       }
+       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{
+                                               ThreadedNews: &ThreadedNews{
+                                                       Categories: map[string]NewsCategoryListData15{
+                                                               "Example Category": {
+                                                                       Type: [2]byte{0, 2},
+                                                                       Name: "",
+                                                                       Articles: map[uint32]*NewsArtData{
+                                                                               uint32(1): {
+                                                                                       Title:  "testTitle",
+                                                                                       Poster: "testPoster",
+                                                                                       Data:   "testBody",
+                                                                               },
+                                                                       },
+                                                                       SubCats:  nil,
+                                                                       GUID:     [16]byte{},
+                                                                       AddSN:    [4]byte{},
+                                                                       DeleteSN: [4]byte{},
+                                                               },
+                                                       },
+                                               },
+
+                                               //Accounts: map[string]*Account{
+                                               //      "guest": {
+                                               //              Name:     "guest",
+                                               //              Login:    "guest",
+                                               //              Password: "zz",
+                                               //              Access:   accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
+                                               //      },
+                                               //},
+                                       },
+                               },
+                               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{
+                                               ConfigDir: "/fakeConfigRoot",
+                                               FS: func() *MockFileStore {
+                                                       mfs := &MockFileStore{}
+                                                       mfs.On("WriteFile", "/fakeConfigRoot/ThreadedNews.yaml", mock.Anything, mock.Anything).Return(nil)
+                                                       return mfs
+                                               }(),
+                                               ThreadedNews: &ThreadedNews{Categories: map[string]NewsCategoryListData15{
+                                                       "test": {
+                                                               Type:    [2]byte{0, 2},
+                                                               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},
+                                       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(),
+               //                      ID:     [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{
+                                               FS: func() *MockFileStore {
+                                                       mfs := &MockFileStore{}
+                                                       mfs.On("WriteFile", "ThreadedNews.yaml", mock.Anything, mock.Anything).Return(nil, os.ErrNotExist)
+                                                       return mfs
+                                               }(),
+                                               mux:             sync.Mutex{},
+                                               threadedNewsMux: sync.Mutex{},
+                                               ThreadedNews: &ThreadedNews{
+                                                       Categories: map[string]NewsCategoryListData15{
+                                                               "www": {
+                                                                       Type:       [2]byte{},
+                                                                       Name:       "www",
+                                                                       Articles:   map[uint32]*NewsArtData{},
+                                                                       SubCats:    nil,
+                                                                       GUID:       [16]byte{},
+                                                                       AddSN:      [4]byte{},
+                                                                       DeleteSN:   [4]byte{},
+                                                                       readOffset: 0,
+                                                               },
+                                                       },
+                                               },
+                                       },
+                                       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))
                })
        }
 }