156 lines
4.9 KiB
Go
156 lines
4.9 KiB
Go
package db
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"errors"
|
||
|
|
"testing"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
func TestAutoCycleDefaultsFromMigration(t *testing.T) {
|
||
|
|
d, ctx := newTestDB(t)
|
||
|
|
|
||
|
|
a, err := d.GetAutoCycle(ctx)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("get auto cycle: %v", err)
|
||
|
|
}
|
||
|
|
if a.Enabled {
|
||
|
|
t.Fatalf("expected disabled by default")
|
||
|
|
}
|
||
|
|
if a.IntervalSeconds != 3600 {
|
||
|
|
t.Fatalf("expected default interval 3600, got %d", a.IntervalSeconds)
|
||
|
|
}
|
||
|
|
if a.MaxRunSeconds != 0 {
|
||
|
|
t.Fatalf("expected default max_run_seconds 0, got %d", a.MaxRunSeconds)
|
||
|
|
}
|
||
|
|
if a.Phase != AutoCyclePhaseIdle {
|
||
|
|
t.Fatalf("expected phase idle, got %q", a.Phase)
|
||
|
|
}
|
||
|
|
if a.RunStartedAt != nil || a.NextRunAt != nil || a.LastRunStartedAt != nil || a.LastRunFinishedAt != nil {
|
||
|
|
t.Fatalf("expected all timestamps unset, got %+v", a)
|
||
|
|
}
|
||
|
|
if a.LastOutcome != "" || a.LastError != "" || a.LastScannedFree != 0 || a.RunsTotal != 0 {
|
||
|
|
t.Fatalf("expected empty last-run info, got %+v", a)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestAutoCycleParamsRoundTripAndPartialUpdate(t *testing.T) {
|
||
|
|
d, ctx := newTestDB(t)
|
||
|
|
|
||
|
|
interval, maxRun := 120, 900
|
||
|
|
if err := d.SetAutoCycleParams(ctx, &interval, &maxRun); err != nil {
|
||
|
|
t.Fatalf("set params: %v", err)
|
||
|
|
}
|
||
|
|
a, err := d.GetAutoCycle(ctx)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("get: %v", err)
|
||
|
|
}
|
||
|
|
if a.IntervalSeconds != 120 || a.MaxRunSeconds != 900 {
|
||
|
|
t.Fatalf("expected 120/900, got %d/%d", a.IntervalSeconds, a.MaxRunSeconds)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Partial: only max_run_seconds changes.
|
||
|
|
newMax := 0
|
||
|
|
if err := d.SetAutoCycleParams(ctx, nil, &newMax); err != nil {
|
||
|
|
t.Fatalf("partial set: %v", err)
|
||
|
|
}
|
||
|
|
a, _ = d.GetAutoCycle(ctx)
|
||
|
|
if a.IntervalSeconds != 120 || a.MaxRunSeconds != 0 {
|
||
|
|
t.Fatalf("expected 120/0 after partial update, got %d/%d", a.IntervalSeconds, a.MaxRunSeconds)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Partial: only interval changes.
|
||
|
|
newInterval := 60
|
||
|
|
if err := d.SetAutoCycleParams(ctx, &newInterval, nil); err != nil {
|
||
|
|
t.Fatalf("partial set interval: %v", err)
|
||
|
|
}
|
||
|
|
a, _ = d.GetAutoCycle(ctx)
|
||
|
|
if a.IntervalSeconds != 60 || a.MaxRunSeconds != 0 {
|
||
|
|
t.Fatalf("expected 60/0, got %d/%d", a.IntervalSeconds, a.MaxRunSeconds)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestAutoCycleParamsValidation(t *testing.T) {
|
||
|
|
d, ctx := newTestDB(t)
|
||
|
|
|
||
|
|
low := 59
|
||
|
|
if err := d.SetAutoCycleParams(ctx, &low, nil); !errors.Is(err, ErrValidation) {
|
||
|
|
t.Fatalf("expected ErrValidation for interval 59, got %v", err)
|
||
|
|
}
|
||
|
|
neg := -1
|
||
|
|
if err := d.SetAutoCycleParams(ctx, nil, &neg); !errors.Is(err, ErrValidation) {
|
||
|
|
t.Fatalf("expected ErrValidation for negative max_run_seconds, got %v", err)
|
||
|
|
}
|
||
|
|
// A rejected request must not partially apply the valid half.
|
||
|
|
ok := 300
|
||
|
|
if err := d.SetAutoCycleParams(ctx, &ok, &neg); !errors.Is(err, ErrValidation) {
|
||
|
|
t.Fatalf("expected ErrValidation, got %v", err)
|
||
|
|
}
|
||
|
|
a, _ := d.GetAutoCycle(ctx)
|
||
|
|
if a.IntervalSeconds != 3600 {
|
||
|
|
t.Fatalf("interval must stay 3600 after rejected update, got %d", a.IntervalSeconds)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestAutoCycleEnabledAndStateRoundTrip(t *testing.T) {
|
||
|
|
d, ctx := newTestDB(t)
|
||
|
|
|
||
|
|
next := Now()
|
||
|
|
if err := d.SetAutoCycleEnabled(ctx, true, &next, ""); err != nil {
|
||
|
|
t.Fatalf("enable: %v", err)
|
||
|
|
}
|
||
|
|
a, _ := d.GetAutoCycle(ctx)
|
||
|
|
if !a.Enabled || a.Phase != AutoCyclePhaseIdle {
|
||
|
|
t.Fatalf("expected enabled+idle, got %+v", a)
|
||
|
|
}
|
||
|
|
if a.NextRunAt == nil || !a.NextRunAt.Equal(next) {
|
||
|
|
t.Fatalf("expected next_run_at=%v, got %v", next, a.NextRunAt)
|
||
|
|
}
|
||
|
|
|
||
|
|
started := next.Add(time.Second)
|
||
|
|
finished := next.Add(time.Minute)
|
||
|
|
nextRun := finished.Add(time.Hour)
|
||
|
|
st := AutoCycleState{
|
||
|
|
Phase: AutoCyclePhaseWaiting,
|
||
|
|
NextRunAt: &nextRun,
|
||
|
|
LastRunStartedAt: &started,
|
||
|
|
LastRunFinishedAt: &finished,
|
||
|
|
LastOutcome: AutoCycleOutcomeCompleted,
|
||
|
|
LastError: "boom",
|
||
|
|
LastScannedFree: 3,
|
||
|
|
RunsTotal: 7,
|
||
|
|
}
|
||
|
|
if err := d.UpdateAutoCycleState(ctx, st); err != nil {
|
||
|
|
t.Fatalf("update state: %v", err)
|
||
|
|
}
|
||
|
|
a, _ = d.GetAutoCycle(ctx)
|
||
|
|
if !a.Enabled {
|
||
|
|
t.Fatalf("UpdateAutoCycleState must not touch the enabled flag")
|
||
|
|
}
|
||
|
|
if a.Phase != AutoCyclePhaseWaiting || a.LastOutcome != AutoCycleOutcomeCompleted ||
|
||
|
|
a.LastError != "boom" || a.LastScannedFree != 3 || a.RunsTotal != 7 {
|
||
|
|
t.Fatalf("state mismatch: %+v", a)
|
||
|
|
}
|
||
|
|
if a.RunStartedAt != nil {
|
||
|
|
t.Fatalf("expected run_started_at nil, got %v", a.RunStartedAt)
|
||
|
|
}
|
||
|
|
if a.NextRunAt == nil || !a.NextRunAt.Equal(nextRun) ||
|
||
|
|
a.LastRunStartedAt == nil || !a.LastRunStartedAt.Equal(started) ||
|
||
|
|
a.LastRunFinishedAt == nil || !a.LastRunFinishedAt.Equal(finished) {
|
||
|
|
t.Fatalf("timestamp mismatch: %+v", a)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Disable with an outcome: phase back to idle, outcome recorded,
|
||
|
|
// last_error cleared, counters preserved.
|
||
|
|
if err := d.SetAutoCycleEnabled(ctx, false, nil, AutoCycleOutcomeStopped); err != nil {
|
||
|
|
t.Fatalf("disable: %v", err)
|
||
|
|
}
|
||
|
|
a, _ = d.GetAutoCycle(ctx)
|
||
|
|
if a.Enabled || a.Phase != AutoCyclePhaseIdle || a.LastOutcome != AutoCycleOutcomeStopped ||
|
||
|
|
a.LastError != "" || a.NextRunAt != nil || a.RunsTotal != 7 {
|
||
|
|
t.Fatalf("unexpected state after disable: %+v", a)
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := d.UpdateAutoCycleState(ctx, AutoCycleState{Phase: "bogus"}); !errors.Is(err, ErrValidation) {
|
||
|
|
t.Fatalf("expected ErrValidation for bogus phase, got %v", err)
|
||
|
|
}
|
||
|
|
}
|