sabisan/internal/app/handlers_public_test.go

220 lines
6.7 KiB
Go
Raw Permalink Normal View History

2026-05-16 23:03:50 +00:00
package app
import (
"io"
"net/http"
"net/http/httptest"
"net/url"
"strconv"
"strings"
"testing"
)
func TestPublicRoutes(t *testing.T) {
srv := newTestServer(t)
handler := srv.Routes()
2026-05-17 12:36:50 +00:00
for _, path := range []string{"/", "/projects", "/about", "/services", "/contact", "/projects/courtyard-house"} {
2026-05-16 23:03:50 +00:00
req := httptest.NewRequest(http.MethodGet, path, nil)
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("%s returned %d", path, rec.Code)
}
}
}
2026-05-17 12:55:41 +00:00
func TestPublicRoutesRenderFullShellOnDirectLoad(t *testing.T) {
srv := newTestServer(t)
req := httptest.NewRequest(http.MethodGet, "/services", nil)
rec := httptest.NewRecorder()
srv.Routes().ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected ok, got %d", rec.Code)
}
body, _ := io.ReadAll(rec.Result().Body)
text := string(body)
for _, want := range []string{"<!doctype html>", `class="drawer drawer-end"`, `id="main-content"`, `id="site-drawer"`} {
if !strings.Contains(text, want) {
t.Fatalf("direct public route missing %q: %s", want, text)
}
}
}
func TestPublicHTMXRoutesReturnMainContentPartial(t *testing.T) {
srv := newTestServer(t)
req := httptest.NewRequest(http.MethodGet, "/services", nil)
req.Header.Set("HX-Request", "true")
rec := httptest.NewRecorder()
srv.Routes().ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected ok, got %d", rec.Code)
}
body, _ := io.ReadAll(rec.Result().Body)
text := string(body)
if strings.Contains(text, "<!doctype html>") || strings.Contains(text, `class="drawer drawer-end"`) {
t.Fatalf("expected partial response, got full document: %s", text)
}
for _, want := range []string{`id="main-content"`, `id="site-header"`, `id="site-drawer-nav"`, `hx-swap-oob="true"`} {
if !strings.Contains(text, want) {
t.Fatalf("HTMX public route missing %q: %s", want, text)
}
}
}
2026-05-17 12:36:50 +00:00
func TestServicesRouteRendersServicesAndFAQs(t *testing.T) {
srv := newTestServer(t)
req := httptest.NewRequest(http.MethodGet, "/services", nil)
rec := httptest.NewRecorder()
srv.Routes().ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected ok, got %d", rec.Code)
}
body, _ := io.ReadAll(rec.Result().Body)
text := string(body)
for _, want := range []string{"Residential architecture", "How projects work", "FAQs", "Start an enquiry"} {
if !strings.Contains(text, want) {
t.Fatalf("services page missing %q: %s", want, text)
}
}
}
func TestHomeRendersPhaseTwoSections(t *testing.T) {
srv := newTestServer(t)
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()
srv.Routes().ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected ok, got %d", rec.Code)
}
body, _ := io.ReadAll(rec.Result().Body)
text := string(body)
for _, want := range []string{"Start an enquiry", "Focused support", "Process", "Studio profile"} {
if !strings.Contains(text, want) {
t.Fatalf("home missing %q: %s", want, text)
}
}
}
func TestStudioRendersExpandedContent(t *testing.T) {
srv := newTestServer(t)
req := httptest.NewRequest(http.MethodGet, "/about", nil)
rec := httptest.NewRecorder()
srv.Routes().ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected ok, got %d", rec.Code)
}
body, _ := io.ReadAll(rec.Result().Body)
text := string(body)
for _, want := range []string{"Philosophy", "Approach", "Experience", "Service area"} {
if !strings.Contains(text, want) {
t.Fatalf("studio missing %q: %s", want, text)
}
}
}
2026-05-16 23:03:50 +00:00
func TestContactSubmissionPersists(t *testing.T) {
srv := newTestServer(t)
2026-05-17 12:36:50 +00:00
form := url.Values{
"name": {"Jane"},
"email": {"jane@example.com"},
"phone": {"123"},
"project_type": {"Renovation or extension"},
"project_location": {"London"},
"budget_range": {"GBP 250k-500k"},
"timeline": {"3-6 months"},
"message": {"New project"},
}
2026-05-16 23:03:50 +00:00
req := httptest.NewRequest(http.MethodPost, "/contact", strings.NewReader(form.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
rec := httptest.NewRecorder()
srv.Routes().ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected ok, got %d", rec.Code)
}
body, _ := io.ReadAll(rec.Result().Body)
if !strings.Contains(string(body), "saved") {
t.Fatalf("expected success message, got %s", body)
}
requests, err := srv.store.ContactRequests(req.Context())
if err != nil {
t.Fatal(err)
}
2026-05-17 12:36:50 +00:00
if len(requests) != 1 || requests[0].Email != "jane@example.com" || requests[0].ProjectType != "Renovation or extension" || requests[0].Status != "new" {
2026-05-16 23:03:50 +00:00
t.Fatalf("unexpected contact requests: %+v", requests)
}
}
2026-05-17 12:36:50 +00:00
func TestContactSubmissionRequiresQualificationFields(t *testing.T) {
srv := newTestServer(t)
form := url.Values{"name": {"Jane"}, "email": {"jane@example.com"}, "message": {"New project"}}
req := httptest.NewRequest(http.MethodPost, "/contact", strings.NewReader(form.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
rec := httptest.NewRecorder()
srv.Routes().ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected ok, got %d", rec.Code)
}
body, _ := io.ReadAll(rec.Result().Body)
if !strings.Contains(string(body), "project type is required") {
t.Fatalf("expected qualification validation message, got %s", body)
}
}
2026-05-16 23:03:50 +00:00
func TestProjectImageOverlay(t *testing.T) {
srv := newTestServer(t)
project, err := srv.store.ProjectBySlug(t.Context(), "courtyard-house")
if err != nil {
t.Fatal(err)
}
if len(project.Images) == 0 {
t.Fatal("expected seeded project images")
}
path := "/projects/" + project.Slug + "/images/" + strconv.FormatInt(project.Images[0].ID, 10) + "/overlay"
req := httptest.NewRequest(http.MethodGet, path, nil)
rec := httptest.NewRecorder()
srv.Routes().ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected ok, got %d", rec.Code)
}
body, _ := io.ReadAll(rec.Result().Body)
if !strings.Contains(string(body), "data-overlay") || !strings.Contains(string(body), project.Images[0].Path) {
t.Fatalf("overlay fragment missing expected content: %s", body)
}
}
2026-05-17 12:36:50 +00:00
func TestProjectDetailRendersDepthFields(t *testing.T) {
srv := newTestServer(t)
req := httptest.NewRequest(http.MethodGet, "/projects/courtyard-house", nil)
rec := httptest.NewRecorder()
srv.Routes().ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected ok, got %d", rec.Code)
}
body, _ := io.ReadAll(rec.Result().Body)
text := string(body)
for _, want := range []string{"Scope", "Completed", "Architecture, interiors, material strategy", "A private house arranged around a quiet internal garden."} {
if !strings.Contains(text, want) {
t.Fatalf("project detail missing %q: %s", want, text)
}
}
}