package store import ( "context" "errors" "testing" "time" ) // makeSchedHost is a minimal host row to hang schedule tests off. func makeSchedHost(t *testing.T, s *Store) string { t.Helper() const id = "01HSCHEDHOST0000000000000" if err := s.CreateHost(context.Background(), Host{ ID: id, Name: "sched-host", OS: "linux", Arch: "amd64", AgentVersion: "dev", ResticVersion: "0.16.0", ProtocolVersion: 1, EnrolledAt: time.Now().UTC(), }, "tokenhash", ""); err != nil { t.Fatalf("create host: %v", err) } return id } // makeGroup is a minimal source-group helper for schedule tests // (schedules need at least one group to point at). func makeGroup(t *testing.T, s *Store, hostID, name, id string) string { t.Helper() if err := s.CreateSourceGroup(context.Background(), &SourceGroup{ ID: id, HostID: hostID, Name: name, Includes: []string{"/etc"}, }); err != nil { t.Fatalf("create group %s: %v", name, err) } return id } func TestSchedulesCRUDAndVersionBump(t *testing.T) { t.Parallel() s := openTestStore(t) ctx := context.Background() hostID := makeSchedHost(t, s) gid := makeGroup(t, s, hostID, "default", "01HSCHEDGRP00000000000001") // Group creation already bumped version to 1. v, _ := s.GetHostScheduleVersion(ctx, hostID) if v != 1 { t.Fatalf("version after group create: got %d, want 1", v) } sched := Schedule{ ID: "01SCHED000000000000000001", HostID: hostID, CronExpr: "0 3 * * *", Enabled: true, SourceGroupIDs: []string{gid}, } if err := s.CreateSchedule(ctx, &sched); err != nil { t.Fatalf("create: %v", err) } if sched.CreatedAt.IsZero() || sched.UpdatedAt.IsZero() { t.Fatalf("Create should populate timestamps") } v, _ = s.GetHostScheduleVersion(ctx, hostID) if v != 2 { t.Fatalf("version after schedule create: got %d, want 2", v) } // Round-trip read — junction populated. got, err := s.GetSchedule(ctx, hostID, sched.ID) if err != nil { t.Fatalf("get: %v", err) } if got.CronExpr != "0 3 * * *" || len(got.SourceGroupIDs) != 1 || got.SourceGroupIDs[0] != gid { t.Fatalf("round-trip: %+v", got) } list, err := s.ListSchedulesByHost(ctx, hostID) if err != nil || len(list) != 1 || list[0].ID != sched.ID { t.Fatalf("list: err=%v rows=%v", err, list) } // Update — flip enabled, swap junction (re-add same gid). Version bumps. sched.CronExpr = "*/30 * * * *" sched.Enabled = false if err := s.UpdateSchedule(ctx, &sched); err != nil { t.Fatalf("update: %v", err) } v, _ = s.GetHostScheduleVersion(ctx, hostID) if v != 3 { t.Fatalf("version after update: got %d, want 3", v) } got, _ = s.GetSchedule(ctx, hostID, sched.ID) if got.CronExpr != "*/30 * * * *" || got.Enabled { t.Fatalf("update did not persist: %+v", got) } // Delete bumps version, returns ErrNotFound on second try. if err := s.DeleteSchedule(ctx, hostID, sched.ID); err != nil { t.Fatalf("delete: %v", err) } v, _ = s.GetHostScheduleVersion(ctx, hostID) if v != 4 { t.Fatalf("version after delete: got %d, want 4", v) } if err := s.DeleteSchedule(ctx, hostID, sched.ID); !errors.Is(err, ErrNotFound) { t.Fatalf("delete after delete: want ErrNotFound, got %v", err) } } func TestSchedulesUsingGroup(t *testing.T) { t.Parallel() s := openTestStore(t) ctx := context.Background() hostID := makeSchedHost(t, s) g1 := makeGroup(t, s, hostID, "default", "01HUSEGRPGRP000000000001") g2 := makeGroup(t, s, hostID, "databases", "01HUSEGRPGRP000000000002") // Schedule A points at g1 only; Schedule B points at both. if err := s.CreateSchedule(ctx, &Schedule{ ID: "01HUSEGRPSCHED0000000001", HostID: hostID, CronExpr: "@hourly", Enabled: true, SourceGroupIDs: []string{g1}, }); err != nil { t.Fatal(err) } if err := s.CreateSchedule(ctx, &Schedule{ ID: "01HUSEGRPSCHED0000000002", HostID: hostID, CronExpr: "0 3 * * *", Enabled: true, SourceGroupIDs: []string{g1, g2}, }); err != nil { t.Fatal(err) } g1Sched, err := s.SchedulesUsingGroup(ctx, g1) if err != nil { t.Fatal(err) } if len(g1Sched) != 2 { t.Fatalf("g1 should be in 2 schedules, got %d", len(g1Sched)) } g2Sched, err := s.SchedulesUsingGroup(ctx, g2) if err != nil { t.Fatal(err) } if len(g2Sched) != 1 { t.Fatalf("g2 should be in 1 schedule, got %d", len(g2Sched)) } } func TestSetHostAppliedScheduleVersion(t *testing.T) { t.Parallel() s := openTestStore(t) ctx := context.Background() hostID := makeSchedHost(t, s) if err := s.SetHostAppliedScheduleVersion(ctx, hostID, 42); err != nil { t.Fatal(err) } host, err := s.GetHost(ctx, hostID) if err != nil { t.Fatal(err) } if host.AppliedScheduleVersion != 42 { t.Fatalf("got %d, want 42", host.AppliedScheduleVersion) } }