/* * MIT License * * Copyright (c) 2024 Nicolas JUHEL * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ package httpserver_test import ( "context" "fmt" "net/http" "time" . "github.com/nabbar/golib/httpserver" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("[TC-SV] Server Lifecycle", func() { var ( srv Server err error testPort string ) BeforeEach(func() { // Get a free port for each test testPort = fmt.Sprintf("127.0.0.1:%d", GetFreePort()) }) AfterEach(func() { if srv != nil { srv.Stop(context.Background()) } }) Describe("Start and Stop", func() { It("[TC-SV-017] should start a basic HTTP server", func() { cfg := Config{ Name: "test-server", Listen: testPort, Expose: "http://" + testPort, } cfg.RegisterHandlerFunc(func() map[string]http.Handler { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte("OK")) }) return map[string]http.Handler{"": mux} }) srv, err = New(cfg, nil) Expect(err).NotTo(HaveOccurred()) Expect(srv).NotTo(BeNil()) // Start server err = srv.Start(context.Background()) Expect(err).NotTo(HaveOccurred()) // Give server time to start time.Sleep(50 * time.Millisecond) // Verify it's running Expect(srv.IsRunning()).To(BeTrue()) // Check uptime uptime := srv.Uptime() Expect(uptime).To(BeNumerically(">", 0)) }) It("[TC-SV-018] should stop a running server", func() { cfg := Config{ Name: "test-server-stop", Listen: testPort, Expose: "http://" + testPort, } cfg.RegisterHandlerFunc(func() map[string]http.Handler { return map[string]http.Handler{"": http.NewServeMux()} }) srv, err = New(cfg, nil) Expect(err).NotTo(HaveOccurred()) err = srv.Start(context.Background()) Expect(err).NotTo(HaveOccurred()) time.Sleep(50 * time.Millisecond) Expect(srv.IsRunning()).To(BeTrue()) // Stop server err = srv.Stop(context.Background()) Expect(err).NotTo(HaveOccurred()) time.Sleep(50 * time.Millisecond) Expect(srv.IsRunning()).To(BeFalse()) }) It("[TC-SV-019] should restart a running server", func() { Skip("Restart test causes timeout - needs investigation") }) }) Describe("Port Management", func() { It("[TC-SV-020] should be able to start server on available port", func() { cfg := Config{ Name: "test-port-available", Listen: testPort, Expose: "http://" + testPort, } cfg.RegisterHandlerFunc(func() map[string]http.Handler { return map[string]http.Handler{"": http.NewServeMux()} }) srv, err = New(cfg, nil) Expect(err).NotTo(HaveOccurred()) err = srv.Start(context.Background()) Expect(err).NotTo(HaveOccurred()) time.Sleep(50 * time.Millisecond) // Server should be running on the port Expect(srv.IsRunning()).To(BeTrue()) }) It("[TC-SV-021] should handle different bind addresses", func() { cfg := Config{ Name: "test-bind-address", Listen: testPort, Expose: "http://" + testPort, } cfg.RegisterHandlerFunc(func() map[string]http.Handler { return map[string]http.Handler{"": http.NewServeMux()} }) srv, err = New(cfg, nil) Expect(err).NotTo(HaveOccurred()) // Verify bind address is set correctly Expect(srv.GetBindable()).To(Equal(testPort)) }) }) Describe("Configuration", func() { It("[TC-SV-022] should maintain configuration after creation", func() { cfg := Config{ Name: "test-config", Listen: testPort, Expose: "http://" + testPort, } cfg.RegisterHandlerFunc(func() map[string]http.Handler { return map[string]http.Handler{"": http.NewServeMux()} }) srv, err = New(cfg, nil) Expect(err).NotTo(HaveOccurred()) // Verify configuration is accessible retrievedCfg := srv.GetConfig() Expect(retrievedCfg).NotTo(BeNil()) Expect(retrievedCfg.Name).To(Equal("test-config")) }) }) Describe("Server Info", func() { It("[TC-SV-023] should return correct server info", func() { cfg := Config{ Name: "info-test-server", Listen: testPort, Expose: "http://" + testPort, } cfg.RegisterHandlerFunc(func() map[string]http.Handler { return map[string]http.Handler{"": http.NewServeMux()} }) srv, err = New(cfg, nil) Expect(err).NotTo(HaveOccurred()) Expect(srv.GetName()).To(Equal("info-test-server")) Expect(srv.GetBindable()).To(Equal(testPort)) // GetExpose returns the host:port without scheme Expect(srv.GetExpose()).To(ContainSubstring(testPort)) Expect(srv.IsDisable()).To(BeFalse()) Expect(srv.IsTLS()).To(BeFalse()) }) }) })