待ち行列への登録と状態表示を追加(新着からの登録、/queue、DATABASE_URL)

This commit is contained in:
2026-10-03 19:07:53 +09:00
parent a71b915c81
commit 4a02526e76
20 changed files with 1146 additions and 14 deletions
+83
View File
@@ -407,6 +407,89 @@ func TestRejectedTransitions(t *testing.T) {
}
}
func TestRetryEpisode(t *testing.T) {
s := New(testPool(t, true))
ctx := context.Background()
job, _, err := s.Enqueue(ctx, sampleEpisode("retry-by-episode"), "manual")
if err != nil {
t.Fatal(err)
}
if err := s.RetryEpisode(ctx, job.EpisodeID); !errors.Is(err, ErrInvalidTransition) {
t.Fatalf("待機中の再試行: %v", err)
}
if err := s.Cancel(ctx, job.ID); err != nil {
t.Fatal(err)
}
if err := s.RetryEpisode(ctx, job.EpisodeID); err != nil {
t.Fatal(err)
}
got, err := s.GetJob(ctx, job.ID)
if err != nil || got.Status != "queued" || got.ErrorKind != nil || got.FinishedAt != nil {
t.Fatalf("エピソード ID による再試行: %+v %v", got, err)
}
claimed, err := s.ClaimNext(ctx)
if err != nil {
t.Fatal(err)
}
downloaded, total := int64(10), int64(20)
format := "video-1"
if err := s.UpdateProgress(ctx, claimed.ID, Progress{
Phase: "video", DownloadedBytes: &downloaded, TotalBytes: &total,
TotalIsEstimate: true, FormatID: &format,
}); err != nil {
t.Fatal(err)
}
if err := s.Fail(ctx, claimed.ID, "network", "通信失敗"); err != nil {
t.Fatal(err)
}
if err := s.RetryEpisode(ctx, job.EpisodeID); err != nil {
t.Fatalf("失敗からの再試行: %v", err)
}
got, err = s.GetJob(ctx, job.ID)
if err != nil || got.Status != "queued" || got.Phase != nil || got.DownloadedBytes != nil || got.TotalBytes != nil || got.TotalIsEstimate || got.FormatID != nil || got.ErrorKind != nil || got.ErrorMessage != nil || got.HeartbeatAt != nil || got.StartedAt != nil || got.FinishedAt != nil || got.Attempts != 1 {
t.Fatalf("失敗情報の消去: %+v %v", got, err)
}
if err := s.RetryEpisode(ctx, "unknown"); !errors.Is(err, ErrInvalidTransition) {
t.Fatalf("存在しないエピソード: %v", err)
}
}
func TestCancelQueuedDoesNotCancelRunning(t *testing.T) {
s := New(testPool(t, true))
ctx := context.Background()
queued, _, err := s.Enqueue(ctx, sampleEpisode("queue-cancel"), "manual")
if err != nil {
t.Fatal(err)
}
if err := s.CancelQueued(ctx, queued.ID); err != nil {
t.Fatal(err)
}
canceled, err := s.GetJob(ctx, queued.ID)
if err != nil || canceled.Status != "canceled" || canceled.ErrorKind == nil || *canceled.ErrorKind != "canceled" || canceled.FinishedAt == nil {
t.Fatalf("待機中の取り消し: %+v %v", canceled, err)
}
if err := s.CancelQueued(ctx, queued.ID); !errors.Is(err, ErrInvalidTransition) {
t.Fatalf("再取り消し: %v", err)
}
if err := s.CancelQueued(ctx, -1); !errors.Is(err, ErrInvalidTransition) {
t.Fatalf("存在しないジョブの取り消し: %v", err)
}
if err := s.Retry(ctx, queued.ID); err != nil {
t.Fatal(err)
}
claimed, err := s.ClaimNext(ctx)
if err != nil {
t.Fatal(err)
}
if err := s.CancelQueued(ctx, claimed.ID); !errors.Is(err, ErrInvalidTransition) {
t.Fatalf("実行中の取り消し: %v", err)
}
job, err := s.GetJob(ctx, claimed.ID)
if err != nil || job.Status != "running" {
t.Fatalf("実行中の状態: %+v %v", job, err)
}
}
func TestClaimNextConcurrent(t *testing.T) {
s := New(testPool(t, true))
ctx := context.Background()