Honor cancellation before transcript delivery

This commit is contained in:
2026-11-10 20:28:41 +00:00
parent 56d39fca80
commit bbac312b32
4 changed files with 99 additions and 8 deletions

View File

@@ -3,6 +3,7 @@ package cli
import (
"bytes"
"context"
"errors"
"os"
"path/filepath"
"strings"
@@ -127,6 +128,70 @@ func TestDeliveryFailureSuppressesCompletionSummary(t *testing.T) {
}
func TestCanceledEmptyPipelineLeavesTranscriptAndFinalizesReports(t *testing.T) {
dir := t.TempDir()
for name, contents := range map[string]string{
"config.yml": "version: 3\npipeline: {stages: []}\n",
"transcript.json": `[{"speaker":"Alice","start":0,"end":1,"text":"hello"}]`,
"output.json": "previous transcript",
} {
if err := os.WriteFile(filepath.Join(dir, name), []byte(contents), 0600); err != nil {
t.Fatal(err)
}
}
input := processInput{
Config: commandkit.Supplied[string]{Value: filepath.Join(dir, "config.yml"), Set: true},
Transcript: filepath.Join(dir, "transcript.json"),
Output: commandkit.Supplied[string]{Value: filepath.Join(dir, "output.json"), Set: true},
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
cmd := commandkit.NewCommand(commandkit.CommandSpec[struct{}]{Name: "deliver", Run: func(inv commandkit.Invocation, _ struct{}) commandkit.Outcome {
loaded, err := loadProcessInputs(inv.Context, input, inv.ReportingMode())
if err != nil {
t.Fatal(err)
}
prepared, err := prepareProcess(inv.Context, input, loaded, inv.Reporter().Output())
if err != nil {
t.Fatal(err)
}
defer prepared.Prepared.Output.Close()
result, err := executePipeline(inv.Context, loaded, prepared, sourceToCanonicalTranscript(loaded.Source), func(error) {})
if err != nil {
t.Fatal(err)
}
cancel()
return finishProcess(inv, input, loaded, prepared, result.Snapshot, []byte("new transcript"), nil)
}})
app, err := commandkit.New(commandkit.Identity{Name: "test"}, cmd)
if err != nil {
t.Fatal(err)
}
var stdout, stderr bytes.Buffer
outcome := app.Execute(ctx, []string{"deliver", "--verbose"}, commandkit.Streams{Out: &stdout, Err: &stderr})
if outcome.Status == 0 || !errors.Is(outcome.Cause, context.Canceled) || strings.Contains(stderr.String(), "Completed:") {
t.Fatalf("outcome=%+v stderr=%q", outcome, stderr.String())
}
if data, err := os.ReadFile(input.Output.Value); err != nil || string(data) != "previous transcript" {
t.Fatalf("transcript changed after cancellation: %q, %v", data, err)
}
var errorsDoc reporting.ErrorsDocument
var recoveries reporting.RecoveriesDocument
for name, target := range map[string]any{"errors.yml": &errorsDoc, "recoveries.yml": &recoveries} {
data, err := os.ReadFile(filepath.Join(dir, name))
if err != nil {
t.Fatal(err)
}
if err := yaml.Unmarshal(data, target); err != nil {
t.Fatal(err)
}
}
if errorsDoc.Status != reporting.StatusFailed || recoveries.Status != reporting.StatusFailed ||
len(errorsDoc.Errors) != 1 || errorsDoc.Errors[0].Reason != "canceled" {
t.Fatalf("reports did not capture cancellation: %+v %+v", errorsDoc, recoveries)
}
}
type shortProcessWriter struct{}
func (shortProcessWriter) Write(data []byte) (int, error) { return len(data) / 2, nil }