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 }