|
| 1 | +// Copyright (c) 2025-2026 Netresearch DTT GmbH |
| 2 | +// SPDX-License-Identifier: MIT |
| 3 | + |
| 4 | +package core |
| 5 | + |
| 6 | +import ( |
| 7 | + "testing" |
| 8 | + |
| 9 | + "github.com/stretchr/testify/assert" |
| 10 | + "github.com/stretchr/testify/require" |
| 11 | +) |
| 12 | + |
| 13 | +// A refused job is remembered so /health can name it. That memory has to end |
| 14 | +// when the job does, or an operator who fixed the problem by deleting the job |
| 15 | +// would be left with a permanently degraded daemon and no job to blame it on. |
| 16 | + |
| 17 | +func TestSchedulerRecordsRefusedJob(t *testing.T) { |
| 18 | + t.Parallel() |
| 19 | + |
| 20 | + job := &TestJob{} |
| 21 | + job.Name = "broken" |
| 22 | + job.Schedule = "not-a-schedule" |
| 23 | + |
| 24 | + sc := NewScheduler(newDiscardLogger()) |
| 25 | + require.Error(t, sc.AddJob(job)) |
| 26 | + |
| 27 | + refused := sc.GetUnschedulableJobs() |
| 28 | + require.Contains(t, refused, "broken") |
| 29 | + assert.NotEmpty(t, refused["broken"], "the recorded reason is what makes the report actionable") |
| 30 | +} |
| 31 | + |
| 32 | +// TestSchedulerRemoveJobClearsUnschedulable covers the removal half: dropping |
| 33 | +// the job from the config has to drop the complaint with it. |
| 34 | +func TestSchedulerRemoveJobClearsUnschedulable(t *testing.T) { |
| 35 | + t.Parallel() |
| 36 | + |
| 37 | + job := &TestJob{} |
| 38 | + job.Name = "broken" |
| 39 | + job.Schedule = "not-a-schedule" |
| 40 | + |
| 41 | + sc := NewScheduler(newDiscardLogger()) |
| 42 | + require.Error(t, sc.AddJob(job)) |
| 43 | + require.Contains(t, sc.GetUnschedulableJobs(), "broken") |
| 44 | + |
| 45 | + require.NoError(t, sc.RemoveJob(job)) |
| 46 | + |
| 47 | + assert.NotContains(t, sc.GetUnschedulableJobs(), "broken", |
| 48 | + "a job removed from the config still held /health degraded") |
| 49 | +} |
| 50 | + |
| 51 | +// TestSchedulerGetUnschedulableJobsIsACopy pins that callers cannot reach into |
| 52 | +// the scheduler's state through the returned map: the health checker reads it |
| 53 | +// on every check, and a shared map would be a data race as well as a way to |
| 54 | +// silently erase a refusal. |
| 55 | +func TestSchedulerGetUnschedulableJobsIsACopy(t *testing.T) { |
| 56 | + t.Parallel() |
| 57 | + |
| 58 | + job := &TestJob{} |
| 59 | + job.Name = "broken" |
| 60 | + job.Schedule = "not-a-schedule" |
| 61 | + |
| 62 | + sc := NewScheduler(newDiscardLogger()) |
| 63 | + require.Error(t, sc.AddJob(job)) |
| 64 | + |
| 65 | + delete(sc.GetUnschedulableJobs(), "broken") |
| 66 | + |
| 67 | + assert.Contains(t, sc.GetUnschedulableJobs(), "broken") |
| 68 | +} |
0 commit comments