From ae84fe23f96ed5041680369442f1aecc62b23393 Mon Sep 17 00:00:00 2001 From: Jay Pipes Date: Sun, 18 Feb 2024 18:27:21 -0500 Subject: [PATCH 1/2] exec: fix debug handler For the exec plugin, we were writing debug output even when the user had not called `gdtcontext.WithDebug()`. In addition, stdout and stderr contents were only being written to debug writers when executed during `on.fail.exec`. These two problems are fixed in this commit in addition to correcting an erroneous `$LOCATION` in the README.md that needed to be `$$LOCATION`. Signed-off-by: Jay Pipes --- README.md | 2 +- debug/print.go | 10 ++-------- plugin/exec/action.go | 6 ++++++ plugin/exec/eval.go | 10 ---------- plugin/exec/eval_test.go | 2 ++ scenario/run_test.go | 13 ++++++++++--- 6 files changed, 21 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 62e8eeb..016933a 100644 --- a/README.md +++ b/README.md @@ -402,7 +402,7 @@ tests: headers: - Location - name: look up that created book - GET: $LOCATION + GET: $$LOCATION response: status: 200 json: diff --git a/debug/print.go b/debug/print.go index c26c2bf..87567de 100644 --- a/debug/print.go +++ b/debug/print.go @@ -23,13 +23,10 @@ func Printf( ) { t.Helper() writers := gdtcontext.Debug(ctx) - if writers == nil { - return - } - t.Logf(format, args...) if len(writers) == 0 { return } + t.Logf(format, args...) if !strings.HasPrefix(format, "[gdt] ") { format = "[gdt] " + t.Name() + " " + format @@ -50,14 +47,11 @@ func Println( ) { t.Helper() writers := gdtcontext.Debug(ctx) - if writers == nil { + if len(writers) == 0 { return } // NOTE(jaypipes): T.Logf() automatically adds newlines... t.Logf(format, args...) - if len(writers) == 0 { - return - } if !strings.HasPrefix(format, "[gdt] ") { format = "[gdt] " + t.Name() + " " + format diff --git a/plugin/exec/action.go b/plugin/exec/action.go index 9884972..cadd4b8 100644 --- a/plugin/exec/action.go +++ b/plugin/exec/action.go @@ -79,9 +79,15 @@ func (a *Action) Do( } if outbuf != nil { outbuf.ReadFrom(outpipe) + if outbuf.Len() > 0 { + debug.Println(ctx, t, "exec: stdout: %s", outbuf.String()) + } } if errbuf != nil { errbuf.ReadFrom(errpipe) + if errbuf.Len() > 0 { + debug.Println(ctx, t, "exec: stderr: %s", errbuf.String()) + } } err = cmd.Wait() diff --git a/plugin/exec/eval.go b/plugin/exec/eval.go index 243680f..34c7fd7 100644 --- a/plugin/exec/eval.go +++ b/plugin/exec/eval.go @@ -42,16 +42,6 @@ func (s *Spec) Eval(ctx context.Context, t *testing.T) *result.Result { if err != nil { debug.Println(ctx, t, "error in on.fail.exec: %s", err) } - if outbuf.Len() > 0 { - debug.Println( - ctx, t, "on.fail.exec: stdout: %s", outbuf.String(), - ) - } - if errbuf.Len() > 0 { - debug.Println( - ctx, t, "on.fail.exec: stderr: %s", errbuf.String(), - ) - } } } } diff --git a/plugin/exec/eval_test.go b/plugin/exec/eval_test.go index b598aaf..4fc024a 100644 --- a/plugin/exec/eval_test.go +++ b/plugin/exec/eval_test.go @@ -199,7 +199,9 @@ func TestDebugWriter(t *testing.T) { require.NotEqual(b.Len(), 0) debugout := b.String() require.Contains(debugout, "exec: echo [cat]") + require.Contains(debugout, "exec: stdout: cat") require.Contains(debugout, "exec: sh [-c echo cat 1>&2]") + require.Contains(debugout, "exec: stderr: cat") } func TestWait(t *testing.T) { diff --git a/scenario/run_test.go b/scenario/run_test.go index a114602..722313e 100644 --- a/scenario/run_test.go +++ b/scenario/run_test.go @@ -5,6 +5,8 @@ package scenario_test import ( + "bufio" + "bytes" "context" "os" "path/filepath" @@ -72,9 +74,9 @@ func TestDebugFlushing(t *testing.T) { f, err := os.Open(fp) require.Nil(err) - ctx := gdtcontext.New( - gdtcontext.WithDebug(), - ) + var b bytes.Buffer + w := bufio.NewWriter(&b) + ctx := gdtcontext.New(gdtcontext.WithDebug(w)) s, err := scenario.FromReader(f, scenario.WithPath(fp)) require.Nil(err) @@ -82,6 +84,11 @@ func TestDebugFlushing(t *testing.T) { err = s.Run(ctx, t) require.Nil(err) + require.False(t.Failed()) + w.Flush() + require.NotEqual(b.Len(), 0) + debugout := b.String() + require.Contains(debugout, "TestDebugFlushing/foo-debug-wait-flush wait: 250ms before") } func TestTimeoutCascade(t *testing.T) { From b7300c5a4bcf7db92219c480ed546474aa175059 Mon Sep 17 00:00:00 2001 From: Jay Pipes Date: Mon, 27 May 2024 08:20:03 -0400 Subject: [PATCH 2/2] add context.Context arg to assertions.OK() **BREAKING API CHANGE for plugin developers** This changes the `gdt.Assertions.OK()` interface signature to accept a `context.Context` object. This context object is required for debug output/inspection in the kube plugin. Signed-off-by: Jay Pipes --- assertion/json/json.go | 3 ++- assertion/json/json_test.go | 28 ++++++++++++++++++---------- plugin/exec/assertions.go | 9 +++++---- plugin/exec/eval.go | 2 +- types/assertions.go | 4 +++- 5 files changed, 29 insertions(+), 17 deletions(-) diff --git a/assertion/json/json.go b/assertion/json/json.go index b614c21..7a60584 100644 --- a/assertion/json/json.go +++ b/assertion/json/json.go @@ -5,6 +5,7 @@ package json import ( + "context" "encoding/json" "fmt" "os" @@ -170,7 +171,7 @@ func (a *assertions) Failures() []error { } // OK returns true if all contained assertions pass successfully -func (a *assertions) OK() bool { +func (a *assertions) OK(ctx context.Context) bool { if a == nil || a.exp == nil { return true } diff --git a/assertion/json/json_test.go b/assertion/json/json_test.go index f914568..3eed0e2 100644 --- a/assertion/json/json_test.go +++ b/assertion/json/json_test.go @@ -5,6 +5,7 @@ package json_test import ( + "context" "io/ioutil" "path/filepath" "testing" @@ -98,6 +99,7 @@ func content() []byte { func TestLength(t *testing.T) { require := require.New(t) + ctx := context.TODO() c := content() expLen := len(c) @@ -106,12 +108,12 @@ func TestLength(t *testing.T) { } a := gdtjson.New(&exp, c) - require.True(a.OK()) + require.True(a.OK(ctx)) require.Empty(a.Failures()) expLen = 0 a = gdtjson.New(&exp, c) - require.False(a.OK()) + require.False(a.OK(ctx)) failures := a.Failures() require.Len(failures, 1) require.ErrorIs(failures[0], gdterrors.ErrNotEqual) @@ -120,6 +122,7 @@ func TestLength(t *testing.T) { func TestJSONUnmarshalError(t *testing.T) { require := require.New(t) + ctx := context.TODO() c := []byte(`not { value } json`) exp := gdtjson.Expect{ @@ -129,7 +132,7 @@ func TestJSONUnmarshalError(t *testing.T) { } a := gdtjson.New(&exp, c) - require.False(a.OK()) + require.False(a.OK(ctx)) failures := a.Failures() require.Len(failures, 1) require.ErrorIs(failures[0], gdtjson.ErrJSONUnmarshalError) @@ -138,6 +141,7 @@ func TestJSONUnmarshalError(t *testing.T) { func TestJSONPathError(t *testing.T) { require := require.New(t) + ctx := context.TODO() c := content() exp := gdtjson.Expect{ @@ -147,7 +151,7 @@ func TestJSONPathError(t *testing.T) { } a := gdtjson.New(&exp, c) - require.False(a.OK()) + require.False(a.OK(ctx)) failures := a.Failures() require.Len(failures, 1) require.ErrorIs(failures[0], gdtjson.ErrJSONPathNotFound) @@ -156,6 +160,7 @@ func TestJSONPathError(t *testing.T) { func TestJSONPathConversionError(t *testing.T) { require := require.New(t) + ctx := context.TODO() c := content() exp := gdtjson.Expect{ @@ -165,7 +170,7 @@ func TestJSONPathConversionError(t *testing.T) { } a := gdtjson.New(&exp, c) - require.False(a.OK()) + require.False(a.OK(ctx)) failures := a.Failures() require.Len(failures, 1) require.ErrorIs(failures[0], gdtjson.ErrJSONPathConversionError) @@ -174,6 +179,7 @@ func TestJSONPathConversionError(t *testing.T) { func TestJSONPathNotEqual(t *testing.T) { require := require.New(t) + ctx := context.TODO() c := content() exp := gdtjson.Expect{ @@ -183,7 +189,7 @@ func TestJSONPathNotEqual(t *testing.T) { } a := gdtjson.New(&exp, c) - require.True(a.OK()) + require.True(a.OK(ctx)) require.Empty(a.Failures()) exp = gdtjson.Expect{ @@ -193,7 +199,7 @@ func TestJSONPathNotEqual(t *testing.T) { } a = gdtjson.New(&exp, c) - require.False(a.OK()) + require.False(a.OK(ctx)) failures := a.Failures() require.Len(failures, 1) require.ErrorIs(failures[0], gdtjson.ErrJSONPathNotEqual) @@ -202,6 +208,7 @@ func TestJSONPathNotEqual(t *testing.T) { func TestJSONPathFormatNotFound(t *testing.T) { require := require.New(t) + ctx := context.TODO() c := content() exp := gdtjson.Expect{ @@ -211,7 +218,7 @@ func TestJSONPathFormatNotFound(t *testing.T) { } a := gdtjson.New(&exp, c) - require.False(a.OK()) + require.False(a.OK(ctx)) failures := a.Failures() require.Len(failures, 1) require.ErrorIs(failures[0], gdtjson.ErrJSONPathNotFound) @@ -220,6 +227,7 @@ func TestJSONPathFormatNotFound(t *testing.T) { func TestJSONPathFormatNotEqual(t *testing.T) { require := require.New(t) + ctx := context.TODO() c := content() exp := gdtjson.Expect{ @@ -229,7 +237,7 @@ func TestJSONPathFormatNotEqual(t *testing.T) { } a := gdtjson.New(&exp, c) - require.True(a.OK()) + require.True(a.OK(ctx)) require.Empty(a.Failures()) exp = gdtjson.Expect{ @@ -239,7 +247,7 @@ func TestJSONPathFormatNotEqual(t *testing.T) { } a = gdtjson.New(&exp, c) - require.False(a.OK()) + require.False(a.OK(ctx)) failures := a.Failures() require.Len(failures, 1) require.ErrorIs(failures[0], gdtjson.ErrJSONFormatNotEqual) diff --git a/plugin/exec/assertions.go b/plugin/exec/assertions.go index 330b34c..b72553a 100644 --- a/plugin/exec/assertions.go +++ b/plugin/exec/assertions.go @@ -6,6 +6,7 @@ package exec import ( "bytes" + "context" "strings" "github.com/gdt-dev/gdt/errors" @@ -76,7 +77,7 @@ func (a *pipeAssertions) Terminal() bool { // OK checks all the assertions in the pipeAssertions against the supplied pipe // contents and returns true if all assertions pass. -func (a *pipeAssertions) OK() bool { +func (a *pipeAssertions) OK(ctx context.Context) bool { if a == nil || a.pipe == nil { return true } @@ -167,17 +168,17 @@ func (a *assertions) Terminal() bool { // OK checks all the assertions against the supplied arguments and returns true // if all assertions pass. -func (a *assertions) OK() bool { +func (a *assertions) OK(ctx context.Context) bool { res := true if a.expExitCode != a.exitCode { a.Fail(errors.NotEqual(a.expExitCode, a.exitCode)) res = false } - if !a.expOutPipe.OK() { + if !a.expOutPipe.OK(ctx) { a.failures = append(a.failures, a.expOutPipe.Failures()...) res = false } - if !a.expErrPipe.OK() { + if !a.expErrPipe.OK(ctx) { a.failures = append(a.failures, a.expErrPipe.Failures()...) res = false } diff --git a/plugin/exec/eval.go b/plugin/exec/eval.go index 34c7fd7..565ab16 100644 --- a/plugin/exec/eval.go +++ b/plugin/exec/eval.go @@ -30,7 +30,7 @@ func (s *Spec) Eval(ctx context.Context, t *testing.T) *result.Result { return result.New(result.WithRuntimeError(ExecRuntimeError(err))) } a := newAssertions(s.Assert, ec, outbuf, errbuf) - if !a.OK() { + if !a.OK(ctx) { for _, fail := range a.Failures() { t.Error(fail) } diff --git a/types/assertions.go b/types/assertions.go index dbb51f7..510d436 100644 --- a/types/assertions.go +++ b/types/assertions.go @@ -4,12 +4,14 @@ package types +import "context" + // Assertions track zero or more assertions about some result type Assertions interface { // OK returns true if all contained assertions pass successfully, false // otherwise. If false is returned, Failures() is guaranteed to be // non-empty. - OK() bool + OK(context.Context) bool // Fail appends a supplied error to the set of failed assertions Fail(error) // Failures returns a slice of failure messages indicating which assertions