//go:build linux || freebsd package contactform import ( "strings" "testing" ) func TestValidateContact_Happy(t *testing.T) { r := &Request{ Name: "Jane Doe", Email: "jane@example.com", Service: "architecture", Message: "Hello, I would like to discuss a project.", } if errs := Validate(r, "contact"); len(errs) > 0 { t.Fatalf("expected no errors, got %v", errs) } } func TestValidateContact_DefaultType(t *testing.T) { // Unknown / empty type falls back to contact validation. r := &Request{Name: "Jane Doe", Email: "jane@example.com", Message: "Hello there."} if errs := Validate(r, ""); len(errs) > 0 { t.Fatalf("expected no errors with empty type, got %v", errs) } } func TestValidateContact_RejectsInvalidEmail(t *testing.T) { r := &Request{Name: "Jane", Email: "not-an-email", Message: "A message long enough."} errs := Validate(r, "contact") if len(errs) == 0 { t.Fatal("expected error for invalid email") } if errs[0].Field != "email" { t.Errorf("expected field=email, got %q", errs[0].Field) } } func TestValidateContact_RejectsBadService(t *testing.T) { r := &Request{Name: "Jane", Email: "jane@example.com", Service: "hairstyling", Message: "A message long enough."} errs := Validate(r, "contact") if len(errs) == 0 || errs[0].Field != "service" { t.Fatalf("expected service error, got %v", errs) } } func TestValidateFeedback_OmitsService(t *testing.T) { // Feedback validates like generic: name, email, message; no service field. r := &Request{Name: "Jane", Email: "jane@example.com", Message: "A message long enough."} if errs := Validate(r, "feedback"); len(errs) > 0 { t.Fatalf("expected no errors, got %v", errs) } } func TestValidateGeneric_RejectsShortMessage(t *testing.T) { r := &Request{Name: "Jane", Email: "jane@example.com", Message: "short"} errs := Validate(r, "generic") if len(errs) == 0 || errs[0].Field != "message" { t.Fatalf("expected message error, got %v", errs) } } func TestValidateNewsletter_Happy(t *testing.T) { r := &Request{Email: "jane@example.com"} if errs := Validate(r, "newsletter"); len(errs) > 0 { t.Fatalf("expected no errors, got %v", errs) } } func TestValidateNewsletter_RequiresEmail(t *testing.T) { r := &Request{Email: "garbage"} errs := Validate(r, "newsletter") if len(errs) == 0 { t.Fatal("expected error for missing/invalid email") } } func TestValidateNewsletter_IgnoresNameAndMessage(t *testing.T) { // Newsletter type only cares about the email field. r := &Request{Email: "jane@example.com", Name: "", Message: ""} if errs := Validate(r, "newsletter"); len(errs) > 0 { t.Fatalf("newsletter should ignore empty name/message, got %v", errs) } } func TestValidateRejectsUnknownTypeOnlyWhenTypeExplicitlyInvalid(t *testing.T) { // Sanity: an unsupported form type (e.g. "foo") falls back to contact // validation, not to a hard error. The hard error is enforced at the // config layer, not in Validate itself. r := &Request{Name: "Jane", Email: "jane@example.com", Message: "Hello there."} if errs := Validate(r, "foo"); len(errs) > 0 { t.Fatalf("Validate with unknown type should fall back to contact, got %v", errs) } } func TestValidateTrimsWhitespace(t *testing.T) { r := &Request{ Name: " Jane ", Email: " jane@example.com ", Service: " architecture ", Message: " hello there ", } if errs := Validate(r, "contact"); len(errs) > 0 { t.Fatalf("expected no errors, got %v", errs) } if r.Name != "Jane" || r.Email != "jane@example.com" || r.Message != "hello there" { t.Errorf("expected whitespace to be trimmed, got %+v", r) } } func TestErrorMessageHasMinLengthPlaceholder(t *testing.T) { // Sanity: the minLength error message in the en.json UI mirror should // remain a simple string. Here we just check that error messages are // human-readable, not empty. r := &Request{Name: "J", Email: "jane@example.com", Message: "hi"} errs := Validate(r, "contact") if len(errs) < 2 { t.Fatalf("expected at least 2 errors, got %v", errs) } for _, e := range errs { if strings.TrimSpace(e.Message) == "" { t.Errorf("error message is empty for field %q", e.Field) } } }