oficina / calendar Public

Test that a native create re-arms the reminder wake

The native API is JSON POSTs, not page loads. Main's scheduledWork
middleware re-arms after every request; this pins that a native create
arms the wake for the reminder it schedules. Fails (timer not armed)
with the middleware removed from routes.go.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Paul Campbell pushed by paul@keymail.dev 9ebcd95abaa6562e64e69015a3103285e5dbaad9 parent d37c339
1 file changed, +64 −0
  • internal/calendartest/native_schedule_test.go +64 −0

CI passed — run details

CI log
=== vet ===
go vet ./...
=== fmt ===
=== test ===
go test ./...
?   	amadan.net/oficina/calendar/cmd/calendar	[no test files]
ok  	amadan.net/oficina/calendar/internal/calendar	4.468s
?   	amadan.net/oficina/calendar/internal/calendar/icons	[no test files]
?   	amadan.net/oficina/calendar/internal/calendar/seal	[no test files]
ok  	amadan.net/oficina/calendar/internal/calendartest	1.850s
ok  	amadan.net/oficina/calendar/internal/guardstest	0.039s
=== migration-check ===
go run github.com/carlosframework/rastrillo/cmd/rastrillo migration check
rastrillo migration check: models and migrations agree
diff --git a/internal/calendartest/native_schedule_test.go b/internal/calendartest/native_schedule_test.go
new file mode 100644
index 0000000..0b74d7d
--- /dev/null
+++ b/internal/calendartest/native_schedule_test.go
@@ -0,0 +1,64 @@
+package calendartest
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+ "time"
+
+ calendar "amadan.net/oficina/calendar/internal/calendar"
+)
+
+// A native create is a JSON POST, not a page load. Main's scheduledWork
+// middleware re-arms the wake after EVERY request, so the reminder the
+// native create schedules is armed before anyone opens a page. The
+// merge of native/testflight kept that behaviour; this stands in front
+// of it.
+func TestNativeCreateArmsTheWakeForItsReminder(t *testing.T) {
+ control := controlForTest(t)
+ app, session := signedIn(t, "someone@example.com")
+
+ rec := getAs(t, app.Handler(), session, "/api/native/session")
+ var sess struct {
+ DefaultCalendarID int64 `json:"default_calendar_id"`
+ }
+ if rec.Code != http.StatusOK || json.Unmarshal(rec.Body.Bytes(), &sess) != nil || sess.DefaultCalendarID == 0 {
+ t.Fatalf("native session = %d: %s", rec.Code, rec.Body.String())
+ }
+ if at, _ := control.snapshot(); !at.IsZero() {
+ t.Fatalf("timer armed before any work existed: %v", at)
+ }
+
+ start := time.Now().UTC().Add(2 * time.Minute)
+ body, _ := json.Marshal(map[string]any{
+ "calendar_id": sess.DefaultCalendarID, "title": "Native standup", "tz": "UTC",
+ "start_local": start.Format("2006-01-02T15:04"), "end_local": start.Add(15 * time.Minute).Format("2006-01-02T15:04"),
+ })
+ req := httptest.NewRequest(http.MethodPost, "/api/native/events", bytes.NewReader(body))
+ req.Header.Set("Origin", calendar.TestOrigin)
+ req.Header.Set("Content-Type", "application/json")
+ req.AddCookie(session)
+ rec = httptest.NewRecorder()
+ app.Handler().ServeHTTP(rec, req)
+ var saved struct {
+ ID int64 `json:"id"`
+ Warning string `json:"warning"`
+ }
+ if rec.Code != http.StatusOK || json.Unmarshal(rec.Body.Bytes(), &saved) != nil || saved.ID == 0 || saved.Warning != "" {
+ t.Fatalf("native create = %d: %s", rec.Code, rec.Body.String())
+ }
+
+ expected := app.StoreForTest().NextDueAt(context.Background())
+ if expected.IsZero() {
+ t.Fatal("native create scheduled no reminder, so this test proves nothing")
+ }
+ control.waitArmed(t)
+ // The default reminder is already overdue, so the wake is "now":
+ // no earlier than the due moment and no later than the check.
+ if at, _ := control.snapshot(); at.Before(expected) || at.After(time.Now()) {
+ t.Fatalf("native POST armed %v, want between %v and now", at, expected)
+ }
+}