package calendartest import ( "context" "net/http" "strings" "testing" "time" calendar "amadan.net/oficina/calendar/internal/calendar" ) // Regressions from the code review of the booking branch. // A line break in a name would reach the mail subject, and the SMTP // sender refuses a folded header with an error that quotes the whole // subject — title, name and all — into the log (code review finding 1). // Refused at the form instead. func TestANameWithALineBreakIsRefused(t *testing.T) { app, _, path, _ := publishedPage(t) h := app.Handler() _, starts := futureDaySlots(t, h, path) rec := strangerPost(t, h, path, holdForm(starts[0], "Jane\nPrivate", "j@example.com")) if rec.Code != http.StatusUnprocessableEntity || !strings.Contains(rec.Body.String(), "one line") { t.Errorf("a name with a newline: %d", rec.Code) } } // The host's confirmation carries the booker's note (spec §6); the // booker's mail never carries anything but their own words back. func TestTheHostsConfirmationCarriesTheNote(t *testing.T) { app, mailer, path, _ := publishedPage(t) h := app.Handler() _, starts := futureDaySlots(t, h, path) form := holdForm(starts[0], "Doc", "doc@tito.io") form.Set("note", "Bring the folder") strangerPost(t, h, path, form) link := reManage.FindString(mailer.body(0)) strangerPost(t, h, link+"/confirm", nil) if !strings.Contains(mailer.body(1), "Bring the folder") { t.Errorf("the host's mail lacks the note:\n%s", mailer.body(1)) } } // The reschedule picker shows the booking's snapshotted title, not the // page's live one (B20). func TestTheReschedulePickerShowsTheSnapshot(t *testing.T) { app, mailer, path, host := publishedPage(t) h := app.Handler() _, starts := futureDaySlots(t, h, path) strangerPost(t, h, path, holdForm(starts[0], "Doc", "doc@tito.io")) link := reManage.FindString(mailer.body(0)) strangerPost(t, h, link+"/confirm", nil) st := app.StoreForTest() pages, _ := st.BookingPages(context.Background(), host.ID) ws, _ := calendar.ParseAvailability("every day 9-5") if err := st.UpdateBookingPage(context.Background(), host.ID, pages[0].ID, calendar.BookingPageInput{ Title: "Renamed page", Availability: "every day 9-5", DurationMin: 30, HorizonDays: 30}, ws); err != nil { t.Fatal(err) } body := strangerGet(t, h, link+"/reschedule").Body.String() if strings.Contains(body, "Renamed page") || !strings.Contains(body, "Quick chat") { t.Error("the reschedule picker shows the page's live title, not the booking's snapshot") } } // A hold mail that is still owed when the booker confirms (the first // try failed, or was ambiguous) is retired unsent, not delivered as an // invitation to confirm what is already confirmed (B19). func TestAnOwedHoldMailIsRetiredOnceConfirmed(t *testing.T) { t.Setenv("CARLOS_ADMIN_TOKEN", "test-token") app, mailer, path, _ := publishedPage(t) h := app.Handler() _, starts := futureDaySlots(t, h, path) mailer.refuse = "doc@tito.io" strangerPost(t, h, path, holdForm(starts[0], "Doc", "doc@tito.io")) mailer.refuse = "" // The hold mail is owed; confirm through the store, as the booker // who somehow got the link would. var id int64 app.DBForTest().QueryRow(`SELECT id FROM bookings`).Scan(&id) if _, err := app.StoreForTest().ConfirmBooking(context.Background(), id, time.Now()); err != nil { t.Fatal(err) } app.DBForTest().Exec(`UPDATE booking_mails SET due_at = ? WHERE sent_at IS NULL`, time.Now().Add(-time.Second).Unix()) tick(t, h) for i := 0; i < mailer.count(); i++ { if strings.Contains(mailer.body(i), "confirm it here") { t.Error("the drain sent a hold mail for a booking already confirmed") } } }