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" ) func TestCreateRun_BlocksNonRepeatableAfterSuccess(t *testing.T) { ctx := context.Background() template := demoTemplate() template.Repeatable = false template.DedupeKeys = []string{"scope_id"} runs := newMemoryRunRepo(nil) queue := newMemoryQueueRepo() uc := testUseCaseFull(template, runs, nil, queue) first, err := uc.CreateRun(ctx, domusecase.CreateRunRequest{ TemplateType: template.Type, Scope: "user", ScopeID: "u1", }) if err != nil { t.Fatalf("first CreateRun() error = %v", err) } first.Status = enum.RunStatusSucceeded first.DedupeKey = buildDedupeKey(template, "user", "u1", nil) _, _ = runs.Update(ctx, first) _, err = uc.CreateRun(ctx, domusecase.CreateRunRequest{ TemplateType: template.Type, Scope: "user", ScopeID: "u1", }) if err == nil { t.Fatal("expected duplicate completed job error") } } func TestCreateRun_DedupeLeaseBlocksParallel(t *testing.T) { ctx := context.Background() template := demoTemplate() template.Repeatable = true template.DedupeKeys = []string{"scope_id"} queue := newMemoryQueueRepo() uc := testUseCaseFull(template, newMemoryRunRepo(nil), nil, queue) first, err := uc.CreateRun(ctx, domusecase.CreateRunRequest{ TemplateType: template.Type, Scope: "user", ScopeID: "u1", }) if err != nil { t.Fatalf("first CreateRun() error = %v", err) } if first.Status != enum.RunStatusQueued { t.Fatalf("first status = %s, want queued", first.Status) } _, err = uc.CreateRun(ctx, domusecase.CreateRunRequest{ TemplateType: template.Type, Scope: "user", ScopeID: "u1", }) if err == nil { t.Fatal("expected in-progress dedupe error") } } func TestBuildDedupeKey_IncludesScopeID(t *testing.T) { template := &entity.Template{ Type: "demo", DedupeKeys: []string{"scope_id", "target"}, } key1 := buildDedupeKey(template, "user", "a", map[string]any{"target": "x"}) key2 := buildDedupeKey(template, "user", "b", map[string]any{"target": "x"}) if key1 == key2 { t.Fatal("dedupe keys should differ by scope_id") } }