package generate import ( "strings" "testing" "time" "github.com/geo-platform/tenant-api/internal/shared/config" ) func TestNormalizeGenerationTaskRecoveryConfigUsesGenerationDefaults(t *testing.T) { cfg := normalizeGenerationTaskRecoveryConfig(config.GenerationConfig{ ArticleTimeout: 90 * time.Second, }) if cfg.LeaseTTL != 3*time.Minute+30*time.Second { t.Fatalf("expected lease ttl article_timeout+2m, got %s", cfg.LeaseTTL) } if cfg.RecoveryInterval != time.Minute { t.Fatalf("expected recovery interval 1m, got %s", cfg.RecoveryInterval) } if cfg.RecoveryTimeout != 30*time.Second { t.Fatalf("expected recovery timeout 30s, got %s", cfg.RecoveryTimeout) } if cfg.BatchSize != 100 { t.Fatalf("expected batch size 100, got %d", cfg.BatchSize) } if cfg.QueuedStaleAfter != 2*time.Minute { t.Fatalf("expected queued stale after 2m, got %s", cfg.QueuedStaleAfter) } if cfg.MaxAttempts != 3 { t.Fatalf("expected max attempts 3, got %d", cfg.MaxAttempts) } stateCheck := normalizeGenerationTaskStateCheckConfig(config.GenerationConfig{}) if stateCheck.Interval != 5*time.Minute { t.Fatalf("expected state check interval 5m, got %s", stateCheck.Interval) } if stateCheck.Timeout != 10*time.Second { t.Fatalf("expected state check timeout 10s, got %s", stateCheck.Timeout) } if stateCheck.BatchSize != 100 { t.Fatalf("expected state check batch size 100, got %d", stateCheck.BatchSize) } if stateCheck.Lookback != 24*time.Hour { t.Fatalf("expected state check lookback 24h, got %s", stateCheck.Lookback) } } func TestNormalizeGenerationTaskRecoveryConfigKeepsOverrides(t *testing.T) { cfg := normalizeGenerationTaskRecoveryConfig(config.GenerationConfig{ ArticleTimeout: 5 * time.Minute, TaskLeaseTTL: 7 * time.Minute, TaskRecoveryInterval: 15 * time.Second, TaskRecoveryTimeout: 10 * time.Second, TaskRecoveryBatchSize: 12, TaskQueuedStaleAfter: 45 * time.Second, TaskMaxAttempts: 5, }) if cfg.LeaseTTL != 7*time.Minute || cfg.RecoveryInterval != 15*time.Second || cfg.RecoveryTimeout != 10*time.Second || cfg.BatchSize != 12 || cfg.QueuedStaleAfter != 45*time.Second || cfg.MaxAttempts != 5 { t.Fatalf("unexpected recovery config: %#v", cfg) } stateCheck := normalizeGenerationTaskStateCheckConfig(config.GenerationConfig{ TaskStateCheckInterval: 9 * time.Minute, TaskStateCheckTimeout: 11 * time.Second, TaskStateCheckBatchSize: 13, TaskStateCheckLookback: 2 * time.Hour, }) if stateCheck.Interval != 9*time.Minute || stateCheck.Timeout != 11*time.Second || stateCheck.BatchSize != 13 || stateCheck.Lookback != 2*time.Hour { t.Fatalf("unexpected state check config: %#v", stateCheck) } } func TestGenerationWorkerOwnerIsBounded(t *testing.T) { owner := generationWorkerOwner(strings.Repeat("x", 256)) if len(owner) > 128 { t.Fatalf("expected owner to be bounded to 128 chars, got %d", len(owner)) } if owner == "" { t.Fatalf("expected non-empty owner") } } func TestGenerationTaskRecoveryFailureMessage(t *testing.T) { message := formatGenerationTaskRecoveryFailure() if !strings.Contains(message, "worker_recovery") { t.Fatalf("expected recovery marker in failure message, got %q", message) } }