package web import ( "context" "testing" "time" "github.com/stretchr/testify/require" "go.uber.org/goleak" ) func TestWebServingResultWaitsForCancelledWorkerAndFinalOutput(t *testing.T) { defer goleak.VerifyNone(t) manager := NewJobManager() server := &Server{jobManager: manager, shutdownCh: make(chan struct{})} _, serving, err := server.Start("127.0.0.1:0") require.NoError(t, err) job, created := manager.CreateJobWithResources("install", "test", "MPV", "test-resource") require.True(t, created) cancelled, release := make(chan struct{}), make(chan struct{}) require.True(t, manager.StartWorker(job.ID, func() { <-job.Context().Done() close(cancelled) <-release manager.AddOutput(job.ID, "rollback and output drain finished") manager.SetError(job.ID, context.Canceled, "worker joined") })) done := make(chan error, 1) go func() { done <- server.Shutdown() }() select { case <-cancelled: case <-time.After(2 * time.Second): t.Fatal("shutdown did not cancel staging worker") } select { case <-serving: t.Fatal("Serve returned before worker joined") default: } select { case <-done: t.Fatal("Shutdown returned before worker joined") default: } _, admitted := manager.CreateJobWithResources("install", "other", "Other", "unrelated") require.False(t, admitted) manager.mu.RLock() require.Equal(t, job.ID, manager.resources["test-resource"]) manager.mu.RUnlock() close(release) require.NoError(t, <-done) require.NoError(t, <-serving) recent := manager.GetRecentJobs() require.Len(t, recent, 1) require.Equal(t, "cancelled", recent[0].Status) require.Contains(t, recent[0].Output[0], "output drain finished") } func TestShutdownLetsCommittedWorkerFinishBeforeReturning(t *testing.T) { defer goleak.VerifyNone(t) manager := NewJobManager() server := &Server{jobManager: manager, shutdownCh: make(chan struct{})} job, created := manager.CreateJobWithResources("install", "test", "MPV") require.True(t, created) require.NoError(t, manager.BeginCommit(job.ID)) release := make(chan struct{}) require.True(t, manager.StartWorker(job.ID, func() { <-release manager.CompleteJob(job.ID) })) done := make(chan error, 1) go func() { done <- server.Shutdown() }() <-server.shutdownDone() require.NoError(t, job.Context().Err()) select { case <-done: t.Fatal("shutdown abandoned a committed worker") default: } close(release) require.NoError(t, <-done) require.Equal(t, "complete", manager.GetRecentJobs()[0].Status) _, _, err := server.Start("127.0.0.1:0") require.ErrorContains(t, err, "shutting down") }