Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions tests/e2e/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ coverage instrumentation and runs it as a real subprocess:

- `-format json` — exit 0, nothing on stderr, every stdout line decodes into
`reporter.ProgressEvent` with unknown fields rejected, every `type` is a
known `EventType`, every `timestamp` is RFC3339, and the last event is
`complete`;
known `EventType`, every `timestamp` is RFC3339, the last event is
`complete`, and the full decoded event-type sequence matches an exact
sequence derived from the example's fixed fixture data — a missing, extra,
or reordered intermediate `step`/`progress`/`message`/`warning`/`error`
event fails the run instead of passing on shape and terminal event alone;
- `-format text` — exit 0, non-empty output whose first line is not JSON;
- `-format noop` — exit 0, nothing on stderr, no JSON event lines, and
strictly less output than text mode (the reporter's contribution is gone;
Expand Down
68 changes: 68 additions & 0 deletions tests/e2e/examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,13 @@ func TestExamples(t *testing.T) {
if last := events[len(events)-1]; last.Type != reporter.EventTypeComplete {
t.Errorf("last event type = %q, want %q", last.Type, reporter.EventTypeComplete)
}
got := make([]reporter.EventType, len(events))
for i, ev := range events {
got[i] = ev.Type
}
if want := expectedEventSequence(t, name); !reflect.DeepEqual(got, want) {
t.Errorf("event type sequence = %v, want %v", got, want)
}
})

t.Run("text", func(t *testing.T) {
Expand Down Expand Up @@ -357,6 +364,67 @@ func TestDecodeEventLine(t *testing.T) {
}
}

// expectedEventSequence returns the exact JSON EventType sequence a runnable
// example must emit, derived from its fixed fixture data in
// _examples/<name>/main.go. A missing, extra, or reordered intermediate event
// (Step/Progress/Message/Warning/Error) fails the calling test even when the
// stream still ends on a complete event.
func expectedEventSequence(t *testing.T, name string) []reporter.EventType {
t.Helper()
step, progress, message, warning, errType, complete :=
reporter.EventTypeStep, reporter.EventTypeProgress, reporter.EventTypeMessage,
reporter.EventTypeWarning, reporter.EventTypeError, reporter.EventTypeComplete

switch name {
case "deploy":
// Step 1: Message, Warning. Step 2: Message. Step 3: Message, Warning.
// Step 4: Message. Then Complete.
return []reporter.EventType{
step, message, warning,
step, message,
step, message, warning,
step, message,
complete,
}
case "fileprocess":
// document.pdf, photo.jpg, and report.docx are processed (Message,
// Progress); archive.tar.gz and notes.txt are skipped (Message only).
return []reporter.EventType{
step, message, progress,
step, message, progress,
step, message,
step, message, progress,
step, message,
complete,
}
case "healthcheck":
// database and cache are healthy (Message); API gateway is degraded
// (Warning); message queue is down (Error). MessagePlain before
// Complete decodes as a message event.
return []reporter.EventType{
step, message,
step, message,
step, warning,
step, errType,
message,
complete,
}
case "migration":
// Phase 1: Message. Phase 2: five batches of Progress, with a
// Warning after the third batch's Progress. Phase 3: Message.
return []reporter.EventType{
step, message,
step,
progress, progress, progress, warning, progress, progress,
step, message,
complete,
}
default:
t.Fatalf("expectedEventSequence: no fixed event sequence known for example %q", name)
return nil
}
}

func validEventType(et reporter.EventType) bool {
switch et {
case reporter.EventTypeStep, reporter.EventTypeProgress, reporter.EventTypeMessage,
Expand Down