97 lines
4.1 KiB
Go
97 lines
4.1 KiB
Go
package writers
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestExecutionOutcomes(t *testing.T) {
|
|
for _, atomic := range []bool{false, true} {
|
|
for _, continuation := range []bool{false, true} {
|
|
t.Run(strings.Join([]string{map[bool]string{false: "independent", true: "atomic"}[atomic], map[bool]string{false: "stop", true: "continue"}[continuation]}, "/"), func(t *testing.T) {
|
|
attempted, rollbacks := 0, 0
|
|
cause := errors.New("injected failure")
|
|
backend := ExecutionBackend{
|
|
Exec: func(_ context.Context, i int, _ string) error {
|
|
attempted++
|
|
if i == 1 {
|
|
return cause
|
|
}
|
|
return nil
|
|
},
|
|
Begin: func(context.Context) error { return nil }, Commit: func(context.Context) error { t.Fatal("must not commit"); return nil },
|
|
Rollback: func(context.Context) error { rollbacks++; return nil },
|
|
}
|
|
opts := &WriterOptions{ContinueOnError: continuation, AtomicExecution: atomic}
|
|
report := &ExecutionReport{Dialect: "test"}
|
|
err := RunExecution(context.Background(), opts, report, StatementOperations([]string{"one", "bad", "three"}), backend)
|
|
if atomic && continuation {
|
|
if err == nil || attempted != 0 || report.SkippedStatements != 3 {
|
|
t.Fatalf("invalid options must not execute: %+v %v", report, err)
|
|
}
|
|
return
|
|
}
|
|
var failure *ExecutionFailure
|
|
if !errors.As(err, &failure) || !errors.Is(err, cause) {
|
|
t.Fatalf("must preserve typed report and cause: %v", err)
|
|
}
|
|
if report.FailedStatements != 1 || report.TotalStatements != 3 || report.Errors[0].StatementNumber != 2 {
|
|
t.Fatalf("failure report: %+v", report)
|
|
}
|
|
if atomic {
|
|
if rollbacks != 1 || report.ExecutedStatements != 0 || report.RolledBackStatements != 1 || report.Operations[0].Status != "rolled_back" || report.Outcome != "rolled_back" {
|
|
t.Fatalf("rollback report: %+v", report)
|
|
}
|
|
} else if continuation {
|
|
if attempted != 3 || report.ExecutedStatements != 2 || report.SkippedStatements != 0 {
|
|
t.Fatalf("continuation report: %+v", report)
|
|
}
|
|
} else if attempted != 2 || report.ExecutedStatements != 1 || report.SkippedStatements != 1 {
|
|
t.Fatalf("stop report: %+v", report)
|
|
}
|
|
if opts.Metadata["execution_report"] != report {
|
|
t.Fatal("typed results missing")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestExecutionUncertainFinalization(t *testing.T) {
|
|
for _, failStatement := range []bool{false, true} {
|
|
t.Run(map[bool]string{false: "commit", true: "rollback"}[failStatement], func(t *testing.T) {
|
|
finalErr := errors.New("connection lost during finalization")
|
|
backend := ExecutionBackend{
|
|
Begin: func(context.Context) error { return nil },
|
|
Exec: func(_ context.Context, i int, _ string) error {
|
|
if failStatement && i == 1 {
|
|
return errors.New("statement failed")
|
|
}
|
|
return nil
|
|
},
|
|
Commit: func(context.Context) error { return finalErr }, Rollback: func(context.Context) error { return finalErr },
|
|
}
|
|
report := &ExecutionReport{}
|
|
err := RunExecution(context.Background(), &WriterOptions{AtomicExecution: true}, report, StatementOperations([]string{"one", "two"}), backend)
|
|
if !errors.Is(err, finalErr) || report.Outcome != "unknown" || report.Operations[0].Status != "unknown" || report.RolledBackStatements != 0 || report.ExecutedStatements != 0 {
|
|
t.Fatalf("uncertain outcome misreported: %+v %v", report, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestExecutionReportFailureAndFatalPreflight(t *testing.T) {
|
|
opts := &WriterOptions{ContinueOnError: true, Metadata: map[string]interface{}{"report_path": filepath.Join(t.TempDir(), "missing", "report.json")}}
|
|
report := &ExecutionReport{}
|
|
calls := 0
|
|
ops := []ExecutionOperation{{Statement: "preflight", StopOnFailure: true}, {Statement: "must not run"}}
|
|
err := RunExecution(context.Background(), opts, report, ops, ExecutionBackend{Exec: func(context.Context, int, string) error { calls++; return errors.New("invalid data") }})
|
|
var failure *ExecutionFailure
|
|
if !errors.As(err, &failure) || !strings.Contains(err.Error(), "requested execution report") || calls != 1 || report.SkippedStatements != 1 {
|
|
t.Fatalf("must retain both failures and stop: %+v %v", report, err)
|
|
}
|
|
}
|