๐พ Archived View for source.community โบ ckaznocha โบ gemini โบ blob โบ main โบ server_test.go captured on 2023-07-10 at 13:31:07. Gemini links have been rewritten to link to archived content
โฌ ๏ธ Previous capture (2023-01-29)
โก๏ธ Next capture (2024-02-05)
-=-=-=-=-=-=-
. ,-. ,-. . . ,-. ,-. ,-. ,-. ,-. ,-,-. ,-,-. . . ,-. . |- . . `-. | | | | | | |-' | | | | | | | | | | | | | | | | | `-' `-' `-^ ' `-' `-' :: `-' `-' ' ' ' ' ' ' `-^ ' ' ' `' `-| /| `-'
git clone https://source.community/ckaznocha/gemini.git
View raw contents of /server_test.go (main)
โโโโโฎ 1โ package gemini_test 2โ 3โ import ( 4โ "bufio" 5โ "context" 6โ "crypto/tls" 7โ "errors" 8โ "fmt" 9โ "net" 10โ "strings" 11โ "sync" 12โ "testing" 13โ "time" 14โ 15โ "source.community/ckaznocha/gemini" 16โ ) 17โ 18โ func failOnErrIfServerRunning(t *testing.T, done <-chan struct{}, err error, msg string) bool { 19โ t.Helper() 20โ 21โ select { 22โ case <-done: 23โ return true 24โ default: 25โ if err != nil { 26โ t.Error(msg) 27โ 28โ return true 29โ } 30โ 31โ return false 32โ } 33โ } 34โ 35โ func TestServer_ListenAndServeTLS(t *testing.T) { 36โ t.Parallel() 37โ 38โ type fields struct { 39โ Handler gemini.Handler 40โ LogHandler func(message string, isError bool) 41โ BaseContext func(net.Listener) context.Context 42โ ConnContext func(ctx context.Context, c net.Conn) context.Context 43โ TLSConfig *tls.Config 44โ } 45โ 46โ type args struct { 47โ addr string 48โ certFile string 49โ keyFile string 50โ } 51โ 52โ tests := []struct { 53โ name string 54โ fields fields 55โ args args 56โ wantErr bool 57โ shutdownFirst bool 58โ }{ 59โ { 60โ name: "Starts a server on the given address", 61โ fields: fields{ 62โ Handler: gemini.NewServeMux(), 63โ }, 64โ args: args{ 65โ addr: "localhost:8049", 66โ certFile: "testdata/cert.pem", 67โ keyFile: "testdata/key.pem", 68โ }, 69โ }, 70โ { 71โ name: "Exits if the server has already been shutdown", 72โ shutdownFirst: true, 73โ }, 74โ } 75โ 76โ for _, tt := range tests { 77โ tt := tt 78โ t.Run(tt.name, func(t *testing.T) { 79โ t.Parallel() 80โ s := &gemini.Server{ 81โ Handler: tt.fields.Handler, 82โ LogHandler: tt.fields.LogHandler, 83โ BaseContext: tt.fields.BaseContext, 84โ ConnContext: tt.fields.ConnContext, 85โ TLSConfig: tt.fields.TLSConfig, 86โ } 87โ 88โ if tt.shutdownFirst { 89โ s.Shutdown(context.Background()) 90โ } 91โ 92โ clientDone := make(chan struct{}, 1) 93โ serverDone := make(chan struct{}, 1) 94โ 95โ go func() { 96โ defer func() { 97โ if err := s.Shutdown(context.Background()); err != nil { 98โ t.Errorf("Server.ListenAndServeTLS(%v, %v) Unable to shutdown: error = %v", tt.args.certFile, tt.args.keyFile, err) 99โ } 100โ 101โ clientDone <- struct{}{} 102โ }() 103โ 104โ conn, err := tls.DialWithDialer( 105โ &net.Dialer{Timeout: 1 * time.Second}, 106โ "tcp", 107โ tt.args.addr, 108โ &tls.Config{ 109โ InsecureSkipVerify: true, 110โ }, 111โ ) 112โ if failOnErrIfServerRunning( 113โ t, 114โ serverDone, 115โ err, 116โ fmt.Sprintf("Server.ListenAndServeTLS(%v, %v) Unable to dial: error = %v", tt.args.certFile, tt.args.keyFile, err), 117โ ) { 118โ return 119โ } 120โ 121โ if _, err = fmt.Fprint(conn, "gemini://localhost.com/\r\n"); failOnErrIfServerRunning( 122โ t, 123โ serverDone, 124โ err, 125โ fmt.Sprintf("Server.ListenAndServeTLS(%v, %v) Unable to write request: error = %v", tt.args.certFile, tt.args.keyFile, err), 126โ ) { 127โ return 128โ } 129โ 130โ b := bufio.NewReader(conn) 131โ got, err := b.ReadBytes('\n') 132โ if failOnErrIfServerRunning( 133โ t, 134โ serverDone, 135โ err, 136โ fmt.Sprintf("Server.ListenAndServeTLS(%v, %v) Unable to read response: error = %v", tt.args.certFile, tt.args.keyFile, err), 137โ ) { 138โ return 139โ } 140โ 141โ if string(got) != "51 The requested resource could not be found but may be available in the future.\r\n" { 142โ t.Errorf("Server.ListenAndServeTLS(%v, %v) Incorrect response: got = %v", tt.args.certFile, tt.args.keyFile, string(got)) 143โ 144โ return 145โ } 146โ 147โ if err = conn.Close(); failOnErrIfServerRunning( 148โ t, 149โ serverDone, 150โ err, 151โ fmt.Sprintf("Server.ListenAndServeTLS(%v, %v) Unable to close connection: error = %v", tt.args.certFile, tt.args.keyFile, err), 152โ ) { 153โ return 154โ } 155โ }() 156โ 157โ if err := s.ListenAndServeTLS( 158โ tt.args.addr, 159โ tt.args.certFile, 160โ tt.args.keyFile, 161โ ); !errors.Is(err, gemini.ErrServerShutdown) != tt.wantErr { 162โ t.Errorf("Server.ListenAndServeTLS(%v, %v) error = %v, wantErr %v", tt.args.certFile, tt.args.keyFile, err, tt.wantErr) 163โ } 164โ 165โ serverDone <- struct{}{} 166โ <-clientDone 167โ }) 168โ } 169โ } 170โ 171โ func TestServer_ServeTLS(t *testing.T) { 172โ t.Parallel() 173โ 174โ type fields struct { 175โ Handler gemini.Handler 176โ LogHandler func(message string, isError bool) 177โ BaseContext func(net.Listener) context.Context 178โ ConnContext func(ctx context.Context, c net.Conn) context.Context 179โ TLSConfig *tls.Config 180โ } 181โ 182โ type args struct { 183โ certFile string 184โ keyFile string 185โ } 186โ 187โ tests := []struct { 188โ name string 189โ fields fields 190โ args args 191โ wantErr bool 192โ shutdownFirst bool 193โ }{ 194โ { 195โ name: "", 196โ fields: fields{ 197โ Handler: gemini.NewServeMux(), 198โ }, 199โ args: args{ 200โ certFile: "", 201โ keyFile: "", 202โ }, 203โ wantErr: true, 204โ }, 205โ { 206โ name: "", 207โ args: args{ 208โ certFile: "testdata/cert.pem", 209โ keyFile: "testdata/key.pem", 210โ }, 211โ }, 212โ { 213โ name: "", 214โ fields: fields{ 215โ TLSConfig: &tls.Config{MinVersion: tls.VersionTLS11}, 216โ }, 217โ args: args{ 218โ certFile: "testdata/cert.pem", 219โ keyFile: "testdata/key.pem", 220โ }, 221โ wantErr: true, 222โ }, 223โ { 224โ name: "", 225โ fields: fields{}, 226โ args: args{ 227โ certFile: "testdata/cert.pem", 228โ keyFile: "testdata/key.pem", 229โ }, 230โ shutdownFirst: true, 231โ }, 232โ } 233โ 234โ for _, tt := range tests { 235โ tt := tt 236โ t.Run(tt.name, func(t *testing.T) { 237โ t.Parallel() 238โ s := &gemini.Server{ 239โ Handler: tt.fields.Handler, 240โ LogHandler: tt.fields.LogHandler, 241โ BaseContext: tt.fields.BaseContext, 242โ ConnContext: tt.fields.ConnContext, 243โ TLSConfig: tt.fields.TLSConfig, 244โ } 245โ 246โ l, err := net.Listen("tcp", "localhost:0") 247โ if err != nil { 248โ t.Fatalf("unable to open listener: %s", err) 249โ } 250โ 251โ if tt.shutdownFirst { 252โ s.Shutdown(context.Background()) 253โ } 254โ 255โ clientDone := make(chan struct{}, 1) 256โ serverDone := make(chan struct{}, 1) 257โ 258โ go func() { 259โ defer func() { 260โ if err := s.Shutdown(context.Background()); err != nil { 261โ t.Errorf("Server.ListenAndServeTLS(%v, %v) Unable to shutdown: error = %v", tt.args.certFile, tt.args.keyFile, err) 262โ } 263โ 264โ clientDone <- struct{}{} 265โ }() 266โ 267โ conn, err := tls.DialWithDialer( 268โ &net.Dialer{Timeout: 1 * time.Second}, 269โ "tcp", 270โ l.Addr().String(), 271โ &tls.Config{ 272โ InsecureSkipVerify: true, 273โ }, 274โ ) 275โ if failOnErrIfServerRunning( 276โ t, 277โ serverDone, 278โ err, 279โ fmt.Sprintf("Server.ListenAndServeTLS(%v, %v) Unable to dial: error = %v", tt.args.certFile, tt.args.keyFile, err), 280โ ) { 281โ return 282โ } 283โ 284โ if _, err = fmt.Fprint(conn, "gemini://localhost.com/\r\n"); failOnErrIfServerRunning( 285โ t, 286โ serverDone, 287โ err, 288โ fmt.Sprintf("Server.ListenAndServeTLS(%v, %v) Unable to write request: error = %v", tt.args.certFile, tt.args.keyFile, err), 289โ ) { 290โ return 291โ } 292โ 293โ b := bufio.NewReader(conn) 294โ got, err := b.ReadBytes('\n') 295โ if failOnErrIfServerRunning( 296โ t, 297โ serverDone, 298โ err, 299โ fmt.Sprintf("Server.ListenAndServeTLS(%v, %v) Unable to read response: error = %v", tt.args.certFile, tt.args.keyFile, err), 300โ ) { 301โ return 302โ } 303โ 304โ if string(got) != "51 The requested resource could not be found but may be available in the future.\r\n" { 305โ t.Errorf("Server.ListenAndServeTLS(%v, %v) Incorrect response: got = %v", tt.args.certFile, tt.args.keyFile, string(got)) 306โ 307โ return 308โ } 309โ 310โ if err = conn.Close(); failOnErrIfServerRunning( 311โ t, 312โ serverDone, 313โ err, 314โ fmt.Sprintf("Server.ListenAndServeTLS(%v, %v) Unable to close connection: error = %v", tt.args.certFile, tt.args.keyFile, err), 315โ ) { 316โ return 317โ } 318โ }() 319โ 320โ if err := s.ServeTLS(l, tt.args.certFile, tt.args.keyFile); !errors.Is(err, gemini.ErrServerShutdown) != tt.wantErr { 321โ t.Errorf("Server.ServeTLS(%v, %v, %v) error = %v, wantErr %v", l, tt.args.certFile, tt.args.keyFile, err, tt.wantErr) 322โ } 323โ 324โ serverDone <- struct{}{} 325โ <-clientDone 326โ }) 327โ } 328โ } 329โ 330โ func TestServer_Serve(t *testing.T) { 331โ t.Parallel() 332โ 333โ type fields struct { 334โ Handler gemini.Handler 335โ LogHandler func(message string, isError bool) 336โ BaseContext func(net.Listener) context.Context 337โ ConnContext func(ctx context.Context, c net.Conn) context.Context 338โ TLSConfig *tls.Config 339โ } 340โ 341โ tests := []struct { 342โ fields fields 343โ name string 344โ reqBody string 345โ wantResponse string 346โ wantErr bool 347โ shutdownFirst bool 348โ wantNonTLS bool 349โ }{ 350โ { 351โ name: "works", 352โ fields: fields{ 353โ Handler: gemini.NewServeMux(), 354โ }, 355โ wantResponse: "51 The requested resource could not be found but may be available in the future.\r\n", 356โ }, 357โ { 358โ name: "errors when its not a TLS connection", 359โ fields: fields{ 360โ Handler: gemini.NewServeMux(), 361โ }, 362โ wantErr: true, 363โ wantNonTLS: true, 364โ }, 365โ { 366โ name: "errors when the server has already been shutdown", 367โ fields: fields{ 368โ Handler: gemini.NewServeMux(), 369โ }, 370โ shutdownFirst: true, 371โ }, 372โ { 373โ name: "errors base context is nil", 374โ fields: fields{ 375โ Handler: gemini.NewServeMux(), 376โ BaseContext: func(l net.Listener) context.Context { return nil }, 377โ }, 378โ wantErr: true, 379โ }, 380โ { 381โ name: "errors conn context is nil", 382โ fields: fields{ 383โ Handler: gemini.NewServeMux(), 384โ ConnContext: func(ctx context.Context, c net.Conn) context.Context { return nil }, 385โ }, 386โ wantErr: true, 387โ }, 388โ { 389โ name: "handles a too large request", 390โ fields: fields{ 391โ Handler: gemini.NewServeMux(), 392โ }, 393โ wantResponse: "59 the request length exceeded the max size of 1024 bytes\r\n", 394โ reqBody: "gemini://localhost/" + strings.Repeat("n", 1028-len("gemini://localhost/\r\n")) + "\r\n", 395โ }, 396โ } 397โ 398โ for _, tt := range tests { 399โ tt := tt 400โ t.Run(tt.name, func(t *testing.T) { 401โ t.Parallel() 402โ s := &gemini.Server{ 403โ Handler: tt.fields.Handler, 404โ LogHandler: tt.fields.LogHandler, 405โ BaseContext: tt.fields.BaseContext, 406โ ConnContext: tt.fields.ConnContext, 407โ TLSConfig: tt.fields.TLSConfig, 408โ } 409โ 410โ l, err := net.Listen("tcp", "localhost:0") 411โ if err != nil { 412โ t.Fatalf("unable to open listener: %s", err) 413โ } 414โ 415โ if !tt.wantNonTLS { 416โ var cert tls.Certificate 417โ cert, err = tls.LoadX509KeyPair("testdata/cert.pem", "testdata/key.pem") 418โ if err != nil { 419โ t.Fatalf("unable to open listener: %s", err) 420โ } 421โ 422โ config := &tls.Config{ 423โ Certificates: []tls.Certificate{cert}, 424โ } 425โ l = tls.NewListener(l, config) 426โ } 427โ 428โ if tt.shutdownFirst { 429โ s.Shutdown(context.Background()) 430โ } 431โ 432โ clientDone := make(chan struct{}, 1) 433โ serverDone := make(chan struct{}, 1) 434โ 435โ go func() { 436โ defer func() { 437โ if err = s.Shutdown(context.Background()); err != nil { 438โ t.Errorf("Server.Serve(%v) Unable to shutdown: error = %v", l, err) 439โ } 440โ 441โ clientDone <- struct{}{} 442โ }() 443โ 444โ var conn *tls.Conn 445โ conn, err = tls.DialWithDialer( 446โ &net.Dialer{Timeout: 1 * time.Second}, 447โ "tcp", 448โ l.Addr().String(), 449โ &tls.Config{ 450โ InsecureSkipVerify: true, 451โ }, 452โ ) 453โ if failOnErrIfServerRunning( 454โ t, 455โ serverDone, 456โ err, 457โ fmt.Sprintf("Server.Serve(%v) Unable to dial: error = %v", l, err), 458โ ) { 459โ return 460โ } 461โ 462โ body := "gemini://localhost.com/\r\n" 463โ if tt.reqBody != "" { 464โ body = tt.reqBody 465โ } 466โ 467โ if _, err = fmt.Fprint(conn, body); failOnErrIfServerRunning( 468โ t, 469โ serverDone, 470โ err, 471โ fmt.Sprintf("Server.Serve(%v) Unable to write request: error = %v", l, err), 472โ ) { 473โ return 474โ } 475โ 476โ b := bufio.NewReader(conn) 477โ var got []byte 478โ got, err = b.ReadBytes('\n') 479โ if failOnErrIfServerRunning( 480โ t, 481โ serverDone, 482โ err, 483โ fmt.Sprintf("Server.Serve(%v) Unable to read response: error = %v", l, err), 484โ ) { 485โ return 486โ } 487โ 488โ if string(got) != tt.wantResponse { //!= "51 The requested resource could not be found but may be available in the future.\r\n" { 489โ t.Errorf("Server.Serve(%v) Incorrect response: got = %v, want %s", l, string(got), tt.wantResponse) 490โ 491โ return 492โ } 493โ 494โ if err = conn.Close(); failOnErrIfServerRunning( 495โ t, 496โ serverDone, 497โ err, 498โ fmt.Sprintf("Server.Serve(%v) Unable to close connection: error = %v", l, err), 499โ ) { 500โ return 501โ } 502โ }() 503โ 504โ err = s.Serve(l) 505โ if !errors.Is(err, gemini.ErrServerShutdown) != tt.wantErr { 506โ t.Errorf("Server.Serve(%v) error = %v, wantErr %v", l, err, tt.wantErr) 507โ } 508โ 509โ serverDone <- struct{}{} 510โ <-clientDone 511โ }) 512โ } 513โ } 514โ 515โ func TestServer_RegisterOnShutdown(t *testing.T) { 516โ t.Parallel() 517โ 518โ type args struct { 519โ i int 520โ } 521โ 522โ tests := []struct { 523โ name string 524โ args args 525โ }{ 526โ { 527โ name: "", 528โ args: args{i: 0}, 529โ }, 530โ { 531โ name: "", 532โ args: args{i: 1}, 533โ }, 534โ { 535โ name: "", 536โ args: args{i: 2}, 537โ }, 538โ } 539โ 540โ for _, tt := range tests { 541โ tt := tt 542โ t.Run(tt.name, func(t *testing.T) { 543โ t.Parallel() 544โ s := &gemini.Server{} 545โ var wg sync.WaitGroup 546โ wg.Add(tt.args.i) 547โ 548โ for i := 0; i < tt.args.i; i++ { 549โ s.RegisterOnShutdown(func() { wg.Done() }) 550โ } 551โ 552โ l, err := net.Listen("tcp", "localhost:0") 553โ if err != nil { 554โ t.Fatalf("unable to open listener: %s", err) 555โ } 556โ 557โ cert, err := tls.LoadX509KeyPair("testdata/cert.pem", "testdata/key.pem") 558โ if err != nil { 559โ t.Fatalf("unable to open listener: %s", err) 560โ } 561โ 562โ config := &tls.Config{ 563โ Certificates: []tls.Certificate{cert}, 564โ } 565โ 566โ l = tls.NewListener(l, config) 567โ 568โ go func() { 569โ defer s.Shutdown(context.Background()) 570โ time.Sleep(5 * time.Millisecond) 571โ }() 572โ 573โ if err := s.Serve(l); err != nil && !errors.Is(err, gemini.ErrServerShutdown) { 574โ t.Errorf("Server.Serve(%v) error = %v", l, err) 575โ } 576โ 577โ wg.Wait() 578โ }) 579โ } 580โ } 581โ 582โ func TestServer_Shutdown(t *testing.T) { 583โ t.Parallel() 584โ 585โ type fields struct { 586โ Handler gemini.Handler 587โ LogHandler func(message string, isError bool) 588โ BaseContext func(net.Listener) context.Context 589โ ConnContext func(ctx context.Context, c net.Conn) context.Context 590โ TLSConfig *tls.Config 591โ Addr string 592โ } 593โ 594โ type args struct { 595โ ctx context.Context 596โ } 597โ 598โ tests := []struct { 599โ args args 600โ fields fields 601โ name string 602โ wantErr bool 603โ }{ 604โ // TODO: Add test cases. 605โ } 606โ 607โ for _, tt := range tests { 608โ tt := tt 609โ t.Run(tt.name, func(t *testing.T) { 610โ t.Parallel() 611โ s := &gemini.Server{ 612โ Handler: tt.fields.Handler, 613โ LogHandler: tt.fields.LogHandler, 614โ BaseContext: tt.fields.BaseContext, 615โ ConnContext: tt.fields.ConnContext, 616โ TLSConfig: tt.fields.TLSConfig, 617โ } 618โ if err := s.Shutdown(tt.args.ctx); (err != nil) != tt.wantErr { 619โ t.Errorf("Server.Shutdown(%v) error = %v, wantErr %v", tt.args.ctx, err, tt.wantErr) 620โ } 621โ }) 622โ } 623โ } โโโโโฏ
ยท ยท ยท
ยฉ 2023 source.community