// Copyright 2026 Mihna123 Software // // 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 handlers import ( "net/http" "net/http/httptest" "strings" "testing" "git.mihna123.com/openstore/internal/config" ) func newTestConfig() *config.Config { return &config.Config{ Serve: config.Serve{Addr: ":3000"}, Instance: config.Instance{ Name: "TestStore", ContactEmail: "test@store.com", BaseURL: "https://test.store.com", }, } } func get(t *testing.T, mux *http.ServeMux, method, path string) *httptest.ResponseRecorder { t.Helper() rec := httptest.NewRecorder() mux.ServeHTTP(rec, httptest.NewRequest(method, path, nil)) return rec } func TestRoutes(t *testing.T) { mux := Routes(newTestConfig()) tests := []struct { name string method string path string want int }{ {"home", http.MethodGet, "/", http.StatusOK}, {"stylesheet", http.MethodGet, "/css/style.css", http.StatusOK}, {"missing stylesheet", http.MethodGet, "/css/nope.css", http.StatusNotFound}, {"templates not served", http.MethodGet, "/templates/index.html", http.StatusNotFound}, {"unknown path", http.MethodGet, "/nonsense", http.StatusNotFound}, {"wrong method on home", http.MethodPost, "/", http.StatusMethodNotAllowed}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { rec := get(t, mux, tt.method, tt.path) if rec.Code != tt.want { t.Errorf("%s %s: got status %d, want %d", tt.method, tt.path, rec.Code, tt.want) } }) } } func TestStylesheetIsServed(t *testing.T) { mux := Routes(newTestConfig()) rec := get(t, mux, http.MethodGet, "/css/style.css") if rec.Code != http.StatusOK { t.Fatalf("got status %d, want 200", rec.Code) } if ct := rec.Header().Get("Content-Type"); !strings.HasPrefix(ct, "text/css") { t.Errorf("got Content-Type %q, want text/css", ct) } if rec.Body.Len() == 0 { t.Error("stylesheet served empty") } } func TestHomeLinksServedStylesheet(t *testing.T) { mux := Routes(newTestConfig()) body := get(t, mux, http.MethodGet, "/").Body.String() if !strings.Contains(body, `href="/css/style.css"`) { t.Fatalf("home does not link the stylesheet, got:\n%s", body) } if code := get(t, mux, http.MethodGet, "/css/style.css").Code; code != http.StatusOK { t.Errorf("linked stylesheet returned %d, want 200", code) } } func TestHomeRendersInstanceName(t *testing.T) { cfg := newTestConfig() cfg.Instance.Name = "Prodavnica123" mux := Routes(cfg) rec := get(t, mux, http.MethodGet, "/") if rec.Code != http.StatusOK { t.Fatalf("got status %d, want 200", rec.Code) } body := rec.Body.String() if strings.Count(body, "Prodavnica123") != 2 { t.Errorf("instance name should appear in title and heading, 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 TestHomeEscapesInstanceName(t *testing.T) { cfg := newTestConfig() cfg.Instance.Name = `` mux := Routes(cfg) body := get(t, mux, http.MethodGet, "/").Body.String() if strings.Contains(body, "