package app import ( "reflect" "testing" "time" "gitea.maximumdirect.net/eric/narratio/internal/manifest" ) func TestFirstNonSucceededIndex(t *testing.T) { stages := BuildFullPlan() m := manifest.New("2026-05-03", time.Now().UTC()) m.MarkStageSucceeded("prepare", time.Now().UTC(), nil) m.MarkStageSucceeded("transcribe", time.Now().UTC(), nil) got := firstNonSucceededIndex(stages, m) if got != 2 { t.Fatalf("firstNonSucceededIndex() = %d, want 2", got) } } func TestDecideStageActions(t *testing.T) { stages := BuildFullPlan()[:2] m := manifest.New("2026-05-03", time.Now().UTC()) m.MarkStageSucceeded("prepare", time.Now().UTC(), nil) got := decideStageActions(stages, m, false) if len(got) != 2 { t.Fatalf("len(decisions) = %d, want 2", len(got)) } if got[0].Action != stageActionSkip { t.Fatalf("prepare action = %q, want %q", got[0].Action, stageActionSkip) } if got[1].Action != stageActionRun { t.Fatalf("transcribe action = %q, want %q", got[1].Action, stageActionRun) } forced := decideStageActions(stages, m, true) if forced[0].Action != stageActionRun { t.Fatalf("forced prepare action = %q, want %q", forced[0].Action, stageActionRun) } } func TestDownstreamStageNames(t *testing.T) { got := downstreamStageNames("polish") want := []string{"normalize", "trim", "analyze", "archive", "notify"} if !reflect.DeepEqual(got, want) { t.Fatalf("downstreamStageNames(polish) = %#v, want %#v", got, want) } missing := downstreamStageNames("unknown") if len(missing) != 0 { t.Fatalf("downstreamStageNames(unknown) = %#v, want empty", missing) } } func TestInvalidateDownstreamSucceededStages(t *testing.T) { now := time.Now().UTC() m := manifest.New("2026-05-03", now) m.MarkStageSucceeded("prepare", now, nil) m.MarkStageSucceeded("transcribe", now, nil) m.MarkStageSucceeded("merge", now, nil) m.MarkStageSucceeded("polish", now, nil) m.MarkStageSucceeded("normalize", now, nil) m.MarkStageSucceeded("trim", now, nil) m.MarkStageFailed("analyze", now, "analysis failed") m.MarkStageSucceeded("archive", now, nil) m.MarkStageSucceeded("notify", now, nil) got := invalidateDownstreamSucceededStages(m, "polish", now.Add(1*time.Second)) want := []string{"normalize", "trim", "archive", "notify"} if !reflect.DeepEqual(got, want) { t.Fatalf("invalidateDownstreamSucceededStages() = %#v, want %#v", got, want) } for _, stageName := range want { if m.Stages[stageName].Status != manifest.StatusStale { t.Fatalf("%s status = %q, want stale", stageName, m.Stages[stageName].Status) } } if m.Stages["analyze"].Status != manifest.StatusFailed { t.Fatalf("analyze status = %q, want failed", m.Stages["analyze"].Status) } if m.Stages["prepare"].Status != manifest.StatusSucceeded { t.Fatalf("prepare status = %q, want succeeded", m.Stages["prepare"].Status) } }