package handler import ( "bytes" "context" "encoding/json" "net/http" "net/http/httptest" "testing" ) func newContactSalesRequest(body CreateContactSalesRequest) *http.Request { var buf bytes.Buffer json.NewEncoder(&buf).Encode(body) req := httptest.NewRequest("POST", "/api/contact-sales", &buf) req.Header.Set("Content-Type", "application/json") return req } func validContactSalesRequest() CreateContactSalesRequest { return CreateContactSalesRequest{ FirstName: "Ada", LastName: "Lovelace", BusinessEmail: "ada@analytical-engine.example", CompanyName: "Analytical Engine Co.", CompanySize: "11-50", CountryRegion: "United Kingdom", UseCase: "evaluate", Goals: "We want to compound agent productivity across the team.", ConsentOutreach: true, ConsentUpdates: false, } } func clearContactSalesForEmail(t *testing.T, email string) { t.Helper() if _, err := testPool.Exec(context.Background(), `DELETE FROM contact_sales_inquiry WHERE business_email = $1`, email); err != nil { t.Fatalf("clear contact_sales_inquiry: %v", err) } t.Cleanup(func() { testPool.Exec(context.Background(), `DELETE FROM contact_sales_inquiry WHERE business_email = $1`, email) }) } func TestCreateContactSalesHappyPath(t *testing.T) { body := validContactSalesRequest() clearContactSalesForEmail(t, body.BusinessEmail) w := httptest.NewRecorder() testHandler.CreateContactSales(w, newContactSalesRequest(body)) if w.Code != http.StatusCreated { t.Fatalf("expected 201, got %d: %s", w.Code, w.Body.String()) } var resp ContactSalesResponse if err := json.NewDecoder(w.Body).Decode(&resp); err != nil { t.Fatalf("decode response: %v", err) } if resp.ID == "" { t.Fatal("expected inquiry id in response") } } func TestCreateContactSalesRejectsFreeEmail(t *testing.T) { body := validContactSalesRequest() body.BusinessEmail = "ada@gmail.com" w := httptest.NewRecorder() testHandler.CreateContactSales(w, newContactSalesRequest(body)) if w.Code != http.StatusBadRequest { t.Fatalf("expected 400, got %d: %s", w.Code, w.Body.String()) } } func TestCreateContactSalesRejectsInvalidEmail(t *testing.T) { body := validContactSalesRequest() body.BusinessEmail = "not-an-email" w := httptest.NewRecorder() testHandler.CreateContactSales(w, newContactSalesRequest(body)) if w.Code != http.StatusBadRequest { t.Fatalf("expected 400, got %d: %s", w.Code, w.Body.String()) } } func TestCreateContactSalesRejectsUnknownCompanySize(t *testing.T) { body := validContactSalesRequest() body.CompanySize = "ten-ish" w := httptest.NewRecorder() testHandler.CreateContactSales(w, newContactSalesRequest(body)) if w.Code != http.StatusBadRequest { t.Fatalf("expected 400, got %d: %s", w.Code, w.Body.String()) } } func TestCreateContactSalesRejectsUnknownUseCase(t *testing.T) { body := validContactSalesRequest() body.UseCase = "world-domination" w := httptest.NewRecorder() testHandler.CreateContactSales(w, newContactSalesRequest(body)) if w.Code != http.StatusBadRequest { t.Fatalf("expected 400, got %d: %s", w.Code, w.Body.String()) } } func TestCreateContactSalesMissingFirstName(t *testing.T) { body := validContactSalesRequest() body.FirstName = " " w := httptest.NewRecorder() testHandler.CreateContactSales(w, newContactSalesRequest(body)) if w.Code != http.StatusBadRequest { t.Fatalf("expected 400, got %d: %s", w.Code, w.Body.String()) } } func TestCreateContactSalesPerEmailRateLimit(t *testing.T) { body := validContactSalesRequest() body.BusinessEmail = "ratelimit-ada@analytical-engine.example" clearContactSalesForEmail(t, body.BusinessEmail) for i := 0; i < contactSalesHourlyEmailCap; i++ { w := httptest.NewRecorder() testHandler.CreateContactSales(w, newContactSalesRequest(body)) if w.Code != http.StatusCreated { t.Fatalf("iteration %d: expected 201, got %d: %s", i, w.Code, w.Body.String()) } } w := httptest.NewRecorder() testHandler.CreateContactSales(w, newContactSalesRequest(body)) if w.Code != http.StatusTooManyRequests { t.Fatalf("expected 429, got %d: %s", w.Code, w.Body.String()) } } func TestIsBusinessEmailDomain(t *testing.T) { cases := []struct { email string want bool }{ {"ada@multica.ai", true}, {"ada@example.com", true}, {"ada@gmail.com", false}, {"ada@Gmail.COM", false}, {"ada@yahoo.co.uk", false}, {"ada@qq.com", false}, {"weird-no-at", false}, {"ada@", false}, } for _, c := range cases { got := isBusinessEmailDomain(c.email) if got != c.want { t.Errorf("isBusinessEmailDomain(%q) = %v, want %v", c.email, got, c.want) } } } func TestCanonicalBusinessEmail(t *testing.T) { cases := []struct { name string input string want string wantOk bool }{ {"plain", "ada@multica.ai", "ada@multica.ai", true}, {"uppercase normalized", "Ada@Multica.AI", "ada@multica.ai", true}, {"trim whitespace", " ada@multica.ai ", "ada@multica.ai", true}, {"display name stripped", "Ada Lovelace ", "ada@multica.ai", true}, {"angle-bracketed", "", "ada@multica.ai", true}, {"empty", "", "", false}, {"only whitespace", " ", "", false}, {"missing at", "no-at-sign", "", false}, {"missing local", "@multica.ai", "", false}, {"missing domain", "ada@", "", false}, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { got, ok := canonicalBusinessEmail(c.input) if ok != c.wantOk || got != c.want { t.Errorf("canonicalBusinessEmail(%q) = (%q, %v), want (%q, %v)", c.input, got, ok, c.want, c.wantOk) } }) } } // Regression: net/mail.ParseAddress accepts display-name forms like // `Ada `. Earlier versions of the handler ran the // free-email check against the raw user input, so the domain it saw was // `gmail.com>` (with the trailing angle bracket) — which is not in the // block list, allowing a personal address to bypass the gate. The handler // must canonicalize through the parsed addr.Address before the check. func TestCreateContactSalesRejectsFreeEmailWithDisplayName(t *testing.T) { body := validContactSalesRequest() body.BusinessEmail = "Ada Lovelace " w := httptest.NewRecorder() testHandler.CreateContactSales(w, newContactSalesRequest(body)) if w.Code != http.StatusBadRequest { t.Fatalf("expected 400, got %d: %s", w.Code, w.Body.String()) } } func TestCreateContactSalesNormalizesDisplayNameEmail(t *testing.T) { body := validContactSalesRequest() body.BusinessEmail = "Ada Lovelace " clearContactSalesForEmail(t, "ada@display-name.example") w := httptest.NewRecorder() testHandler.CreateContactSales(w, newContactSalesRequest(body)) if w.Code != http.StatusCreated { t.Fatalf("expected 201, got %d: %s", w.Code, w.Body.String()) } var stored string if err := testPool.QueryRow(context.Background(), `SELECT business_email FROM contact_sales_inquiry WHERE business_email = $1`, "ada@display-name.example").Scan(&stored); err != nil { t.Fatalf("expected row with canonical email persisted: %v", err) } if stored != "ada@display-name.example" { t.Fatalf("expected canonical email persisted, got %q", stored) } }