package orbit import ( "context" "encoding/json" "testing" "time" ) func TestIntegrationWorkerRuntimeDelayedSubmission(t *testing.T) { api, queue, _ := integrationWorkerClient(t) ctx, stop := context.WithTimeout(context.Background(), 20*time.Second) defer stop() name := "delay." + workerTestID(t) key := workerTestID(t) in := SubmitTaskRequest{QueueID: queue, Handler: name, HandlerVersion: "v1", PayloadVersion: "v1", Payload: json.RawMessage(`{"n":9007199254740993}`), DelayMS: 1500, TaskTimeoutMS: 15000, Retry: RetryPolicy{MaxAttempts: 1, AttemptTimeoutMS: 5000}} delayed, err := api.SubmitTask(ctx, key, in) if err != nil || !delayed.Task.AvailableAt.Equal(delayed.Task.CreatedAt.Add(1500*time.Millisecond)) { t.Fatal("relative delay not persisted", err) } at := delayed.Task.CreatedAt.Add(3 * time.Second) absoluteInput := in absoluteInput.DelayMS, absoluteInput.AvailableAt = 0, &at absolute, err := api.SubmitTask(ctx, workerTestID(t), absoluteInput) if err != nil || !absolute.Task.AvailableAt.Equal(at) { t.Fatal("absolute release time changed", err) } r, err := NewWorkerRegistration(queue, 1, []WorkerCapability{{Handler: name, HandlerVersion: "v1", PayloadVersion: "v1"}}) if err != nil { t.Fatal(err) } w, err := api.DialWorker(ctx, r) if err != nil { t.Fatal(err) } defer w.Close() if _, err := w.Capacity(ctx, workerTestID(t), WorkerCapacity{Sequence: 1, Capacity: 1}); err != nil { t.Fatal(err) } for _, want := range []Task{delayed.Task, absolute.Task} { a, err := w.NextAssignment(ctx) if err != nil || a.Task.ID != want.ID || a.ServerTime.Before(want.AvailableAt) || a.Epoch != 1 { t.Fatal("task dispatched before its persisted release time", err) } if _, err := w.Start(ctx, workerTestID(t), a.Attempt()); err != nil { t.Fatal(err) } if _, err := w.Finish(ctx, workerTestID(t), WorkerFinish{Ref: a.Attempt(), Outcome: WorkerSucceeded, Result: a.Task.Payload}); err != nil { t.Fatal(err) } history, err := api.GetTaskAttempt(ctx, want.ID, a.AttemptID) if err != nil || history.CreatedAt.Before(want.AvailableAt) || history.AuthorizedAt == nil || history.AuthorizedAt.Before(want.AvailableAt) { t.Fatal("persisted history allowed an early start", err) } } // 原等待时长已经经过,原键重报仍返回首次回执,不能建立新的计时或任务。 dupe, err := api.SubmitTask(ctx, key, in) if err != nil || !dupe.Deduplicated || dupe.Task.ID != delayed.Task.ID || !dupe.Task.AvailableAt.Equal(delayed.Task.AvailableAt) || !dupe.Task.DeadlineAt.Equal(delayed.Task.DeadlineAt) { t.Fatal("late replay reset original delay", err) } current, err := api.GetTask(ctx, delayed.Task.ID) if err != nil || current.State != TaskSucceeded || current.AttemptCount != 1 { t.Fatal("late replay reopened a completed task", err) } t.Log("relative and absolute release times obey database history; replay after elapsed delay returns original receipt without reopening") }