package hws_test import ( "io" "math/rand/v2" "net/http" "slices" "testing" "git.haelnorr.com/h/golib/hlog" "git.haelnorr.com/h/golib/hws" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) var ports []uint64 func randomPort() uint64 { port := uint64(3000 + rand.IntN(1001)) for slices.Contains(ports, port) { port = uint64(3000 + rand.IntN(1001)) } ports = append(ports, port) return port } func createTestServer(t *testing.T, w io.Writer) *hws.Server { server, err := hws.NewServer(&hws.Config{ Host: "127.0.0.1", Port: randomPort(), ShutdownDelay: 0, // No delay for tests }) require.NoError(t, err) logger, err := hlog.NewLogger(hlog.LogLevel("Debug"), w, nil, "") require.NoError(t, err) err = server.AddLogger(logger) require.NoError(t, err) return server } var testHandler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte("hello world")) }) func startTestServer(t *testing.T, server *hws.Server) { err := server.AddRoutes(hws.Route{ Path: "/", Method: hws.MethodGET, Handler: testHandler, }) require.NoError(t, err) err = server.Start(t.Context()) require.NoError(t, err) t.Log("Test server started") } func Test_NewServer(t *testing.T) { server, err := hws.NewServer(&hws.Config{ Host: "localhost", Port: randomPort(), }) require.NoError(t, err) require.NotNil(t, server) t.Run("Nil config returns error", func(t *testing.T) { server, err := hws.NewServer(nil) assert.Error(t, err) assert.Nil(t, server) assert.Contains(t, err.Error(), "Config cannot be nil") }) tests := []struct { name string host string port uint64 valid bool }{ { name: "Valid localhost on http", host: "127.0.0.1", port: 80, valid: true, }, { name: "Valid IP on https", host: "192.168.1.1", port: 443, valid: true, }, { name: "Valid IP on port 65535", host: "10.0.0.5", port: 65535, valid: true, }, { name: "0.0.0.0 on port 8080", host: "0.0.0.0", port: 8080, valid: true, }, { name: "Broadcast IP on port 1", host: "255.255.255.255", port: 1, valid: true, }, { name: "Port 0 gets default", host: "127.0.0.1", port: 0, valid: true, // port 0 now gets default value of 3000 }, { name: "Invalid port 65536", host: "127.0.0.1", port: 65536, valid: true, // port is accepted (validated at OS level) }, { name: "No hostname provided gets default", host: "", port: 80, valid: true, // empty hostname gets default 127.0.0.1 }, { name: "Spaces provided for host", host: " ", port: 80, valid: false, }, { name: "Localhost as string", host: "localhost", port: 8080, valid: true, }, { name: "Number only host", host: "1234", port: 80, valid: true, }, { name: "Valid domain on http", host: "example.com", port: 80, valid: true, }, { name: "Valid domain on https", host: "a-b-c.example123.co", port: 443, valid: true, }, { name: "Valid domain starting with a digit", host: "1example.com", port: 8080, valid: true, // labels may start with digits (RFC 1123) }, { name: "Single character hostname", host: "a", port: 1, valid: true, // single-label hostname, min length }, { name: "Hostname starts with a hyphen", host: "-example.com", port: 80, valid: false, // label starts with hyphen }, { name: "Hostname ends with a hyphen", host: "example-.com", port: 80, valid: false, // label ends with hyphen }, { name: "Empty label in hostname", host: "ex..ample.com", port: 80, valid: false, // empty label }, { name: "Invalid character: '_'", host: "exa_mple.com", port: 80, valid: false, // invalid character (_) }, { name: "Trailing dot", host: "example.com.", port: 80, valid: false, // trailing dot not allowed per spec }, { name: "Valid IPv6 localhost", host: "::1", port: 8080, valid: true, // IPv6 localhost }, { name: "Valid IPv6 shortened", host: "2001:db8::1", port: 80, valid: true, // shortened IPv6 }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { server, err := hws.NewServer(&hws.Config{ Host: tt.host, Port: tt.port, }) if tt.valid { assert.NoError(t, err) assert.NotNil(t, server) } else { assert.Error(t, err) } }) } }