]> git.r.bdr.sh - rbdr/mobius/blobdiff - hotline/server_blackbox_test.go
Fix bug that clears account password on permission edit
[rbdr/mobius] / hotline / server_blackbox_test.go
index 3718bd29c560d334979e1b77bf239c92ad78d254..138a17f285ca782e64c8c2c79811c893ef9789e4 100644 (file)
 package hotline
 
-// Guest login
-// Admin login is
-//
-//type testCase struct {
-//     name        string       // test case description
-//     account     Account      // Account struct for a user that will test transaction will execute under
-//     request     *Transaction // transaction that will be sent by the client to the server
-//     setup       func()       // Optional test-specific setup required for the scenario
-//     teardown    func()       // Optional test-specific teardown for the scenario
-//     mockHandler map[int]*mockClientHandler
-//}
-//
-//func (tt *testCase) Setup(srv *Server) error {
-//     if err := srv.NewUser(tt.account.Login, tt.account.Name, NegatedUserString([]byte(tt.account.Password)), *tt.account.Access); err != nil {
-//             return err
-//     }
-//
-//     if tt.setup != nil {
-//             tt.setup()
-//     }
-//
-//     return nil
-//}
-//
-//func (tt *testCase) Teardown(srv *Server) error {
-//     if err := srv.DeleteUser(tt.account.Login); err != nil {
-//             return err
-//     }
-//
-//     if tt.teardown != nil {
-//             tt.teardown()
-//     }
-//
-//     return nil
-//}
-//
-//func NewTestLogger() *zap.SugaredLogger {
-//     encoderCfg := zap.NewProductionEncoderConfig()
-//     encoderCfg.TimeKey = "timestamp"
-//     encoderCfg.EncodeTime = zapcore.ISO8601TimeEncoder
-//
-//     core := zapcore.NewCore(
-//             zapcore.NewConsoleEncoder(encoderCfg),
-//             zapcore.Lock(os.Stdout),
-//             zap.DebugLevel,
-//     )
-//
-//     cores := []zapcore.Core{core}
-//     l := zap.New(zapcore.NewTee(cores...))
-//     defer func() { _ = l.Sync() }()
-//     return l.Sugar()
-//}
-//
-//func StartTestServer() (*Server, context.Context, context.CancelFunc) {
-//     ctx, cancelRoot := context.WithCancel(context.Background())
-//
-//     srv, err := NewServer("test/config/", "localhost", 0, NewTestLogger())
-//     if err != nil {
-//             panic(err)
-//     }
-//
-//     go func() {
-//             err := srv.ListenAndServe(ctx, cancelRoot)
-//             if err != nil {
-//                     panic(err)
-//             }
-//     }()
-//
-//     return srv, ctx, cancelRoot
-//}
-//
-//func TestHandshake(t *testing.T) {
-//     srv, _, cancelFunc := StartTestServer()
-//     defer cancelFunc()
-//
-//     port := srv.APIListener.Addr().(*net.TCPAddr).Port
-//
-//     conn, err := net.Dial("tcp", fmt.Sprintf(":%v", port))
-//     if err != nil {
-//             t.Fatal(err)
-//     }
-//     defer conn.Close()
-//
-//     conn.Write([]byte{0x54, 0x52, 0x54, 0x50, 0x00, 0x01, 0x00, 0x00})
-//
-//     replyBuf := make([]byte, 8)
-//     _, _ = conn.Read(replyBuf)
-//
-//     want := []byte{84, 82, 84, 80, 0, 0, 0, 0}
-//     if bytes.Compare(replyBuf, want) != 0 {
-//             t.Errorf("%q, want %q", replyBuf, want)
-//     }
-//
-//}
-//
-////func TestLogin(t *testing.T) {
-////
-////   tests := []struct {
-////           name   string
-////           client *Client
-////   }{
-////           {
-////                   name:   "when login is successful",
-////                   client: NewClient("guest", NewTestLogger()),
-////           },
-////   }
-////   for _, test := range tests {
-////           t.Run(test.name, func(t *testing.T) {
-////
-////           })
-////   }
-////}
-//
-//func TestNewUser(t *testing.T) {
-//     srv, _, _ := StartTestServer()
-//
-//     tests := []testCase{
-//             //{
-//             //      name: "a valid new account",
-//             //      mockHandler: func() mockClientHandler {
-//             //              mh := mockClientHandler{}
-//             //              mh.On("Handle", mock.AnythingOfType("*hotline.Client"), mock.MatchedBy(func(t *Transaction) bool {
-//             //                      println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@")
-//             //                      spew.Dump(t.Type)
-//             //                      spew.Dump(bytes.Equal(t.Type, []byte{0x01, 0x5e}))
-//             //                      //if !bytes.Equal(t.GetField(fieldError).Data, []byte("You are not allowed to create new accounts.")) {
-//             //                      //      return false
-//             //                      //}
-//             //                      return bytes.Equal(t.Type, []byte{0x01, 0x5e},
-//             //                      )
-//             //              })).Return(
-//             //                      []Transaction{}, nil,
-//             //              )
-//             //
-//             //              clientHandlers[tranNewUser] = mh
-//             //              return mh
-//             //      }(),
-//             //      client: func() *Client {
-//             //              c := NewClient("testUser", NewTestLogger())
-//             //              return c
-//             //      }(),
-//             //      teardown: func() {
-//             //              _ = srv.DeleteUser("testUser")
-//             //      },
-//             //      account: Account{
-//             //              Login:    "test",
-//             //              Name:     "unnamed",
-//             //              Password: "test",
-//             //              Access:   &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
-//             //      },
-//             //      request: NewTransaction(
-//             //              tranNewUser, nil,
-//             //              NewField(fieldUserLogin, []byte(NegatedUserString([]byte("testUser")))),
-//             //              NewField(fieldUserName, []byte("testUserName")),
-//             //              NewField(fieldUserPassword, []byte(NegatedUserString([]byte("testPw")))),
-//             //              NewField(fieldUserAccess, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}),
-//             //      ),
-//             //      want: &Transaction{
-//             //              Fields: []Field{},
-//             //      },
-//             //},
-//             //{
-//             //      name: "a newUser request from a user without the required access",
-//             //      mockHandler: func() *mockClientHandler {
-//             //              mh := mockClientHandler{}
-//             //              mh.On("Handle", mock.AnythingOfType("*hotline.Client"), mock.MatchedBy(func(t *Transaction) bool {
-//             //                      if !bytes.Equal(t.GetField(fieldError).Data, []byte("You are not allowed to create new accounts.")) {
-//             //                              return false
-//             //                      }
-//             //                      return bytes.Equal(t.Type, []byte{0x01, 0x5e})
-//             //              })).Return(
-//             //                      []Transaction{}, nil,
-//             //              )
-//             //              return &mh
-//             //      }(),
-//             //      teardown: func() {
-//             //              _ = srv.DeleteUser("testUser")
-//             //      },
-//             //      account: Account{
-//             //              Login:    "test",
-//             //              Name:     "unnamed",
-//             //              Password: "test",
-//             //              Access:   &[]byte{0, 0, 0, 0, 0, 0, 0, 0},
-//             //      },
-//             //      request: NewTransaction(
-//             //              tranNewUser, nil,
-//             //              NewField(fieldUserLogin, []byte(NegatedUserString([]byte("testUser")))),
-//             //              NewField(fieldUserName, []byte("testUserName")),
-//             //              NewField(fieldUserPassword, []byte(NegatedUserString([]byte("testPw")))),
-//             //              NewField(fieldUserAccess, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}),
-//             //      ),
-//             //},
-//             {
-//                     name: "when user does not have required permission",
-//                     mockHandler: func() map[int]*mockClientHandler {
-//                             mockHandlers := make(map[int]*mockClientHandler)
-//
-//                             mh := mockClientHandler{}
-//                             mh.On("Handle", mock.AnythingOfType("*hotline.Client"), mock.MatchedBy(func(t *Transaction) bool {
-//                                     return t.equal(Transaction{
-//                                             Type:      []byte{0x01, 0x5e},
-//                                             IsReply:   1,
-//                                             ErrorCode: []byte{0, 0, 0, 1},
-//                                             Fields: []Field{
-//                                                     NewField(fieldError, []byte("You are not allowed to create new accounts.")),
-//                                             },
-//                                     })
-//                             })).Return(
-//                                     []Transaction{}, nil,
-//                             )
-//                             mockHandlers[tranNewUser] = &mh
-//
-//                             return mockHandlers
-//                     }(),
-//
-//                     teardown: func() {
-//                             _ = srv.DeleteUser("testUser")
-//                     },
-//                     account: Account{
-//                             Login:    "test",
-//                             Name:     "unnamed",
-//                             Password: "test",
-//                             Access:   &[]byte{0, 0, 0, 0, 0, 0, 0, 0},
-//                     },
-//                     request: NewTransaction(
-//                             tranNewUser, nil,
-//                             NewField(fieldUserLogin, []byte(NegatedUserString([]byte("testUser")))),
-//                             NewField(fieldUserName, []byte("testUserName")),
-//                             NewField(fieldUserPassword, []byte(NegatedUserString([]byte("testPw")))),
-//                             NewField(fieldUserAccess, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}),
-//                     ),
-//             },
-//
-//             //{
-//             //      name: "a request to create a user that already exists",
-//             //      setup: func() {
-//             //
-//             //      },
-//             //      teardown: func() {
-//             //              _ = srv.DeleteUser("testUser")
-//             //      },
-//             //      account: Account{
-//             //              Login:    "test",
-//             //              Name:     "unnamed",
-//             //              Password: "test",
-//             //              Access:   &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
-//             //      },
-//             //      request: NewTransaction(
-//             //              tranNewUser, nil,
-//             //              NewField(fieldUserLogin, []byte(NegatedUserString([]byte("guest")))),
-//             //              NewField(fieldUserName, []byte("testUserName")),
-//             //              NewField(fieldUserPassword, []byte(NegatedUserString([]byte("testPw")))),
-//             //              NewField(fieldUserAccess, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}),
-//             //      ),
-//             //      want: &Transaction{
-//             //              Fields: []Field{
-//             //                      NewField(fieldError, []byte("Cannot create account guest because there is already an account with that login.")),
-//             //              },
-//             //      },
-//             //},
-//     }
-//
-//     for _, test := range tests {
-//             t.Run(test.name, func(t *testing.T) {
-//                     test.Setup(srv)
-//
-//                     // move to Setup?
-//                     c := NewClient(test.account.Name, NewTestLogger())
-//                     err := c.JoinServer(fmt.Sprintf(":%v", srv.APIPort()), test.account.Login, test.account.Password)
-//                     if err != nil {
-//                             t.Errorf("login failed: %v", err)
-//                     }
-//                     // end move to Setup??
-//
-//                     for key, value := range test.mockHandler {
-//                             c.Handlers[uint16(key)] = value
-//                     }
-//
-//                     // send test case request
-//                     _ = c.Send(*test.request)
-//
-//                     //time.Sleep(1 * time.Second)
-//                     // ===
-//
-//                     transactions, _ := readN(c.Connection, 1)
-//                     for _, t := range transactions {
-//                             _ = c.HandleTransaction(&t)
-//                     }
-//
-//                     // ===
-//
-//                     for _, handler := range test.mockHandler {
-//                             handler.AssertExpectations(t)
-//                     }
-//
-//                     test.Teardown(srv)
-//             })
-//     }
-//}
-//
-//// equal is a utility function used only in tests that determines if transactions are equal enough
-//func (t Transaction) equal(otherT Transaction) bool {
-//     t.ID = []byte{0, 0, 0, 0}
-//     otherT.ID = []byte{0, 0, 0, 0}
-//
-//     t.TotalSize = []byte{0, 0, 0, 0}
-//     otherT.TotalSize = []byte{0, 0, 0, 0}
-//
-//     t.DataSize = []byte{0, 0, 0, 0}
-//     otherT.DataSize = []byte{0, 0, 0, 0}
-//
-//     t.ParamCount = []byte{0, 0}
-//     otherT.ParamCount = []byte{0, 0}
-//
-//     //spew.Dump(t)
-//     //spew.Dump(otherT)
-//
-//     return reflect.DeepEqual(t, otherT)
-//}
+import (
+       "bytes"
+       "encoding/hex"
+       "github.com/stretchr/testify/assert"
+       "go.uber.org/zap"
+       "go.uber.org/zap/zapcore"
+       "os"
+       "testing"
+)
+
+func NewTestLogger() *zap.SugaredLogger {
+       encoderCfg := zap.NewProductionEncoderConfig()
+       encoderCfg.TimeKey = "timestamp"
+       encoderCfg.EncodeTime = zapcore.ISO8601TimeEncoder
+
+       core := zapcore.NewCore(
+               zapcore.NewConsoleEncoder(encoderCfg),
+               zapcore.Lock(os.Stdout),
+               zap.DebugLevel,
+       )
+
+       cores := []zapcore.Core{core}
+       l := zap.New(zapcore.NewTee(cores...))
+       defer func() { _ = l.Sync() }()
+       return l.Sugar()
+}
+
+// assertTransferBytesEqual takes a string with a hexdump in the same format that `hexdump -C` produces and compares with
+// a hexdump for the bytes in got, after stripping the create/modify timestamps.
+// I don't love this, but as git does not  preserve file create/modify timestamps, we either need to fully mock the
+// filesystem interactions or work around in this way.
+// TODO: figure out a better solution
+func assertTransferBytesEqual(t *testing.T, wantHexDump string, got []byte) bool {
+       if wantHexDump == "" {
+               return true
+       }
+
+       var clean []byte
+       clean = append(clean, got[:92]...)         // keep the first 92 bytes
+       clean = append(clean, make([]byte, 16)...) // replace the next 16 bytes for create/modify timestamps
+       clean = append(clean, got[108:]...)        // keep the rest
+
+       return assert.Equal(t, wantHexDump, hex.Dump(clean))
+}
+
+// tranAssertEqual compares equality of transactions slices after stripping out the random ID
+func tranAssertEqual(t *testing.T, tran1, tran2 []Transaction) bool {
+       var newT1 []Transaction
+       var newT2 []Transaction
+
+       for _, trans := range tran1 {
+               trans.ID = []byte{0, 0, 0, 0}
+               var fs []Field
+               for _, field := range trans.Fields {
+                       if bytes.Equal(field.ID, []byte{0x00, 0x6b}) {
+                               continue
+                       }
+                       fs = append(fs, field)
+               }
+               trans.Fields = fs
+               newT1 = append(newT1, trans)
+       }
+
+       for _, trans := range tran2 {
+               trans.ID = []byte{0, 0, 0, 0}
+               var fs []Field
+               for _, field := range trans.Fields {
+                       if bytes.Equal(field.ID, []byte{0x00, 0x6b}) {
+                               continue
+                       }
+                       fs = append(fs, field)
+               }
+               trans.Fields = fs
+               newT2 = append(newT2, trans)
+       }
+
+       return assert.Equal(t, newT1, newT2)
+}