package web import ( "context" "io" "log" "net/http" "net/http/httptest" "strings" "testing" "gitea.dooplex.hu/admin/felhom-controller/internal/scheduler" ) // schedWith3Legs builds a real scheduler carrying the three daily legs at their default times, so // UpdateDaily has jobs to find. func schedWith3Legs() *scheduler.Scheduler { s := scheduler.New(log.New(io.Discard, "", 0)) noop := func(context.Context) error { return nil } s.Daily("db-dump", "02:30", noop) s.Daily("tier2-backup", "03:30", noop) s.Daily("offbox-backup", "04:15", noop) return s } func legTimes(t *testing.T, sch *scheduler.Scheduler, name string) string { t.Helper() for _, j := range sch.GetJobs() { if j.Name == name { return j.Schedule } } t.Fatalf("job %q not found", name) return "" } // Group E — a valid save writes the setting, fans the three legs out via UpdateDaily, and redirects // with a success flash. Red-proof: skip the Valid() check in the handler and an invalid value would // be written — TestBackupWindowSave_Invalid then fails. func TestBackupWindowSave_Valid(t *testing.T) { s := testServer(t) s.scheduler = schedWith3Legs() req := httptest.NewRequest(http.MethodPost, "/backups/window", strings.NewReader("window_start=22:00")) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") rec := httptest.NewRecorder() s.backupWindowSaveHandler(rec, req) if rec.Code != http.StatusSeeOther { t.Fatalf("status = %d, want 303", rec.Code) } if loc := rec.Header().Get("Location"); !strings.Contains(loc, "flash=") || strings.Contains(loc, "flash_error=") { t.Errorf("redirect Location = %q, want a success flash", loc) } if got := s.settings.GetBackupWindowStart(); got != "22:00" { t.Errorf("settings BackupWindowStart = %q, want 22:00", got) } // The three legs must have been rescheduled to W / W+60m / W+105m. if got := legTimes(t, s.scheduler, "db-dump"); got != "22:00" { t.Errorf("db-dump = %q, want 22:00", got) } if got := legTimes(t, s.scheduler, "tier2-backup"); got != "23:00" { t.Errorf("tier2-backup = %q, want 23:00", got) } if got := legTimes(t, s.scheduler, "offbox-backup"); got != "23:45" { t.Errorf("offbox-backup = %q, want 23:45", got) } } // Group E — an invalid time stores nothing, leaves the legs untouched, and redirects with an error // flash. func TestBackupWindowSave_Invalid(t *testing.T) { s := testServer(t) s.scheduler = schedWith3Legs() req := httptest.NewRequest(http.MethodPost, "/backups/window", strings.NewReader("window_start=2500")) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") rec := httptest.NewRecorder() s.backupWindowSaveHandler(rec, req) if rec.Code != http.StatusSeeOther { t.Fatalf("status = %d, want 303", rec.Code) } if loc := rec.Header().Get("Location"); !strings.Contains(loc, "flash_error=") { t.Errorf("redirect Location = %q, want an error flash", loc) } if got := s.settings.GetBackupWindowStart(); got != "" { t.Errorf("settings BackupWindowStart = %q, want empty (nothing stored)", got) } if got := legTimes(t, s.scheduler, "db-dump"); got != "02:30" { t.Errorf("db-dump = %q, want 02:30 (unchanged)", got) } if got := legTimes(t, s.scheduler, "offbox-backup"); got != "04:15" { t.Errorf("offbox-backup = %q, want 04:15 (unchanged)", got) } }