github.com/cmd-stream/base-go@v0.0.0-20230813145615-dd6ac24c16f5/server/server_test.go (about)

     1  package server
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"net"
     7  	"sync"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/cmd-stream/base-go"
    12  	"github.com/cmd-stream/base-go/testdata/mock"
    13  	"github.com/ymz-ncnk/mok"
    14  )
    15  
    16  const Delta = 100 * time.Millisecond
    17  
    18  func TestServer(t *testing.T) {
    19  
    20  	t.Run("Serving should fail if Conf.WorkersCount == 0", func(t *testing.T) {
    21  		var (
    22  			wantErr = ErrNoWorkers
    23  			conf    = Conf{WorkersCount: 0}
    24  			server  = &Server{Conf: conf}
    25  			err     = server.Serve(nil)
    26  		)
    27  		if err != wantErr {
    28  			t.Errorf("unexpected error, want '%v' actual '%v'", wantErr, err)
    29  		}
    30  	})
    31  
    32  	t.Run("Server should be able to handle several connections",
    33  		func(t *testing.T) {
    34  			var (
    35  				wantErr       = ErrClosed
    36  				wantHandleErr = errors.New("handle conn failed")
    37  				wg            = func() *sync.WaitGroup {
    38  					wg := &sync.WaitGroup{}
    39  					wg.Add(2)
    40  					return wg
    41  				}()
    42  				addr1    = &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 9900}
    43  				conn1    = MakeConn(addr1)
    44  				addr2    = &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 9900}
    45  				conn2    = MakeConn(addr2)
    46  				delegate = mock.NewServerDelegate().RegisterNHandle(2,
    47  					func(ctx context.Context, conn net.Conn) (err error) {
    48  						// TODO
    49  						return wantHandleErr
    50  					},
    51  				)
    52  				conf = Conf{
    53  					ConnReceiverConf: ConnReceiverConf{FirstConnTimeout: time.Second},
    54  					WorkersCount:     2,
    55  					LostConnCallback: func(addr net.Addr, err error) {
    56  						if err != wantHandleErr {
    57  							t.Errorf("unexpected error, want '%v' actual '%v'", ErrClosed, err)
    58  						}
    59  						wg.Done()
    60  					},
    61  				}
    62  				listenerErr = errors.New("listener closed")
    63  				listener    = func() mock.Listener {
    64  					done := make(chan struct{})
    65  					return mock.NewListener().RegisterNSetDeadline(2,
    66  						func(t time.Time) error { return nil },
    67  					).RegisterAccept(
    68  						func() (net.Conn, error) { return conn1, nil },
    69  					).RegisterAccept(
    70  						func() (net.Conn, error) { return conn2, nil },
    71  					).RegisterAccept(
    72  						func() (net.Conn, error) {
    73  							<-done
    74  							return nil, listenerErr
    75  						},
    76  					).RegisterClose(
    77  						func() error { close(done); return nil },
    78  					)
    79  					// .RegisterClose(func() (err error) { return nil })
    80  				}()
    81  				mocks = []*mok.Mock{conn1.Mock, conn2.Mock, listener.Mock}
    82  			)
    83  			server, errs := MakeServerAndServe(conf, listener, delegate)
    84  			wg.Wait()
    85  			if err := server.Close(); err != nil {
    86  				t.Fatal(err)
    87  			}
    88  			testAsyncErr(wantErr, errs, mocks, t)
    89  		})
    90  
    91  	t.Run("We should be able to shutdown the server after it receives a conn",
    92  		func(t *testing.T) {
    93  			var (
    94  				wantErr         = ErrShutdown
    95  				wantLostConnErr = errors.New("conn closed by client")
    96  				wg              = func() *sync.WaitGroup {
    97  					wg := &sync.WaitGroup{}
    98  					wg.Add(2)
    99  					return wg
   100  				}()
   101  				addr = &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 9000}
   102  				conn = MakeConn(addr)
   103  				conf = func() Conf {
   104  					return Conf{
   105  						WorkersCount: 1,
   106  						LostConnCallback: func(a net.Addr, err error) {
   107  							if a != addr {
   108  								t.Errorf("unexpected addr, want '%v' actual '%v'", addr, a)
   109  							}
   110  							if err != wantLostConnErr {
   111  								t.Errorf("unexpected err, want '%v' actual '%v'",
   112  									wantLostConnErr,
   113  									err)
   114  							}
   115  						},
   116  					}
   117  				}()
   118  				listener = func() mock.Listener {
   119  					listenerDone := make(chan struct{})
   120  					return mock.NewListener().RegisterAccept(
   121  						func() (net.Conn, error) { return conn, nil },
   122  					).RegisterAccept(
   123  						func() (net.Conn, error) {
   124  							wg.Done()
   125  							<-listenerDone
   126  							return nil, errors.New("listener closed")
   127  						},
   128  					).RegisterClose(
   129  						func() error { close(listenerDone); return nil },
   130  					)
   131  				}()
   132  				delegate = mock.NewServerDelegate().RegisterHandle(
   133  					func(ctx context.Context, conn net.Conn) (err error) {
   134  						wg.Done()
   135  						time.Sleep(100 * time.Millisecond)
   136  						return wantLostConnErr
   137  					},
   138  				)
   139  				mocks = []*mok.Mock{listener.Mock}
   140  			)
   141  			server, errs := MakeServerAndServe(conf, listener,
   142  				delegate)
   143  			wg.Wait()
   144  			if err := server.Shutdown(); err != nil {
   145  				t.Fatal(err)
   146  			}
   147  			testAsyncErr(wantErr, errs, mocks, t)
   148  		})
   149  
   150  	t.Run("We should be able to close the server after it receives a conn",
   151  		func(t *testing.T) {
   152  			var (
   153  				wantErr         = ErrClosed
   154  				wantLostConnErr = ErrClosed
   155  				wg              = func() *sync.WaitGroup {
   156  					wg := &sync.WaitGroup{}
   157  					wg.Add(2)
   158  					return wg
   159  				}()
   160  				addr = &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 9000}
   161  				conn = MakeConn(addr)
   162  				conf = func() Conf {
   163  					return Conf{
   164  						WorkersCount: 1,
   165  						LostConnCallback: func(a net.Addr, err error) {
   166  							if a != addr {
   167  								t.Errorf("unexpected addr, want '%v' actual '%v'", addr, a)
   168  							}
   169  							if err != wantLostConnErr {
   170  								t.Errorf("unexpected err, want '%v' actual '%v'",
   171  									wantLostConnErr,
   172  									err)
   173  							}
   174  						},
   175  					}
   176  				}()
   177  				listener = func() mock.Listener {
   178  					listenerDone := make(chan struct{})
   179  					return mock.NewListener().RegisterAccept(
   180  						func() (net.Conn, error) { return conn, nil },
   181  					).RegisterAccept(
   182  						func() (net.Conn, error) {
   183  							wg.Done()
   184  							<-listenerDone
   185  							return nil, errors.New("listener closed")
   186  						},
   187  					).RegisterClose(
   188  						func() error { close(listenerDone); return nil },
   189  					)
   190  				}()
   191  				delegate = mock.NewServerDelegate().RegisterHandle(
   192  					func(ctx context.Context, conn net.Conn) (err error) {
   193  						wg.Done()
   194  						<-ctx.Done()
   195  						return context.Canceled
   196  					},
   197  				)
   198  				mocks = []*mok.Mock{listener.Mock}
   199  			)
   200  			server, errs := MakeServerAndServe(conf, listener,
   201  				delegate)
   202  			wg.Wait()
   203  			if err := server.Close(); err != nil {
   204  				t.Fatal(err)
   205  			}
   206  			testAsyncErr(wantErr, errs, mocks, t)
   207  		})
   208  
   209  	t.Run("We should be able to shutdown the server before it receives a conn",
   210  		func(t *testing.T) {
   211  			var (
   212  				wantErr  = ErrShutdown
   213  				conf     = Conf{WorkersCount: 1}
   214  				listener = func() mock.Listener {
   215  					done := make(chan struct{})
   216  					listener := mock.NewListener().RegisterAccept(
   217  						func() (conn net.Conn, err error) {
   218  							<-done
   219  							err = errors.New("listener closed")
   220  							return
   221  						},
   222  					).RegisterClose(
   223  						func() (err error) {
   224  							close(done)
   225  							return nil
   226  						},
   227  					)
   228  					return listener
   229  				}()
   230  				delegate = mock.NewServerDelegate()
   231  				mocks    = []*mok.Mock{listener.Mock, delegate.Mock}
   232  			)
   233  			server, errs := MakeServerAndServe(conf, listener, delegate)
   234  			time.Sleep(100 * time.Millisecond)
   235  			err := server.Shutdown()
   236  			if err != nil {
   237  				t.Fatal(err)
   238  			}
   239  			testAsyncErr(wantErr, errs, mocks, t)
   240  		})
   241  
   242  	t.Run("We should be able to close the server before it receives a conn",
   243  		func(t *testing.T) {
   244  			var (
   245  				wantErr  = ErrClosed
   246  				conf     = Conf{WorkersCount: 1}
   247  				listener = func() mock.Listener {
   248  					done := make(chan struct{})
   249  					listener := mock.NewListener().RegisterAccept(
   250  						func() (conn net.Conn, err error) {
   251  							<-done
   252  							err = errors.New("listener closed")
   253  							return
   254  						},
   255  					).RegisterClose(
   256  						func() (err error) {
   257  							close(done)
   258  							return nil
   259  						},
   260  					)
   261  					return listener
   262  				}()
   263  				delegate = mock.NewServerDelegate()
   264  				mocks    = []*mok.Mock{listener.Mock, delegate.Mock}
   265  			)
   266  			server, errs := MakeServerAndServe(conf, listener, delegate)
   267  			time.Sleep(100 * time.Millisecond)
   268  			err := server.Close()
   269  			if err != nil {
   270  				t.Fatal(err)
   271  			}
   272  			testAsyncErr(wantErr, errs, mocks, t)
   273  		})
   274  
   275  	t.Run("Shutdown should fail with an error, if server is not serving",
   276  		func(t *testing.T) {
   277  			var (
   278  				wantErr = ErrNotServing
   279  				conf    = Conf{WorkersCount: 1}
   280  				server  = &Server{Conf: conf}
   281  				err     = server.Shutdown()
   282  			)
   283  			if err != wantErr {
   284  				t.Errorf("unexpected err, want '%v' actual '%v'", wantErr, err)
   285  			}
   286  		})
   287  
   288  	t.Run("Close should fail with an error, if server is not serving",
   289  		func(t *testing.T) {
   290  			var (
   291  				wantErr = ErrNotServing
   292  				conf    = Conf{WorkersCount: 1}
   293  				server  = &Server{Conf: conf}
   294  				err     = server.Close()
   295  			)
   296  			if err != wantErr {
   297  				t.Errorf("unexpected err, want '%v' actual '%v'", wantErr, err)
   298  			}
   299  		})
   300  
   301  }
   302  
   303  func MakeServerAndServe(conf Conf, listener base.Listener,
   304  	delegate mock.ServerDelegate) (server *Server, errs <-chan error) {
   305  	server = &Server{Conf: conf, Delegate: delegate}
   306  	ch := make(chan error, 1)
   307  	go func() {
   308  		err := server.Serve(listener)
   309  		ch <- err
   310  		close(ch)
   311  	}()
   312  	return server, ch
   313  }
   314  
   315  func MakeConn(addr net.Addr) mock.Conn {
   316  	return mock.NewConn().RegisterRemoteAddr(
   317  		func() net.Addr { return addr },
   318  	)
   319  }
   320  
   321  func SameTime(t1, t2 time.Time) bool {
   322  	return !(t1.Before(t2.Truncate(Delta)) || t1.After(t2.Add(Delta)))
   323  }
   324  
   325  func testAsyncErr(wantErr error, errs <-chan error, mocks []*mok.Mock,
   326  	t *testing.T) {
   327  	select {
   328  	case <-time.NewTimer(500 * time.Millisecond).C:
   329  		t.Error("test lasts too long")
   330  	case err := <-errs:
   331  		if err != wantErr {
   332  			t.Errorf("unexpected error, want '%v' actual '%v'", wantErr, err)
   333  		}
   334  	}
   335  	if infomap := mok.CheckCalls(mocks); len(infomap) > 0 {
   336  		t.Error(infomap)
   337  	}
   338  }