diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-07-23 15:13:17 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-07-23 15:13:17 +0200 |
| commit | a600efbe3f3d2c29e3aa6e7877937a29429b9a1e (patch) | |
| tree | e471123b0356e40af246842eff9223e7bb935464 /internal/web/server_test.go | |
| parent | aa6c9dad764089a3f0e287b5f90937e97dde3921 (diff) | |
| download | lectio-a600efbe3f3d2c29e3aa6e7877937a29429b9a1e.tar.gz lectio-a600efbe3f3d2c29e3aa6e7877937a29429b9a1e.zip | |
web: default lectio-web to port 1099 with free-port fallback
chooseListener(port) factors the listen logic out of Run: port==0 now
tries defaultWebPort (1099) first and only falls back to an OS-picked
free port if 1099 is taken; a non-zero port is still bound exactly,
surfacing its bind error as before. config.Default() keeps WebPort: 0
unchanged (0 still means "auto"). Adds TestChooseListener, robust to
sandboxes that can't bind 1099 or :0 (t.Skip instead of failing).
Also folds the ?ref= whitespace trim into renderLookup itself so GET /
and GET /lookup treat a whitespace-only ref identically (previously
only lookupHandler trimmed it).
Diffstat (limited to 'internal/web/server_test.go')
| -rw-r--r-- | internal/web/server_test.go | 58 |
1 files changed, 58 insertions, 0 deletions
diff --git a/internal/web/server_test.go b/internal/web/server_test.go index b5cf78a..0268d3b 100644 --- a/internal/web/server_test.go +++ b/internal/web/server_test.go @@ -1,6 +1,7 @@ package web import ( + "net" "net/http" "net/http/httptest" "os" @@ -196,3 +197,60 @@ func TestServer(t *testing.T) { } }) } + +// TestChooseListener exercises chooseListener's port-selection logic +// directly (no HTTP serving): port==0 prefers defaultWebPort (1099) and +// falls back to a free OS port when 1099 is taken, and a non-zero port is +// bound exactly. Sandboxed/CI environments may not permit binding 1099 (or +// may race another process for it), so those assertions t.Skip rather than +// fail the suite. +func TestChooseListener(t *testing.T) { + t.Run("zero port returns a listener with a non-zero port", func(t *testing.T) { + ln, err := chooseListener(0) + if err != nil { + t.Fatalf("chooseListener(0) error: %v", err) + } + defer ln.Close() + port := ln.Addr().(*net.TCPAddr).Port + if port == 0 { + t.Errorf("chooseListener(0) returned port 0, want non-zero") + } + }) + + t.Run("falls back to a free port when 1099 is taken", func(t *testing.T) { + pre, err := net.Listen("tcp", ":1099") + if err != nil { + t.Skipf("cannot bind :1099 in this environment, skipping fallback assertion: %v", err) + } + defer pre.Close() + + ln, err := chooseListener(0) + if err != nil { + t.Fatalf("chooseListener(0) error while 1099 is taken: %v", err) + } + defer ln.Close() + port := ln.Addr().(*net.TCPAddr).Port + if port == 1099 { + t.Errorf("chooseListener(0) returned 1099 even though it was already taken") + } + }) + + t.Run("explicit non-zero port is bound exactly", func(t *testing.T) { + probe, err := net.Listen("tcp", ":0") + if err != nil { + t.Skipf("cannot bind :0 to pick a free port in this environment: %v", err) + } + want := probe.Addr().(*net.TCPAddr).Port + probe.Close() + + ln, err := chooseListener(want) + if err != nil { + t.Skipf("could not bind explicit port %d (likely a race with another process): %v", want, err) + } + defer ln.Close() + got := ln.Addr().(*net.TCPAddr).Port + if got != want { + t.Errorf("chooseListener(%d) bound port %d, want exactly %d", want, got, want) + } + }) +} |
