package app import ( "bytes" "context" "io" "reflect" "strings" "testing" "gitea.maximumdirect.net/eric/narratio/internal/config" ) func TestRegenerateArtifactsForwardsCanonicalPipelineRequest(t *testing.T) { workspaceRoot := t.TempDir() pipelinePath, campaignPath, sessionPath := writeValidConfigFilesWithScriptoriumArtifacts(t, workspaceRoot) enableCommandTestOverlays(t, pipelinePath) var capturedPlan BoundedPlan var capturedOptions RunOptions executor := func(_ context.Context, cfg *config.Config, plan BoundedPlan, options RunOptions) (*RunSummary, error) { capturedPlan = plan capturedOptions = options return &RunSummary{SessionID: cfg.Session.SessionID, ManifestPath: "manifest.json"}, nil } code := executeWithPipelineExecutor([]string{ "regenerate-artifacts", "2026-05-03", "--artifacts", "session_recap,player_handout", "--artifacts=player_handout", "--config", pipelinePath, "--campaign-file", campaignPath, "--session", sessionPath, "--overlay", "testing", }, io.Discard, io.Discard, executor) if code != 0 { t.Fatalf("executeWithPipelineExecutor() code = %d, want 0", code) } if got, want := capturedPlan.Names(), []string{"extract", "analyze"}; !reflect.DeepEqual(got, want) { t.Fatalf("plan = %#v, want %#v", got, want) } if !capturedOptions.Force { t.Fatal("force = false, want true") } if got, want := capturedOptions.SelectedArtifacts, []string{"player_handout", "session_recap"}; !reflect.DeepEqual(got, want) { t.Fatalf("selected artifacts = %#v, want %#v", got, want) } } func TestRegenerateArtifactsForwardsSessionIDCompatibilityFlag(t *testing.T) { workspaceRoot := t.TempDir() pipelinePath, campaignPath, sessionPath := writeValidConfigFilesWithScriptoriumArtifacts(t, workspaceRoot) var capturedSessionID string executor := func(_ context.Context, cfg *config.Config, _ BoundedPlan, _ RunOptions) (*RunSummary, error) { capturedSessionID = cfg.Session.SessionID return &RunSummary{SessionID: cfg.Session.SessionID, ManifestPath: "manifest.json"}, nil } code := executeWithPipelineExecutor([]string{ "regenerate-artifacts", "--config", pipelinePath, "--campaign-file", campaignPath, "--session", sessionPath, "--session-id", "2026-05-03", "--artifacts", "session_recap", }, io.Discard, io.Discard, executor) if code != 0 { t.Fatalf("executeWithPipelineExecutor() code = %d, want 0", code) } if capturedSessionID != "2026-05-03" { t.Fatalf("session id = %q, want compatibility flag value", capturedSessionID) } } func TestRegenerateArtifactsHelpDoesNotInvokeRun(t *testing.T) { called := false executor := func(context.Context, *config.Config, BoundedPlan, RunOptions) (*RunSummary, error) { called = true return nil, nil } var stdout bytes.Buffer var stderr bytes.Buffer if code := executeWithPipelineExecutor([]string{"regenerate-artifacts", "--help"}, &stdout, &stderr, executor); code != 0 { t.Fatalf("Execute() code = %d, stderr = %q", code, stderr.String()) } if called { t.Fatal("help invoked canonical run handler") } for _, detail := range []string{ "narratio run --force --from extract --through analyze", "Extraction always runs", "Publish and notify never run", } { if !strings.Contains(stdout.String(), detail) { t.Fatalf("help = %q, want %q", stdout.String(), detail) } } if stderr.Len() != 0 { t.Fatalf("stderr = %q, want empty", stderr.String()) } } func TestRegenerateArtifactsOwnedOptionsFailThroughRunParser(t *testing.T) { for _, test := range []struct { name string args []string owned string }{ {name: "force", args: []string{"--force"}, owned: "force"}, {name: "from", args: []string{"--from=render"}, owned: "from"}, {name: "through", args: []string{"--through", "publish"}, owned: "through"}, } { t.Run(test.name, func(t *testing.T) { args := []string{"regenerate-artifacts", "2026-05-03"} args = append(args, test.args...) var stderr bytes.Buffer if code := Execute(args, io.Discard, &stderr); code == 0 { t.Fatalf("Execute(%#v) code = 0", args) } if !strings.Contains(stderr.String(), "--"+test.owned+" may be specified only once") { t.Fatalf("stderr = %q, want shared duplicate %s error", stderr.String(), test.owned) } }) } } func TestRegenerateArtifactsRejectsUnknownOptionsThroughRunParser(t *testing.T) { var stderr bytes.Buffer if code := Execute([]string{"regenerate-artifacts", "2026-05-03", "--regenerate-only"}, io.Discard, &stderr); code == 0 { t.Fatal("Execute() code = 0") } if !strings.Contains(stderr.String(), "flag provided but not defined") || !strings.Contains(stderr.String(), "regenerate-only") { t.Fatalf("stderr = %q, want canonical parser unknown-option error", stderr.String()) } }