109 lines
3.4 KiB
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")
|
|
}
|
|
}
|