thread-master/old/backend/internal/model/job/usecase/cancel_test.go

109 lines
3.4 KiB
Go

package usecase
import (
"context"
"testing"
"haixun-backend/internal/model/job/domain/entity"
"haixun-backend/internal/model/job/domain/enum"
domusecase "haixun-backend/internal/model/job/domain/usecase"
jobrepo "haixun-backend/internal/model/job/repository"
goredis "github.com/redis/go-redis/v9"
)
func TestRequestCancel_RunningSetsRedisCancelSignal(t *testing.T) {
ctx := context.Background()
client := goredis.NewClient(&goredis.Options{Addr: "127.0.0.1:6379"})
if err := client.Ping(ctx).Err(); err != nil {
t.Skip("redis not available:", err)
}
defer client.Close()
jobID := "507f1f77bcf86cd799439011"
template := demoTemplate()
run := &entity.Run{
TemplateType: template.Type,
Status: enum.RunStatusRunning,
WorkerType: string(enum.WorkerTypeGo),
}
uc := NewUseCase(
&memoryTemplateRepo{template: template},
newMemoryRunRepo(run),
&memoryScheduleRepo{},
&memoryEventRepo{},
jobrepo.NewRedisQueueRepository(client),
)
updated, err := uc.RequestCancel(ctx, domusecase.CancelRunRequest{
JobID: jobID,
Reason: "test cancel",
})
if err != nil {
t.Fatalf("RequestCancel() error = %v", err)
}
if updated.Status != enum.RunStatusCancelRequested {
t.Fatalf("status = %s, want cancel_requested", updated.Status)
}
value, err := client.Get(ctx, "jobs:cancel:"+jobID).Result()
if err != nil {
t.Fatalf("redis cancel key missing: %v", err)
}
if value != "test cancel" {
t.Fatalf("cancel value = %q, want test cancel", value)
}
_ = client.Del(ctx, "jobs:cancel:"+jobID)
}
// A pending/queued run has no worker executing it yet, so cancelling it is
// always safe. This must succeed even for templates like publish-analytics
// that opt out of *cooperative* cancel (CancelPolicy.Supported = false),
// otherwise a not-yet-started job can never be removed from the queue.
func TestRequestCancel_PendingAllowedEvenWhenTemplateDoesNotSupportCancel(t *testing.T) {
ctx := context.Background()
template := publishAnalyticsTemplate()
if template.CancelPolicy.Supported {
t.Fatal("test setup assumes publish-analytics does not support cooperative cancel")
}
runs := newMemoryRunRepo(&entity.Run{
TemplateType: template.Type,
Status: enum.RunStatusPending,
WorkerType: template.Steps[0].WorkerType,
})
uc := testUseCaseFull(template, runs, nil, newMemoryQueueRepo())
updated, err := uc.RequestCancel(ctx, domusecase.CancelRunRequest{
JobID: "507f1f77bcf86cd799439011",
Reason: "user requested cancel",
})
if err != nil {
t.Fatalf("RequestCancel() error = %v", err)
}
if updated.Status != enum.RunStatusCancelled {
t.Fatalf("status = %s, want cancelled", updated.Status)
}
}
// Cooperative cancel (interrupting an already-running worker) must still
// respect the template's CancelPolicy.
func TestRequestCancel_RunningRejectedWhenTemplateDoesNotSupportCancel(t *testing.T) {
ctx := context.Background()
template := publishAnalyticsTemplate()
runs := newMemoryRunRepo(&entity.Run{
TemplateType: template.Type,
Status: enum.RunStatusRunning,
WorkerType: template.Steps[0].WorkerType,
})
uc := testUseCaseFull(template, runs, nil, newMemoryQueueRepo())
_, err := uc.RequestCancel(ctx, domusecase.CancelRunRequest{
JobID: "507f1f77bcf86cd799439011",
Reason: "user requested cancel",
})
if err == nil {
t.Fatal("expected error cancelling a running job whose template does not support cooperative cancel")
}
}