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() for _, path := range []string{"/", "/projects", "/about", "/services", "/contact", "/projects/courtyard-house"} { 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) } } } 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) } } } func TestContactSubmissionPersists(t *testing.T) { srv := newTestServer(t) 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"}, } 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) } if len(requests) != 1 || requests[0].Email != "jane@example.com" || requests[0].ProjectType != "Renovation or extension" || requests[0].Status != "new" { t.Fatalf("unexpected contact requests: %+v", requests) } } 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) } } 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) } } 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) } } }