8 "go.uber.org/zap/zapcore"
15 type testCase struct {
16 name string // test case description
17 account Account // Account struct for a user that will test transaction will execute under
18 request *Transaction // transaction that will be sent by the client to the server
19 setup func() // Optional test-specific setup required for the scenario
20 teardown func() // Optional test-specific teardown for the scenario
21 mockHandler map[int]*mockClientHandler
24 func (tt *testCase) Setup(srv *Server) error {
25 if err := srv.NewUser(tt.account.Login, tt.account.Name, NegatedUserString([]byte(tt.account.Password)), *tt.account.Access); err != nil {
36 func (tt *testCase) Teardown(srv *Server) error {
37 if err := srv.DeleteUser(tt.account.Login); err != nil {
41 if tt.teardown != nil {
48 func NewTestLogger() *zap.SugaredLogger {
49 encoderCfg := zap.NewProductionEncoderConfig()
50 encoderCfg.TimeKey = "timestamp"
51 encoderCfg.EncodeTime = zapcore.ISO8601TimeEncoder
53 core := zapcore.NewCore(
54 zapcore.NewConsoleEncoder(encoderCfg),
55 zapcore.Lock(os.Stdout),
59 cores := []zapcore.Core{core}
60 l := zap.New(zapcore.NewTee(cores...))
61 defer func() { _ = l.Sync() }()
65 func StartTestServer() (*Server, context.Context, context.CancelFunc) {
66 ctx, cancelRoot := context.WithCancel(context.Background())
68 srv, err := NewServer("test/config/", "localhost", 0, NewTestLogger())
74 err := srv.ListenAndServe(ctx, cancelRoot)
80 return srv, ctx, cancelRoot
83 func TestHandshake(t *testing.T) {
84 srv, _, cancelFunc := StartTestServer()
87 port := srv.APIListener.Addr().(*net.TCPAddr).Port
89 conn, err := net.Dial("tcp", fmt.Sprintf(":%v", port))
95 conn.Write([]byte{0x54, 0x52, 0x54, 0x50, 0x00, 0x01, 0x00, 0x00})
97 replyBuf := make([]byte, 8)
98 _, _ = conn.Read(replyBuf)
100 want := []byte{84, 82, 84, 80, 0, 0, 0, 0}
101 if bytes.Compare(replyBuf, want) != 0 {
102 t.Errorf("%q, want %q", replyBuf, want)
107 //func TestLogin(t *testing.T) {
109 // tests := []struct {
114 // name: "when login is successful",
115 // client: NewClient("guest", NewTestLogger()),
118 // for _, test := range tests {
119 // t.Run(test.name, func(t *testing.T) {
126 func TestNewUser(t *testing.T) {
127 srv, _, _ := StartTestServer()
131 // name: "a valid new account",
132 // mockHandler: func() mockClientHandler {
133 // mh := mockClientHandler{}
134 // mh.On("Handle", mock.AnythingOfType("*hotline.Client"), mock.MatchedBy(func(t *Transaction) bool {
135 // println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@")
137 // spew.Dump(bytes.Equal(t.Type, []byte{0x01, 0x5e}))
138 // //if !bytes.Equal(t.GetField(fieldError).Data, []byte("You are not allowed to create new accounts.")) {
141 // return bytes.Equal(t.Type, []byte{0x01, 0x5e},
144 // []Transaction{}, nil,
147 // clientHandlers[tranNewUser] = mh
150 // client: func() *Client {
151 // c := NewClient("testUser", NewTestLogger())
154 // teardown: func() {
155 // _ = srv.DeleteUser("testUser")
161 // Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
163 // request: NewTransaction(
165 // NewField(fieldUserLogin, []byte(NegatedUserString([]byte("testUser")))),
166 // NewField(fieldUserName, []byte("testUserName")),
167 // NewField(fieldUserPassword, []byte(NegatedUserString([]byte("testPw")))),
168 // NewField(fieldUserAccess, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}),
170 // want: &Transaction{
171 // Fields: []Field{},
175 // name: "a newUser request from a user without the required access",
176 // mockHandler: func() *mockClientHandler {
177 // mh := mockClientHandler{}
178 // mh.On("Handle", mock.AnythingOfType("*hotline.Client"), mock.MatchedBy(func(t *Transaction) bool {
179 // if !bytes.Equal(t.GetField(fieldError).Data, []byte("You are not allowed to create new accounts.")) {
182 // return bytes.Equal(t.Type, []byte{0x01, 0x5e})
184 // []Transaction{}, nil,
188 // teardown: func() {
189 // _ = srv.DeleteUser("testUser")
195 // Access: &[]byte{0, 0, 0, 0, 0, 0, 0, 0},
197 // request: NewTransaction(
199 // NewField(fieldUserLogin, []byte(NegatedUserString([]byte("testUser")))),
200 // NewField(fieldUserName, []byte("testUserName")),
201 // NewField(fieldUserPassword, []byte(NegatedUserString([]byte("testPw")))),
202 // NewField(fieldUserAccess, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}),
206 // name: "when user does not have required permission",
207 // mockHandler: func() map[int]*mockClientHandler {
208 // mockHandlers := make(map[int]*mockClientHandler)
210 // mh := mockClientHandler{}
211 // mh.On("Handle", mock.AnythingOfType("*hotline.Client"), mock.MatchedBy(func(t *Transaction) bool {
212 // return t.equal(Transaction{
213 // Type: []byte{0x01, 0x5e},
215 // ErrorCode: []byte{0, 0, 0, 1},
217 // NewField(fieldError, []byte("You are not allowed to create new accounts.")),
221 // []Transaction{}, nil,
223 // mockHandlers[tranNewUser] = &mh
225 // return mockHandlers
228 // teardown: func() {
229 // _ = srv.DeleteUser("testUser")
235 // Access: &[]byte{0, 0, 0, 0, 0, 0, 0, 0},
237 // request: NewTransaction(
239 // NewField(fieldUserLogin, []byte(NegatedUserString([]byte("testUser")))),
240 // NewField(fieldUserName, []byte("testUserName")),
241 // NewField(fieldUserPassword, []byte(NegatedUserString([]byte("testPw")))),
242 // NewField(fieldUserAccess, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}),
247 // name: "a request to create a user that already exists",
251 // teardown: func() {
252 // _ = srv.DeleteUser("testUser")
258 // Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
260 // request: NewTransaction(
262 // NewField(fieldUserLogin, []byte(NegatedUserString([]byte("guest")))),
263 // NewField(fieldUserName, []byte("testUserName")),
264 // NewField(fieldUserPassword, []byte(NegatedUserString([]byte("testPw")))),
265 // NewField(fieldUserAccess, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}),
267 // want: &Transaction{
269 // NewField(fieldError, []byte("Cannot create account guest because there is already an account with that login.")),
275 for _, test := range tests {
276 t.Run(test.name, func(t *testing.T) {
280 c := NewClient(test.account.Name, NewTestLogger())
281 err := c.JoinServer(fmt.Sprintf(":%v", srv.APIPort()), test.account.Login, test.account.Password)
283 t.Errorf("login failed: %v", err)
285 // end move to Setup??
287 for key, value := range test.mockHandler {
288 c.Handlers[uint16(key)] = value
291 // send test case request
292 _ = c.Send(*test.request)
294 //time.Sleep(1 * time.Second)
297 transactions, _ := readN(c.Connection, 1)
298 for _, t := range transactions {
299 _ = c.HandleTransaction(&t)
304 for _, handler := range test.mockHandler {
305 handler.AssertExpectations(t)
315 // equal is a utility function used only in tests that determines if transactions are equal enough
316 func (t Transaction) equal(otherT Transaction) bool {
317 t.ID = []byte{0, 0, 0, 0}
318 otherT.ID = []byte{0, 0, 0, 0}
320 t.TotalSize = []byte{0, 0, 0, 0}
321 otherT.TotalSize = []byte{0, 0, 0, 0}
323 t.DataSize = []byte{0, 0, 0, 0}
324 otherT.DataSize = []byte{0, 0, 0, 0}
326 t.ParamCount = []byte{0, 0}
327 otherT.ParamCount = []byte{0, 0}
332 return reflect.DeepEqual(t, otherT)