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 } func TestSchedulesCRUDAndVersionBump(t *testing.T) { t.Parallel() s := openTestStore(t) ctx := context.Background() hostID := makeSchedHost(t, s) // Initial version is 0 (no row). v, err := s.GetHostScheduleVersion(ctx, hostID) if err != nil { t.Fatal(err) } if v != 0 { t.Fatalf("initial version: got %d, want 0", v) } keepLast := 7 sched := Schedule{ ID: "01SCHED000000000000000001", HostID: hostID, Kind: "backup", CronExpr: "0 3 * * *", Paths: []string{"/etc", "/home"}, Tags: []string{"nightly"}, RetentionPolicy: RetentionPolicy{KeepLast: &keepLast}, Enabled: true, } 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 != 1 { t.Fatalf("version after create: got %d, want 1", v) } // Round-trip read. got, err := s.GetSchedule(ctx, hostID, sched.ID) if err != nil { t.Fatalf("get: %v", err) } if got.CronExpr != "0 3 * * *" || len(got.Paths) != 2 { t.Fatalf("round-trip lost data: %+v", got) } if got.RetentionPolicy.KeepLast == nil || *got.RetentionPolicy.KeepLast != 7 { t.Fatalf("retention round-trip: %+v", got.RetentionPolicy) } // List sees it. 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 bumps version. 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 != 2 { t.Fatalf("version after update: got %d, want 2", 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 != 3 { t.Fatalf("version after delete: got %d, want 3", v) } if err := s.DeleteSchedule(ctx, hostID, sched.ID); !errors.Is(err, ErrNotFound) { t.Fatalf("delete after delete: want ErrNotFound, got %v", err) } } 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) } }