diff options
| author | mihna123 <mihailonvojinovic@gmail.com> | 2026-08-21 22:00:36 +0200 |
|---|---|---|
| committer | mihna123 <mihailonvojinovic@gmail.com> | 2026-08-21 22:00:36 +0200 |
| commit | ba8b50f25ec898d77f736c4435770c80143ae5a1 (patch) | |
| tree | 10cf464eb45100c2d5b5a270b20442a5ccb2a449 | |
| parent | e7fc0e9d5d116ba7337ae4f7ed032e1752db75f0 (diff) | |
| download | openstore-ba8b50f25ec898d77f736c4435770c80143ae5a1.tar.gz openstore-ba8b50f25ec898d77f736c4435770c80143ae5a1.zip | |
test: add tests for server
| -rw-r--r-- | internal/server/server_test.go | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/internal/server/server_test.go b/internal/server/server_test.go new file mode 100644 index 0000000..af86868 --- /dev/null +++ b/internal/server/server_test.go @@ -0,0 +1,122 @@ +package server + +import ( + "io" + "log/slog" + "net/http" + "net/http/httptest" + "os" + "strings" + "testing" +) + +// TestMain silences slog so the request logs from logRequests don't +// clutter test output. +func TestMain(m *testing.M) { + slog.SetDefault(slog.New(slog.NewTextHandler(io.Discard, nil))) + os.Exit(m.Run()) +} + +func newTestServer(t *testing.T) *Server { + t.Helper() + return New(&Config{Name: "test"}) +} + +func TestRoutes(t *testing.T) { + s := newTestServer(t) + + tests := []struct { + name string + method string + path string + want int + }{ + {"index", http.MethodGet, "/", http.StatusOK}, + {"index style", http.MethodGet, "/css/style.css", http.StatusOK}, + {"unknown path", http.MethodGet, "/nonsense", http.StatusNotFound}, + {"wrong method", http.MethodPost, "/", http.StatusMethodNotAllowed}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rec := httptest.NewRecorder() + s.mux.ServeHTTP(rec, httptest.NewRequest(tt.method, tt.path, nil)) + + if rec.Code != tt.want { + t.Errorf("%s %s: got status %d, want %d", tt.method, tt.path, rec.Code, tt.want) + } + }) + } +} + +func TestIndexRendersTemplate(t *testing.T) { + s := New(&Config{Name: "testname123"}) + + rec := httptest.NewRecorder() + s.mux.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/", nil)) + + body := rec.Body.String() + if !strings.Contains(body, "testname123") { + t.Errorf("index body doesn't contain server name, got:\n%s", body) + } + + if ct := rec.Header().Get("Content-Type"); !strings.HasPrefix(ct, "text/html") { + t.Errorf("got Content-Type %q, want text/html", ct) + } +} + +func TestStatusRecorder(t *testing.T) { + t.Run("records and forwards WriteHeader", func(t *testing.T) { + rec := httptest.NewRecorder() + sr := &statusRecorder{ResponseWriter: rec, status: http.StatusOK} + + sr.WriteHeader(http.StatusTeapot) + + if sr.status != http.StatusTeapot { + t.Errorf("recorder kept status %d, want %d", sr.status, http.StatusTeapot) + } + if rec.Code != http.StatusTeapot { + t.Errorf("underlying writer got status %d, want %d", rec.Code, http.StatusTeapot) + } + }) + + t.Run("defaults to 200 when handler never calls WriteHeader", func(t *testing.T) { + rec := httptest.NewRecorder() + sr := &statusRecorder{ResponseWriter: rec, status: http.StatusOK} + + if _, err := sr.Write([]byte("hello")); err != nil { + t.Fatalf("Write: %v", err) + } + + if sr.status != http.StatusOK { + t.Errorf("recorder kept status %d, want %d", sr.status, http.StatusOK) + } + }) +} + +// TestLogRequests checks the middleware passes requests through untouched, +// on both the Write path (200) and the WriteHeader path (404). +func TestLogRequests(t *testing.T) { + s := newTestServer(t) + handler := logRequests(s.mux) + + tests := []struct { + name string + path string + want int + }{ + {"success passes through", "/", http.StatusOK}, + {"not found passes through", "/nonsense", http.StatusNotFound}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rec := httptest.NewRecorder() + handler.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, tt.path, nil)) + + if rec.Code != tt.want { + t.Errorf("got status %d, want %d", rec.Code, tt.want) + } + }) + } +} |
